Calmer的文章

  • 首页
  • 文章归档
  • 关于页面

  • 搜索
体验游戏 笔记 推荐 工具链 工具使用 小游戏 插件 UI 软件 教程

游戏开发资源管理方案(待完善)(Addresssabless Assets System)

发表于 2020-07-05 | 分类于 游戏开发 | 0 | 阅读次数 1278

前言

游戏开发过程中资源管理一直都是很重要的一个模块,涉及到的东西也比较复杂一些:资源加载/卸载(处理依赖) 远程拉取(网络下载) 分包(DLC) 热更


Unity3d的资源解决方案

加载的方法:文件(AssetDatabase或System.IO等)/Resources/AssetBundle/Addressable asset System(官方给的AssetBundle的轮子)
与之对应卸载。

加载方式分为:同步加载和异步加载。 现在一般采用异步加载。

a. 资源目录(分包) 存放资源的目录,包括一般美术资源和代码资源
b. 资源加载 涉及到解压、解密, 普通美术资源,就几种常用方法, 代码一般就是加载程序集或者像Lua这种解释执行的脚本(其实也是普通资源)
c. 打包工具(自动化工具)
这里会涉及到打包的平台,压缩方式,加密方式等 。然后还有一些分包策略。
d. 热更
热更资源很容易,热更代码一般两种方案ILRumtime 第二就是成熟的Lua方案(Xlua等) ,如果不需要热更新功能,只是修复Bug,甚至都用不到以上方案。有一个InjectFix,很轻量级修复Bug。
热更步骤:需要有个版本文件,还会为每个AB生成md5,对比后把新生成的Ab和修改的AB复制到新目录,此为需要热更的包。 配置服务器文件,每次开始游戏从服务器拉取文件对比serverInfo,如果需要热更,则下载相应的资源,然后加载方式配合一下,就可以重定位到新的资源。
e. 旧包跨版本更新资源问题

学习了解文档:如下
官方文档


常用的两种管理方案:

1.(旧)使用AssetBundle 要自己写一套管理依赖的流程,以及下载等等。
2.(新)Addressable asset System

整个资源管理的难题:就在于资源的依赖/下载/卸载等之间

两者的比较:
1.AB是Unity官方最早提出的资源管理方式,但是没有一套成熟的体系,都是开发者自己造轮子,然后就是百花齐放的样子。
2.Addressable Asset System是官方提供的一套轮子,避免了重复造轮子,但是可能会存在一定的Bug。


处理依赖的问题:

  1. 要么一开始生成所有依赖的字典,加载时候去读取
  2. 要么就临时判断依赖

使用方法一,直观,可以用开发工具生成相应的依赖管理文件
使用方法二,不是那么直观,可能会造成后期难以管理


AB管理思路

待更新

Adressable 管理思路

官方文档:
https://docs.unity3d.com/Packages/com.unity.addressables@1.10/manual/AddressableAssetsGettingStarted.html
参考连接
https://www.jianshu.com/p/e79b2eef97bf
https://www.jianshu.com/p/8009c16fcab3
The Addressable Asset System 正式版应用(一)
The Addressable Asset System 正式版应用(二)
UWA学堂官网文档翻译


代码

using FairyGUI;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.U2D;

public class AddressableTools
{
    private struct SceneStruct
    {
        public Action<UnityEngine.ResourceManagement.ResourceProviders.SceneInstance> callback;
        public UnityEngine.SceneManagement.LoadSceneMode mode;
        public bool autoReleaseHandle;
    }

    private struct ObjectStruct
    {
        public Action<UnityEngine.Object> callback;
        public bool autoReleaseHandle;
    }

    private static Dictionary<object, AsyncOperationHandle<UnityEngine.Object>> dicObjectHandles = new Dictionary<object, AsyncOperationHandle<UnityEngine.Object>>();

    private static Dictionary<object, Queue<ObjectStruct>> dicObjectCallbacks = new Dictionary<object, Queue<ObjectStruct>>();

    public static void InitializeAsync(Action<IResourceLocator> callback)
    {
#if UNITY_EDITOR
        callback?.Invoke(null);
        return;
#endif
        var handle = Addressables.InitializeAsync();
        Action<AsyncOperationHandle<IResourceLocator>> complete = null;
        complete = data =>
        {
            handle.Completed -= complete;
            if (data.Status == AsyncOperationStatus.Succeeded && data.IsValid())
            {
                callback?.Invoke(null);
            }
            else
            {

            }
        };
        handle.Completed += complete;
    }

