Unity简单资源加载框架(四):打包AB包三(生成AB包配置文件)

一目录结构

增加两个类
在这里插入图片描述

二代码:

AssetUtility

/****************************************************
    文件:AssetUtility.cs
	作者:赵深圳
    邮箱: 1329346609@qq.com
    日期:2022/5/7 18:24:12
	功能:资源管理相关的工具函数
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Security.Cryptography;
using System.IO;
using System.Text;
using System;

public static class AssetUtility 
{
    private static readonly CRC32 crc32 = new CRC32();

    /// <summary>
    /// 计算一个Stream对象的CRC32散列码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string GetCRC32Hash(Stream input)
    {
        byte[] data = crc32.ComputeHash(input);
        return ToHash(data);
    }

    private static string ToHash(byte[] data)
    {
        StringBuilder sb = new StringBuilder();
        foreach (byte t in data)
        {
            sb.Append(t.ToString("x2"));
        }
        return sb.ToString();
    }

    internal sealed class CRC32 : HashAlgorithm
    {
        private const uint DefaultPolynomial = 0xedb88320u;
        private const uint DefaultSeed = 0xffffffffu;

        private static uint[] _defaultTable;

        private readonly uint _seed;
        private readonly uint[] _table;
        private uint _hash;

        public CRC32()
            : this(DefaultPolynomial, DefaultSeed)
        {
        }

        public CRC32(uint polynomial, uint seed)
        {
            if (!BitConverter.IsLittleEndian)
                throw new PlatformNotSupportedException("Not supported on Big Endian processors");

            _table = InitializeTable(polynomial);
            _seed = _hash = seed;
        }

        public override int HashSize
        {
            get { return 32; }
        }

        public override void Initialize()
        {
            _hash = _seed;
        }

        protected override void HashCore(byte[] array, int ibStart, int cbSize)
        {
            _hash = CalculateHash(_table, _hash, array, ibStart, cbSize);
        }

        protected override byte[] HashFinal()
        {
            var hashBuffer = UInt32ToBigEndianBytes(~_hash);
            HashValue = hashBuffer;
            return hashBuffer;
        }

        public static uint Compute(byte[] buffer)
        {
            return Compute(DefaultSeed, buffer);
        }

        public static uint Compute(uint seed, byte[] buffer)
        {
            return Compute(DefaultPolynomial, seed, buffer);
        }

        public static uint Compute(uint polynomial, uint seed, byte[] buffer)
        {
            return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
        }

        private static uint[] InitializeTable(uint polynomial)
        {
            if (polynomial == DefaultPolynomial && _defaultTable != null)
                return _defaultTable;

            var createTable = new uint[256];
            for (var i = 0; i < 256; i++)
            {
                var entry = (uint)i;
                for (var j = 0; j < 8; j++)
                    if ((entry & 1) == 1)
                        entry = (entry >> 1) ^ polynomial;
                    else
                        entry >>= 1;
                createTable[i] = entry;
            }

            if (polynomial == DefaultPolynomial)
                _defaultTable = createTable;

            return createTable;
        }

        private static uint CalculateHash(uint[] table, uint seed, IList<byte> buffer, int start, int size)
        {
            var hash = seed;
            for (var i = start; i < start + size; i++)
                hash = (hash >> 8) ^ table[buffer[i] ^ (hash & 0xff)];
            return hash;
        }

        private static byte[] UInt32ToBigEndianBytes(uint uint32)
        {
            var result = BitConverter.GetBytes(uint32);

            if (BitConverter.IsLittleEndian)
                Array.Reverse(result);

            return result;
        }
    }
}

ModuleABConfig:

/****************************************************
    文件:ModuleABConfig.cs
	作者:赵深圳
    邮箱: 1329346609@qq.com
    日期:2022/5/7 18:15:20
	功能: AB、Asset信息
*****************************************************/

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 一个Bundle数据 【用于序列化为json文件】
/// </summary>
public class BundleInfo 
{
    /// <summary>
    /// bundle名字
    /// </summary>
    public string bundle_name;

    /// <summary>
    /// 这个bundle资源对应的crc
    /// </summary>
    public string crc;

