Unity热更新系列之AssetBundle(2) ——详述如何打AssetBundle资源

本文详细介绍了在Unity中如何为热更新准备AssetBundle资源,特别是UI资源的打包流程。通过设置Assetbundle Name并处理依赖,避免内存占用过高。文章指出,图集和字体是资源的主要占用部分,通过置空引用并自处理依赖,可以降低资源大小。此外,还讨论了UI热更新资源的整理和打包步骤,以及如何编写插件简化这一过程。
摘要由CSDN通过智能技术生成

Unity热更新系列之AssetBundle(2) ——详述如何打AssetBundle资源

前一段时间,写了篇笔记,详细的讲了如何设置资源的Assetbundle 名称,和其中的区别。
最主要的区别其实就在于项目中要不要处理资源之间的依赖。
详述如何设置Assetbundle Name

在设置好资源名称后,输出资源的过程并不复杂。只需要调用内置的API

 BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions, BuildTarget);

就可以输出已经设置好名称的资源。
具体的对应关系,在上面的文章中已经介绍过了。

现在介绍一下,如果我们想要做UI热更新时,打资源的流程。
该方法中,我们不处理资源间的依赖,而是使用另一种方式来自己记录并处理依赖。
模拟真实的手游项目中的MVC模式,每一个UI面板都会有一个相应的UI面板类,这些面板类继承同一个基类并会统一到一个面板管理器类中,方便游戏其他逻辑访问和调用。

我们新建一个场景,在场景中导入NGUI插件,并随手拉一个界面出来。
这里写图片描述
并定义出相应的脚本,其中各个面板的基类PadBase定义如下:

public class PadBase : MonoBehaviour {
       public List<UIAtlas> allAtlas;
    public List<UIFont> allFonts;
    protected virtual void Awake()
    {

    }

    public virtual void Start () {

    }

    public virtual void OnEnable() {
    }
    public virtual void OnDisable()
    {
    }
    public virtual void Update()
    {

    }
}

这个脚本中除了定义了常用生命周期的虚函数外,最重要的就是定义了变量allAtlas和allFonts。
这是因为在UI热更新中,每一个UI上面最重要的资源可以分为三部分分别打成AssetBundle,分别是面板的预制体本身、面板所引用的图集和面板所引用的字体。
之所以这样划分,是因为对于一个游戏中,所有的面板所引用的图集和字体是相互耦合的,如果我们将每一个面板上引用到的所有资源
如果当然我们可以按照上一篇文章中说的方法,将他们分开打包,然后加载的时候使用Manifest文件处理相互之间的依赖关系。

但是这里我们需要用自己的方法来处理UI上的依赖,因为在游戏运行过程中,内存占用过高的元凶,和造成性能瓶颈的元凶,往往都是过多的UI图集资源。如果使用Manifest处理依赖,U3D会自动的将该面板所引用的资源长期占用在内存中,不方便我们进行内存的管理。
用这种方法,我们可以自PadBase类中,每一个面板执行OnDisable时,自己来处理是否要释放内存资源。
这样在其每一个子类中,都会继承相应的变量,用来在热更新UI时,记录面板上的引用信息。
这里写图片描述
这样做的原理就是,当一个预制体上面挂了脚本,并且该脚本上有一些Pulic 变量在预制体上赋了值,那么该变量的值就会记录在预制体上,并随之打入AssetBundle资源。
我们在打资源时,预制体上通过这样的方式储存了引用的图集和字体信息,真正打资源时,需要将图集和字体置为空,这样打出的资源里面虽然讲面板引用的所有的图集和字体都打在了一起,但其实只打进去了图集和字体的预制体,真正占用内存最大的资源是图集上引用的Texture和字体预制体上引用的字体库

举个例子,我们测试的面板中引用了两个图集Fantasy Atlas和Wooden Atlas
如果我们不做任何操作,直接给Pad_Test预制体设置一个AssetBundle Name,打出资源如下
这里写图片描述

现在我们将引用的图集、字体上的预制体都置空
这里写图片描述

打出的资源结果如下:
这里写图片描述

