修改BundleEditor
添加了热更之后,打包就有了普通打包和热更打包的区别,我们需要修改BundleEditor
的Build
方法
首先在BundleEditor
添加NormalBuild
方法,代表的是普通打包
1 2 3 4 5 6
| [MenuItem("Tools/打AB包")] public static void NormalBuild() { Build(); }
|
然后修改Build
方法,给这个方法添加参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public static void Build(bool hotfix = false,string abmd5Path = "",string hotCount = "1") { for (int i = 0; i < oldNames.Length; i++) { AssetDatabase.RemoveAssetBundleName(oldNames[i], true); EditorUtility.DisplayProgressBar("清除AB包名", "名字:" + oldNames[i],i*1.0f/oldNames.Length); } if (hotfix) { ReadMD5Calc(abmd5Path,hotCount); } else { WriteABMD5(); } AssetDatabase.SaveAssets(); }
|
修改BuildApp
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| [MenuItem("Build/标准包")] public static void Build() { BundleEditor.NormalBuild(); SaveVersion(PlayerSettings.bundleVersion, PlayerSettings.applicationIdentifier); }
#region 打包机调用打包PC版本 public static void BuildPC() { BundleEditor.NormalBuild(); } #endregion #region 打包机调用打包安卓版本 public static void BuildAndroid() { BundleEditor.NormalBuild(); } #endregion #region 打包机调用打包IOS版本 public static void BuildIOS() { BundleEditor.NormalBuild(); } #endregion
|
进行热更包打包
热更包打包的流程是:先打普通AB包——读取指定的ABMD5文件中的MD5信息——将旧MD5信息与新AB包的MD5比对,筛选出要打的包和新包——存储新包
修改BundleEditor
,添加m_PacMD5Dic
,用来储存旧ABMD5信息方便筛选。
添加ReadMD5Calc
方法,这是生成热更包的主要方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| private static Dictionary<string,ABMD5Base> m_PacMD5Dic = new Dictionary<string,ABMD5Base>();
static void ReadMD5Calc(string abmd5Path,string hotCount) { m_PacMD5Dic.Clear(); using(FileStream fs = new FileStream(abmd5Path,FileMode.Open, FileAccess.Read)) { BinaryFormatter bf = new BinaryFormatter(); ABMD5 aBMD5 = bf.Deserialize(fs) as ABMD5; foreach (ABMD5Base aBMD5Base in aBMD5.ABMD5List) { m_PacMD5Dic.Add(aBMD5Base.Name, aBMD5Base); } }
List<string> changeList = new List<string>(); DirectoryInfo directoryInfo = new DirectoryInfo(m_BundleTargetPath); FileInfo[] abFiles = directoryInfo.GetFiles("*", SearchOption.AllDirectories); for (int i = 0; i < abFiles.Length; i++) { if (!abFiles[i].Name.EndsWith(".manifest") && !abFiles[i].Name.EndsWith(".meta")) { string name = abFiles[i].Name; string md5 = MD5Manager.Instance.BuildFileMD5(abFiles[i].FullName); ABMD5Base aBMD5Base = null; if (!m_PacMD5Dic.ContainsKey(name)) { changeList.Add(name); } else { if (m_PacMD5Dic.TryGetValue(name, out aBMD5Base)) { if (md5 != aBMD5Base.MD5) { changeList.Add(name); } } } } }
CopyABAndGenerateConfig(changeList, hotCount); } static void CopyABAndGenerateConfig(List<string> changeList,string hotCount) {
}
|
热更包存储
在工程文件夹内新建Hot文件夹,用来存储热更包
将Hot文件夹上传SVN
在BundleEditor
中添加Hot文件夹路径
1 2 3 4 5 6
| public class BundleEditor { private static string m_HotPath = Application.dataPath + "/../Hot/" + EditorUserBuildSettings.activeBuildTarget.ToString();
}
|
在BundleEditor
中添加CopyABAndGenerateConfig
方法和DeleteAllFile
方法
每次生成热更包时都要清理旧的热更包,热更包的hotCount只在热更包对应的服务器URL指示里用到
配合Jenkins,旧的热更包在被清理之前就被归档了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| static void CopyABAndGenerateConfig(List<string> changeList,string hotCount) { if (!Directory.Exists(m_HotPath)) { Directory.CreateDirectory(m_HotPath); } DeleteAllFile(m_HotPath); foreach (string abName in changeList) { if (!abName.EndsWith(".manifest")) { File.Copy(m_BundleTargetPath + "/" + abName, m_HotPath + "/" + abName); } } } static void DeleteAllFile(string fullPath) { if(Directory.Exists(fullPath)) { try { DirectoryInfo directory = new DirectoryInfo(fullPath); FileInfo[] files = directory.GetFiles("*",SearchOption.AllDirectories); foreach (FileInfo file in files) { if (file.Name.EndsWith(".meta")) { continue; } File.Delete(file.FullName); } } catch (Exception e) { Debug.LogException(e); } } } }
|
完全删除文件的方法,可以在BuildApp
文件内的DeleteDir
方法查看
热更包配置表
热更配置表的内容
游戏每次更新,有固定的版本号,每个版本都可能会打一次或多次热更补丁(Patches),每个热更补丁有固定的补丁号(补丁号就是我们热更打包工具中“hotCount”指示的内容)
ServerInfo
:服务器整体的数据结构,里面记录了各个版本的信息
VersionInfo
:记录游戏产品的版本号,以及此版本下对应的补丁。如果有渠道服,还要记录不同渠道的版本信息
Patches
:记录某个版本某个补丁的信息。包括此补丁的补丁号(Version
)、对于此补丁的描述(Description
)、所有的补丁文件(Files
)
Patch
:记录具体的每个补丁的信息。包括此补丁对应的AB包名(Name
)、下载地址(Url
)、平台(Platform
)、MD5码、AB包的大小(Size
)
ServerInfo数据结构
我们在ResFrame——Frames——ResourceFrm文件夹内新建Download文件夹 ,在其中新建ServerInfo
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| using System.Collections.Generic; using System.Xml.Serialization;
[System.Serializable] public class ServerInfo { [XmlElement("GameVersion")] public VersionInfo[] GameVersion; }
[System.Serializable] public class VersionInfo { [XmlAttribute] public string Version; [XmlElement] public Patches[] Patches; }
[System.Serializable] public class Patches { [XmlAttribute] public int Version; [XmlAttribute] public string Description; [XmlElement] public List<Patch> Files; }
[System.Serializable] public class Patch { [XmlAttribute] public string Name; [XmlAttribute] public string Url; [XmlAttribute] public string Platform; [XmlAttribute] public string MD5; [XmlAttribute] public float Size; }
|
生成配置表
在CopyABAndGenerateConfig
方法中添加生成的方法:
每次生成热更包时有一个Patch.Xml和各个热更包放在一起,它里面就记录了这些热更包的具体信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| static void CopyABAndGenerateConfig(List<string> changeList,string hotCount) { if (!Directory.Exists(m_HotPath)) { Directory.CreateDirectory(m_HotPath); } DeleteAllFile(m_HotPath); foreach (string abName in changeList) { if (!abName.EndsWith(".manifest")) { File.Copy(m_BundleTargetPath + "/" + abName, m_HotPath + "/" + abName); } }
DirectoryInfo directoryInfo = new DirectoryInfo(m_HotPath); FileInfo[] files = directoryInfo.GetFiles("*",SearchOption.AllDirectories); Patches patches = new Patches(); patches.Version = 1; patches.Files = new List<Patch>(); foreach (FileInfo file in files) { Patch patch = new Patch(); patch.Name = file.Name; patch.Url = "http://127.0.0.1/AssetBundle/" + PlayerSettings.bundleVersion + "/" + hotCount + "/" + file.Name; patch.Platform = EditorUserBuildSettings.activeBuildTarget.ToString(); patch.MD5 = MD5Manager.Instance.BuildFileMD5(file.FullName); patch.Size = file.Length / 1024.0f; patches.Files.Add(patch); } BinarySerializeOpt.XmlSerialize(m_HotPath + "/Patch.xml", patches); }
|
这里的127.0.0.1是Apache本地服务器的入口,端口号是默认的80,所以这里不用指定端口号。
这里指定的URL就是Apache服务器的文件目录(本地地址例:www/AssetBundle/0.1/1/热更文件
),详情见下一节