b[toc]

# 连接

https://github.com/smilehao/xlua-framework

# XLuaManager

先预定义了几个常量

1
2
3
4
5
public const string luaAssetbundleAssetName = "Lua";
public const string luaScriptsFolder = "LuaScripts";
const string commonMainScriptName = "Common.Main";
const string gameMainScriptName = "GameMain";
const string hotfixMainScriptName = "XLua.HotfixMain";

定义两个 Lua 运行时变量

1
2
LuaEnv luaEnv = null;
LuaUpdater luaUpdater = null;

初始化 XLuaManager 函数中

1
2
3
4
5
6
7
8
9
10
protected override void Init()
{
base.Init();
//得到lua的AB包路径
string path = AssetBundleUtility.PackagePathToAssetsPath(luaAssetbundleAssetName);
//得到对应ab包的包名
AssetbundleName = AssetBundleUtility.AssetBundlePathToAssetBundleName(path);
//初始化Lua环境
InitLuaEnv();
}

初始化 Lua 环境

其中这一步中 CustomLoader 是自定义的 Lua 文件字节读取函数,可以看下它本身的实现,本质上就是在 Assetbundle 下读取所有的 lua script luaEnv.AddBuildin 这局本质上加载 protobuf - Lua, 文档上说是加载的 ToLua 里的 Proto-gen-lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public bool HasGameStart
{
get;
protected set;
}

void InitLuaEnv()
{
luaEnv = new LuaEnv();
HasGameStart = false;
if (luaEnv != null)
{
luaEnv.AddLoader(CustomLoader);
luaEnv.AddBuildin("pb", XLua.LuaDLL.Lua.LoadPb);
}
else
{
Logger.LogError("InitLuaEnv null!!!");
}
}

OnInit()

这个是在资源管理器加载完 Lua AB 包以后才会调用

先调用 LoadScript (MainLuaName) 启动 Lua 的入口
然后初始化 LuaUpdater, 启动 Lua 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 这里必须要等待资源管理模块加载Lua AB包以后才能初始化
public void OnInit()
{
if (luaEnv != null)
{
LoadScript(commonMainScriptName);
luaUpdater = gameObject.GetComponent<LuaUpdater>();
if (luaUpdater == null)
{
luaUpdater = gameObject.AddComponent<LuaUpdater>();
}
luaUpdater.OnInit(luaEnv);
}
}

Restart () 重启虚拟机

  • 停止热更
  • 释放资源
  • 重新初始化 Lua 环境
  • 重新初始化 Lua 层
1
2
3
4
5
6
7
8
9
// 重启虚拟机:热更资源以后被加载的lua脚本可能已经过时,需要重新加载
// 最简单和安全的方式是另外创建一个虚拟器,所有东西一概重启
public void Restart()
{
StopHotfix();
Dispose();
InitLuaEnv();
OnInit();
}

SafeDoString(LuaScript)

安全的执行 Lua 脚本,传递进来的是 Lua 脚本内容,就是套了层 Try - Catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void SafeDoString(string scriptContent)
{
if (luaEnv != null)
{
try
{
luaEnv.DoString(scriptContent);
}
catch (System.Exception ex)
{
string msg = string.Format("xLua exception : {0}\n {1}", ex.Message, ex.StackTrace);
Logger.LogError(msg, null);
}
}
}

StartHotfix 开启 Lua 热更

如果是重启状态,则重启热更,否则 Lua 层开始执行热更 Lua 模块的主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void StartHotfix(bool restart = false)
{
if (luaEnv == null)
{
return;
}

if (restart)
{
StopHotfix();
ReloadScript(hotfixMainScriptName);
}
else
{
LoadScript(hotfixMainScriptName);
}
SafeDoString("HotfixMain.Start()");
}

public void StopHotfix()
{
SafeDoString("HotfixMain.Stop()");
}

StartGame,Lua 层的启动游戏,在 Lua 初始化完成后调用

1
2
3
4
5
6
7
8
9
public void StartGame()
{
if (luaEnv != null)
{
LoadScript(gameMainScriptName);
SafeDoString("GameMain.Start()");
HasGameStart = true;
}
}

Update 中的处理

每 100 帧清一下 Lua 的 GC, 且每帧都要调一下 Lua 的 Tick

1
2
3
4
5
6
7
8
9
10
11
12
private void Update()
{
if (luaEnv != null)
{
luaEnv.Tick();

if (Time.frameCount % 100 == 0)
{
luaEnv.FullGc();
}
}
}

OnLevelWasLoaded 新场景加载进入时调用

1
2
3
4
if (luaEnv != null && HasGameStart)
{
SafeDoString("GameMain.OnLevelWasLoaded()");
}