资源加载框架之-AssetBundle打包管理

资源打包策略

正常的打包策略:

  1. 在编辑器下设置文件夹或者文件AB包名
  2. 根据manifest进行依赖加载

优点:上手简单,方便操作。

缺点:容易产生冗余AB包;文件夹或文件等AB包名设置混乱,难以管理。

我的打包策略:

  1. 编写编辑器工具统一设置AB包名称及路径管理
  2. 根据依赖关系生成不冗余AB包
  3. 生成自己的依赖关系表
  4. 根据自己的依赖关系表加载AB包

优点:不会生成冗余的AB包,文件AB包名设置简单方便,容易管理

缺点:对于新手理解又一定难度。

实现:

1、自定义打包配置表

创建ABConfig脚本继承ScripttableObject 

using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;

namespace Editor.Loader
{
    // 创建资源AB配置菜单
    [CreateAssetMenu(fileName = "ABConfig",menuName = "CreateABConfig")]
    public class ABConfig:ScriptableObject
    {
        public List<ABDirInfo> abDirInfo = new List<ABDirInfo>();
         [Serializable]
         // 需要打AB包的文件夹信息
         public struct ABDirInfo
         {
             [LabelText("报名")]
             public string ABName;
             [LabelText("包路径")]
             public string ABPath;
         }
    }
}

效果:

 

 2、生成AB包

  1. 根据文件夹设置AB包
  2. 剔除冗余AB包
  3.  生成AB包配置表(二进制文件和xml文件,xml文件用来直观的看出打包结果)
  4. 清空资源的AB包配置
  5. 删除旧的AB包(AB包配置发生改动后下一次打包需要清理掉旧的AB文件)
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;
using CS.Core.Loader;
using CS.Core.Utlis;
using UnityEditor;
using UnityEngine;

namespace Editor.Loader
{
    public class AssetEditor
    {
        private static bool needExtension = false;                  // 是否需要扩展名
        private static string ABOutPutPath = "Assets/AssetBundles"; // Application.streamingAssetsPath;
        private static ABConfig abConfig;
        // key 包名 value 包路径
        private static Dictionary<string,string> abConfigDic = new Dictionary<string, string>();
        
        [MenuItem("My菜单/打包AB包")]
        private static void Pack()
        {
            abConfig = AssetDatabase.LoadAssetAtPath<ABConfig>("Assets/Resources/GameConfigData/ABConfig.asset");
            GenerateAbConfigData();
            SetBundleName();
            GenerateXmlConfig();
            BuildAssetBundle();
            ClearAssetBundleName();
            DeleteAssetBundle();
        }
        // 生成ab配置数据
        private static void GenerateAbConfigData()
        {
            abConfigDic.Clear();
            for (int i = 0; i < abConfig.abDirInfo.Count; i++)
            {
                var abInfo = abConfig.abDirInfo[i];
                if (abConfigDic.ContainsKey(abInfo.ABName))
                {
                    Debug.LogError($"配置错误:配置了相同的包名 name:{abInfo.ABName} path: {abInfo.ABPath}");
                    continue;                    
                }
                abConfigDic.Add(abInfo.ABName,abInfo.ABPath);
                // Debug.Log($"生成包数据成功name:{abInfo.ABName} path:{abInfo.ABPath}");
            }
        }

        private static void SetBundleName()
        {
            foreach (var abName in abConfigDic.Keys)
            {
                string[] guids = AssetDatabase.FindAssets("", new[] {abConfigDic[abName]});
                for (int i = 0; i < guids.Length; i++)
                {
                    string path = AssetDatabase.GUIDToAssetPath(guids[i]);
                    if(path.EndsWith(".cs")) continue;
                    SetBundleName(path,abName);
                }
            }
        }
        // 设置包
        private static void SetBundleName(string path, string abName)
        {
            AssetImporter assetImporter = AssetImporter.GetAtPath(path);
            if (assetImporter)
            {
                assetImporter.assetBundleName = abName;
            }
        }

        // 清空资源的bundle名配置
        private static void ClearAssetBundleName()
        {
            string[] abNames = AssetDatabase.GetAllAssetBundleNames();
            for (int i = 0; i < abNames.Length; i++)
            {
                string[] allPaths = AssetDatabase.GetAssetPathsFromAssetBundle(abNames[i]);
                for (int j = 0; j < allPaths.Length; j++)
                {
                    SetBundleName(allPaths[j],null);
                }
                AssetDatabase.RemoveAssetBundleName(abNames[i],true);
            }
        }

