在编辑器中,实例化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表示加载的是资源包的。