尊重原著:
Unity中AB包详解(超详细,特性,打包,加载,管理器)_unity ab包_窗外听轩雨的博客-CSDN博客
1.调用示例
using Common;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartGet : MonoBehaviour
{
async void Start()
{
//模型加载
ABManager.Instance.LoadResourceAsync("m", "Cube02.prefab", (obj) =>
{
Debug.Log("obj.name=" + obj.name);
GameObject.Instantiate(obj);
});
//场景加载
var ab = ABManager.Instance.LoadABPackage("init");
SceneManager.LoadSceneAsync(ab.name);
}
}
2.AB包管理器
using System;
using System.Net.Mime;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Common
{
/// <summary>
/// AB包管理器 全局唯一 使用单例模式
/// </summary>
public class ABManager : MonoSingleton<ABManager>
{
//AB包缓存---解决AB包无法重复加载的问题 也有利于提高效率。
private Dictionary<string, AssetBundle> abCache;
private AssetBundle mainAB = null; //主包
private AssetBundleManifest mainManifest = null; //主包中配置文件---用以获取依赖包
//各个平台下的基础路径 --- 利用宏判断当前平台下的streamingAssets路径
private string basePath
{
get
{
//使用StreamingAssets路径注意AB包打包时 勾选copy to streamingAssets
#if UNITY_EDITOR || UNITY_STANDALONE
return Application.dataPath + "/StreamingAssets/";
#elif UNITY_IPHONE
return Application.dataPath + "/Raw/";
#elif UNITY_ANDROID
return Application.dataPath + "!/assets/";
#endif
}
}
//各个平台下的主包名称 --- 用以加载主包获取依赖信息
private string mainABName
{
get
{
#if UNITY_EDITOR || UNITY_STANDALONE
return "StandaloneWindows";
#elif UNITY_IPHONE
return "IOS";
#elif UNITY_ANDROID
return "Android";
#endif
}
}
//继承了单例模式提供的初始化函数
protected override void Init()
{
base.Init();
//初始化字典
abCache = new Dictionary<string, AssetBundle>();
}
//加载AB包
private AssetBundle LoadABPackage(string abName)
{
AssetBundle ab;
//加载ab包,需一并加载其依赖包。
if (mainAB == null)
{
//根据各个平台下的基础路径和主包名加载主包
mainAB = AssetBundle.LoadFromFile(basePath + mainABName);
//获取主包下的AssetBundleManifest资源文件(存有依赖信息)
mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
//根据manifest获取所有依赖包的名称 固定API
string[] dependencies = mainManifest.GetAllDependencies(abName);
//循环加载所有依赖包
for (int i = 0; i < dependencies.Length; i++)
{
//如果不在缓存则加入
if (!abCache.ContainsKey(dependencies[i]))
{
//根据依赖包名称进行加载
ab = AssetBundle.LoadFromFile(basePath + dependencies[i]);
//注意添加进缓存 防止重复加载AB包
abCache.Add(dependencies[i], ab);
}
}
//加载目标包 -- 同理注意缓存问题
if (abCache.ContainsKey(abName)) return abCache[abName];
else
{
ab = AssetBundle.LoadFromFile(basePath + abName);
abCache.Add(abName, ab);
return ab;
}
}
//==================三种资源同步加载方式==================
//提供多种调用方式 便于其它语言的调用(Lua对泛型支持不好)
#region 同步加载的三个重载
/// <summary>
/// 同步加载资源---泛型加载 简单直观 无需显示转换
/// </summary>
/// <param name="abName">ab包的名称</param>
/// <param name="resName">资源名称</param>
public T LoadResource<T>(string abName, string resName) where T : Object
{
//加载目标包
AssetBundle ab = LoadABPackage(abName);
//返回资源
return ab.LoadAsset<T>(resName);
}
//不指定类型 有重名情况下不建议使用 使用时需显示转换类型
public Object LoadResource(string abName, string resName)
{
//加载目标包
AssetBundle ab = LoadABPackage(abName);
//返回资源
return ab.LoadAsset(resName);
}
//利用参数传递类型,适合对泛型不支持的语言调用,使用时需强转类型
public Object LoadResource(string abName, string resName, System.Type type)
{
//加载目标包
AssetBundle ab = LoadABPackage(abName);
//返回资源
return ab.LoadAsset(resName, type);
}
#endregion
//================三种资源异步加载方式======================
/// <summary>
/// 提供异步加载----注意 这里加载AB包是同步加载,只是加载资源是异步
/// </summary>
/// <param name="abName">AssetBundle包名称</param>
/// <param name="resName">资源名称如:Cube.prefab</param>
public void LoadResourceAsync(string abName, string resName, System.Action<Object> finishLoadObjectHandler)
{
AssetBundle ab = LoadABPackage(abName);
//开启协程 提供资源加载成功后的委托
StartCoroutine(LoadRes(ab, resName, finishLoadObjectHandler));
}
private IEnumerator LoadRes(AssetBundle ab, string resName, System.Action<Object> finishLoadObjectHandler)
{
if (ab == null) yield break;
//异步加载资源API
AssetBundleRequest abr = ab.LoadAssetAsync(resName);
yield return abr;
//委托调用处理逻辑
finishLoadObjectHandler(abr.asset);
}
//根据Type异步加载资源
public void LoadResourceAsync(string abName, string resName, System.Type type, System.Action<Object> finishLoadObjectHandler)
{
AssetBundle ab = LoadABPackage(abName);
StartCoroutine(LoadRes(ab, resName, type, finishLoadObjectHandler));
}
private IEnumerator LoadRes(AssetBundle ab, string resName, System.Type type, System.Action<Object> finishLoadObjectHandler)
{
if (ab == null) yield break;
AssetBundleRequest abr = ab.LoadAssetAsync(resName, type);
yield return abr;
//委托调用处理逻辑
finishLoadObjectHandler(abr.asset);
}
//泛型加载
public void LoadResourceAsync<T>(string abName, string resName, System.Action<Object> finishLoadObjectHandler) where T : Object
{
AssetBundle ab = LoadABPackage(abName);
StartCoroutine(LoadRes<T>(ab, resName, finishLoadObjectHandler));
}
private IEnumerator LoadRes<T>(AssetBundle ab, string resName, System.Action<Object> finishLoadObjectHandler) where T : Object
{
if (ab == null) yield break;
AssetBundleRequest abr = ab.LoadAssetAsync<T>(resName);
yield return abr;
//委托调用处理逻辑
finishLoadObjectHandler(abr.asset as T);
}
//====================AB包的两种卸载方式=================
//单个包卸载
public void UnLoad(string abName)
{
if (abCache.ContainsKey(abName))
{
abCache[abName].Unload(false);
//注意缓存需一并移除
abCache.Remove(abName);
}
}
//所有包卸载
public void UnLoadAll()
{
AssetBundle.UnloadAllAssetBundles(false);
//注意清空缓存
abCache.Clear();
mainAB = null;
mainManifest = null;
}
}
}
3.单列
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Common
{
///<summary>
///脚本单例类,负责为唯一脚本创建实例
///<summary>
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> //注意此约束为T必须为其本身或子类
{
/*
相较于直接在需要唯一创建的脚本中创建实例,Awake初始化的过程需要解决的问题
1.代码重复
2.在Awake里面初始化,其它脚本在Awake中调用其可能会为Null的异常情况
*/
//解决1:使用泛型创建实例 解决2:使用按需加载(即有其它脚本调用时在get中加载)
private static T instance; //创建私有对象记录取值,可只赋值一次避免多次赋值
public static T Instance
{
//实现按需加载
get
{
//当已经赋值,则直接返回即可
if (instance != null) return instance;
instance = FindObjectOfType<T>();
//为了防止脚本还未挂到物体上,找不到的异常情况,可以自行创建空物体挂上去
if (instance == null)
{
//如果创建对象,则会在创建时调用其身上脚本的Awake即调用T的Awake(T的Awake实际上是继承的父类的)
//所以此时无需为instance赋值,其会在Awake中赋值,自然也会初始化所以无需init()
/*instance = */
new GameObject("Singleton of " + typeof(T)).AddComponent<T>();
}
else instance.Init(); //保证Init只执行一次
return instance;
}
}
private void Awake()
{
//若无其它脚本在Awake中调用此实例,则可在Awake中自行初始化instance
instance = this as T;
//初始化
Init();
}
//子类对成员进行初始化如果放在Awake里仍会出现Null问题所以自行制作一个init函数解决(可用可不用)
protected virtual void Init()
{
}
}
}