XLua API ///
C# API
LuaEnv类
object[] DoString(string chunk, string chunkName = "chuck", LuaTable env = null)
描述:
执行一个代码块。
参数:
chunk: Lua代码的字符串;
chunkName: 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
env :这个代码块的环境变量;
返回值:
代码块里return语句的返回值;
比如:return 1, “hello”,DoString返回将包含两个object的数组, 一个是double类型的1, 一个是string类型的“hello”
例如:
LuaEnv luaenv = new LuaEnv();
object[] ret = luaenv.DoString("print(‘hello’)\r\nreturn 1")
UnityEngine.Debug.Log("ret="+ret[0]);
luaenv.Dispose()
T LoadString(string chunk, string chunkName = "chunk", LuaTable env = null)
描述:
加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunction
参数:
chunk: Lua代码的字符串;
chunkName: 发生error时的debug显示信息中使用,指明某某代码块的某行错误;
env :这个代码块的环境变量;
返回值:
代表该代码块的delegate或者LuaFunction类;
LuaTable Global;
描述:
代表lua全局环境的LuaTable
void Tick()
描述:
清除Lua的未手动释放的LuaBase对象(比如:LuaTable, LuaFunction),以及其它一些事情。
需要定期调用,比如在MonoBehaviour的Update中调用。
void AddLoader(CustomLoader loader)
描述:
增加一个自定义loader
参数:
loader:一个包括了加载函数的委托,其类型为delegate byte[] CustomLoader(ref string filepath),当一个文件被require时,这个loader会被回调,其参数是调用require所使用的参数,如果该loader找到文件,可以将其读进内存,返回一个byte数组。如果需要支持调试的话,而filepath要设置成IDE能找到的路径(相对或者绝对都可以)
void Dispose()
描述:
Dispose该LuaEnv。
LuaEnv的使用建议:全局就一个实例,并在Update中调用GC方法,完全不需要时调用Dispose
LuaTable类
T Get(string key)
描述:
获取在key下,类型为T的value,如果不存在或者类型不匹配,返回null;
T GetInPath(string path)
描述:
和Get的区别是,这个函数会识别path里头的“.”,比如var i = tbl.GetInPath<int>(“a.b.c”)相当于在lua里头执行i = tbl.a.b.c,避免仅为了获取中间变量而多次调用Get,执行效率更高。
void SetInPath(string path, T val)
描述:
和GetInPaht<T>对应的setter;
void Get<TKey, TValue>(TKey key, out TValue value)
描述:
上面的API的Key都只能是string,而这个API无此限制;
void Set<TKey, TValue>(TKey key, TValue value)
描述:
对应Get<TKey, TValue>的setter;
T Cast()
描述:
把该table转成一个T指明的类型,可以是一个加了CSharpCallLua声明的interface,一个有默认构造函数的class或者struct,一个Dictionary,List等等。
void SetMetaTable(LuaTable metaTable)
描述:
设置metaTable为table的metatable
LuaFunction类
注意:用该类访问Lua函数会有boxing,unboxing的开销,为了性能考虑,需要频繁调用的地方不要用该类。建议通过table.Get获取一个delegate再调用(假设ABCDelegate是C#的一个delegate)。在使用使用table.Get之前,请先把ABCDelegate加到代码生成列表。
object[] Call(params object[] args)
描述:
以可变参数调用Lua函数,并返回该调用的返回值。
object[] Call(object[] args, Type[] returnTypes)
描述:
调用Lua函数,并指明返回参数的类型,系统会自动按指定类型进行转换。
void SetEnv(LuaTable env)
描述:
相当于lua的setfenv函数。
Lua API
CS对象
CS.namespace.class(...)
描述:
调用一个C#类型的构造函数,并返回类型实例
例如:
local v1=CS.UnityEngine.Vector3(1,1,1)
CS.namespace.class.field
描述:
访问一个C#静态成员
例如:
Print(CS.UnityEngine.Vector3.one)
CS.namespace.enum.field
描述:
访问一个枚举值
typeof函数
描述:
类似C#里头的typeof关键字,返回一个Type对象,比如GameObject.AddComponent其中一个重载需要一个Type参数
例如:
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
无符号64位支持
uint64.tostring
描述:
无符号数转字符串。
uint64.divide
描述:
无符号数除法。
uint64.compare
描述:
无符号比较,相对返回0,大于返回正数,小于返回负数。
uint64.remainder
描述:
无符号数取模。
uint64.parse
描述: 字符串转无符号数。
xlua.structclone
描述:
克隆一个c#结构体
xlua.private_accessible(class)
描述:
让一个类的私有字段,属性,方法等可用
例子:
xlua.private_accessible(CS.UnityEngine.GameObject)
xlua.getgenericmethod
描述:
获取一个泛型方法
例子:
~~~lua local foogeneric = xlua.getgenericmethod(CS.GetGenericMethodTest, 'Foo') local bargeneric = xlua.getgenericmethod(CS.GetGenericMethodTest, 'Bar')
local foo = foogeneric(CS.System.Int32, CS.System.Double) local bar = bargeneric(CS.System.Double, CS.UnityEngine.GameObject)
-- call instance method local o = CS.GetGenericMethodTest() local ret = foo(o, 1, 2) print(ret)
-- call static method bar(2, nil) ~~~
/自己的注释多个参数的泛型方法在lua中调用///
local func = xlua.getgenericmethod(CS.cs类名, '泛型方法名', 0) // 0是代表一个参数,1是代表两个参数
local call = func(CS.System.Int32)
call(泛型方法的第一个参数,反省方法的多参数的依次写,并用,分开啊)
cast函数
描述:
指明以特定的接口访问对象,这在实现类无法访问的时候(比如internal修饰)很有用,这时可以这么来(假设下面的calc对象实现了C#的PerformentTest.ICalc接口)
例子:
cast(calc, typeof(CS.PerformentTest.ICalc))
然后就木有其它API了 访问csharp对象和访问一个table一样,调用函数跟调用lua函数一样,也可以通过操作符访问c#的操作符,下面是一个例如:
local v1=CS.UnityEngine.Vector3(1,1,1)
local v2=CS.UnityEngine.Vector3(1,1,1)
v1.x = 100
v2.y = 100
print(v1, v2)
local v3 = v1 + v2
print(v1.x, v2.x)
print(CS.UnityEngine.Vector3.one)
print(CS.UnityEngine.Vector3.Distance(v1, v2))
类型映射
基本数据类型
|C#类型|Lua类型| |-|-| |sbyte,byte,short,ushort,int,uint,double,char,float|number| |decimal|userdata| |long,ulong|userdata/lua_Integer(lua53)| |bytes[]|string| |bool|boolean| |string|string|
复杂数据类型
|C#类型|Lua类型| |-|-| |LuaTable|table| |LuaFunction|function| |class或者 struct的实例|userdata,table| |method,delegate|function|
LuaTable:
C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值)LuaTable类型,则要求Lua侧为table。或者Lua侧的table,在C#侧未指明类型的情况下转换成LuaTable。
LuaFunction:
C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值)LuaFunction类型,则要求Lua侧为function。或者Lua侧的function,在C#侧未指明类型的情况下转换成LuaFunction。
LuaUserData:
对应非C# Managered对象的lua userdata。
class或者 struct的实例:
从C#传一个class或者struct的实例,将映射到Lua的userdata,并通过__index访问该userdata的成员 C#侧指明从Lua侧输入指定类型对象,Lua侧为该类型实例的userdata可以直接使用;如果该指明类型有默认构造函数,Lua侧是table则会自动转换,转换规则是:调用构造函数构造实例,并用table对应字段转换到c#对应值后赋值各成员。
method, delegate:
成员方法以及delegate都是对应lua侧的函数。 C#侧的普通参数以及引用参数,对应lua侧函数参数;C#侧的返回值对应于Lua的第一个返回值;引用参数和out参数则按序对应于Lua的第2到第N个参数。
宏
HOTFIX_ENABLE
打开hotfix功能。
NOTGENWARNING
反射时打印warning。
GENCODEMINIMIZE
以偏向减少代码段的方式生成代码。
///XLua API
// CSharpCallLua /
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;
namespace Tutorial
{
public class CSCallLua : MonoBehaviour
{
LuaEnv luaenv = null;
string script = @"
a = 1
b = 'hello world'
c = true
d = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print('d.add called')
return a + b
end
}
function e()
print('i am e')
end
function f(a, b)
print('a', a, 'b', b)
return 1, {f1 = 1024}
end
function ret_e()
print('ret_e called')
return e
end
";
public class DClass
{
public int f1;
public int f2;
public int f3; // My Add
}
[CSharpCallLua]
public interface ItfD
{
int f1 { get; set; }
int f2 { get; set; }
int add(int a, int b);
}
[CSharpCallLua]
public delegate int FDelegate(int a, string b, out DClass c);
[CSharpCallLua]
public delegate Action GetE();
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString(script);
Debug.Log("_G.a = " + luaenv.Global.Get<int>("a"));
Debug.Log("_G.b = " + luaenv.Global.Get<string>("b"));
Debug.Log("_G.c = " + luaenv.Global.Get<bool>("c"));
// Log output---- _G.a = 1
// Log output---- _G.b = hello world
// Log output---- _G.c = True
DClass d = luaenv.Global.Get<DClass>("d");//映射到有对应字段的class,by value
//Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}");
Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}" + ", f3=" + d.f3); // My Add
// Log output---- _G.d = {f1=12, f2=34}, f3=0
Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>("d");//映射到Dictionary<string, double>,by value
Debug.Log("_G.d = {f1=" + d1["f1"] + ", f2=" + d1["f2"] + "}, d.Count=" + d1.Count);
// Log output---- _G.d = {f1=12, f2=34}, d.Count=2
// ---- My Add ----
Dictionary<int, double> d1_Temp = luaenv.Global.Get<Dictionary<int, double>>("d");//映射到Dictionary<int, double>,by value
// ---- 表里 还是按 1 开始
Debug.Log("- My Add --_G.d = {d1_Temp[1]=" + d1_Temp[1] + ", d1_Temp[2]=" + d1_Temp[2] + ", d1_Temp[3]=" + d1_Temp[3] + "}, d.Count=" + d1_Temp.Count); // My Add
// ---- My Add ----
// Log output---- - My Add --_G.d = {d1_Temp[1]=1, d1_Temp[2]=2, d1_Temp[3]=3}, d.Count=3
List<double> d2 = luaenv.Global.Get<List<double>>("d"); //映射到List<double>,by value
//Debug.Log("_G.d.len = " + d2.Count);
// ---- 这里是 List 从 0 开始
Debug.Log("_G.d.len = " + d2.Count + ", d2[0]=" + d2[0] + ", d2[1]=" + d2[1] + ", d2[2]=" + d2[2]); // My Add
// Log output---- _G.d.len = 3, d2[0]=1, d2[1]=2, d2[2]=3
ItfD d3 = luaenv.Global.Get<ItfD>("d"); //映射到interface实例,by ref,这个要求interface加到生成列表,否则会返回null,建议用法
d3.f2 = 1000;
Debug.Log("_G.d = {f1=" + d3.f1 + ", f2=" + d3.f2 + "}");
Debug.Log("_G.d:add(1, 2)=" + d3.add(1, 2));
// Log output---- _G.d = {f1=12, f2=1000}
// Log output---- LUA: d.add called
// Log output---- _G.d:add(1, 2)=3
LuaTable d4 = luaenv.Global.Get<LuaTable>("d");//映射到LuaTable,by ref
//Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}");
// ---- 这里是 LuaTable 从 1 开始
Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}" + " ,____" + d4.Get<int>(1)); // My Add
// Log output---- _G.d = {f1=12, f2=1000} ,____1
Action e = luaenv.Global.Get<Action>("e");//映射到一个delgate,要求delegate加到生成列表,否则返回null,建议用法
e();
// Log output---- LUA: i am e
FDelegate f = luaenv.Global.Get<FDelegate>("f");
DClass d_ret;
int f_ret = f(100, "John", out d_ret);//lua的多返回值映射:从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数
Debug.Log("ret.d = {f1=" + d_ret.f1 + ", f2=" + d_ret.f2 + "}, ret=" + f_ret);
// Log output---- LUA: a 100 b John
// Log output---- ret.d = {f1=1024, f2=0}, ret=1
GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//delegate可以返回更复杂的类型,甚至是另外一个delegate
e = ret_e();
e();
// Log output---- LUA: ret_e called
// Log output---- LUA: i am e
LuaFunction d_e = luaenv.Global.Get<LuaFunction>("e");
d_e.Call();
// Log output---- LUA: i am e
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}
}
/
LuaCallCSharp /
using UnityEngine;
using System.Collections;
using System;
using XLua;
using System.Collections.Generic;
namespace Tutorial
{
[LuaCallCSharp]
public class BaseClass
{
public static void BSFunc()
{
Debug.Log("Derived Static Func, BSF = " + BSF);
}
public static int BSF = 1;
public void BMFunc()
{
Debug.Log("Derived Member Func, BMF = " + BMF);
}
public int BMF { get; set; }
}
public struct Param1
{
public int x;
public string y;
}
[LuaCallCSharp]
public enum TestEnum
{
E1,
E2
}
[LuaCallCSharp]
public class DerivedClass : BaseClass
{
[LuaCallCSharp]
public enum TestEnumInner
{
E3,
E4
}
public void DMFunc()
{
Debug.Log("Derived Member Func, DMF = " + DMF);
}
public int DMF { get; set; }
public double ComplexFunc(Param1 p1, ref int p2, out string p3, Action luafunc, out Action csfunc)
{
Debug.Log("P1 = {x=" + p1.x + ",y=" + p1.y + "},p2 = " + p2);
luafunc();
p2 = p2 * p1.x;
p3 = "hello " + p1.y;
csfunc = () =>
{
Debug.Log("csharp callback invoked!");
};
return 1.23;
}
public void TestFunc(int i)
{
Debug.Log("TestFunc(int i)");
}
public void TestFunc(string i)
{
Debug.Log("TestFunc(string i)");
}
public static DerivedClass operator +(DerivedClass a, DerivedClass b)
{
DerivedClass ret = new DerivedClass();
ret.DMF = a.DMF + b.DMF;
return ret;
}
public void DefaultValueFunc(int a = 100, string b = "cccc", string c = null)
{
UnityEngine.Debug.Log("DefaultValueFunc: a=" + a + ",b=" + b + ",c=" + c);
}
public void VariableParamsFunc(int a, params string[] strs)
{
UnityEngine.Debug.Log("VariableParamsFunc: a =" + a);
foreach (var str in strs)
{
UnityEngine.Debug.Log("str:" + str);
}
}
public TestEnum EnumTestFunc(TestEnum e)
{
Debug.Log("EnumTestFunc: e=" + e);
return TestEnum.E2;
}
public Action<string> TestDelegate = (param) =>
{
Debug.Log("TestDelegate in c#:" + param);
};
public event Action TestEvent;
public void CallEvent()
{
TestEvent();
}
public ulong TestLong(long n)
{
return (ulong)(n + 1);
}
class InnerCalc : ICalc
{
public int add(int a, int b)
{
return a + b;
}
public int id = 100;
}
public ICalc GetCalc()
{
return new InnerCalc();
}
public void GenericMethod<T>()
{
Debug.Log("GenericMethod<" + typeof(T) + ">");
}
}
[LuaCallCSharp]
public interface ICalc
{
int add(int a, int b);
}
[LuaCallCSharp]
public static class DerivedClassExtensions
{
public static int GetSomeData(this DerivedClass obj)
{
Debug.Log("GetSomeData ret = " + obj.DMF);
return obj.DMF;
}
public static int GetSomeBaseData(this BaseClass obj)
{
Debug.Log("GetSomeBaseData ret = " + obj.BMF);
return obj.BMF;
}
public static void GenericMethodOfString(this DerivedClass obj)
{
obj.GenericMethod<string>();
}
}
}
public class LuaCallCs : MonoBehaviour
{
LuaEnv luaenv = null;
string script = @"
function demo()
--new C#对象
local newGameObj = CS.UnityEngine.GameObject()
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
print(newGameObj, newGameObj2)
-- Log output==== LUA: New Game Object (UnityEngine.GameObject): -5572 helloworld (UnityEngine.GameObject): -5576
--访问静态属性,方法
local GameObject = CS.UnityEngine.GameObject
print('UnityEngine.Time.deltaTime:', CS.UnityEngine.Time.deltaTime) --读静态属性
-- Log output==== LUA: UnityEngine.Time.deltaTime: 0.019999999552965
CS.UnityEngine.Time.timeScale = 0.5 --写静态属性
print('helloworld', GameObject.Find('helloworld')) --静态方法调用
-- Log output==== LUA: helloworld helloworld (UnityEngine.GameObject): -5576
--访问成员属性,方法
local DerivedClass = CS.Tutorial.DerivedClass
local testobj = DerivedClass()
testobj.DMF = 1024--设置成员属性
print(testobj.DMF)--读取成员属性
-- Log output==== LUA: 1024
testobj:DMFunc()--成员方法
-- Log output==== Derived Member Func, DMF = 1024
--基类属性,方法
print(DerivedClass.BSF)--读基类静态属性
-- Log output==== LUA: 1
DerivedClass.BSF = 2048--写基类静态属性
DerivedClass.BSFunc();--基类静态方法
-- Log output==== Derived Static Func, BSF = 2048
print(testobj.BMF)--读基类成员属性
-- Log output==== LUA: 0
testobj.BMF = 4096--写基类成员属性
testobj:BMFunc()--基类方法调用
-- Log output==== Derived Member Func, BMF = 4096
--复杂方法调用
local ret, p2, p3, csfunc = testobj:ComplexFunc({x=3, y = 'john'}, 100, function()
print('i am lua callback')
end)
-- Log output==== P1 = {x=3,y=john},p2 = 100
-- Log output==== LUA: i am lua callback
print('ComplexFunc ret:', ret, p2, p3, csfunc)
-- Log output==== LUA: ComplexFunc ret: 1.23 300 hello john System.Action: 163023872
csfunc()
-- Log output==== csharp callback invoked!
--重载方法调用
testobj:TestFunc(100)
-- Log output==== TestFunc(int i)
testobj:TestFunc('hello')
-- Log output==== TestFunc(string i)
--操作符
local testobj2 = DerivedClass()
testobj2.DMF = 2048
print('(testobj + testobj2).DMF = ', (testobj + testobj2).DMF)
-- Log output==== LUA: (testobj + testobj2).DMF = 3072
--默认值
testobj:DefaultValueFunc(1)
-- Log output==== DefaultValueFunc: a=1,b=cccc,c=
testobj:DefaultValueFunc(3, 'hello', 'john')
-- Log output==== DefaultValueFunc: a=3,b=hello,c=john
--可变参数
testobj:VariableParamsFunc(5, 'hello', 'john')
-- Log output==== VariableParamsFunc: a =5
-- Log output==== str:hello
-- Log output==== str:john
--Extension methods
print(testobj:GetSomeData())
-- Log output==== GetSomeData ret = 1024
-- Log output==== LUA: 1024
print(testobj:GetSomeBaseData()) --访问基类的Extension methods
-- Log output==== GetSomeBaseData ret = 4096
-- Log output==== LUA: 4096
testobj:GenericMethodOfString() --通过Extension methods实现访问泛化方法
-- Log output==== GenericMethod<System.String>
--枚举类型
local e = testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
-- Log output==== EnumTestFunc: e=E1
print(e, e == CS.Tutorial.TestEnum.E2)
-- Log output==== LUA: E2: 1 true
print(CS.Tutorial.TestEnum.__CastFrom(1), CS.Tutorial.TestEnum.__CastFrom('E1'))
-- Log output==== LUA: E2: 1 E1: 0
print(CS.Tutorial.DerivedClass.TestEnumInner.E3)
-- Log output==== LUA: E3: 0
assert(CS.Tutorial.BaseClass.TestEnumInner == nil) -- 这个没输出
--Delegate
testobj.TestDelegate('hello') --直接调用
-- Log output==== TestDelegate in c#:hello
local function lua_delegate(str)
print('TestDelegate in lua:', str)
end
testobj.TestDelegate = lua_delegate + testobj.TestDelegate --combine,这里演示的是C#delegate作为右值,左值也支持
-- 这个没输出
testobj.TestDelegate('hello')
-- Log output==== LUA: TestDelegate in lua: hello
-- Log output==== TestDelegate in c#:hello
testobj.TestDelegate = testobj.TestDelegate - lua_delegate --remove
testobj.TestDelegate('hello')
-- Log output==== TestDelegate in c#:hello
--事件
local function lua_event_callback1() print('lua_event_callback1') end
local function lua_event_callback2() print('lua_event_callback2') end
testobj:TestEvent('+', lua_event_callback1)
testobj:CallEvent()
-- Log output==== LUA: lua_event_callback1
testobj:TestEvent('+', lua_event_callback2)
testobj:CallEvent()
-- Log output==== LUA: lua_event_callback1
-- Log output==== LUA: lua_event_callback2
testobj:TestEvent('-', lua_event_callback1)
testobj:CallEvent()
-- Log output==== LUA: lua_event_callback2
testobj:TestEvent('-', lua_event_callback2) -- 这个 反注册 完全
--64位支持
local l = testobj:TestLong(11)
print(type(l), l, l + 100, 70000 + l)
-- Log output==== LUA: number 12 112 70012
--typeof
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem)) -- 这个在untiy里已经加上ParticleSystem脚本
--cast
local calc = testobj:GetCalc()
print('assess instance of InnerCalc via reflection', calc:add(1, 2))
-- Log output==== LUA: assess instance of InnerCalc via reflection 3
assert(calc.id == 100) -- 这个不输出
cast(calc, typeof(CS.Tutorial.ICalc))
print('cast to interface ICalc', calc:add(1, 2))
-- Log output==== LUA: cast to interface ICalc 3
assert(calc.id == nil) -- 这个不输出 --[[ --]]
end
demo()
--协程下使用
local co = coroutine.create(function()
print('------------------------------------------------------')
demo()
end)
assert(coroutine.resume(co)) --[[ --]]
-- Log output==== LUA: ------------------------------------------------------
-- Log output==== LUA: New Game Object (UnityEngine.GameObject): -16440 helloworld (UnityEngine.GameObject): -16444
-- Log output==== LUA: UnityEngine.Time.deltaTime: 0.019999999552965
-- Log output==== LUA: helloworld helloworld (UnityEngine.GameObject): -16432
-- Log output==== LUA: 1024
-- Log output==== Derived Member Func, DMF = 1024
-- Log output==== LUA: 2048
-- Log output==== Derived Static Func, BSF = 2048
-- Log output==== LUA: 0
-- Log output==== Derived Member Func, BMF = 4096
-- Log output==== P1 = {x=3,y=john},p2 = 100
-- Log output==== LUA: i am lua callback
-- Log output==== LUA: ComplexFunc ret: 1.23 300 hello john System.Action: -484261120
-- Log output==== csharp callback invoked!
-- Log output==== TestFunc(int i)
-- Log output==== TestFunc(string i)
-- Log output==== LUA: (testobj + testobj2).DMF = 3072
-- Log output==== DefaultValueFunc: a=1,b=cccc,c=
-- Log output==== DefaultValueFunc: a=3,b=hello,c=john
-- Log output==== VariableParamsFunc: a =5
-- Log output==== str:hello
-- Log output==== str:john
-- Log output==== GetSomeData ret = 1024
-- Log output==== LUA: 1024
-- Log output==== GetSomeBaseData ret = 4096
-- Log output==== LUA: 4096
-- Log output==== GenericMethod<System.String>
-- Log output==== EnumTestFunc: e=E1
-- Log output==== LUA: E2: 1 true
-- Log output==== LUA: E2: 1 E1: 0
-- Log output==== LUA: E3: 0
-- Log output==== TestDelegate in c#:hello
-- Log output==== LUA: TestDelegate in lua: hello
-- Log output==== TestDelegate in c#:hello
-- Log output==== TestDelegate in c#:hello
-- Log output==== LUA: lua_event_callback1
-- Log output==== LUA: lua_event_callback1
-- Log output==== LUA: lua_event_callback2
-- Log output==== LUA: lua_event_callback2
-- Log output==== LUA: number 12 112 10012
-- Log output==== LUA: assess instance of InnerCalc via reflection 3
-- Log output==== LUA: cast to interface ICalc 3
";
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString(script);
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}
/
/热重载/
热重载,就是不重新开unity让代码的变化直接看出来,一般在开发时候使用
lua中通过require导入的文件,最终都存在package.loaded这个table中。
require会判断是否文件已经加载避免重复加载同一文件
所以需要先把package.loaded中对应的内容置空,然后再重新导入
for key,val in pairs(package.loaded) do
print(key)
if(key == "xxx") then
package.loaded[key] = nil;
require(key);
end
end
这种重新require会让这个脚本在内存中的数据丢失,谨慎使用
1. 将package.loaded[filename] = nil, 将模块置空.
2. 重新调用require,require(filename).
所以lua如果做了分层,数据和逻辑分离,只reload逻辑层,应该是可以的
注意的是,如果重新require的代码出错了,那么
package.loaded[filename]
会一直为nil的,会影响后面的模块
解决办法是把之前的保存一份,如果出错用老的,并且提示
-- require_ex.lua
function require_ex(filename)
local old_content
if package.loaded[filename] then
-- 把旧的模块保存起来
old = package.loaded[filename]
-- 然后package.loaded[filename]赋空
package.loaded[filename] = nil
end
-- xpcall下执行require
local ok,err = pcall(require, filename)
if not ok then
--热更失败,将旧值赋回去
print("hotfix fail, err msg ",err)
package.loaded[filename] = old_content
return false
end
return true
end
///
要热更一个已经被加载(require)过的模块,要做两件事件.
1. 将package.loaded[filename] = nil, 将模块置空.
2. 重新调用require,require(filename).
如果要热更的模块里面所用到的都是全局变量,那么只需要这样写:
global_var = global_var
ok,新的模块内容就会重新被加载进内存了,而且全局变量也成功保留了热更前的值。
然而每个文件里面只定义全局变量,会破坏模块的可读性,试想一下,定义了一个类,但它并没有成员变量,用到的变量全是全局变量,这是一种怎样的感受。
所以我们要换一种写法,模块既可以有自己的变量,又可以被热更。
形如一个类既有成员变量,又有成员函数,要实现热更一个模块同时又保留其原有变量,我们可以把模块的变量定义和函数定义分开,分别写在两个文件。
目录如下:
data目录: modA.lua; modB.lua; modC.lua
logic目录: modA.lua; modB.lua; modC.lua
把模块的变量定义放在data文件夹,把模块函数的实现放在logic目录。
下面是实现:
-- data.modA
local mod = {}
-- 这里定义模块变量
mod.players = {}
mod.total_online = 0
-- 最后记得return这个表
return mod
-- logic.lua
local mod = require("data.modA")
-- 定义函数
function mod.login()
-- xxxxxx
end
function mod.load_data()
-- xxxxxxx
End
这里只是一个例子,logic.modA想要执行 `require("data.modA")` 成功,还要修改package.path,这个是Lua虚拟机启动时自己去定义了,这里不做讨论。
这里把模块的变量和函数的定义分离了,我们只热更function目录下的文件,data目录下的则不管。
热更文件的实现如下:
-- require_ex.lua
function require_ex(filename)
local old_content
if package.loaded[filename] then
-- 把旧的模块保存起来
old = package.loaded[filename]
-- 然后package.loaded[filename]赋空
package.loaded[filename] = nil
end
-- xpcall下执行require
local ok,err = pcall(require, filename)
if not ok then
--热更失败,将旧值赋回去
print("hotfix fail, err msg ",err)
package.loaded[filename] = old_content
return false
end
return true
end
切记: 重新require模块的时候,必须在pcall下执行,万一模块有语法错误,require挂掉的话,package.loaded[filename]就会一直为nil。当有其他文件需要执行 require(filename)的时候,则会报错,后面的逻辑无法执行。
/
常见问题:Lua脚本热重载,内存状态数据丢失?
KSFramework中,所有的UI Lua脚本是可以重载的。脚本中的一些内存数据,在重载后会丢失,比如:
-- 记录一个UI界面被打开了多少次
local openCount = 0
function UILogin:OnOpen()
openCount = openCount + 1
end
return UILogin
如上,每一次的脚本Reload,都是对openCount变量重新初始化为0,这与实际需求不符。
为此,KSFramework中引入了Cookie机制——把状态值存起来,避免被脚本重载所影响,以上代码用加入Cookie机制:
function UILogin:OnOpen()
local openCount= Cookie.Get('UILogin.OpenCount')
if not openCount then
openCount = 0
end
openCount = openCount + 1
Cookie.Set('UILogin.OpenCount', openCount)
end
return UILogin
Cookie是什么?
cookie常见于http开发中,网站为了辨别用户身份而储存在用户本地终端上的数据,可以叫做浏览器缓存。
http是一种无状态协议,比如在用php语言开发http网站时,开发者对代码的改动只需刷新浏览器就可以立刻看到自己的改动,无需进行进程的启停操作,开发起来十分方便。这也是php语言大热的其中一个原因。
KSFramework采用Lua来进行UI开发,支持热重载来迅速修改代码;对Lua代码的热重载最重要的考虑因素就是Lua运行内存状态会丢失。
因此,KSFramework参考将HTTP领域的Cookie机制引入游戏开发,所有的本地状态值,都存放在Cookie中,逻辑与状态分离。写代码的过程即逻辑的过程,并不会影响当前的状态。
Cookie的具体实现非常的简单,它只不过是一个Hashtable,进行get/set操作,获取或设置任意的对象:
Cookie的代码实现
以(快速入门)的随机公告为例子:每一次重载lua脚本,都会重新进行随机。 有什么办法,让这个例子中,首次加载进行随机出1~3的数字,这个数字保存到Cookie。
在我们对脚本逻辑修改后,进行LUA脚本重载,这时候从Cookie中拿回之前随机的值进行使用。
-- 当不存在Cookie时,进行随机;存在Cookie,直接取值
local rand = Cookie.Get('UIBillboard.RandomNumber')
if not rand then
rand = math.random(1,3)
Cookie.Set('UIBillboard.RandomNumber')
end
简而言之——把状态信息保存到Cookie中,与逻辑代码分离。
当然了,这里说的Cookie,跟HTTP的Cookie是不同的,仅仅是名称的借用,来解决类似的问题。
//