上一篇讲到了如何打包assetsbundle,紧跟而来的就是加载assetsbundle的问题。资源的动态的加载用到Resources和AssetsBundle这两种方式,而后一种方式无论是在热更还是大小,安全等方面更具优势。下面要贴出作者一直在用的资源管理方式。
大概是利用枚举区分这两种方式,在unity编辑器里可以随意切换,打包发布时也可以根据自己的需求切换。
利用assetsbundle加载资源时,需要在启动游戏的时候,将所需的assetsbundle加载进内存,再通过具体方法加载assets bundle的资源。
这里我设置了一个最大资源加载数的选项, 因为游戏要加载的,动态生成的prefab等往往不会频繁使用,通过queue队列的方式,可以有效减少动态加载量很少的资源,常驻内存的情况。
废话不多说了,贴两张效果图
下面贴一下代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
using LitJson;
using System.Linq;
enum LoadAssetsType
{
AssetsBundle = 0,
Resources = 1
}
public class AssetsManager : MonoBehaviour
{
///
/// 单例
///
private static AssetsManager _instance;
[SerializeField]
[Tooltip("加载资源的方式")]
private LoadAssetsType _loadAssetsType;
[SerializeField]
[Tooltip("最大资源缓存数")]
private int _maxResourcesCount = 30;
///
/// 数据字典
///
private Dictionary _dicAssets = new Dictionary();
///
/// 资源key值的队列
///
private Queue _assetsName = new Queue();
///
/// AssetBundle字典集合
///
private Dictionary _dicAssetBundles = new Dictionary();
private List _AssetsList = new List();
//不同平台下StreamingAssets的路径是不同的,这里需要注意一下。
private static string _streamingAssetsPath;
private static string _resourcesPath;
//配置文件的路径
private static string _configPath;
private void Awake()
{
SetResourcesPath();
StartCoroutine(Initassetsbundle());
}
private static void SetResourcesPath()
{
_streamingAssetsPath =
#if UNITY_ANDROID
"jar:file://" + Application.dataPath + "!/assets/AssetsBundle/";
#elif UNITY_IOS
Application.dataPath + "/Raw/AssetsBundle/";
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
"file://" + Application.dataPath + "/StreamingAssets/AssetsBundle/";
#else
string.Empty;
#endif
if (Application.platform == RuntimePlatform.Android)
{
_resourcesPath = _streamingAssetsPath + "Android/";
}
else if (Application.platform == RuntimePlatform.IPhonePlayer)
{
_resourcesPath = _streamingAssetsPath + "IOS/";
}
else
{
_resourcesPath = _streamingAssetsPath + "Windows/";
}
_configPath = _streamingAssetsPath + "AssetsBundleConfig.Config";
}
///
/// 单例
///
///
public static AssetsManager GetInstance()
{
if (_instance == null)
{
var go = GameObject.Find("game");
if (go == null)
{
go = new GameObject("game");
}
var assetsManager = go.GetComponent();
if (assetsManager == null)
{
assetsManager = go.AddComponent();
}
_instance = assetsManager;
if (string.IsNullOrEmpty(_resourcesPath))
{
SetResourcesPath();
}
}
return _instance;
}
///
/// 初始化assetsbundle
///
///
IEnumerator Initassetsbundle()
{
WWW w = new WWW(_configPath);
yield return w;
string content = null;
if (string.IsNullOrEmpty(w.error))
{
content = w.text;
}
else
{
yield break;
}
_AssetsList.Clear();
var config = JsonMapper.ToObject(content);
//判断平台,只加载相应平台的assetbundle
for (int i = 0; i < config.AssetsBundles.Count; i++)
{
string outPutPlat;
#if UNITY_ANDROID
outPutPlat = "Android";
#elif UNITY_IOS
outPutPlat = "IOS";
#else
outPutPlat = "Windows";
#endif
var assets = config.AssetsBundles[i];
if (assets.platform == outPutPlat)
{
_AssetsList.Add(assets);
}
}
_AssetsList.Reverse();
LoadAllAssetsBundle();
}
///
/// 加载所有assetsbundle进字典
///
private void LoadAllAssetsBundle()
{
if (_AssetsList != null && _AssetsList.Count > 0)
{
_AssetsList.ForEach((m) =>
{
StartCoroutine(LoadOneAssetsBundle(m));
});
}
}
///
/// 加载单个assetsbundle进字典
///
///
///
IEnumerator LoadOneAssetsBundle(Assets assets)
{
WWW www = new WWW(_resourcesPath + assets.name + ".assetbundle");
yield return www;
if (string.IsNullOrEmpty(www.error))
{
var asset = www.assetBundle;
_dicAssetBundles.Add(assets.name, asset);
float p = ((float)_dicAssetBundles.Count / _AssetsList.Count);
//是否加载完了所有的assetsbundle,TODO 通知游戏启动项,为了完成demo,先这样写
if (p==1)
{
GameObject.Find("game").AddComponent();
}
}
}
public T LoadAssets(string path) where T : Object
{
if (string.IsNullOrEmpty(path))
{
Debug.LogError("传进的路径不合法");
return null;
}
string[] paths = null;
if (_dicAssets.ContainsKey(path))
{
return (T)_dicAssets[path];
}
switch (_loadAssetsType)
{
case LoadAssetsType.AssetsBundle:
paths = path.Split('/');
path = paths[paths.Length - 1];
return AssetsBundleLoad(path);
case LoadAssetsType.Resources:
paths = path.Split('.');
path = paths[0];
return ResourcesLoad(path);
default:
throw new ArgumentOutOfRangeException();
}
}
private T AssetsBundleLoad(string path) where T : Object
{
foreach (var key in _dicAssetBundles.Keys)
{
var a = _dicAssetBundles[key];
var data = (T)a.LoadAsset(path, typeof(T));
if (data != null)
{
AddResouce(path, data);
return data;
}
}
Debug.LogError("根据这个路径什么都没有找到" + path);
return null;
}
private T ResourcesLoad(string path) where T : Object
{
var data = Resources.Load(path);
if (data != null)
{
AddResouce(path, data);
}
else
{
Debug.LogError("根据这个路径什么都没有找到" + path);
}
return data;
}
///
/// 往字典里添加资源
///
///
///
private void AddResouce(string path, Object obj)
{
if (_dicAssets.Count >= _maxResourcesCount)
{
var lastResourceName = _assetsName.Dequeue();
_dicAssets.Remove(lastResourceName);
}
_assetsName.Enqueue(path);
_dicAssets.Add(path, obj);
}
}
这里加载assetsbundle需要根据上次打包生成的json配置文件,去读到具体的assetbundle,然后加载,很好的支持了热更
todo这里,需要用事件机制告诉其他组件,已经准备好了assetsbundle。下节讲解事件机制吧,敬请期待。using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
void Start () {
//加载资源,参数需要这样传,Resources下面的第一个目录开始,需要后缀结尾
var cube = AssetsManager.GetInstance().LoadAssets("view/ui.prefab");
Debug.LogError(cube.name);
}
}
点击下载资源,示例demo点击打开链接