|
assetbundle打包
打包使用assetbundle browser 插件即可,具体打包流程可参考Unity3d的AssetBundle打包,这个工具的好处在于方便打包,能更好的解决资源依赖复用的问题。
打包lua文件
unity是默认不支持.lua文件类型的,所以也不能对后缀为.lua的文件打包,但是unity官方是有相关接口的。具体实现代码如下ST_LuaImporter.cs- [ScriptedImporter(1,".lua")]publicclassST_LuaImporter:ScriptedImporter{publicoverridevoidOnImportAsset(AssetImportContext ctx){var luaTxt = File.ReadAllText(ctx.assetPath);var assetText =newTextAsset(luaTxt);
- ctx.AddObjectToAsset("main obj", assetText);//将对象assetText作为导入操作的主要对象。
- ctx.SetMainObject(assetText);}}
复制代码 将此代码放到Editor目录下即可正常打包lua文件。
开始打包
创建一个Cube预制体并给他绑定一个Lua脚本,设置他的bundle标签model
至于Cube对象是如何绑定TestMono脚本的,可以参见文章Unity + XLua 简单框架搭建的详细过程,这里不做过多描述
TestMono.lua文件内容如下- local TestMono =class()-- 自定义数据-- 构造函数function TestMono:Ctor(params)end-- 开始函数function TestMono:Start()endfunction TestMono:Update()--self.transform:Rotate(CS.UnityEngine.Vector3(0,1,0));
- self.transform:Rotate(CS.UnityEngine.Vector3(1,0,0));endreturn TestMono
复制代码 同样给TestMono脚本打上lua标签
开始打包
打包输出路径是自定的,可上传至服务器方便热更新,这个后面介绍。压缩方式随意,勾选拷贝到StreamAssets方便本地测试(如果用服务器数据,那么不勾选)。打包成功如下
可以看到除了lua包和model包还有一个StandloneWindows包,这个包里面包含了bundle各个模块的信息以及依赖。Md5FileList.txt为了方便后面做热更对比文件对比而生成的,打包时生成这个文件需要修改assetbundle browser源码,具体修改如下
在assetbundle browser找到AssetBundleBuildTab文件,添加以下代码- /// <summary>/// Create FileList/// </summary>voidCreateFileList(){string filePath = m_UserData.m_OutputPath +"\\Md5FileList.txt";if(File.Exists(filePath)){
- File.Delete(filePath);}StreamWriter streamWriter =newStreamWriter(filePath);string[] files = Directory.GetFiles(m_UserData.m_OutputPath);for(int i =0; i < files.Length; i++){string tmpfilePath = files[i];if(tmpfilePath.Equals(filePath)|| tmpfilePath.EndsWith(".manifest"))continue;
- Debug.Log(tmpfilePath);
- tmpfilePath.Replace("\","/");
- streamWriter.WriteLine(tmpfilePath.Substring(m_UserData.m_OutputPath.Length +1)+"|"+GetFileMD5(tmpfilePath));}
- streamWriter.Close();
- streamWriter.Dispose();}/// <summary>/// 获取文件的MD5/// </summary>stringGetFileMD5(string filePath){System.Security.Cryptography.MD5 MD5 =newSystem.Security.Cryptography.MD5CryptoServiceProvider();System.Text.StringBuilder stringBuilder =newSystem.Text.StringBuilder();FileStream fileStream =newFileStream(filePath, FileMode.Open);byte[] bytes = MD5.ComputeHash(fileStream);
- fileStream.Close();for(int i =0; i < bytes.Length; i++){
- stringBuilder.Append(bytes[i].ToString("x2"));}return stringBuilder.ToString();}
复制代码
在Build 事件里加上函数即可。这样就可以在打包的同时生成包体MD5,Md5FileList.txt内容如下,格式是包名加生成的16进制哈希码。
加载本地Bundle资源测试
- /// <summary>/// 加载资源/// </summary>/// <typeparam name="T">资源类型</typeparam>/// <param name="assetBundleName">资源名</param>/// <param name="assetBundleGroupName">组名</param>/// <returns></returns>publicTLoadResource<T>(string assetBundleName,string assetBundleGroupName)whereT:UnityEngine.Object{if(string.IsNullOrEmpty(assetBundleGroupName)){returndefault(T);}if(!DicAssetBundle.TryGetValue(assetBundleGroupName,out assetbundle)){
- assetbundle = AssetBundle.LoadFromFile(GetStreamingAssetsPath()+ assetBundleGroupName);
- DicAssetBundle.Add(assetBundleGroupName, assetbundle);}object obj = assetbundle.LoadAsset(assetBundleName,typeof(T));var one = obj asT;return one;}/// <summary>/// 卸载资源组/// </summary>/// <param name="assetBundleGroupName">组名</param>publicvoidUnLoadResource(string assetBundleGroupName){if(DicAssetBundle.TryGetValue(assetBundleGroupName,out assetbundle)){
- Debug.Log(assetBundleGroupName);
- assetbundle.Unload(false);if(assetbundle !=null){
- assetbundle =null;}
- DicAssetBundle.Remove(assetBundleGroupName);
- Resources.UnloadUnusedAssets();}}publicstringGetStreamingAssetsPath(){string StreamingAssetsPath =#if UNITY_EDITOR || UNITY_STANDALONE_WIN
- Application.streamingAssetsPath +"/";#elif UNITY_ANDROID"jar:file://"+ Application.dataPath +"!/assets/";#elif UNITY_IPHONE
- Application.dataPath +"/Raw/";#elsestring.Empty;#endifreturn StreamingAssetsPath;}
复制代码 修改Lua自定义Loader,require时使用Bundle资源
ST_LuaManger.cs- publicstaticbyte[]LuaLoader(refstring filename){TextAsset ta = ST_BundleManager.Instance.LoadResource<TextAsset>(filename,"lua");return System.Text.Encoding.UTF8.GetBytes(ta.text);}//移除requier(lua环境只会Require 文件一次,移除后会从新require)//require 会调用自定义LoaderpublicvoidRemoveRequire(string name){
- MyLuaEnv.DoString($@"
- for key, _ in pairs(package.preload) do
- if string.find(tostring(key),'{name}') == 1 then
- package.preload[key] = nil
- end
- end
- for key, _ in pairs(package.loaded) do
- if string.find(tostring(key), '{name}') == 1 then
- package.loaded[key] = nil
- end
- end");}
复制代码 测试脚本- if(GUILayout.Button("Cube")){GameObject cube = ST_BundleManager.Instance.LoadResource<GameObject>("Cube","model");Instantiate(cube,newVector3(0,y++,0),Quaternion.identity);}
复制代码 测试结果
可以看到资源被正确加载了。
远程资源下载与热更
第一次使用服务器数据的时候要清空本地StreamAsset文件夹。热更新的思路是对比本地的Md5FileList.txt,和服务器上的,如果有md5不同的包,或者新的包那么就请求下载,并保存到本地(也可以用先对比版本号的方法。可以将打包的资源放到了[GitCode]方便下载(https://about.gitcode.net/)
右键点击这个按钮即可复制原始下载链接,亲测可用
ST_AssetBundleManager.cs 完整代码- usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Threading.Tasks;usingUnityEditor;usingUnityEngine;usingUnityEngine.Networking;publicstaticclassBundleConfig{publicstaticreadonlystring AssetBundlePath ="https://。。。。";publicstaticreadonlystring Md5FileName ="Md5FileList.txt";publicstaticreadonlystring OnFireUpdate ="OnFireUpdate";publicstaticreadonlystring OnStarDownLoad ="OnStarDownLoad";publicstaticreadonlystring OnStarSave ="OnStarSave";publicstaticreadonlystring OnUpdateEnd ="OnUpdateEnd";}publicclassST_BundleManager:MonoBehaviour{staticbool _isDesdroy =false;privatestaticreadonlyobject padlock =newobject();privatestaticST_BundleManager instance;staticAssetBundle assetbundle =null;staticDictionary<string, AssetBundle> DicAssetBundle;publicstaticST_BundleManager Instance
- {get{if(_isDesdroy){returnnull;}if(instance ==null){lock(padlock){if(instance ==null){GameObject go =newGameObject("AssetBundleManager");DontDestroyOnLoad(go);
- instance = go.AddComponent<ST_BundleManager>();
- DicAssetBundle =newDictionary<string, AssetBundle>();}}}return instance;}}/// <summary>/// bundel资源加载/// </summary>/// <typeparam name="T">资源类型</typeparam>/// <param name="assetBundleName">资源名</param>/// <param name="assetBundleGroupName">资源组</param>/// <returns></returns>publicTLoadResource<T>(string assetBundleName,string assetBundleGroupName)whereT:UnityEngine.Object{if(string.IsNullOrEmpty(assetBundleGroupName)){returndefault(T);}if(!DicAssetBundle.TryGetValue(assetBundleGroupName,out assetbundle)){
- assetbundle = AssetBundle.LoadFromFile(GetStreamingAssetsPath()+ assetBundleGroupName);
- DicAssetBundle.Add(assetBundleGroupName, assetbundle);}object obj = assetbundle.LoadAsset(assetBundleName,typeof(T));var one = obj asT;return one;}/// <summary>/// 资源卸载/// </summary>/// <param name="assetBundleGroupName">资源名</param>publicvoidUnLoadResource(string assetBundleGroupName){if(DicAssetBundle.TryGetValue(assetBundleGroupName,out assetbundle)){
- assetbundle.Unload(false);if(assetbundle !=null){
- assetbundle =null;}
- DicAssetBundle.Remove(assetBundleGroupName);
- Resources.UnloadUnusedAssets();}}/// <summary>/// 获取本地资源路径/// </summary>/// <returns></returns>publicstringGetStreamingAssetsPath(){string StreamingAssetsPath =#if UNITY_EDITOR || UNITY_STANDALONE_WIN
- Application.streamingAssetsPath +"/";#elif UNITY_ANDROID"jar:file://"+ Application.dataPath +"!/assets/";#elif UNITY_IPHONE
- Application.dataPath +"/Raw/";#elsestring.Empty;#endifreturn StreamingAssetsPath;}//下载跟新资源publicIEnumeratorDownloadAssetBundles(UnityWebRequest www){List<string> downLoads =GetDownloadMoudle(www.downloadHandler.text);SaveAssetBundle(BundleConfig.Md5FileName,www.downloadHandler.data);int i =0;if(downLoads.Count >0){//触发热更事件
- ST_EventManager.PostEvent(BundleConfig.OnFireUpdate, downLoads.Count);}while(i < downLoads.Count){
- www = UnityWebRequest.Get(BundleConfig.AssetBundlePath + downLoads[i]);//触发下载事件
- ST_EventManager.PostEvent(BundleConfig.OnStarDownLoad, downLoads[i]);yieldreturn www.SendWebRequest();if(www.result != UnityWebRequest.Result.ConnectionError){//触发保存事件
- ST_EventManager.PostEvent(BundleConfig.OnStarSave, downLoads[i]);SaveAssetBundle(downLoads[i],www.downloadHandler.data);}else{thrownewException(www.error);}
- i++;}//触发跟新完事件
- ST_EventManager.PostEvent(BundleConfig.OnUpdateEnd);}/// <summary>/// 检查跟新/// </summary>/// <param name="callback">回调函数</param>/// <returns></returns>publicIEnumeratorCheckUpdate(Action<bool,UnityWebRequest> callback){UnityWebRequest www = UnityWebRequest.Get(BundleConfig.AssetBundlePath + BundleConfig.Md5FileName);yieldreturn www.SendWebRequest();if(www.result != UnityWebRequest.Result.ConnectionError){List<string> downLoads =GetDownloadMoudle(www.downloadHandler.text);callback(downLoads.Count >0, www);}else{thrownewException(www.error);}}/// <summary>/// 根据Md5FileList获取需要跟新的模块/// </summary>/// <param name="webFileList"></param>/// <returns></returns>List<string>GetDownloadMoudle(string webFileList){List<string> ret =newList<string>();string[] sArray = webFileList.Split(newchar[2]{'\r','\n'});//AssetBundleManifest assetBundleManifest;if(!File.Exists(GetStreamingAssetsPath()+ BundleConfig.Md5FileName)){foreach(string str in sArray){if(str !=""){
- ret.Add(str.Split('|')[0]);}}}else{string pastMd5 = File.ReadAllText(GetStreamingAssetsPath()+ BundleConfig.Md5FileName);Dictionary<string,string> dict =newDictionary<string,string>();string[] psArray = pastMd5.Split(newchar[2]{'\r','\n'});foreach(string str in psArray){if(str !=""){var nv = str.Split('|');
- dict.Add(nv[0], nv[1]);}}foreach(string str in sArray){if(str !=""){var nv = str.Split('|');if(!dict.ContainsKey(nv[0])|| dict[nv[0]]!= nv[1]){
- ret.Add(nv[0]);}}}}return ret;}/// <summary>/// 保存资源到本地/// </summary>/// <param name="fileName">资源名</param>/// <param name="bytes">数据</param>/// <param name="saveLocalComplate">完成回调</param>voidSaveAssetBundle(string fileName,byte[] bytes,Action saveLocalComplate =null){string path =GetStreamingAssetsPath()+ fileName;FileInfo fileInfo =newFileInfo(path);FileStream fs = fileInfo.Create();
- fs.Write(bytes,0, bytes.Length);
- fs.Flush();
- fs.Close();
- fs.Dispose();if(saveLocalComplate !=null){saveLocalComplate();}}privatevoidOnDestroy(){
- _isDesdroy =true;}}
复制代码 测试热更
- int y =0;voidStart(){
- ST_EventManager.RegisterEvent<int>(BundleConfig.OnFireUpdate,(e)=>{
- Debug.Log("开始热跟新,资源数:"+ e);});
- ST_EventManager.RegisterEvent<string>(BundleConfig.OnStarDownLoad,(e)=>{
- Debug.Log("开始下载资源:"+ e);});
- ST_EventManager.RegisterEvent<string>(BundleConfig.OnStarSave,(e)=>{
- Debug.Log("开始保存资源:"+ e);});
- ST_EventManager.RegisterEvent(BundleConfig.OnUpdateEnd,()=>{
- Debug.Log("更新完成");//取消TestMono的Requrie,卸载lua,保证使用新的代码
- ST_LuaMannager.Instance.RemoveRequire("TestMono");
- ST_BundleManager.Instance.UnLoadResource("lua");});}privatevoidOnGUI(){if(GUILayout.Button("开始下载")){StartCoroutine(ST_BundleManager.Instance.CheckUpdate((needUp, www)=>{if(needUp){
- Debug.Log("检测到资源更新");StartCoroutine(ST_BundleManager.Instance.DownloadAssetBundles(www));}else{
- Debug.Log("无更新");}}));}if(GUILayout.Button("Cube")){GameObject cube = ST_BundleManager.Instance.LoadResource<GameObject>("Cube","model");Instantiate(cube,newVector3(0,y++,0),Quaternion.identity);
- ST_LuaMannager.Instance.RemoveRequire("TestMono");}}
复制代码
点击开始下载,输出如下:
点击cube正常加载:
修改TestMono的Update函数,原来Cube是沿着X轴旋转,现在让他沿着Y周旋转,从新打包并上传到服务器- function TestMono:Update()
- self.transform:Rotate(CS.UnityEngine.Vector3(0,1,0));--self.transform:Rotate(CS.UnityEngine.Vector3(1,0,0));end
复制代码
可以看到只跟新了lua和standlone,并且跟新后创建的Cube是按Y轴旋转的,热更大功告成,,写完博客11点半了,睡觉睡觉。。。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|