上篇文章说到了 bundle的打包加载等一些功能
上篇文章只做了本地同步加载资源,对于Android和iOS平台则是需要吧bundle资源放入到 StreamingAssets 打入 包体内才行, 对于有些项目是想要吧资源放到远程的资源服务器上,等需要用的时候在下载到本地,这里在做一篇文章说明
1 部署资源服务器
所谓资源服,就是可以吧我们的bundle资源上传上去,通过web链接可以直接下载,例如阿里云的OSS服务,这需要我们项目正式上线才用得到,至于开发或学的时候,我们搞一个本地的局域网服务器就行了,可以做这个事情的软件有很多,我就不一一介绍了,我用的软件是 nginx ,用法这里就不过多介绍了,自行查询资料吧
2 打包/上传资源
打包就不多说了,与上篇文章是一样的,只是需要吧打包好的资源放到资源服上去 ,为了方便管理,资源目录结构需与本地一致(web链接除外)
我这里运行nginx之后,texture的bundle目录就是 http://192.168.1.56/AssetBundle/Windows/texture
吧目录复制到浏览器的网址页 如果能成功下载到资源,则证明我们的资源服搭建成功了
.
3 资源下载
目录新版本的Unity已经基本舍弃了www类,推荐采用 UnityWebRequest 类下载,我这里选用的策略是下载bundle资源,放入内存,在同步加载资源,根据使用情况释放内存
看代码吧
字段 manifest储存、bundle缓存列表
/// <summary>
/// 它保存了各个AssetBundle的依赖信息
/// </summary>
private AssetBundleManifest abManifestAsync;
private Dictionary<string, LoadedAssetBundle> abCacheAsync = new Dictionary<string, LoadedAssetBundle>();
函数:获取远程资源根目录
//获取bundle远程目录
public static string GetBundleRemotePath()
{
string path = $"http://192.168.1.56/AssetBundle/{GetPlatformName()}";
return path;
}
函数:加载 manifest
/// <summary>
/// 加载Manifest文件 异步
/// </summary>
/// <returns></returns>
private async Task LoadManifestAsync()
{
if (abManifestAsync == null)
{
string url = $"{GetBundleRemotePath()}/{GetPlatformName()}";
UnityWebRequest www = UnityWebRequest.Get(url);
UnityWebRequestAsyncOperation asyncOperation = www.SendWebRequest();
while (!asyncOperation.isDone)
await Task.Yield();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Download failed: " + www.error);
}
else
{
AssetBundle ab = AssetBundle.LoadFromMemory(www.downloadHandler.data);
abManifestAsync = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
}
}
函数: 加载远程bundle
/// <summary>
/// 加载AssetsBundle 异步
/// </summary>
/// <param name="abName">bundle名称</param>
/// <returns></returns>
private async Task<LoadedAssetBundle> LoadAssetBundleAsync(string abName)
{
LoadedAssetBundle lab = null;
if (!abCacheAsync.ContainsKey(abName))
{
string url = $"{GetBundleRemotePath()}/{abName}";
UnityWebRequest www = UnityWebRequest.Get(url);
UnityWebRequestAsyncOperation asyncOperation = www.SendWebRequest();
while (!asyncOperation.isDone)
await Task.Yield();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Download failed: " + www.error);
}
else
{
AssetBundle ab = AssetBundle.LoadFromMemory(www.downloadHandler.data);
lab = new LoadedAssetBundle(ab);
abCacheAsync.Add(abName, lab);
}
}
else
{
lab = abCacheAsync[abName];
}
lab.AddReferenced();
return lab;
}
函数:加载依赖项
/// <summary>
/// 加载依赖项 异步
/// </summary>
/// <param name="abName"></param>
/// <returns></returns>
private async Task<List<string>> LoadAssetBundleDependAsync(string abName)
{
await LoadManifestAsync();
List<string> dependNames = new List<string>();
string[] dependences = abManifestAsync.GetAllDependencies(abName);
if (dependences.Length > 0)
{
foreach (var item in dependences)
{
LoadedAssetBundle lab = await LoadAssetBundleAsync(item);
dependNames.Add(lab.GetName());
}
}
return dependNames;
}
公开函数:实例化Prefab
/// <summary>
/// 加载prefab 并实例化它 异步
/// </summary>
/// <param name="assetbundleName">AssetBundle名称</param>
/// <param name="assetName">资源名称</param>
/// <returns></returns>
public async Task<GameObject> InstantiatePrefabAsync(string assetbundleName, string assetName)
{
assetbundleName = assetbundleName.ToLower();
assetName = assetName.ToLower();
LoadedAssetBundle lab = await LoadAssetBundleAsync(assetbundleName);
List<string> dependNames = await LoadAssetBundleDependAsync(assetbundleName);
GameObject prefab = lab.GetAsset<GameObject>(assetName);
GameObject go = GameObject.Instantiate(prefab);
AssetWatcher watcher = go.AddComponent<AssetWatcher>();
watcher.Add(lab.GetName());
watcher.Add(dependNames);
return go;
}
公开函数:加载资源
/// <summary>
/// 加载资源 异步
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assetbundleName">AssetBundle名称</param>
/// <param name="assetName">资源名称</param>
/// <returns></returns>
public async Task<T> LoadAssetAsync<T>(string assetbundleName, string assetName, GameObject watcherGo) where T : Object
{
if (watcherGo == null)
{
Debug.LogError("加载资源必须绑定GameObject,用于检测卸载资源");
return null;
}
assetbundleName = assetbundleName.ToLower();
assetName = assetName.ToLower();
AssetWatcher watcher = watcherGo.GetComponent<AssetWatcher>();
if (watcher == null)
{
watcher = watcherGo.AddComponent<AssetWatcher>();
}
LoadedAssetBundle lab = await LoadAssetBundleAsync(assetbundleName);
List<string> dependNames = await LoadAssetBundleDependAsync(assetbundleName);
watcher.Add(lab.GetName());
watcher.Add(dependNames);
T t = lab.GetAsset<T>(assetName);
return t;
}
公开函数:卸载资源
/// <summary>
/// 卸载bundle
/// </summary>
public void UnloadAssetBundle(string abName)
{
if (abCacheAsync.TryGetValue(abName, out LoadedAssetBundle lab2))
{
lab2.RemReferenced();
if (!lab2.CheckHaveReferenced())
{
lab2.Unload();
abCacheAsync.Remove(abName);
}
}
}
OK 至此使用远程资源的功能就完成了,下面使用测试脚本测试下吧
using UnityEngine;
public class Test : MonoBehaviour
{
private async void OnGUI()
{
if (GUILayout.Button("LoadCube Async"))
{
LoadCube();
}
if (GUILayout.Button("LoadSphere Async"))
{
LoadSphere();
}
if (GUILayout.Button("LoadCapsule Async"))
{
LoadCapsule();
}
}
async void LoadCube()
{
await ResourcesManager.Instance.InstantiatePrefabAsync("prefab/cube", "cube");
}
async void LoadSphere()
{
await ResourcesManager.Instance.InstantiatePrefabAsync("prefab/Sphere", "Sphere");
}
async void LoadCapsule()
{
GameObject Capsule = await ResourcesManager.Instance.InstantiatePrefabAsync("prefab/Capsule", "Capsule");
Material mat = await ResourcesManager.Instance.LoadAssetAsync<Material>("material/SphereMaterial2", "SphereMaterial2", Capsule);
Capsule.GetComponent<MeshRenderer>().material = mat;
}
}
代码直接拿去会有报错,请配合上篇文章的代码看