    /// <summary>
    /// 这个bundle所包含资源的路径列表
    /// </summary>
    public List<string> assetLst;
}

/// <summary>
/// 一个Asset数据 【用于序列化为json文件】
/// </summary>
public class AssetInfo 
{
    /// <summary>
    /// 这个资源的相对路径
    /// </summary>
    public string asset_path;

    /// <summary>
    /// 这个资源所属的AB包名字
    /// </summary>
    public string bundle_name;

    /// <summary>
    /// 这个资源所依赖的AB名字列表
    /// </summary>
    public List<string> dependenciesLst;
}

/// <summary>
/// ModuleABConfig对象 对应 整个单个模块的json文件
/// </summary>
public class ModuleABConfig  
{
    public ModuleABConfig(int assetCount) 
    {
        BundleInfoDic = new Dictionary<string, BundleInfo>();
        AssetInfoArr = new AssetInfo[assetCount];
    }

    public ModuleABConfig() { }

    /// <summary>
    /// 所有的assetbundle信息
    /// key:AssetBundle名字
    /// </summary>
    public Dictionary<string, BundleInfo> BundleInfoDic;

    /// <summary>
    /// 所有的Asset资源
    /// </summary>
    public AssetInfo[] AssetInfoArr;

    /// <summary>
    /// 新增一个bundle
    /// </summary>
    /// <param name="bundleName">assetbundle名字</param>
    /// <param name="info">assetbundle名字信息</param>
    public void AddBundle(string bundleName,BundleInfo info) 
    {
        BundleInfoDic[bundleName] = info;
    }

    /// <summary>
    /// 新增一个资源记录
    /// </summary>
    /// <param name="index"></param>
    /// <param name="assetInfo"></param>
    public void AddAsset(int index, AssetInfo assetInfo)
    {
        AssetInfoArr[index] = assetInfo;
    }
}

ABEditor

/****************************************************
    文件:ABEditor.cs
	作者:赵深圳
    邮箱: 1329346609@qq.com
    日期:2022/5/7 15:54:51
	功能:AB包编辑器
*****************************************************/

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;

/// <summary>
/// 生成AB包编辑器工具
/// </summary>
public class ABEditor  
{
    /// <summary>
    /// 需要打包的资源所在目标
    /// </summary>
    public static string rootPath = Application.dataPath + "/1SimpleABManager/Download";

    /// <summary>
    /// AB包输出路径
    /// </summary>
    public static string outABPath=Application.streamingAssetsPath;

    /// <summary>
    /// 记录每个资源所有在的AB包文件
    /// key为路径
    /// value为AB包名字
    /// </summary>
    public static Dictionary<string, string> m_Asset2BundleDic = new Dictionary<string, string>();

    /// <summary>
    /// 所有需要打包的资源对应的AssetBundleBuild
    /// 一个文件对应一个AssetBundleBuild
    /// </summary>
    public static List<AssetBundleBuild> m_AssetBundleBuildLst = new List<AssetBundleBuild>();

    /// <summary>
    /// 记录每个Asset资源所依赖的AB包文件
    /// key:Asset名字
    /// Value:每个资源所依赖的AB包列表
    /// </summary>
    public static Dictionary<string, List<string>> m_Asset2DepenendciesDic = new Dictionary<string, List<string>>();

