在使用ToLua的LuaFramework作为代码热更框架,进行AssetBundle打包的时候,有时会莫名其妙出现Lua文件读不出来的问题,即require了某个Lua文件,且文件确实存在,却报出该文件的全局变量为nil的error。
问题分析
经过仔细的查看和debug,发现是同一个文件夹下存在两个Lua文件的文件名发生了尾部包含的关系,例如文件b.lua和aaaaaab.lua,当require文件b.lua时,实际获取出来的却是aaaaaab.lua。
原因所在
发现了这个问题之后(鬼知道我经历了什么才查出来……),就去翻了一下LuaFramework的代码,发现在ToLua/Core/LuaFileUtils.cs中,读取bundle中的文件的函数是这样写的:
| 12
 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
 
 | byte[] ReadZipFile(string fileName){
 ……
 
 if (pos > 0)
 {
 zipName = fileName.Substring(0, pos);
 zipName = zipName.Replace('/', '_');
 zipName = string.Format("Lua_{0}", zipName);
 fileName = fileName.Substring(pos + 1);
 }
 
 zipMap.TryGetValue(zipName.ToLower(), out zipFile);
 
 if (zipFile != null)
 {
 #if UNITY_5
 string[] names = zipFile.GetAllAssetNames();
 for (int i = 0; i < names.Length; i++) {
 if (names[i].EndsWith(fileName.ToLower() + ".bytes")) {
 fileName = names[i];
 break;
 }
 }
 TextAsset luaCode = zipFile.LoadAsset<TextAsset>(fileName);
 #else
 TextAsset luaCode = zipFile.Load(fileName, typeof(TextAsset)) as TextAsset;
 #endif
 
 ……
 
 return buffer;
 }
 
 | 
注意中间的for循环,使用了names[i].EndsWith(fileName.ToLower() + “.bytes”)。
首先要明确一点,LuaFramework在对Lua文件进行打包时,会把每个文件夹打成一个bundle,因此上述代码中的zipFile就是某个文件夹的bundle。在对names和fileName进行匹配时,使用了EndsWith,而bundle内的文件是按字母表顺序排列的。因此require "b.lua"时,EndsWith(“b.lua”),就读到了aaaaaab.lua。
解决方案
一,这个问题存在于LuaFramework 1.0.5.203之前的版本,最新的版本已经修改了Lua文件的打包和读取的方式,所以直接升级框架版本即可解决这个巨坑。
二,升级框架版本本身可能存在隐患,导致代码不稳定。既然已经定位到问题,其实直接修改就可以了。
虽然不是很清楚为什么使用EndsWith来做文件的匹配,大概可以猜到,应该是为了适配多平台导致的bundle内部文件路径不统一的问题。以下修改代码暂时只在PC和Android平台做了测试:
| 12
 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
 
 | byte[] ReadZipFile(string fileName){
 ……
 
 if (pos > 0)
 {
 zipName = fileName.Substring(0, pos);
 zipName = zipName.Replace('/', '_');
 zipName = string.Format("Lua_{0}", zipName);
 
 }
 
 if (!fileName.EndsWith(".lua"))
 {
 fileName += ".lua";
 }
 
 
 
 
 
 fileName = "assets/luatemp/" + fileName;
 
 zipMap.TryGetValue(zipName.ToLower(), out zipFile);
 
 if (zipFile != null)
 {
 #if UNITY_5
 fileName = fileName.ToLower() + ".bytes";
 TextAsset luaCode = zipFile.LoadAsset<TextAsset>(fileName);
 #else
 TextAsset luaCode = zipFile.Load(fileName, typeof(TextAsset)) as TextAsset;
 #endif
 
 ……
 
 return buffer;
 }
 
 | 
小结
LuaFramework本身提供了Lua和C#交互的基础功能,并且扩展了热更新、诸多管理器、消息分发器等功能,当然作为一款开源工具,仍存在些许问题,我踩过其中一些坑,这篇总结的坑连踩了两次,本着事不过三的原则,记录下来,也是一劳永逸地解决了这个问题。