    private static string FindAssetFile(string path)
    {
        var fileNames = path.Split('/');
        var fileName = fileNames[fileNames.Length - 1];
        var directoryPath = Application.dataPath + "/" + path;
        directoryPath = directoryPath.Substring(0, directoryPath.LastIndexOf('/'));
        if (!Directory.Exists(directoryPath))
            return null;
        var files = Directory.GetFiles(directoryPath, "*.*", SearchOption.TopDirectoryOnly);
        string result = null;
        foreach (var f in files)
        {
            if (f.IndexOf(fileName + ".") > -1)
            {
                result = f.Replace(Application.dataPath, "Assets");
                break;
            }
        }
        return result;
    }

    public static AsyncOperationHandle<UnityEngine.Object> Load(object key, Action<UnityEngine.Object> callback = null, bool autoReleaseHandle = false)
    {
        if (string.IsNullOrEmpty(key.ToString()))
        {
            Debug.LogError("Load address 为Null 或 空");
            return default(AsyncOperationHandle<UnityEngine.Object>);
        }
#if UNITY_EDITOR
        string skey = key.ToString();
        string path = key.ToString();
        //if(skey.IndexOf("Zero/")==0)
        //{
        //    path=
        //}
        if (path == null)
        {
            if (skey != "shader" && skey != "pb")
                Debug.LogWarning("找不到路径:" + skey);
            if (callback != null) callback(null);
            return default(AsyncOperationHandle<UnityEngine.Object>);
        }
        var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
        callback?.Invoke(obj);
        return default(AsyncOperationHandle<UnityEngine.Object>);
#endif
        AsyncOperationHandle<UnityEngine.Object> handle;
        if (dicObjectHandles.TryGetValue(key, out handle))
        {
            callback?.Invoke(handle.Result);
            return handle;
        }
        Queue<ObjectStruct> callbacks;
        if (dicObjectCallbacks.TryGetValue(key, out callbacks))
        {
            dicObjectCallbacks[key].Enqueue(new ObjectStruct() { callback = callback, autoReleaseHandle = autoReleaseHandle });
            return default(AsyncOperationHandle<UnityEngine.Object>);
        }
        else
        {
            dicObjectCallbacks[key] = new Queue<ObjectStruct>();
            dicObjectCallbacks[key].Enqueue(new ObjectStruct() { callback = callback, autoReleaseHandle = autoReleaseHandle });
        }
        var loadAsset = Addressables.LoadAssetAsync<UnityEngine.Object>(key);
        Action<AsyncOperationHandle<UnityEngine.Object>> func = null;
        func = data =>
        {
            loadAsset.Completed -= func;
            if (data.IsValid() && data.Status == AsyncOperationStatus.Succeeded && data.Result != null)
            {
                if (!autoReleaseHandle)
                    dicObjectHandles[key] = data;
                while (dicObjectCallbacks[key].Count > 0)
                {
                    var v = dicObjectCallbacks[key].Dequeue();
                    v.callback?.Invoke(data.Result);
                }
                dicObjectCallbacks[key].Clear();
                dicObjectCallbacks.Remove(key);
            }
            else
            {
                Debug.LogError("资源地址加载失败:" + key);
                callback?.Invoke(null);
            }
            if (autoReleaseHandle)
                Addressables.Release(data);
        };
        loadAsset.Completed += func;
        return loadAsset;

    }

    public static AsyncOperationHandle<UnityEngine.Object> LoadBinary(object key, Action<byte[]> callback = null)
    {
        return Load(key, data => {
            if (data != null && data is TextAsset)
            {
                callback((data as TextAsset).bytes);
            }
            else
            {
                Debug.LogError("{0} can't get Bytes(obj({1})) type:{2}" + key + data.GetType());
            }
        }, true);
    }

    public static AsyncOperationHandle<UnityEngine.Object> Load(object key, object subKey, Action<UnityEngine.Object> callback = null, bool autoReleaseHandle = false)
    {
        return Load(key + "[" + subKey + "]", callback, autoReleaseHandle);
    }

    public static AsyncOperationHandle<UnityEngine.Object> LoadSprites(object key, Action<Sprite[]> callback = null, bool autoReleaseHandle=false)
    {
        return Load(key, data => {
            var sa = data as SpriteAtlas;
            var sprites = new Sprite[sa.spriteCount];
            sa.GetSprites(sprites);
            callback?.Invoke(sprites);
        }, autoReleaseHandle);
    }