    /// <summary>
    /// 打包AB包
    /// </summary>
    [MenuItem("ZSTools/BuildABTools")]
    public static void BuildAssetBundle() 
    {
        Log.ZSZLog(LogCategory.Normal,"开始打AB包!!!");

        if (Directory.Exists(outABPath))
        {
            Directory.Delete(outABPath,true);
        }

        //打包所有的文件夹

        DirectoryInfo rootDir = new DirectoryInfo(rootPath);
        //获取资源路径下所有的文件夹
        //这里获取的文件夹,不包括自身,所有在资源所在文件(Application.dataPath + "/1SimpleABManager/Download")下不要放打包资源,会打包不到
        //放在资源所在文件所在的下一级文件,如果需要放在资源文件夹中,请把自身加入到数组中
        DirectoryInfo[] dirs = rootDir.GetDirectories();     
        foreach (DirectoryInfo moduleDir in dirs)
        {
            string moduleName = moduleDir.Name;
            m_AssetBundleBuildLst.Clear();
            m_Asset2BundleDic.Clear();
            m_Asset2DepenendciesDic.Clear();
            ScanChildDireations(moduleDir);
            AssetDatabase.Refresh();
            string moduleOutPath = outABPath + "/" + moduleName;
            if (Directory.Exists(moduleOutPath))
            {
                Directory.Delete(moduleOutPath, true);
            }
            Directory.CreateDirectory(moduleOutPath);
            //打包
            BuildPipeline.BuildAssetBundles(moduleOutPath,m_AssetBundleBuildLst.ToArray(),BuildAssetBundleOptions.None,EditorUserBuildSettings.activeBuildTarget);
             //计算依赖关系
            CalAssetDependencies();
            Log.ZSZLog(LogCategory.Normal, "计算依赖关系完毕!!!");
            //生成配置文件
            SaveModuleABConfig(moduleName);
            Log.ZSZLog(LogCategory.Normal, "生成配置文件完毕!!!");
            AssetDatabase.Refresh();
        }

        Log.ZSZLog(LogCategory.Normal,"打包AB包结束!!!");
    }

    /// <summary>
    /// 根据指定的文件夹
    /// 将这个文件夹下所有的一级子文件夹打成一个AB包
    /// 递归这个文件夹下所有的子文件夹
    /// </summary>
    /// <param name="directoryInfo"></param>
    private static void ScanChildDireations(DirectoryInfo directoryInfo)
    {
        if (directoryInfo.Name.EndsWith("CSProject")|| directoryInfo.Name.EndsWith(".cs"))
        {
            return;
        }

        ScanCurrDirectory(directoryInfo);
        
        //递归当前路径下的所有文件夹
        //使所有的一级文件夹为一个模块
        DirectoryInfo[] dirs = directoryInfo.GetDirectories();
        foreach (var item in dirs)
        {
            ScanChildDireations(item);
        }
    }

    /// <summary>
    /// 遍历当前路径下的文件,并打成一个AB包
    /// </summary>
    /// <param name="directoryInfo"></param>
    private static void ScanCurrDirectory(DirectoryInfo directoryInfo) 
    {
        List<string> assetNameLst = new List<string>();

        //获取指定文件夹中所有的文件
        FileInfo[] fileInfoArr = directoryInfo.GetFiles();

        foreach (FileInfo fileInfo in fileInfoArr)
        {
            if (fileInfo.FullName.EndsWith(".meta"))
            {
                continue;
            }
            //assetName的格式类似 "Assets/GAssets/Launch/Sphere.prefab"
            string assetName = fileInfo.FullName.Substring(Application.dataPath.Length-"Assets".Length).Replace('\\','/');
            assetNameLst.Add(assetName);
        }

        if (assetNameLst.Count > 0)
        {
            string assetBundleName = directoryInfo.FullName.Substring(Application.dataPath.Length + 1).Replace('\\','_').ToLower();
            AssetBundleBuild build = new AssetBundleBuild();
            build.assetBundleName = assetBundleName;
            build.assetNames = new string[assetNameLst.Count];
            for (int i = 0; i < assetNameLst.Count; i++)
            {
                build.assetNames[i] = assetNameLst[i];
                m_Asset2BundleDic.Add(assetNameLst[i],assetBundleName);
            }
            m_AssetBundleBuildLst.Add(build);
        }
    }

