unity资源管理热更必备(三)

上一篇讲到了如何打包assetsbundle,紧跟而来的就是加载assetsbundle的问题。资源的动态的加载用到Resources和AssetsBundle这两种方式,而后一种方式无论是在热更还是大小,安全等方面更具优势。下面要贴出作者一直在用的资源管理方式。

大概是利用枚举区分这两种方式,在unity编辑器里可以随意切换,打包发布时也可以根据自己的需求切换。

利用assetsbundle加载资源时,需要在启动游戏的时候,将所需的assetsbundle加载进内存,再通过具体方法加载assets bundle的资源。

这里我设置了一个最大资源加载数的选项, 因为游戏要加载的,动态生成的prefab等往往不会频繁使用,通过queue队列的方式,可以有效减少动态加载量很少的资源,常驻内存的情况。

废话不多说了,贴两张效果图

unity资源管理热更必备(三)_第1张图片

unity资源管理热更必备(三)_第2张图片

下面贴一下代码

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);
    }
}
unity资源管理热更必备(三)_第3张图片


点击下载资源,示例demo点击打开链接

你可能感兴趣的:(unity资源管理热更必备(三))