天天看點

unity menuitem_Unity熱更_打AssetBundles包Unity熱更_打AssetBundles包

Unity熱更_打AssetBundles包

Unity開發離不了熱更新,現在市面上有很多的熱更方案,XLua、ToLua以及C#熱更方案ILRuntime,以騰訊的XLua為例,若要實作熱更新,AssetBundles是不可規避的一個環節,這其中包括AssetBundles的生成與加載,本文以生成AssetBundles為主,主要來講自動化打AssetBundles包,至于AssetBundles包體的加載抽時間會再寫一篇部落格。

手動添加AssetBundle标簽方式

這是最常見的AssetBundle打包方式,給需要打Bundle包的預制體增加AssetBundle标簽和字尾,然後在代碼中實作Bundle的生成,這種方式在這裡不做介紹,因為在百度上很容易就能搜到很多内容。

全自動打AssetBundle包方式

這種方式隻要你知道需要打Bundle包的檔案夾路徑就可以打Bundle包無論是預制體、Lua腳本、或者字型、貼圖等等都可以自動生成Bundle包體,友善快捷,省去手動添加标簽的繁瑣。

我把所有的預制體都放在了BundleResources檔案夾下

所有的Lua腳本放在了Lua檔案夾下,

通過代碼把預支體和Lua都打成AssetBundle包

生成Md5檔案,後期熱更時需要對比Md5進行資源更新

生成map檔案,記錄資源和AssetBundle包的對應關系

unity menuitem_Unity熱更_打AssetBundles包Unity熱更_打AssetBundles包

直接上代碼,注釋很清晰