    /// <summary>
    /// 计算每个资源依赖的AB包列表
    /// </summary>
    private static void CalAssetDependencies()
    {
        foreach (string asset in m_Asset2BundleDic.Keys)
        {
            //当前资源所在的AB包
            string assetBundle = m_Asset2BundleDic[asset];
            //获取当前资源所有的依赖
            string[] dependencies = AssetDatabase.GetDependencies(asset);
            List<string> assetLst = new List<string>();
            //获取当前所有依赖的资源的名字
            if (dependencies != null && dependencies.Length > 0)
            {
                foreach (string oneAsset in dependencies)
                {
                    if (oneAsset==asset||oneAsset.EndsWith(".cs"))
                    {
                        continue;
                    }
                    assetLst.Add(oneAsset);
                }
            }
            if (assetLst.Count > 0)
            {
                List<string> abList = new List<string>();
                //获取当前所有的依赖资源所在的ab包
                foreach (string oneAsset in assetLst)
                {
                    bool result= m_Asset2BundleDic.TryGetValue(oneAsset,out string bundle);
                    if (result==true)
                    {
                        if (bundle!= assetBundle)
                        {
                            abList.Add(bundle);
                        }
                    }
                }
                //存入依赖字典中
                m_Asset2DepenendciesDic.Add(asset,abList);
            }

        }
    }

    /// <summary>
    /// 将一个模块的资源依赖关系数据保存json文件
    /// </summary>
    /// <param name="moduleNmae">模块名字</param>
    private static void SaveModuleABConfig(string moduleNmae) 
    {
        ModuleABConfig config = new ModuleABConfig();
        //记录AB包信息 遍历所有AssetBundleBuild
        foreach (AssetBundleBuild build in m_AssetBundleBuildLst)
        {
            BundleInfo bundleInfo = new BundleInfo();
            //获取当前AssetBundleBuild的名字(AB包名)
            bundleInfo.bundle_name = build.assetBundleName;
            bundleInfo.assetLst = new List<string>();
            //遍历当前AB包中所有的资源信息
            foreach (string item in build.assetNames)
            {
                bundleInfo.assetLst.Add(item);
            }
            //计算CRC
            string abFilePath = outABPath + "/" + moduleNmae + "/" + bundleInfo.bundle_name;
            using (FileStream stream=File.OpenRead(abFilePath))
            {
                bundleInfo.crc = AssetUtility.GetCRC32Hash(stream);
            }
            config.AddBundle(bundleInfo.bundle_name,bundleInfo);
        }

        //记录每个资源的依赖关系
        int assetIndex = 0;
        //获取所有的AB名字信息
        foreach (var item in m_Asset2BundleDic)
        {
            AssetInfo assetInfo = new AssetInfo();
            assetInfo.asset_path = item.Key;//设置路径
            assetInfo.bundle_name = item.Value;//设置AB包名字
            assetInfo.dependenciesLst = new List<string>();
            //获取所有的依赖路径
            bool result = m_Asset2DepenendciesDic.TryGetValue(item.Key,out List<string> dependencies);
            if (result==true)
            {
                for (int i = 0; i < dependencies.Count; i++)
                {
                    string bundleNmae = dependencies[i];
                    assetInfo.dependenciesLst.Add(bundleNmae);//加入到资源所依赖的资源列表中
                }
            }
            config.AddAsset(assetIndex,assetInfo);
            assetIndex++;
        }

        string moduleConfigName = moduleNmae.ToLower() + ".json";
        string jsonPath = outABPath + "/" + moduleNmae + "/" + moduleConfigName;
        if (File.Exists(jsonPath))
        {
            File.Delete(jsonPath);
        }
        File.Create(jsonPath).Dispose();
        string jsonData = LitJson.JsonMapper.ToJson(config);
        File.WriteAllText(jsonPath,ConvertJsonString( jsonData));
    }

    /// <summary>
    /// 格式化json
    /// </summary>
    /// <param name="str">输入json字符串</param>
    /// <returns>返回格式化后的字符串</returns>
    private static string ConvertJsonString(string str)
    {
        JsonSerializer serializer = new JsonSerializer();

        TextReader tr = new StringReader(str);

        JsonTextReader jtr = new JsonTextReader(tr);

        object obj = serializer.Deserialize(jtr);
        if (obj != null)
        {
            StringWriter textWriter = new StringWriter();

            JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
            {
                Formatting = Formatting.Indented,

                Indentation = 4,

                IndentChar = ' '
            };

            serializer.Serialize(jsonWriter, obj);

            return textWriter.ToString();
        }
        else
        {
            return str;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值