//////////////////////////// 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是不同的,僅僅是名稱的借用,來解決類似的問題。
//////////////////////////////////////////////////////////////////////////////////////////////////////////