        // 生成xml配置文件
        private static void GenerateXmlConfig()
        {
            // key 资源路径 value abName
            Dictionary<string,string> resDic = new Dictionary<string, string>();
            foreach (string abName in abConfigDic.Keys)
            {
                string[] assetPath = AssetDatabase.GetAssetPathsFromAssetBundle(abName);
                for (int i = 0; i < assetPath.Length; i++)
                {
                    string path = assetPath[i];

                    if(!resDic.ContainsKey(path))
                        resDic.Add(path, abName);
                    
                    if (path.EndsWith(".prefab"))
                    {
                        // 预制体的依赖
                        string[] depends = AssetDatabase.GetDependencies(path);
                        for (int j = 0; j < depends.Length; j++)
                        {
                            if (!depends[j].EndsWith(".cs") && depends[j] != path)
                            {
                                AssetImporter assetImporter = AssetImporter.GetAtPath(depends[j]);
                                if(!resDic.ContainsKey(path))
                                    resDic.Add(depends[j], assetImporter.assetBundleName);
                            }
                        }
                    }
                }
            }

            AssetBundleConfig assetBundleConfig = new AssetBundleConfig();
            foreach (var path in resDic.Keys)
            {
                ABInfo info = new ABInfo();
                info.AseetName = Path.GetFileNameWithoutExtension(path);
                info.AseetPath = path;
                info.ABName = resDic[path];
                info.Crc = CrcUtlis.GetCRC32(path);
                info.Depends = new List<string>();
                if (path.EndsWith(".prefab"))
                {
                    // 预制体的依赖
                    string[] depends = AssetDatabase.GetDependencies(path);
                    for (int j = 0; j < depends.Length; j++)
                    {
                        if (!depends[j].EndsWith(".cs") && depends[j] != path)
                        {
                            AssetImporter assetImporter = AssetImporter.GetAtPath(depends[j]);
                            if (assetImporter.assetBundleName != info.ABName &&
                                !info.Depends.Contains(assetImporter.assetBundleName))
                                info.Depends.Add(assetImporter.assetBundleName);
                        }
                    }
                }
                
                assetBundleConfig.abInfoList.Add(info);
            }

            // 移除扩展名
            if (!needExtension)
            {
                for (int i = 0; i < assetBundleConfig.abInfoList.Count; i++)
                {
                    var abInfo = assetBundleConfig.abInfoList[i];
                    abInfo.AseetPath = GetFilePath(abInfo.AseetPath);
                    abInfo.Crc = CrcUtlis.GetCRC32(abInfo.AseetPath);
                    assetBundleConfig.abInfoList[i] = abInfo;
                }
            }

            // 保存xml
            FileStream fs = new FileStream("Assets/Resources/GameConfigData/AssetBundleConfig.xml",FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite);
            StreamWriter streamWriter = new StreamWriter(fs,Encoding.UTF8);
            XmlSerializer xmlSerializer = new XmlSerializer(assetBundleConfig.GetType());
            xmlSerializer.Serialize(streamWriter,assetBundleConfig);
            streamWriter.Close();
            fs.Close();
            // 保存二进制文件
            FileStream bfs = new FileStream(AssetBundleManager.AssetBundleConfigPath,FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(bfs,assetBundleConfig);
            bfs.Close();
        }

        private static string GetFilePath(string path)
        {
            if (!needExtension)
            {
                path = path.Replace(Path.GetExtension(path), String.Empty);
            }
            return path;
        }

        // 获取AB包资源配置信息
        public static AssetBundleConfig GetAssetBundleConfig()
        {
            // FileStream fs = new FileStream("Assets/Resources/GameConfigData/AssetBundleConfig.xml", FileMode.Open,
            //     FileAccess.ReadWrite, FileShare.ReadWrite);
            // 序列化文件
            // XmlSerializer xmlSerializer = new XmlSerializer(typeof(AssetBundleConfig));
            // AssetBundleConfig assetBundleConfig = xmlSerializer.Deserialize(fs) as AssetBundleConfig;
            FileStream fs = new FileStream("Assets/Resources/GameConfigData/AssetBundleConfig.bin", FileMode.Open,
                FileAccess.ReadWrite, FileShare.ReadWrite);
            BinaryFormatter bf = new BinaryFormatter();
            AssetBundleConfig assetBundleConfig = bf.Deserialize(fs) as AssetBundleConfig;
            fs.Close();
            return assetBundleConfig;
        }

        // 构建AB包
        private static void BuildAssetBundle()
        {
            if (!Directory.Exists(ABOutPutPath))
            {
                Directory.CreateDirectory(ABOutPutPath);
            }
            // BuildAssetBundleOptions.None LZ4  BuildAssetBundleOptions.ChunkBasedCompression LZMA
            BuildPipeline.BuildAssetBundles(ABOutPutPath, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.StandaloneWindows);
        }

        // 删除旧的AB文件
        private static void DeleteAssetBundle()
        {
            string[] guids = AssetDatabase.FindAssets("",new []{ABOutPutPath});
            string rootFileName = Path.GetFileNameWithoutExtension(ABOutPutPath);
            for (int i = 0; i < guids.Length; i++)
            {
                string path = AssetDatabase.GUIDToAssetPath(guids[i]);
                string abName = Path.GetFileNameWithoutExtension(path);
                if (rootFileName != abName && !abConfigDic.ContainsKey(abName))
                {
                    AssetDatabase.DeleteAsset(path);
                }
            }
        }
    }
}

效果:

扫描二维码关注公众号,回复: 14654005 查看本文章

 导出的xml文件里可以直观的看出 Assets/Resources/UI/Prefab/Win/Hero/Win_Hero 文件属于prefabAB包,并且依赖了image AB包

猜你喜欢

转载自blog.csdn.net/weixin_41316824/article/details/127261946