• [Unity Asset]AssetBundle系列——游戏资源打包


    转载:http://www.cnblogs.com/sifenkesi/p/3557231.html

    将本地资源打包,然后放到资源服务器上供游戏客户端下载或更新。服务器上包含以下资源列表:
    (1)游戏内容资源assetbundle
    (2)资源维护列表,包含每个资源的名字(完整路径名)和对应的版本号[资源名,版本号],如下表所示(VersionNum.xml):

    <VersionNum>
      <File FileName="Assets.Resources.BigLevelTexture.TestLevel.assetbundle" Num="1" />
      <File FileName="Assets.Resources.EquipmentTexture.Test001.assetbundle" Num="1" />
      <File FileName="Assets.Resources.EquipmentTexture.Test002.assetbundle" Num="1" />
      <File FileName="Assets.Resources.EquipmentTexture.Test003.assetbundle" Num="1" />
      <File FileName="Assets.Resources.EquipmentTexture.Test004.assetbundle" Num="1" />
      <File FileName="Assets.Resources.PetTexture.Empty.assetbundle" Num="1" />
    </VersionNum>
    

    那么本地客户端的资源打包编辑器就需要完成以下工作:将资源打包、生成版本号。
    我们采用通过MD5码对比的方式来对版本号进行管理,如果某资源的MD5码变更了,则将其版本号+1,否则不变。那么,可以将编辑器的具体任务划分如下:
    (1)将资源打包成assetbundle,并放到指定目录下
    (2)为每个assetbund生成最新MD5码,用于检查资源是否有修改
    (3)比较新旧MD5码列表,产生资源变更列表,对于每个变更的资源,将其版本号+1
    (4)将变更列表文件也打包成assetbundle

    各个平台使用的资源包时各自独立的,打包资源代码时的选项不一样,编辑器界面如下,图2中的4个按钮每个对应上述的一步操作:

    最终生成的资源目录结构如下所示:

    编辑器代码如下所示(包括菜单项和窗口):

    using UnityEditor;
    using UnityEngine;
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    
    public class AssetBundleController : EditorWindow
    {
        public static AssetBundleController window;
        public static UnityEditor.BuildTarget buildTarget = BuildTarget.StandaloneWindows;
    
        [MenuItem("XiYouEditor/AssetBundle/AssetBundle For Windows32", false, 1)]
        public static void ExecuteWindows32()
        {
            if (window == null)
            {
                window = (AssetBundleController)GetWindow(typeof(AssetBundleController));
            }
            buildTarget = UnityEditor.BuildTarget.StandaloneWindows;
            window.Show();
        }
    
        [MenuItem("XiYouEditor/AssetBundle/AssetBundle For IPhone", false, 2)]
        public static void ExecuteIPhone()
        {
            if (window == null)
            {
                window = (AssetBundleController)GetWindow(typeof(AssetBundleController));
            }
            buildTarget = UnityEditor.BuildTarget.iPhone;
            window.Show();
        }
    
        [MenuItem("XiYouEditor/AssetBundle/AssetBundle For Mac", false, 3)]
        public static void ExecuteMac()
        {
            if (window == null)
            {
                window = (AssetBundleController)GetWindow(typeof(AssetBundleController));
            }
            buildTarget = UnityEditor.BuildTarget.StandaloneOSXUniversal;
            window.Show();
        }
    
        [MenuItem("XiYouEditor/AssetBundle/AssetBundle For Android", false, 4)]
        public static void ExecuteAndroid()
        {
            if (window == null)
            {
                window = (AssetBundleController)GetWindow(typeof(AssetBundleController));
            }
            buildTarget = UnityEditor.BuildTarget.Android;
            window.Show();
        }
    
        [MenuItem("XiYouEditor/AssetBundle/AssetBundle For WebPlayer", false, 5)]
        public static void ExecuteWebPlayer()
        {
            if (window == null)
            {
                window = (AssetBundleController)GetWindow(typeof(AssetBundleController));
            }
            buildTarget = UnityEditor.BuildTarget.WebPlayer;
            window.Show();
        }
    
        void OnGUI()
        {
            if (GUI.Button(new Rect(10f, 10f, 200f, 50f), "(1)CreateAssetBundle"))
            {
                CreateAssetBundle.Execute(buildTarget);
                EditorUtility.DisplayDialog("", "Step (1) Completed", "OK");
            }
    
            if (GUI.Button(new Rect(10f, 80f, 200f, 50f), "(2)Generate MD5"))
            {
                CreateMD5List.Execute(buildTarget);
                EditorUtility.DisplayDialog("", "Step (2) Completed", "OK");
            }
    
            if (GUI.Button(new Rect(10f, 150f, 200f, 50f), "(3)Compare MD5"))
            {
                CampareMD5ToGenerateVersionNum.Execute(buildTarget);
                EditorUtility.DisplayDialog("", "Step (3) Completed", "OK");
            }
    
            if (GUI.Button(new Rect(10f, 220f, 200f, 50f), "(4)Build VersionNum.xml"))
            {
                CreateAssetBundleForXmlVersion.Execute(buildTarget);
                EditorUtility.DisplayDialog("", "Step (4) Completed", "OK");
            }
        }
    
        public static string GetPlatformPath(UnityEditor.BuildTarget target)
        {
            string SavePath = "";
            switch (target)
            {
                case BuildTarget.StandaloneWindows:
                    SavePath = "Assets/AssetBundle/Windows32/";
                    break;
                case BuildTarget.StandaloneWindows64:
                    SavePath = "Assets/AssetBundle/Windows64/";
                    break;
                case BuildTarget.iPhone:
                    SavePath = "Assets/AssetBundle/IOS/";
                    break;
                case BuildTarget.StandaloneOSXUniversal:
                    SavePath = "Assets/AssetBundle/Mac/";
                    break;
                case BuildTarget.Android:
                    SavePath = "Assets/AssetBundle/Android/";
                    break;
                case BuildTarget.WebPlayer:
                    SavePath = "Assets/AssetBundle/WebPlayer/";
                    break;
                default:
                    SavePath = "Assets/AssetBundle/";
                    break;
            }
    
            if (Directory.Exists(SavePath) == false)
                Directory.CreateDirectory(SavePath);
    
            return SavePath;
        }
    
        public static string GetPlatformName(UnityEditor.BuildTarget target)
        {
            string platform = "Windows32";
            switch (target)
            {
                case BuildTarget.StandaloneWindows:
                    platform = "Windows32";
                    break;
                case BuildTarget.StandaloneWindows64:
                    platform = "Windows64";
                    break;
                case BuildTarget.iPhone:
                    platform = "IOS";
                    break;
                case BuildTarget.StandaloneOSXUniversal:
                    platform = "Mac";
                    break;
                case BuildTarget.Android:
                    platform = "Android";
                    break;
                case BuildTarget.WebPlayer:
                    platform = "WebPlayer";
                    break;
                default:
                    break;
            }
            return platform;
        }
    
    }
    

    (1)将资源打包成assetbundle,并放到自定目录下:

    using UnityEditor;
    using UnityEngine;
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    
    public class CreateAssetBundle
    {
        public static void Execute(UnityEditor.BuildTarget target)
        {
            string SavePath = AssetBundleController.GetPlatformPath(target);
            
            // 当前选中的资源列表
            foreach (Object o in Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets))
            {
                string path = AssetDatabase.GetAssetPath(o);
    
                // 过滤掉meta文件和文件夹
                if(path.Contains(".meta") || path.Contains(".") == false)
                    continue;
    
                // 过滤掉UIAtlas目录下的贴图和材质(UI/Common目录下的所有资源都是UIAtlas)
                if (path.Contains("UI/Common"))
                {
                    if ((o is Texture) || (o is Material))
                        continue;
                }
    
                path = SavePath + ConvertToAssetBundleName(path);
                path = path.Substring(0, path.LastIndexOf('.'));
                path += ".assetbundle";
    
                BuildPipeline.BuildAssetBundle(o, null, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, target);
            }
    
            // scene目录下的资源
    
    
            AssetDatabase.Refresh();
        }
    
        static string ConvertToAssetBundleName(string ResName)
        {
            return ResName.Replace('/', '.');
        }
    
    }
    

    (2)为每个assetbund生成MD5码,用于检查资源是否有修改

    using UnityEngine;
    using UnityEditor;
    using System.IO;
    using System.Xml;
    using System.Collections;
    using System.Collections.Generic;
    using System.Security.Cryptography;
    
    public class CreateMD5List
    {
        public static void Execute(UnityEditor.BuildTarget target)
        {
            string platform = AssetBundleController.GetPlatformName(target);
            Execute(platform);
            AssetDatabase.Refresh();
        }
    
        public static void Execute(string platform)
        {
            Dictionary<string, string> DicFileMD5 = new Dictionary<string, string>();
            MD5CryptoServiceProvider md5Generator = new MD5CryptoServiceProvider();
    
            string dir = System.IO.Path.Combine(Application.dataPath, "AssetBundle/" + platform);
            foreach (string filePath in Directory.GetFiles(dir))
            {
                if (filePath.Contains(".meta") || filePath.Contains("VersionMD5") || filePath.Contains(".xml"))
                    continue;
    
                FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                byte[] hash = md5Generator.ComputeHash(file);
                string strMD5 = System.BitConverter.ToString(hash);
                file.Close();
    
                string key = filePath.Substring(dir.Length + 1, filePath.Length - dir.Length - 1);
    
                if (DicFileMD5.ContainsKey(key) == false)
                    DicFileMD5.Add(key, strMD5);
                else
                    Debug.LogWarning("<Two File has the same name> name = " + filePath);
            }
    
            string savePath = System.IO.Path.Combine(Application.dataPath, "AssetBundle/") + platform + "/VersionNum";
            if (Directory.Exists(savePath) == false)
                Directory.CreateDirectory(savePath);
    
            // 删除前一版的old数据
            if (File.Exists(savePath + "/VersionMD5-old.xml"))
            {
                System.IO.File.Delete(savePath + "/VersionMD5-old.xml");
            }
    
            // 如果之前的版本存在,则将其名字改为VersionMD5-old.xml
            if (File.Exists(savePath + "/VersionMD5.xml"))
            {
                System.IO.File.Move(savePath + "/VersionMD5.xml", savePath + "/VersionMD5-old.xml");
            }
    
            XmlDocument XmlDoc = new XmlDocument();
            XmlElement XmlRoot = XmlDoc.CreateElement("Files");
            XmlDoc.AppendChild(XmlRoot);
            foreach (KeyValuePair<string, string> pair in DicFileMD5)
            {
                XmlElement xmlElem = XmlDoc.CreateElement("File");
                XmlRoot.AppendChild(xmlElem);
    
                xmlElem.SetAttribute("FileName", pair.Key);
                xmlElem.SetAttribute("MD5", pair.Value);
            }
    
            // 读取旧版本的MD5
            Dictionary<string, string> dicOldMD5 = ReadMD5File(savePath + "/VersionMD5-old.xml");
            // VersionMD5-old中有,而VersionMD5中没有的信息,手动添加到VersionMD5
            foreach (KeyValuePair<string, string> pair in dicOldMD5)
            {
                if (DicFileMD5.ContainsKey(pair.Key) == false)
                    DicFileMD5.Add(pair.Key, pair.Value);
            }
    
            XmlDoc.Save(savePath + "/VersionMD5.xml");
            XmlDoc = null;
        }
    
        static Dictionary<string, string> ReadMD5File(string fileName)
        {
            Dictionary<string, string> DicMD5 = new Dictionary<string, string>();
    
            // 如果文件不存在,则直接返回
            if (System.IO.File.Exists(fileName) == false)
                return DicMD5;
    
            XmlDocument XmlDoc = new XmlDocument();
            XmlDoc.Load(fileName);
            XmlElement XmlRoot = XmlDoc.DocumentElement;
    
            foreach (XmlNode node in XmlRoot.ChildNodes)
            {
                if ((node is XmlElement) == false)
                    continue;
    
                string file = (node as XmlElement).GetAttribute("FileName");
                string md5 = (node as XmlElement).GetAttribute("MD5");
    
                if (DicMD5.ContainsKey(file) == false)
                {
                    DicMD5.Add(file, md5);
                }
            }
    
            XmlRoot = null;
            XmlDoc = null;
    
            return DicMD5;
        }
    
    }
    

    MD5列表如下所示:

    <Files>
      <File FileName="Assets.Resources.BigLevelTexture.TestLevel.assetbundle" MD5="54-00-42-38-D5-86-43-A6-57-9D-7C-09-3A-F8-6E-10" />
      <File FileName="Assets.Resources.EquipmentTexture.Test001.assetbundle" MD5="A1-19-D4-04-17-94-18-61-60-99-35-25-3F-7C-39-93" />
      <File FileName="Assets.Resources.EquipmentTexture.Test002.assetbundle" MD5="CF-36-DA-C8-D2-DB-CE-FD-4A-BF-31-81-A1-D1-D2-21" />
      <File FileName="Assets.Resources.EquipmentTexture.Test003.assetbundle" MD5="EF-30-78-AE-F8-F4-A0-EC-5B-4E-45-3F-1E-EF-42-44" />
      <File FileName="Assets.Resources.EquipmentTexture.Test004.assetbundle" MD5="3D-5D-A7-01-D2-B1-20-5F-B9-89-C5-CB-40-96-EC-89" />
      <File FileName="Assets.Resources.PetTexture.Empty.assetbundle" MD5="D9-AC-54-F8-EB-AA-1C-36-8C-2B-6C-12-37-AB-3B-48" />
    </Files>
    

    (3)比较新旧MD5码,生成资源变更列表

    using UnityEngine;
    using UnityEditor;
    using System.IO;
    using System.Xml;
    using System.Collections;
    using System.Collections.Generic;
    
    public class CampareMD5ToGenerateVersionNum
    {
        public static void Execute(UnityEditor.BuildTarget target)
        {
            string platform = AssetBundleController.GetPlatformName(target);
            Execute(platform);
            AssetDatabase.Refresh();
        }
    
        // 对比对应版本目录下的VersionMD5和VersionMD5-old,得到最新的版本号文件VersionNum.xml
        public static void Execute(string platform)
        {
            // 读取新旧MD5列表
            string newVersionMD5 = System.IO.Path.Combine(Application.dataPath, "AssetBundle/" + platform + "/VersionNum/VersionMD5.xml");
            string oldVersionMD5 = System.IO.Path.Combine(Application.dataPath, "AssetBundle/" + platform + "/VersionNum/VersionMD5-old.xml");
    
            Dictionary<string, string> dicNewMD5Info = ReadMD5File(newVersionMD5);
            Dictionary<string, string> dicOldMD5Info = ReadMD5File(oldVersionMD5);
    
            // 读取版本号记录文件VersinNum.xml
            string oldVersionNum = System.IO.Path.Combine(Application.dataPath, "AssetBundle/" + platform + "/VersionNum/VersionNum.xml");
            Dictionary<string, int> dicVersionNumInfo = ReadVersionNumFile(oldVersionNum);
    
            // 对比新旧MD5信息,并更新版本号,即对比dicNewMD5Info&&dicOldMD5Info来更新dicVersionNumInfo
            foreach (KeyValuePair<string, string> newPair in dicNewMD5Info)
            {
                // 旧版本中有
                if (dicOldMD5Info.ContainsKey(newPair.Key))
                {
                    // MD5一样,则不变
                    // MD5不一样,则+1
                    // 容错:如果新旧MD5都有,但是还没有版本号记录的,则直接添加新纪录,并且将版本号设为1
                    if (dicVersionNumInfo.ContainsKey(newPair.Key) == false)
                    {
                        dicVersionNumInfo.Add(newPair.Key, 1);
                    }
                    else if (newPair.Value != dicOldMD5Info[newPair.Key])
                    {
                        int num = dicVersionNumInfo[newPair.Key];
                        dicVersionNumInfo[newPair.Key] = num + 1;
                    }
                }
                else // 旧版本中没有,则添加新纪录,并=1
                {
                    dicVersionNumInfo.Add(newPair.Key, 1);
                }
            }
            // 不可能出现旧版本中有,而新版本中没有的情况,原因见生成MD5List的处理逻辑
    
            // 存储最新的VersionNum.xml
            SaveVersionNumFile(dicVersionNumInfo, oldVersionNum);
        }
    
        static Dictionary<string, string> ReadMD5File(string fileName)
        {
            Dictionary<string, string> DicMD5 = new Dictionary<string, string>();
    
            // 如果文件不存在,则直接返回
            if (System.IO.File.Exists(fileName) == false)
                return DicMD5;
    
            XmlDocument XmlDoc = new XmlDocument();
            XmlDoc.Load(fileName);
            XmlElement XmlRoot = XmlDoc.DocumentElement;
    
            foreach (XmlNode node in XmlRoot.ChildNodes)
            {
                if ((node is XmlElement) == false)
                    continue;
    
                string file = (node as XmlElement).GetAttribute("FileName");
                string md5 = (node as XmlElement).GetAttribute("MD5");
    
                if (DicMD5.ContainsKey(file) == false)
                {
                    DicMD5.Add(file, md5);
                }
            }
    
            XmlRoot = null;
            XmlDoc = null;
    
            return DicMD5;
        }
    
        static Dictionary<string, int> ReadVersionNumFile(string fileName)
        {
            Dictionary<string, int> DicVersionNum = new Dictionary<string, int>();
    
            // 如果文件不存在,则直接返回
            if (System.IO.File.Exists(fileName) == false)
                return DicVersionNum;
    
            XmlDocument XmlDoc = new XmlDocument();
            XmlDoc.Load(fileName);
            XmlElement XmlRoot = XmlDoc.DocumentElement;
    
            foreach (XmlNode node in XmlRoot.ChildNodes)
            {
                if ((node is XmlElement) == false)
                    continue;
    
                string file = (node as XmlElement).GetAttribute("FileName");
                int num = XmlConvert.ToInt32((node as XmlElement).GetAttribute("Num"));
    
                if (DicVersionNum.ContainsKey(file) == false)
                {
                    DicVersionNum.Add(file, num);
                }
            }
    
            XmlRoot = null;
            XmlDoc = null;
    
            return DicVersionNum;
        }
    
        static void SaveVersionNumFile(Dictionary<string, int> data, string savePath)
        {
            XmlDocument XmlDoc = new XmlDocument();
            XmlElement XmlRoot = XmlDoc.CreateElement("VersionNum");
            XmlDoc.AppendChild(XmlRoot);
    
            foreach (KeyValuePair<string, int> pair in data)
            {
                XmlElement xmlElem = XmlDoc.CreateElement("File");
                XmlRoot.AppendChild(xmlElem);
                xmlElem.SetAttribute("FileName", pair.Key);
                xmlElem.SetAttribute("Num", XmlConvert.ToString(pair.Value));
            }
    
            XmlDoc.Save(savePath);
            XmlRoot = null;
            XmlDoc = null;
        }
    
    }
    

    如下图所示,根据VersionMD5.xml和VersionMD5-old.xml对比产生VersionNum.xml:

    (4)将变更列表文件也打包成assetbundle

    也就是讲VersionNum.xml打包后供下载:

    using UnityEditor;
    using UnityEngine;
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    
    public class CreateAssetBundleForXmlVersion
    {
        public static void Execute(UnityEditor.BuildTarget target)
        {
            string SavePath = AssetBundleController.GetPlatformPath(target);
            Object obj = AssetDatabase.LoadAssetAtPath(SavePath + "VersionNum/VersionNum.xml", typeof(Object));
            BuildPipeline.BuildAssetBundle(obj, null, SavePath + "VersionNum/VersionNum.assetbundle", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, target);
    
            AssetDatabase.Refresh();
        }
    
        static string ConvertToAssetBundleName(string ResName)
        {
            return ResName.Replace('/', '.');
        }
    
    }
    
  • 相关阅读:
    openlayers方法总结
    AJAX 数据库实例
    AJAX 请求服务器
    得到XMLHttpRequest对象
    AJAX 简介
    AJAX 服务器端的脚本
    HTTP GET 最多发送100个字符
    AJAX XMLHttpRequest 对象
    Dictionary、ArrayList、Hashtable和数组 Array 的区别
    AJAX 请求实例
  • 原文地址:https://www.cnblogs.com/daxiaxiaohao/p/4207492.html
Copyright © 2020-2023  润新知