这里我们发现,第一次面板总资源大小为596KB,第二次,面板总资源大小为7KB。
这两次资源主要就差在,第一个资源将面板上引用到的图集和字体的预制体、材质球、材质球引用到的Texture、字体库全都打进了一个AssetBundle。

而第二次资源相比第一次资源只是少了Texture和字体库。

经过比较,可以得到,在面板引用的资源中,图集和字体的预制体本身并不大,全都打在一起也才7KB,在项目中是可以接受的。相比之下Texture和字体库才是占用资源大小的最大元凶。

所以我们需要整理一下UI热更时打资源的流程
这里写图片描述

之所以分了这么多琐碎的步骤,最主要是因为需要将图集和字体先整体打一遍,这时候是要带着Texture和字体库的,打完之后需要将图集和字体上的材质引用置空,然后将其和面板预制体打在一起(测试面板只有7KB)。由于我们自己处理了依赖关系,所以使用不到Manifest文件。

这样打出的资源,我们可以在游戏运行时,动态加载面板的预制体资源,加载完后,通过该预制体上记录的变量信息(图集和字体的名字),再去加载它引用到的图集和字体,并使用
Atlas.replacement = AtlasLoadFromAssetBundle
将面板类上的变量List<UIAtlas> allAtlas; 中的图集全都替换掉就可以了。

但是这样做虽然可以做到面板内容的更新,但是每次出包打资源的步骤太过繁琐。所以我们就需要写个插件,来帮助我们打UI资源。

using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Text;

public class ExportManager : MonoBehaviour
{
    //记录图集和材质的对应关系,用来置空或重新赋值图集材质
    private static Dictionary<UIAtlas, Material> atlasMaterialDic = new Dictionary<UIAtlas, Material>();
    //记录字体和材质的对应关系,用来置空或重新赋值字体材质
    private static Dictionary<UIFont, Font> fontMaterialDic = new Dictionary<UIFont, Font>();
    //所有的面板类
    private static List<PadBase> allPadBase = new List<PadBase>();

    [MenuItem("资源打包/打开操作面板")]
    static void OpenConfigWindow()
    {
        var wizard = EditorWindow.GetWindow(typeof(ExportWindow)) as ExportWindow;
        wizard.minSize = new Vector2(300, 300);
    }


    /// <summary>
    /// 收集所有面板上所使用的图集,为每一个PadBase的AllAtlas赋值
    /// </summary>
    static public void CollectAllUsedAtlasFont()
    {
        atlasMaterialDic.Clear();
        fontMaterialDic.Clear();
        allPadBase.Clear();
        //找到所有的PadBase
        string[] filePaths = Directory.GetFiles(Application.dataPath + "/Panel/PadPrefabs", "*.prefab", SearchOption.AllDirectories);
        for (int i = 0; i < filePaths.Length; i++)
        {
            FileInfo fi = new FileInfo(filePaths[i]);
            string path = "Assets" + filePaths[i].Replace(Application.dataPath, "");
            Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(Object));
            GameObject go = obj as GameObject;
            PadBase pb = go.GetComponent<PadBase>();
            if (go != null && pb != null)
            {
                if (!allPadBase.Contains(pb))
                {
                    allPadBase.Add(pb);
                }
            }
            if (EditorUtility.DisplayCancelableProgressBar("收集所有PadBase:", filePaths[i], (float)i / (float)filePaths.Length))
            {
                break;
            }
        }
        //收集每一个面板上的图集和字体信息
        for (int i = 0; i < allPadBase.Count; i++)
        {
            CollectAtlasInChildren(allPadBase[i].transform, allPadBase[i]);
            if (PrefabUtility.GetPrefabParent(allPadBase[i]) != null)
            {
                PrefabUtility.ReplacePrefab(allPadBase[i].gameObject, PrefabUtility.GetPrefabParent(allPadBase[i]), ReplacePrefabOptions.ConnectToPrefab);
            }
            if (EditorUtility.DisplayCancelableProgressBar("收集所有图集信息:", allPadBase[i].name, (float)i / (float)allPadBase.Count))
            {
                break;
            }
        }
        EditorUtility.ClearProgressBar();
    }
    static private void CollectAtlasInChildren(Transform tf, PadBase padBase)
    {
        List<UIAtlas> atlasList = new List<UIAt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值