在编辑器中,实例化LuaState的时候就会将LuaConst.cs中定义的luaDir和toluaDir添加到lua脚本搜索目录中,所以这两个脚本一定得正确,在编辑器中,运行lua脚本就会使用这两个目录下的,也方便修改后立即看到效果。
void InitLuaPath()//LuaState构造方法中调用的其中一个方法 { InitPackagePath(); if (!LuaFileUtils.Instance.beZip) { #if UNITY_EDITOR if (!Directory.Exists(LuaConst.luaDir)) { string msg = string.Format("luaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.luaDir); throw new LuaException(msg); } if (!Directory.Exists(LuaConst.toluaDir)) { string msg = string.Format("toluaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.toluaDir); throw new LuaException(msg); } AddSearchPath(LuaConst.toluaDir); AddSearchPath(LuaConst.luaDir); #endif if (LuaFileUtils.Instance.GetType() == typeof(LuaFileUtils)) { AddSearchPath(LuaConst.luaResDir); } } }
当在手机上时,lua后缀极其的被排斥,放在哪里都不太行,所以需要打包成assetbundle,从而能够真正的热更新。由于unity中不认.lua文件,所以在打包的时候复制lua脚本到一个临时目录并添加后缀.bytes,然后根据文件夹名字标志assetbundle的名字,tolua中已经有了实现,我删除了打包的步骤,只用来标志名字了,因为我项目已经有了打包的方法了。
//ToLuaMenu.cs [MenuItem("Lua/Build bundle files not jit", false, 55)] public static void BuildNotJitBundles() { ClearAllLuaFiles(); CreateStreamDir(GetOS()); #if UNITY_4_6 || UNITY_4_7 string tempDir = CreateStreamDir("Lua"); #else string tempDir = Application.dataPath + "/temp/Lua"; if (!File.Exists(tempDir)) { Directory.CreateDirectory(tempDir); } #endif CopyLuaBytesFiles(LuaConst.luaDir, tempDir); CopyLuaBytesFiles(LuaConst.toluaDir, tempDir); AssetDatabase.Refresh(); List<string> dirs = new List<string>(); GetAllDirs(tempDir, dirs); for (int i = 0; i < dirs.Count; i++) { string str = dirs[i].Remove(0, tempDir.Length); BuildLuaBundle(str.Replace('\\', '/'), "Assets/temp/Lua"); } BuildLuaBundle(null, "Assets/temp/Lua"); AssetDatabase.SaveAssets(); //只用来标志BUndle的名字就行了 //string output = string.Format("{0}/{1}", Application.streamingAssetsPath, GetOS()); //BuildPipeline.BuildAssetBundles(output, BuildAssetBundleOptions.DeterministicAssetBundle, EditorUserBuildSettings.activeBuildTarget); //Directory.Delete(Application.dataPath + "/temp/", true); AssetDatabase.Refresh(); } static void CopyLuaBytesFiles(string sourceDir, string destDir, bool appendext = true, string searchPattern = "*.lua", SearchOption option = SearchOption.AllDirectories) { if (!Directory.Exists(sourceDir)) { return; } string[] files = Directory.GetFiles(sourceDir, searchPattern, option); int len = sourceDir.Length; if (sourceDir[len - 1] == '/' || sourceDir[len - 1] == '\\') { --len; } for (int i = 0; i < files.Length; i++) { string str = files[i].Remove(0, len); string dest = destDir + "/" + str; if (appendext) dest += ".bytes"; string dir = Path.GetDirectoryName(dest); Directory.CreateDirectory(dir); File.Copy(files[i], dest, true); } } static void GetAllDirs(string dir, List<string> list) { string[] dirs = Directory.GetDirectories(dir); list.AddRange(dirs); for (int i = 0; i < dirs.Length; i++) { GetAllDirs(dirs[i], list); } } static void BuildLuaBundle(string subDir, string sourceDir) { string[] files = Directory.GetFiles(sourceDir + subDir, "*.bytes"); string bundleName = subDir == null ? "lua": "lua" + subDir.Replace('/', '_') ; bundleName = "scripts/" + bundleName.ToLower(); #if UNITY_4_6 || UNITY_4_7 List<Object> list = new List<Object>(); for (int i = 0; i < files.Length; i++) { Object obj = AssetDatabase.LoadMainAssetAtPath(files[i]); list.Add(obj); } BuildAssetBundleOptions options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle; if (files.Length > 0) { string output = string.Format("{0}/{1}/" + bundleName, Application.streamingAssetsPath, GetOS()); File.Delete(output); BuildPipeline.BuildAssetBundle(null, list.ToArray(), output, options, EditorUserBuildSettings.activeBuildTarget); } #else string bundleVariant = com.Bowen.Game.Lobby.GlobalConst.Res.Phone_BundleFileExt.Replace(".", ""); for (int i = 0; i < files.Length; i++) { AssetImporter importer = AssetImporter.GetAtPath(files[i]); if (importer) { importer.assetBundleName = bundleName; importer.assetBundleVariant = bundleVariant; } } #endif }
好了,现在lua脚本都复制到了temp/Lua下面了,并且lua脚本根据所在的子文件夹标志了一个包名字,只需要调用Unity提供的打包方法就可以了,写入自己框架中的资源文件中。
public static bool BuildAssetBundle(string path, BuildTarget target, List<GameConfig> games, ref List<AssetBundleInfo> abList) { try { var manifest = BuildAssetBundle(path, target, games); if (manifest == null) { Debug.Log("null manifest"); return true; } string[] assetBundles = manifest.GetAllAssetBundles(); foreach (var ab in assetBundles) { string name = ab.Replace(Path.GetExtension(ab), ""); var data_type = new AssetBundleInfo(); //data_type.ID = ""; data_type.Name = name; data_type.FileName = ab; data_type.Hash = MD5Util.GetFileMD5(path + ab); data_type.Version = "0"; var oldItem = abList.Find(item => { return string.Compare(item.Name, name, true) == 0; }); if (oldItem != null) { abList.Remove(oldItem); if (string.Compare(oldItem.Hash, data_type.Hash, true) != 0) { data_type.UpdateVersion(); } } Debug.Log("add builded ab:" + data_type + " name:" + name); abList.Add(data_type); } Debug.Log("assetbundle build finish"); return true; } catch (Exception e) { Debug.LogException(e); return false; } } private static AssetBundleManifest BuildAssetBundle(string path, BuildTarget target, List<GameConfig> games) { var builds = new List<AssetBundleBuild>(); var abs = AssetDatabase.GetAllAssetBundleNames(); foreach (var ab in abs) { var build = new AssetBundleBuild(); build.assetBundleName = Path.GetDirectoryName(ab) + "/" + Path.GetFileNameWithoutExtension(ab); build.assetNames = AssetDatabase.GetAssetPathsFromAssetBundle(ab); build.assetBundleVariant = Path.GetExtension(ab).Replace(".", ""); builds.Add(build); Debug.Log("name:" + build.assetBundleName + " variant:" + build.assetBundleVariant + " ab:" + ab); } Debug.Log("Build scene for target:" + target + " by path:" + path); if (EditorUserBuildSettings.activeBuildTarget != target) { EditorUserBuildSettings.SwitchActiveBuildTarget(target); } return BuildPipeline.BuildAssetBundles( path, builds.ToArray(), BuildAssetBundleOptions.DeterministicAssetBundle/* | BuildAssetBundleOptions.ForceRebuildAssetBundle*/, target ); }
在框架下载后,读取assetbundle包中的lua脚本
//LuaManager.cs 在资源下载完成后,调用初始化方法 public void InitStart() { InitLuaBundle(); this.luaState.Start(); //启动LUAVM //this.StartMain(); this.StartLooper(); } /// <summary> /// 初始化LuaBundle,加载下载的Bundle到一个字典中缓存起来 /// </summary> void InitLuaBundle() { if (loader.beZip) { loader.AddBundle("lua.mp"); loader.AddBundle("lua_math.mp"); loader.AddBundle("lua_system.mp"); loader.AddBundle("lua_system_reflection.mp"); loader.AddBundle("lua_unityengine.mp"); loader.AddBundle("lua_common.mp"); loader.AddBundle("lua_logic.mp"); loader.AddBundle("lua_view.mp"); loader.AddBundle("lua_controller.mp"); loader.AddBundle("lua_misc.mp"); loader.AddBundle("lua_protobuf.mp"); loader.AddBundle("lua_3rd_cjson.mp"); loader.AddBundle("lua_3rd_luabitop.mp"); loader.AddBundle("lua_3rd_pbc.mp"); loader.AddBundle("lua_3rd_pblua.mp"); loader.AddBundle("lua_3rd_sproto.mp"); } } //这里是loader的类 LuaLoader.cs using UnityEngine; using System.Collections; using System.IO; using LuaInterface; /// <summary> /// 集成自LuaFileUtils,重写里面的ReadFile,继承LuaFileUtils /// </summary> public class LuaLoader : LuaFileUtils { // Use this for initialization public LuaLoader(bool isZip = false) { instance = this; beZip = isZip; } /// <summary> /// 添加打入Lua代码的AssetBundle /// </summary> /// <param name="bundle"></param> public void AddBundle(string bundleName) { string url = ""; #if !UNITY_EDITOR //lua bundle资源下载的目录 url = LuaConst.luaResDir + "/" + bundleName.ToLower(); #else url = LuaConst.luaWinTestResDir + "/" + bundleName.ToLower(); #endif if (File.Exists(url)) { AssetBundle bundle = AssetBundle.LoadFromFile(url); if (bundle != null) { bundleName = bundleName.Replace("lua/", "").Replace(".mp", ""); base.AddSearchBundle(bundleName.ToLower(), bundle); } } } /// <summary> /// 当LuaVM加载Lua文件的时候,这里就会被调用, /// 用户可以自定义加载行为,只要返回byte[]即可。 /// </summary> /// <param name="fileName"></param> /// <returns></returns> public override byte[] ReadFile(string fileName) { return base.ReadFile(fileName); } }
好了LuaFileUtils里面有个zipMao字典有了所有的lua的AssetBundle包,当我们想调用lua脚本的时候,我们可以自由的去读取了,因为我们有做映射,就是不知道哪个lua脚本在哪个包中,所以我就直接遍历所有的包了,先实现再优化了。
byte[] ReadZipFile(string fileName) { byte[] buffer = null; string zipName = null; using (CString.Block()) { CString sb = CString.Alloc(256); sb.Append("lua"); int pos = fileName.LastIndexOf('/'); if (pos > 0) { sb.Append("_"); sb.Append(fileName, 0, pos).ToLower().Replace('/', '_'); fileName = fileName.Substring(pos + 1); } if (!fileName.EndsWith(".lua")) { fileName += ".lua"; } #if UNITY_5 || UNITY_5_3_OR_NEWER fileName += ".bytes"; #endif zipName = sb.ToString(); //这里要优化的 现在遍历查找 foreach (AssetBundle bundle in zipMap.Values) { #if UNITY_4_6 || UNITY_4_7 TextAsset luaCode = bundle.Load(fileName, typeof(TextAsset)) as TextAsset; #else TextAsset luaCode = bundle.LoadAsset<TextAsset>(fileName); #endif if (luaCode != null) { buffer = luaCode.bytes; Resources.UnloadAsset(luaCode); break; } } } return buffer; }
区分是否读取包的lua还是LuaConst.cs中那两个目录的lua,是LuaFileUtils.cs中一个叫beZip的参数,设置为true表示加载的是资源包的。