1 前言
Lua基础语法 中系统介绍了 Lua 的语法体系,本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新。
逻辑热更新是指:在保持程序正常运行的情况下,在后台修改代码逻辑,修改完成并推送到运行主机上,主机无缝接入更新后的代码逻辑。
Unity3D 中,基于 Lua 的逻辑热更新方案主要有 ToLua、XLua、uLua、sLua,本文将介绍 ToLua 逻辑热更新方案。
1)ToLua 插件下载
2)ToLua 插件导入
将插件的 Assets 目录下的所有文件拷贝到项目的 Assets 目录下,如下:
3) 生成 Wrap 文件
导入插件后,菜单栏会多一个 Lua 窗口,点击 Generate All 会生成一些 Wrap 文件,生成路径见【Assets\Source\Generate】,这些 Wrap 文件是 Unity3D 与 Lua 沟通的桥梁。每次生成文件时,建议先点击下 Clear wrap files,再点击 Generate All。
4)配置 wrap 文件
用户如果想添加新的 wrap 配置,可以在【Assets\Editor\CustomSettings.cs】文件里添加,并且需要在菜单栏里重新点击 Generate All,在【Assets\Source\Generate】中查看是否生成了相应的文件。
5)官方Demo
本文基于官方 Demo 讲解 ToLua 的使用。
2 ToLua 应用
2.1 Unity3D 中执行 Lua 代码串
HelloWorld.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
public
class
HelloWorld :
MonoBehaviour {
-
void Awake()
-
{
-
LuaState lua =
new LuaState();
-
lua.Start();
-
string luaStr =
@"print('Hello World')";
-
lua.DoString(luaStr,
"HelloWorld.cs");
-
lua.CheckTop();
-
lua.Dispose();
-
lua =
null;
-
}
-
}
运行如下:
2.2 Unity3D 中调用 Lua 文件
ScriptFromFile.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
public
class
ScriptFromFile :
MonoBehaviour {
-
private LuaState lua =
null;
-
-
private void Start() {
-
lua =
new LuaState();
-
lua.Start();
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\02");
-
lua.DoFile(
"LuaScript.lua");
-
lua.CheckTop();
-
}
-
-
private void OnApplicationQuit() {
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
print("Load lua script")
2.3 Unity3D 中调用 Lua 函数
CallLuaFunction.cs
-
using UnityEngine;
-
using LuaInterface;
-
using System;
-
-
public
class
CallLuaFunction :
MonoBehaviour {
-
private LuaState lua =
null;
-
private LuaFunction luaFunc =
null;
-
-
private void Start() {
-
lua =
new LuaState();
-
lua.Start();
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\03");
-
lua.DoFile(
"LuaScript.lua");
-
GetFunc();
-
print(
"Invoke1: " + Invoke1());
-
print(
"Invoke2: " + Invoke2());
-
print(
"PCall: " + PCall());
-
print(
"Delegate: " + Delegate());
-
lua.CheckTop();
-
}
-
-
private void GetFunc() {
-
//luaFunc = lua["luaFunc"] as LuaFunction; // 方式一
-
luaFunc = lua.GetFunction(
"luaFunc");
// 方式二
-
}
-
-
private string Invoke1() {
// 方法一
-
string res = luaFunc.Invoke<
int,
string>(
123456);
-
return res;
-
}
-
-
private string Invoke2() {
// 方法二
-
string res = lua.Invoke<
int,
string>(
"luaFunc",
123456,
true);
-
return res;
-
}
-
-
private string PCall() {
// 方法三
-
luaFunc.BeginPCall();
-
luaFunc.Push(
123456);
-
luaFunc.PCall();
-
string res = luaFunc.CheckString();
-
luaFunc.EndPCall();
-
return res;
-
}
-
-
// 需要在CustomSettings.cs的customDelegateList里面添加"_DT(typeof(System.Func<int, string>))", 并且重新点击菜单窗口的Lua->Generate All
-
private string Delegate() {
// 方法四
-
DelegateFactory.Init();
-
Func<
int,
string> func = luaFunc.ToDelegate<Func<
int,
string>>();
-
string res = func(
123456);
-
return res;
-
}
-
-
private void Call() {
// 方法五(lua中无返回值的函数可以调用此方法)
-
luaFunc.Call(
123456);
-
}
-
-
private void OnApplicationQuit() {
// 退出应用回调
-
if (luaFunc !=
null) {
-
luaFunc.Dispose();
-
luaFunc =
null;
-
}
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
function luaFunc(num)
-
return
"num=" .. num
-
end
打印如下:
2.4 Unity3D 中调用 Lua 变量
AccessLuaVar.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
public
class
AccessLuaVar :
MonoBehaviour {
-
private LuaState lua =
null;
-
-
private void Start() {
-
lua =
new LuaState();
-
lua.Start();
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\04");
-
InitValue();
-
AccessTab();
-
lua.CheckTop();
-
}
-
-
private void InitValue() {
// 在执行lua脚本前, 给lua脚本中变量赋值
-
lua[
"var"] =
5;
-
lua.DoFile(
"LuaScript.lua");
-
}
-
-
private void AccessTab() {
// 访问tab
-
LuaTable tab = lua.GetTable(
"tab");
// 获取table
-
object[] list = tab.ToArray();
// 遍历table元素
-
for (
int i =
0; i < list.Length; i++)
-
{
-
print(
"tab[" + i +
"]=" + list[i]);
-
}
-
print(
"a=" + tab[
"a"]);
-
tab[
"a"] =
"yyy";
-
print(
"a=" + tab[
"a"]);
-
LuaTable map = (LuaTable) tab[
"map"];
-
print(
"name=" + map[
"name"] +
", age=" + map[
"age"]);
-
map.Dispose();
-
tab.Dispose();
-
}
-
-
private void OnApplicationQuit() {
// 退出应用回调
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
print(
'var='..var)
-
-
tab = {
1,
2,
3, a =
"xxx"}
-
tab.map = {name =
"zhangsan", age =
23}
打印如下:
2.5 Lua 中使用协程
1)方法一
TestCoroutine.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
//方法一和方法二展示的两套协同系统勿交叉使用,此为推荐方案
-
public
class
TestCoroutine :
MonoBehaviour {
-
private LuaState lua =
null;
-
private LuaLooper looper =
null;
-
private LuaFunction func =
null;
-
-
private void Awake() {
-
lua =
new LuaState();
-
lua.Start();
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\05");
-
LuaBinder.Bind(lua);
-
looper = gameObject.AddComponent<LuaLooper>();
-
looper.luaState = lua;
-
lua.DoFile(
"LuaScript.lua");
-
func = lua.GetFunction(
"TestCortinue");
-
func.Call();
-
}
-
-
private void OnApplicationQuit() {
-
if (func !=
null) {
-
func.Dispose();
-
func =
null;
-
}
-
looper.Destroy();
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
function CortinueFunc()
-
local www = UnityEngine.WWW(
"http://www.baidu.com")
-
coroutine.www(www)
-
local str = tolua.tolstring(www.bytes)
-
print(str:
sub(
1,
128))
-
print(
"current frameCount: "..Time.frameCount)
-
coroutine.step()
--挂起协程, 下一帧继续执行
-
print(
"yield frameCount: "..Time.frameCount)
-
end
-
-
function TestCortinue()
-
coroutine.start(CortinueFunc)
-
end
打印如下:
2)方法二
TestCoroutine.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
//方法一和方法二展示的两套协同系统勿交叉使用,类unity原生,大量使用效率低
-
public
class
TestCoroutine :
LuaClient {
-
-
protected override void OnLoadFinished() {
-
base.OnLoadFinished();
-
luaState.AddSearchPath(Application.dataPath +
"\\Scenes\\05");
-
luaState.DoFile(
"LuaScript.lua");
-
LuaFunction func = luaState.GetFunction(
"TestCortinue");
-
func.Call();
-
func.Dispose();
-
}
-
-
private new void OnApplicationQuit() {
-
base.OnApplicationQuit();
-
}
-
}
说明: LuaClient 继承 MonoBehaviour。
LuaScript.lua
-
function CortinueFunc()
-
WaitForSeconds(
1)
-
print(
'WaitForSeconds end time: '.. UnityEngine.Time.
time)
-
WaitForFixedUpdate()
-
print(
'WaitForFixedUpdate end frameCount: '..UnityEngine.Time.frameCount)
-
WaitForEndOfFrame()
-
print(
'WaitForEndOfFrame end frameCount: '..UnityEngine.Time.frameCount)
-
Yield(null)
-
print(
'yield null end frameCount: '..UnityEngine.Time.frameCount)
-
Yield(
0)
-
print(
'yield(0) end frameCime: '..UnityEngine.Time.frameCount)
-
local www = UnityEngine.WWW(
'http://www.baidu.com')
-
Yield(www)
-
print(
'yield(www) end time: '.. UnityEngine.Time.
time)
-
local str = tolua.tolstring(www.bytes)
-
print(str:
sub(
1,
128))
-
end
-
-
function TestCortinue()
-
StartCoroutine(CortinueFunc)
-
end
打印如下:
2.6 Unity3D 给 Lua 传递数组
TestArray.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
public
class
TestArray :
MonoBehaviour {
-
private LuaState lua =
null;
-
private LuaFunction func =
null;
-
-
private void Start() {
-
lua =
new LuaState();
-
lua.Start();
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\06");
-
lua.DoFile(
"LuaScript.lua");
-
int[] array = {
1,
2,
3};
-
func = lua.GetFunction(
"TestArray");
-
Call(array);
-
//LazyCall(array);
-
lua.CheckTop();
-
}
-
-
private void Call(int[] array) {
// 方式一
-
func.BeginPCall();
-
func.Push(array);
-
func.PCall();
-
double arg1 = func.CheckNumber();
-
string arg2 = func.CheckString();
-
bool arg3 = func.CheckBoolean();
-
func.EndPCall();
-
print(
"arg1: " + arg1 +
", arg2: " + arg2 +
", arg3: " + arg3);
-
}
-
-
private void LazyCall(int[] array) {
// 方式二
-
object[] objs = func.LazyCall((
object)array);
-
if (objs !=
null) {
-
print(
"objs[0]: " + objs[
0] +
", objs[1]: " + objs[
1] +
", objs[2]: " + objs[
2]);
-
}
-
}
-
-
private void OnApplicationQuit() {
-
if (func !=
null) {
-
func.Dispose();
-
func =
null;
-
}
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
function TestArray(array)
-
local
len = array.Length
-
--通过下标遍历数组
-
for i =
0,
len -
1
do
-
print(
'Array: '..
tostring(array[i]))
-
end
-
--通过迭代器遍历数组
-
local iter = array:GetEnumerator()
-
while iter:MoveNext()
do
-
print(
'iter: '..iter.Current)
-
end
-
--通过表格遍历数组
-
local t = array:ToTable()
-
for i =
1, #t
do
-
print(
'table: '..
tostring(t[i]))
-
end
-
--二分查找数组元素, 返回元素下标, 若数组中不存在该元素, 返回负数
-
local pos = array:BinarySearch(
3)
-
print(
'array BinarySearch, pos='..pos..
', value='..array[pos])
-
--查找数组元素下标
-
pos = array:IndexOf(
4)
-
print(
'array indexof, pos='..pos)
-
--返回多值
-
return
1,
'123',
true
-
end
2.7 Unity3D 给 Lua 传递字典
TestDictionary.cs
-
using System.Collections.Generic;
-
using UnityEngine;
-
using LuaInterface;
-
-
public
sealed
class
Account {
-
public
int id;
-
public
string name;
-
public
int sex;
-
-
public Account(int id, string name, int sex) {
-
this.id = id;
-
this.name = name;
-
this.sex = sex;
-
}
-
}
-
-
public
class
TestDictionary :
MonoBehaviour {
-
private LuaState lua =
null;
-
private LuaFunction func =
null;
-
private Dictionary<
int, Account> map =
new Dictionary<
int, Account>();
-
-
private void Awake() {
-
InitDirec();
-
lua =
new LuaState();
-
lua.Start();
-
LuaBinder.Bind(lua);
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\07");
-
lua.DoFile(
"LuaScript.lua");
-
func = lua.GetFunction(
"TestDict");
-
Call();
-
}
-
-
private void Call() {
-
func.BeginPCall();
-
func.Push(map);
-
func.PCall();
-
func.EndPCall();
-
}
-
-
private void InitDirec() {
-
map.Add(
1,
new Account(
1,
"张三",
0));
-
map.Add(
2,
new Account(
2,
"李四",
1));
-
map.Add(
3,
new Account(
3,
"王五",
0));
-
}
-
-
private void OnApplicationQuit() {
-
if (func !=
null) {
-
func.Dispose();
-
func =
null;
-
}
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
function TestDict(map)
-
--遍历map的所有value元素
-
local iter = map:GetEnumerator()
-
while iter:MoveNext()
do
-
local v = iter.Current.Value
-
print(
'id: '..v.id ..
', name: '..v.name..
', sex: '..v.sex)
-
end
-
--遍历map的key集
-
local keys = map.Keys
-
iter = keys:GetEnumerator()
-
print(
'------------print dictionary keys---------------')
-
while iter:MoveNext()
do
-
print(iter.Current)
-
end
-
--遍历map的value集
-
local values = map.Values
-
iter = values:GetEnumerator()
-
print(
'------------print dictionary values---------------')
-
while iter:MoveNext()
do
-
print(iter.Current.name)
-
end
-
--根据key查找value元素
-
local flag, account = map:TryGetValue(
1,
nil)
-
if flag
then
-
print(
'TryGetValue result ok: '..account.name)
-
end
-
print(
'kick '..map[
2].name)
-
--移除元素, 并打印剩余的value
-
map:Remove(
2)
-
iter = map:GetEnumerator()
-
while iter:MoveNext()
do
-
local v = iter.Current.Value
-
print(
'id: '..v.id ..
' name: '..v.name..
' sex: '..v.sex)
-
end
-
end
补充:CustomSettings.cs 里需要配置 Account 如下:
-
//在这里添加你要导出注册到lua的类型列表
-
public
static BindType[] customTypeList = {
-
//------------------------为例子导出--------------------------------
-
_GT(
typeof(Account)),
-
_GT(
typeof(Dictionary<
int, Account>)).SetLibName(
"AccountMap"),
-
_GT(
typeof(KeyValuePair<
int, Account>)),
-
_GT(
typeof(Dictionary<
int, Account>.KeyCollection)),
-
_GT(
typeof(Dictionary<
int, Account>.ValueCollection)),
-
//-------------------------------------------------------------------
-
...
-
}
配置后,需要点击 Generate All 生成相关 Wrap 文件,如果出现以下报错:
LuaException: LuaScript.lua:12: field or property MoveNext does not exist
修改 System_Collections_Generic_Dictionary_int_Account_KeyCollectionWrap.cs 和 System_Collections_Generic_Dictionary_int_Account_ValueCollectionWrap.cs 文件,将 ToLua.PushValue(L, o) 替换为 ToLua.Push(L, o),如下:
-
//ToLua.PushValue(L, o);
-
ToLua.Push(L, o);
2.8 Unity3D 给 Lua 传递列表
TestList.cs
-
using UnityEngine;
-
using LuaInterface;
-
using System.Collections.Generic;
-
-
public
class
TestList :
MonoBehaviour {
-
private LuaState lua =
null;
-
-
private void Awake() {
-
lua =
new LuaState();
-
lua.Start();
-
LuaBinder.Bind(lua);
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\08");
-
lua.DoFile(
"LuaScript.lua");
-
DelegateFactory.Init();
-
List<
string> list = GetList();
-
Call(list);
-
}
-
-
private void Call(List<string> list) {
-
LuaFunction func = lua.GetFunction(
"TestList");
-
func.BeginPCall();
-
func.Push(list);
-
func.PCall();
-
func.EndPCall();
-
func.Dispose();
-
func =
null;
-
}
-
-
private List<string> GetList() {
-
List<
string> list =
new List<
string>();
-
list.Add(
"zhang");
-
list.Add(
"li");
-
list.Add(
"wang");
-
return list;
-
}
-
-
private void OnApplicationQuit() {
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
-
function printList(list)
-
str =
""
-
for i =
0, list.Count -
1
do
-
str = str..(list[i])..
", "
-
end
-
print(str)
-
end
-
-
function TestList(list)
-
printList(list)
--zhang, li, wang,
-
list:Add(
"chen")
-
printList(list)
--zhang, li, wang, chen,
-
list:Sort()
-
printList(list)
--chen, li, wang, zhang,
-
print(
"index="..list:IndexOf(
"wang"))
--index=2
-
list:Remove(
"li")
-
printList(list)
--chen, wang, zhang,
-
end
2.9 Lua 中创建 GameObject 并获取和添加组件
TestGameObject.cs
-
using UnityEngine;
-
using LuaInterface;
-
-
public
class
TestGameObject :
MonoBehaviour {
-
private LuaState lua =
null;
-
-
private void Awake() {
-
lua =
new LuaState();
-
lua.Start();
-
LuaBinder.Bind(lua);
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\09");
-
lua.DoFile(
"LuaScript.lua");
-
}
-
-
private void OnApplicationQuit() {
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
local GameObject = UnityEngine.GameObject
-
local PrimitiveType = UnityEngine.PrimitiveType
--需要在CustomSettings.cs里添加"_GT(typeof(PrimitiveType))"
-
local MeshRenderer = UnityEngine.MeshRenderer
-
local Color = UnityEngine.Color
-
local Rigidbody = UnityEngine.Rigidbody
-
-
go = GameObject.CreatePrimitive(PrimitiveType.Cube)
-
go:GetComponent(
"MeshRenderer").sharedMaterial.color = Color.red
-
rigidbody = go:AddComponent(typeof(Rigidbody))
-
rigidbody.mass =
1000
3 Lua Hook Unity3D 生命周期方法
TestLife.cs
-
using UnityEngine;
-
using LuaInterface;
-
using System.Collections.Generic;
-
-
public
class
TestLife :
MonoBehaviour {
-
private LuaState lua =
null;
-
private Dictionary<
string, LuaFunction> func;
-
-
private void Awake() {
-
lua =
new LuaState();
-
lua.Start();
-
LuaBinder.Bind(lua);
-
lua.AddSearchPath(Application.dataPath +
"\\Scenes\\10");
-
lua.DoFile(
"LuaScript.lua");
-
GetFunc();
-
CallFunc(
"awake");
-
}
-
-
private void OnEnable() {
-
CallFunc(
"onEnable");
-
}
-
-
private void Start() {
-
CallFunc(
"start");
-
}
-
-
private void Update() {
-
CallFunc(
"update");
-
}
-
-
private void OnDisable() {
-
CallFunc(
"onDisable");
-
}
-
-
private void OnDestroy() {
-
CallFunc(
"onDestroy");
-
}
-
-
private void GetFunc() {
-
func =
new Dictionary<
string, LuaFunction>();
-
AddFunc(
"awake");
-
AddFunc(
"onEnable");
-
AddFunc(
"start");
-
AddFunc(
"update");
-
AddFunc(
"onDisable");
-
AddFunc(
"onDestroy");
-
}
-
-
private void AddFunc(string funcName) {
-
LuaFunction fun = lua.GetFunction(funcName);
-
if (fun !=
null) {
-
func.Add(funcName, fun);
-
}
-
}
-
-
private void CallFunc(string funcName) {
-
if (func.ContainsKey(funcName)) {
-
LuaFunction fun = func[funcName];
-
fun.Call();
-
}
-
}
-
-
private void OnApplicationQuit() {
-
foreach(
var fun
in func.Values)
-
{
-
fun.Dispose();
-
}
-
func.Clear();
-
func =
null;
-
lua.Dispose();
-
lua =
null;
-
}
-
}
LuaScript.lua
-
-
function awake()
-
print(
"awake")
-
end
-
-
function onEnable()
-
print(
"onEnable")
-
end
-
-
function start()
-
print(
"start")
-
end
-
-
function update()
-
print(
"update")
-
end
-
-
function onDisable()
-
print(
"onDisable")
-
end
-
-
function onDestroy()
-
print(
"onDestroy")
-
end
转载:https://blog.csdn.net/m0_37602827/article/details/128429135