using System.Collections;using System.Collections.Generic;using System.IO;using UnityEditor;using UnityEngine;public class AssetsBundleEditor : Editor{    public static string luaDirName = "Lua";//Lua原檔案檔案夾    public static string tempLuaDirName = "TempLua";//Lua檔案使用檔案夾    public static string assetsDirName = "AssetBundles";//AssetBundle檔案夾名    public static string prefabsDirName = "BundleResources";//所有預制體的檔案夾名    public static string extName=".unity3d";//AssetBundle檔案字尾名    public static List abList = new List();    [MenuItem("SJL/BuildAndroid")]    static void BuildAndroid() {//出Android包體,可根據需求配置其他的打包方式        Build(BuildTarget.Android);    }    static string GetStreamingAssets() {        return Application.streamingAssetsPath;    }    //打Bundle包    static void Build(BuildTarget buildTarget) {        string assetsBundlePath = GetStreamingAssets() + "/" + assetsDirName;        if (Directory.Exists(assetsBundlePath))        {            Directory.Delete(assetsBundlePath, true);        }        Directory.CreateDirectory(assetsBundlePath);        AssetDatabase.Refresh();        abList.Clear();        LuaCopyToTempLua();        InitLuaABList();        InitPrefabsABList();        BuildAssetBundleOptions options = BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression;        BuildPipeline.BuildAssetBundles(assetsBundlePath, abList.ToArray(), BuildAssetBundleOptions.None, buildTarget);        CreateMd5File();        CreateMapFile();        AssetDatabase.Refresh();        Debug.Log("打AB包成功:\n"+System.DateTime.Now.ToString("yyyy-MM-dd||hh:mm:ss"));    }    //Lua檔案夾下的lua檔案轉移至TempLua檔案夾下    static void LuaCopyToTempLua() {        string luaDir = Application.dataPath + "/" + luaDirName;        string tempDir = Application.dataPath + "/" + tempLuaDirName;        if (!Directory.Exists(luaDir))        {            return;        }                string[] files = Directory.GetFiles(luaDir, "*.lua", SearchOption.AllDirectories);        if (files == null||files.Length==0)        {            return;        }        if (Directory.Exists(tempDir))        {            Directory.Delete(tempDir,true);        }        for (int i = 0; i < files.Length; i++)        {            string filePath = files[i];            string dirPath = Path.GetDirectoryName(filePath);            string tempDirPath = tempDir + dirPath.Replace(luaDir, string.Empty);            if (!Directory.Exists(tempDirPath))            {                Directory.CreateDirectory(tempDirPath);            }            string tempFilePath = tempDirPath + filePath.Replace(dirPath, string.Empty) + ".bytes";            File.Copy(filePath, tempFilePath, true);        }        AssetDatabase.Refresh();    }    //将Lua添加到AssetBundleBuild清單    static void InitLuaABList() {        string tempLuaDirPath = Application.dataPath + "/" + tempLuaDirName;        string[] dirArr = Directory.GetDirectories(tempLuaDirPath);        string bundleName = "lua/lua_" + tempLuaDirName.ToLower() + extName;        AddABList(bundleName,"Assets/"+tempLuaDirName,"*.bytes");        for (int i = 0; i < dirArr.Length; i++)        {            string dirPath = dirArr[i];            bundleName = "lua/lua_"+dirPath.Replace(tempLuaDirPath,"").Replace("/","").ToLower()+extName;            string path = "Assets"+dirPath.Replace(Application.dataPath, "");            AddABList(bundleName,path,"*.bytes");        }            }    //将預制體添加到AssetBundleBuild清單    static void InitPrefabsABList() {        string prefabsDirPath = Application.dataPath + "/" + prefabsDirName;        string[] dirArr = Directory.GetDirectories(prefabsDirPath);        string bundleName = "prefab/prefab_"+prefabsDirName.ToLower() + extName;        AddABList(bundleName,"Assets/"+prefabsDirName, "*.prefab");        for (int i = 0; i < dirArr.Length; i++)        {            string dirPath = dirArr[i];            bundleName = "prefab/prefab_"+dirPath.Replace(prefabsDirPath, "").Replace("/", "").ToLower() + extName;            string path = "Assets" + dirPath.Replace(Application.dataPath, "");            AddABList(bundleName, path, "*.prefab");        }    }    ///     /// 添加檔案至AssetBundleBuild清單    ///     ///     /// 檔案夾相對路徑(例如:Assets/...)    /// 篩查條件    static void AddABList(string bundleName,string path,string pattern) {        string[] files = Directory.GetFiles(path,pattern);        if (files==null||files.Length==0)        {            return;        }        for (int i = 0; i < files.Length; i++)        {            files[i] = files[i].Replace("\\","/");        }        AssetBundleBuild abBuild = new AssetBundleBuild();        abBuild.assetBundleName = bundleName;        abBuild.assetNames = files;        abList.Add(abBuild);    }    //建立Md5檔案    static void CreateMd5File() {        string assetBundlePath = GetStreamingAssets() + "/" + assetsDirName;        string mainBundle = assetBundlePath + "/AssetBundles";        string md5FilePath = assetBundlePath + "/" + "files_md5.md5";        if (File.Exists(md5FilePath))        {            File.Delete(md5FilePath);        }        List<string> mPaths = new List<string>();        List<string> mFiles = new List<string>();        string[] files = GetDirFiles(assetBundlePath,new string[] {".meta",".DS_Store"});        if (files==null||files.Length==0)        {            return;        }        foreach (var item in files)        {            mFiles.Add(item);        }        FileStream fs = new FileStream(md5FilePath,FileMode.Create);        StreamWriter sw = new StreamWriter(fs);        for (int i = 0; i < mFiles.Count; i++)        {            string file = mFiles[i];            if (string.IsNullOrEmpty(file)||file.EndsWith(".meta")||file.Contains(".DS_Store"))            {                continue;            }            string md5 = FileToMd5(file);            long size = GetFileSize(file);            string fileName = file.Replace(assetBundlePath+"/",string.Empty);            string str =fileName+"|"+ md5 + "|" + size;            sw.WriteLine(str);        }        sw.Close();        fs.Close();    }    //建立Map檔案    static void CreateMapFile()    {        string assetBundlePath = GetStreamingAssets() + "/" + assetsDirName;        string mapFilePath = assetBundlePath + "/" + "files_map.map";        if (abList == null || abList.Count == 0)        {            return;        }        if (File.Exists(mapFilePath))        {            File.Delete(mapFilePath);        }        FileStream fs = new FileStream(mapFilePath, FileMode.Create);        StreamWriter sw = new StreamWriter(fs);        string[] filesArr = null;        string abName = null;        string fileName = null;        foreach (AssetBundleBuild ab in abList)        {            filesArr = ab.assetNames;            abName = ab.assetBundleName;            for (int i = 0; i < filesArr.Length; i++)            {                fileName = filesArr[i];                if (fileName.EndsWith(".meta") || fileName.EndsWith(".DS_Store"))                {                    continue;                }                fileName = fileName.Replace("Assets/", string.Empty);                sw.WriteLine(fileName + "|" + abName);            }        }        sw.Close();        fs.Close();    }    //檔案轉化為Md5    static string FileToMd5(string file)    {        try        {            FileStream fs = new FileStream(file, FileMode.Open);            System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();            byte[] retVal = md5.ComputeHash(fs);            fs.Close();            System.Text.StringBuilder sb = new System.Text.StringBuilder();            for (int i = 0; i < retVal.Length; i++)            {                sb.Append(retVal[i].ToString("x2"));            }            return sb.ToString();        }        catch (System.Exception ex)        {            throw new System.Exception("md5file() fail, error:" + ex.Message);        }    }    //擷取檔案大小    static long GetFileSize(string filePath) {        long size = 0;        if (!File.Exists(filePath))        {            size = -1;        }        else        {            FileInfo info = new FileInfo(filePath);            size = info.Length;        }        return size;    }    ///     /// 得到檔案夾下所有的檔案    ///     /// 檔案夾    /// 需要剔除的字尾名    ///     static string[] GetDirFiles(string dirPath,string[] useLessSuffixArr) {        List<string> fileList = new List<string>();        string[] files = Directory.GetFiles(dirPath,"*",SearchOption.AllDirectories);        if (files==null||files.Length==0)        {            return null;        }        for (int i = 0; i < files.Length; i++)        {            string file = files[i];            bool isTrue = true;            for (int j = 0; j < useLessSuffixArr.Length; j++)            {                string extension = useLessSuffixArr[j];                if (Path.GetExtension(file)==extension)                {                    isTrue = false;                    break;                }            }            if (isTrue)            {                fileList.Add(file);            }        }        return fileList.ToArray();    }}