    public static AsyncOperationHandle<IList<UnityEngine.Object>> LoadList(object key, Action<IList<UnityEngine.Object>> callback = null, bool autoReleaseHandle = false)
    {
        var loadAsset = Addressables.LoadAssetAsync<IList<UnityEngine.Object>>(key);
        Action<AsyncOperationHandle<IList<UnityEngine.Object>>> func = null;
        func = data => {
            loadAsset.Completed -= func;
            if (data.Status == AsyncOperationStatus.Succeeded && data.IsValid() && data.Result != null)
            {
                callback?.Invoke(data.Result);
            }
            else
            {
                Debug.LogError("资源加载失败:" + key);
                callback?.Invoke(null);
            }
            if (autoReleaseHandle)
                Addressables.Release(data);
        };
        loadAsset.Completed += func;
        return loadAsset;
    }

    public static AsyncOperationHandle<IList<IList<UnityEngine.Object>>> Loads(IList<object> lists, Action<IList<UnityEngine.Object>> callback = null)
    {
        return Addressables.LoadAssetsAsync(lists, callback);
    }

    public static AsyncOperationHandle GetDownLoadSizeAsync(IList<object> lists, Action<long> callback = null)
    {
        Action<AsyncOperationHandle<long>> func = null;
        func = data =>
        {
            callback?.Invoke(data.Result);
            Addressables.Release(data);
        };
        var handle = Addressables.GetDownloadSizeAsync(lists);
        handle.Completed += func;
        return handle;
    }

    public static AsyncOperationHandle DownLoadDependenciesAsync(IList<object> lists)
    {
        return Addressables.DownloadDependenciesAsync(lists, Addressables.MergeMode.None, true);
    }

    public static AsyncOperationHandle GetObjectHandle(string key)
    {
        AsyncOperationHandle<UnityEngine.Object> handle;
        dicObjectHandles.TryGetValue(key, out handle);
        return handle;
    }

    public static void Release(AsyncOperationHandle handle)
    {
        if(handle.IsValid())
        {
            Addressables.Release(handle);
        }
    }

    public static void Release(string key)
    {
#if !LOAD_ASSETBUNDLE
        return;
#endif
        AsyncOperationHandle<UnityEngine.Object> handle;
        if(dicObjectHandles.TryGetValue(key,out handle))
        {
            Release(handle);
            dicObjectHandles.Remove(key);
        }
        else
        {
            Debug.LogError("Release:"+key+" 为null或空");
        }
    }

    private static AsyncOperationHandle<GameObject> Instantiate(string key,Action<GameObject> callback=null,Transform parent=null,bool isWorldSpace=false,bool trackHandle=true)
    {
        var instantiate = Addressables.InstantiateAsync(key, parent, isWorldSpace, trackHandle);
        Action<AsyncOperationHandle<GameObject>> func = null;
        func = data =>
        {
            instantiate.Completed -= func;
            if (data.Status == AsyncOperationStatus.Succeeded && data.IsValid() && data.Result != null)
            {
                callback?.Invoke(data.Result);
            }
            else
            {
                Debug.LogError("资源Instaniate失败:"+ key);
                callback(null);
            }
        };
        instantiate.Completed += func;
        return instantiate;
        
    }

    private static bool ReleaseInstantiate(GameObject go)
    {
        if(go!=null)
        {
            return Addressables.ReleaseInstance(go);
        }
        return false;
    }

    public static IEnumerator DownLoadUpdate(List<object> list)
    {
        var init = Addressables.InitializeAsync();
        yield return init;
        var downLoadSize = Addressables.GetDownloadSizeAsync(list);
        yield return downLoadSize;
        Debug.Log("Start ready DownLoad:" + downLoadSize.Result);
        var downLoad = Addressables.DownloadDependenciesAsync(list, Addressables.MergeMode.None);
        yield return downLoad;
        Debug.Log("DownLoad Finish");
        Addressables.Release(downLoad);
    }

    public static byte[] GetBytes(UnityEngine.Object obj)
    {
        if(obj is TextAsset)
        {
            return (obj as TextAsset).bytes;
        }
        return null;
    }
}


  • 本文作者: Calmer
  • 本文链接: https://mytechplayer.com/archives/资源管理方案
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
# 工具链
项目解决整套解决方案
提取游戏资源
  • 文章目录
  • 站点概览
Calmer

Calmer

88 日志
7 分类
10 标签
RSS
Creative Commons
0%
© 2020 — 2025 Calmer
由 Halo 强力驱动
蜀ICP备20010026号-1川公网安备51019002006543
Copyright © 2020-2025 Calmer的文章