最近公司要用到lua,在朋友介绍和自己查阅资料之后,便认真研究LuaFramework-UGUI-master这套框架,点这里进行下载。给想学习lua的同学一些建议,少看视频,多看源码。另外以下纯属个人在win10上自己看源码得到的结论,读者若发现错误尽可指出,求不喷。
在从github上下载压缩包以后,打开压缩包可以看到Assets目录的结构如下:
使用unity打开时会自动提示我们自动生成常用类型注册文件,直接点击确定即可,本质是自动生成Assets/LuaFramework/ToLua/Source/Generate下的warp文件,用来将unity常用类的方法和变量注册到LuaState中去(嘘,其实我蒙的,大家随便听听)
这时候直接运行main场景的话,会报以下错误,
因为我们忘记了一个重要的步骤--生成资源(此时还没有StreamingAssets文件夹),点击菜单栏->LuaFramework->Build Windows Resource,然后等待片刻即可(此时自动生成了StreamingAssets资源)。这一步骤至关重要,本章节下面将全面详细讲述该操作的运行流程。
首先,点击菜单栏Build Windows Resource调用的是LuaFramework/Editor/Packager脚本的BuildAssetResource()方法,不同平台都调用该方法,参数就是目标,方法内容如下
该方法首先判断数据路径Util.DataPath(c:/luaframework/)是否存在,存在则调用Delete方法(true表示递归删除,即其内所有文件全被删除),然后删除并重建StreamingAssets文件夹。然后默认调用HandLuaBundle()//处理Lua代码包,下面会详细介绍。
HandleLuaBundle()函数是很重要的一步,用于将源目录LuaFramework/Lua和LuaFramework/ToLua/Lua下的所有.lua文件都通过ToLuaMenu.CopyLuaBytesFiles()方法复制到临时目录Assets/Lua对应的子目录下,并添加后缀名.bytes(意思就是根据在源目录的相对路径,在该临时目录下会自动创建对应子文件夹)。然后就遍历该临时目录的所有子文件夹的路径,去掉每个子文件夹路径对应的相同多余前缀并替换'\'为'_',并分别在头部和尾部拼接"lua/lua_"和".unity3d",最终得到的name就是一个完整的assetbundleName。代码如下:
static void HandleLuaBundle() {
string streamDir = Application.dataPath + "/" + AppConst.LuaTempDir;//创建临时文件夹
if (!Directory.Exists(streamDir)) Directory.CreateDirectory(streamDir);
string[] srcDirs = { CustomSettings.luaDir, CustomSettings.FrameworkPath + "/ToLua/Lua" };//源lua目录
for (int i = 0; i < srcDirs.Length; i++) {
if (AppConst.LuaByteMode) {//默认false
string sourceDir = srcDirs[i];
string[] files = Directory.GetFiles(sourceDir, "*.lua", SearchOption.AllDirectories);
int len = sourceDir.Length;
if (sourceDir[len - 1] == '/' || sourceDir[len - 1] == '\\') {
--len;
}
for (int j = 0; j < files.Length; j++) {
string str = files[j].Remove(0, len);
string dest = streamDir + str + ".bytes";
string dir = Path.GetDirectoryName(dest);
Directory.CreateDirectory(dir);
EncodeLuaFile(files[j], dest);
}
} else {
ToLuaMenu.CopyLuaBytesFiles(srcDirs[i], streamDir);//将源lua文件夹下的所有.lua拷贝到临时文件夹下对应目录(子文件夹根据路径自动生成)
}
}
string[] dirs = Directory.GetDirectories(streamDir, "*", SearchOption.AllDirectories);//获取临时文件下的所有子文件夹路径
for (int i = 0; i < dirs.Length; i++) {
string name = dirs[i].Replace(streamDir, string.Empty);//去掉多余前缀
name = name.Replace('\\', '_').Replace('/', '_');
name = "lua/lua_" + name.ToLower() + AppConst.ExtName;//得到完整的assetBundleName
string path = "Assets" + dirs[i].Replace(Application.dataPath, "");
AddBuildMap(name, "*.bytes", path);
}
AddBuildMap("lua/lua" + AppConst.ExtName, "*.bytes", "Assets/" + AppConst.LuaTempDir);
//-------------------------------处理非Lua文件 将非lua文件全都拷贝到StreamingAssets对应子文件夹下----------------------------------
string luaPath = AppDataPath + "/StreamingAssets/lua/";
for (int i = 0; i < srcDirs.Length; i++) {
paths.Clear(); files.Clear();
string luaDataPath = srcDirs[i].ToLower();
Recursive(luaDataPath);
foreach (string f in files) {
if (f.EndsWith(".meta") || f.EndsWith(".lua")) continue;
string newfile = f.Replace(luaDataPath, "");
string path = Path.GetDirectoryName(luaPath + newfile);
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
string destfile = path + "/" + Path.GetFileName(f);
File.Copy(f, destfile, true);
}
}
AssetDatabase.Refresh();
}
我们在得到完整的assetBundleName以后,就可以调用AddBuildMap()方法来创建AsssetBundleBuild对象设置要打包的名称和资源并添加到List集合中。从HandleLuaBundle()方法可以看出,该框架是遍历临时文件夹下的所有子文件夹,然后根据该子文件夹生成一个独特的abName,一个name对应一个AssetBundleBuild对象,即临时文件夹下的每一个子文件夹下的所有.lua文件都会被打入同一个ab文件中。
最后一个值得一提的是files.txt配置文件的内容以及它是怎样生成的,在BuildFileIndex()方法中,遍历被拷贝到StreamingAssets文件夹中的所有文件(包含非lua文件,不包含.meta文件) 获取绝对路径,去掉路径字符串包含StreamingAssets的多余前缀,得到每个文件的唯一路径,作为files.txt文件的第一列,而第二列则是该文件的md5值。
static void BuildFileIndex() {
string resPath = AppDataPath + "/StreamingAssets/";
///----------------------创建文件列表-----------------------
string newFilePath = resPath + "/files.txt";
if (File.Exists(newFilePath)) File.Delete(newFilePath);
paths.Clear(); files.Clear();
Recursive(resPath);
FileStream fs = new FileStream(newFilePath, FileMode.CreateNew);
StreamWriter sw = new StreamWriter(fs);
for (int i = 0; i < files.Count; i++) {
string file = files[i];
string ext = Path.GetExtension(file);
if (file.EndsWith(".meta") || file.Contains(".DS_Store")) continue;
string md5 = Util.md5file(file);
string value = file.Replace(resPath, string.Empty);
sw.WriteLine(value + "|" + md5);
}
sw.Close(); fs.Close();
}
以上是对点击Build Window Resource时执行流程重要部分的讲解,如有遗漏和错误,请大家尽管指出。我会努力尽快更新。