Unity资源管理和加载

12-05 348 views

垃圾回收器的压缩操作是托管的堆与非托管的堆的区别所在.—C#高级编程第10版

以后写文章以引语开篇, 我感觉这样风格更酷.

代码看过的不一定是你的, 但写过的一定会融化到你血液里 , 最近抽了”点”时间重写了下这里, 有了更深的理解, 不废话上图:

资源的上层池管理

对于上图上层加载说明:

  1. 纯lua/C#类对象, 可以使用弱表/数组缓存, 避免每次new
  2. 对于需要实例化的GameObject/UIComponent(如播放音乐需要多个AudioSource)可以用循环队列管理实例化出来的资源, 并进行实例化时间控制, 实现负载均衡
  3. 当资源refCount为0时放入LRU进行末尾淘汰, 每次取用重置池对象的destroyTime, 池属性可以根据高中低配置动态改变
  4. 特殊的对于Atlas, 只有其中每一个Sprite都不再被引用, 总引用才算清零, 回收其Texture
  5. 短音频同步加载池缓存, 长音频采用流式加载避免过多占用内存
  6. lua池和C#资源池似乎看上去有重叠, 但作用不尽相同, Lua对象池一直持有着资源, 避免了lua从新和资源结合的过程(如transform.Find操作), 而C#端保持引用, 避免从新加载AB和从AB加载资源的过程
//对于unity2019流加载音乐, 有下面格式:

 private IEnumerator WWWLoad(string path, Action<Object> callback)
        {
            string fullPath = string.Empty;
            if (DebugSelector.AllUseResources)
            {
                fullPath = "file://" + PathUtil.ResPath + path;
            }
            else
            {
                fullPath = PathUtil.GetStreamingWWWPath(path);
            }
            //注意Editor下不支持mp3流式加载
            using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.OGGVORBIS))
            {
                var handler = www.downloadHandler as DownloadHandlerAudioClip;
                handler.streamAudio = true;
                handler.compressed = false;
                yield return www.SendWebRequest();

                if (!www.isDone)
                {
                    yield return null;
                }

                if (www.isNetworkError)
                {
                    Debugger.LogError("[UnBundleLoadManager] Can not Load Music, {0}, {1} ", www.error, www.url);
                    callback(null);
                }
                else
                {
                    AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
                    callback(clip);
                }
            }
        }

A依赖B资源, 并分别打包到a, b两个assetbundle中, A资源被第二次加载的情况

对于上图底层加载的说明:

  1. 对于异步加载,无论A资源是从AssetBundle.Load还是Resources.Load总有下面的逻辑:
    1. (可选)检查加载资源的配置, 无配置返回空
    2. 检查所有缓存的资源, 如果已经有此资源缓存, 缓存引用计数+1, 返回资源
    3. 检查此加载任务是否存在, 若存在添加完成回调到此任务, 每个加载完成回调中资源引用计数+1, 返回并缓存资源
    4. 创建新加载任务, 添加完成回调, 加载完成回调中资源引用计数+1, 返回并缓存资源
    5. 特殊对于加载出空资源, 不增加引用计数, 返回空
  2. 对于从AB中加载A资源, 引用的a, b, B的加载都有上述过程
    1. 对于加载A资源, a, b, B任何一个加载失败, A都返回空, 不增加引用计数
    2. A被多次加载, 只在第一次增加依赖资源的引用计数, 并在引用为0时, 减少依赖资源的引用
    3. 如果B资源是不需要显式加载的 , 我们可以忽略B资源存在, 而只需要维护b的引用即可

关于AB标标记,如果资源被公用, 请标记ABName, 否则资源会被复制分别打入引用他的资源所在AB中, 科普文(英文版)

如果需要明确的AssetBundle.Unload(True), 我们需要自己维护A, B, a, b的引用关系,manifests文件表示鸭梨山大恕不奉陪

如果一个资源所在AB没有被公用, 那么我们可以AssetBundle.Unload(False)提前卸掉AB而不用担心资源被加载多份


对于AB, 异步加载和同步加载问题:

  • 异步加载A资源还好说, 但是如果同步加载A资源:
    1. 如果a, B, b刚好全都被提前的加载缓存了非常棒一切顺利;
    2. 如果a, B, b加载任务都不存在, 全创建为同步加载任务也没啥问题;
    3. 但若B未被提前加载出来并缓存, 而是因为被某个C资源引用(C资源被加载导致B需要被加载)而处于异步加载任务中, 则麻烦了, 我们只能碰个运气赌一下B资源这一帧能否被加载出来, 如果可以, 皆大欢喜同步返回被加载好的A资源, 否则只能返回空了,加载失败

可见, 对于上层的加载接口, 最好大部分资源类型规定为异步加载, 同步加载的资源最好限制为可控的几种(如shader, mat, Text)对于底层的加载接口,AB的同步加载 (AssetBundle.LoadFromFile声称只会加载头部, 足够优秀) 尽量使用同步; 而从AB中加载资源也尽量大部分使用同步, 少部分大资源使用异步加载方式, 并保证外部不会尝试同步加载这些资源(或者小心维护好)

Unity Default Shader

修改UI/Default shader实现Gray效果 // Custom Gray by change UI/Default Unity built-in shader source. // https://github.com/TwoTailsGames/Unity-...

阅读全文

Unity EventSystem和EasyTouch

UGUI的点击事件本质是发送射线,之前我们使用EasyTouch兼容ugui 响应事件点击出于下面考虑: 1.不用每个响应点击的ui控件上都去挂事件监听脚本, 统一处理2...

阅读全文

一次简单的Unity资源提取

所谓简单, 那必须是菜鸟也能操作的, 不废话先上三板斧: 把xapk/apk包, 改为zip后缀, 解压缩, 发现是Unity做的, 很好找到ab文件, 用AssetStudio打开(如果...

阅读全文

欢迎留言