博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AssetBundle进阶内存优化(Unity 4.x)
阅读量:5913 次
发布时间:2019-06-19

本文共 3255 字,大约阅读时间需要 10 分钟。

测试环境(Unity 4.7 + NGUI)

在项目中,我们用NGUI制作了2个UI,分别是a.prefab 和b.prefab,它们都使用了一张名为c.png的贴图(这种现象在开发中非常常见,很多时候可能是数十个UI Prefab使用了同一张贴图)。我们使用依赖项打包,打包的结果分别是3个文件:

0.png

我们可以看到,在文件层面上已经没有冗余了,a.bundle及b.bundle都没有贴图的资源,贴图的资源都在c中。

按照常规的加载过程使用如下代码:

WWW wwwc = new WWW("file://c.bundle");        yield return wwwc;        wwwc.assetBundle.LoadAll();        WWW wwwa = new WWW("file://a.bundle");        yield return wwwa;        WWW wwwb = new WWW("file://b.bundle");        yield return wwwb;        GameObject goa = GameObject.Instantiate(wwwa.assetBundle.mainAsset) as GameObject;        GameObject gob = GameObject.Instantiate(wwwb.assetBundle.mainAsset) as GameObject;

这样的代码能成功加载出我们想要的资源,但这个时候如果我们去看Memory,就会发现冗余数据:

请输入图片描述

我们发现,c的纹理已经被加载出来了,并且已经占用了内存,但是c.bundle的文件映射依然还躺在内存中。一份数据出现了2份内存开销,这对于我们来说是不可饶恕的。按照Unity的要求如果要释放掉c.bundle的文件映射内存,需要调用AssetBundle.UnLoad(false),可是调用过这个函数之后对它有依赖的文件就没有办法再生成出来了,即你会发现纹理那个位置纹理指向的是null。比如这个时候还有其他的Prefab也使用了这张纹理,你会发现使用Bundle加载出来后材质指向的纹理为空,即使这个时候c的纹理已经在内存中。当然你还可以再次加载c.bundle,然后再加载其他Prefab,但你会发现内存中出现了两个叫c的纹理,这个更扯了,很明显不是一个合格的解决方法。

这个时候我们可以采用自我管理引用的方法来解决这个问题。这里以修改UIAtlas为例,同样的原理可以解决其他复杂引用的问题,比如UIFont,甚至3D场景的纹理管理等)。

首先我们调整UIAtlas的代码,在编辑器模式下存下UIAtlas的纹理及与材质的引用关系。这样我们在Bundle中就可以获得这个UIAtlas的纹理引用关系。

[SerializeField] public string[] propertiesName;    //材质中对应使用的纹理PropertyName    [SerializeField] public string[] propertiesTextureName;     //纹理中的名字public void MarkAsChanged ()    {#if UNITY_EDITOR        NGUITools.SetDirty(gameObject);        ReFlushTextureName();    …..#endif}public void ReFlushTextureName()    {        #if UNITY_EDITOR                    if(material.shader)        {            List
pns = new List
(); List
tns = new List
(); for(int i = 0 ; i < ShaderUtil.GetPropertyCount(material.shader);i++) { if(ShaderUtil.GetPropertyType(material.shader,i) == ShaderUtil.ShaderPropertyType.TexEnv) { string propertyname = ShaderUtil.GetPropertyName(material.shader,i); Texture t = material.GetTexture(propertyname); pns.Add(propertyname); tns.Add(t.name); } } propertiesName = pns.ToArray(); propertiesTextureName = tns.ToArray(); } #endif }

正如代码所示,这个UIAtlas所使用的纹理信息已经被我们保存到 PropertiesName 和 PropertiesTextureName 这两个变量中。这些变量名将会被打包到Bundle中,并且在我们重新解开Bundle的时候可以得到。

其次,我们在c.bundle加载出来后,建立纹理名字对纹理的关系表,然后Unload(false)掉。

Texture2DBundlerCache 是一个简单的名字纹理的查找表,提供名字对纹理的添加、查找 、删除等功能,这里不再重复代码了,相信各位都能搞定。

最后我们再修改UIAtlas的代码,使得当材质在使用的时候,会重新将材质与纹理的引用关系恢复。是的,纹理已经在内存中了。

public Material spriteMaterial    {        get        {            if(material.mainTexture == null)            {                if(propertiesName != null)                {                    for(int i = 0;i < propertiesName.Length;i++)                    {                        material.SetTexture(propertiesName[i], Texture2DBundlerCache.Instance.Get(propertiesTextureName[i]) );                    }                }            }}}

这样,我们就可以通过一个很小的表的开销及数个字符串的开销,来避免大量的文件内存占用,特别是在一些复杂引用关系中可以游刃有余地Unload掉资源,非常有效地控制住内存开销,而且由于是自己做的引用表,因而更加可控。

原文出处:侑虎科技

本文作者:admin
转载请与作者联系,同时请务必标明文章原始出处和原文链接及本声明。

你可能感兴趣的文章
利用excel办公软件快速拼凑sql语句
查看>>
Python语音合成
查看>>
Hadoop学习笔记(1) ——菜鸟入门
查看>>
Twisted模块
查看>>
异常以及异常处理框架探析
查看>>
洛谷P2805 植物大战僵尸
查看>>
后缀数组专题
查看>>
一次因为文件名开头包含空格而导致FTP文件一直无法下载的悲剧!
查看>>
linux测试某进程占用oi、cpu、内存的使用情况
查看>>
jQery简单Tab选项卡效果
查看>>
Java Object类及其equals方法
查看>>
[摘录]为你的感情开个账户
查看>>
redhat下配置SEED DVS6446开发环境2
查看>>
团队博客作业Week4 --- 学霸网站--NABC
查看>>
python 安装 setuptools Compression requires the (missing) zlib module 的解决方案
查看>>
这次真的是项目感受!
查看>>
jquery1.0源码【1-60行】构造函数及全局$变量
查看>>
REST 规范
查看>>
python 冒泡排序,快排
查看>>
二分查找
查看>>