使用XLua 與 Lua 熱重載 記錄

//////////////////////////// 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),以及其它一些事情。

需要定期調用,比如在MonoBehaviourUpdate中調用。

void AddLoader(CustomLoader loader)

描述:

增加一個自定義loader

參數:

loader:一個包括了加載函數的委託,其類型爲delegate byte[] CustomLoader(ref string filepath),當一個文件被require時,這個loader會被回調,其參數是調用require所使用的參數,如果該loader找到文件,可以將其讀進內存,返回一個byte數組。如果需要支持調試的話,而filepath要設置成IDE能找到的路徑(相對或者絕對都可以)

void Dispose()

描述:

DisposeLuaEnv

LuaEnv的使用建議:全局就一個實例,並在Update中調用GC方法,完全不需要時調用Dispose

LuaTable

T Get(string key)

描述:

獲取在key下,類型爲Tvalue,如果不存在或者類型不匹配,返回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)

描述:

 上面的APIKey都只能是string,而這個API無此限制;

void Set<TKey, TValue>(TKey key, TValue value)

描述:

 對應Get<TKey, TValue>setter

T Cast()

描述:

把該table轉成一個T指明的類型,可以是一個加了CSharpCallLua聲明的interface,一個有默認構造函數的class或者struct,一個DictionaryList等等。

void SetMetaTable(LuaTable metaTable)

描述:

設置metaTabletablemetatable

LuaFunction

注意:用該類訪問Lua函數會有boxingunboxing的開銷,爲了性能考慮,需要頻繁調用的地方不要用該類。建議通過table.Get獲取一個delegate再調用(假設ABCDelegateC#的一個delegate)。在使用使用table.Get之前,請先把ABCDelegate加到代碼生成列表。

object[] Call(params object[] args)

描述:

以可變參數調用Lua函數,並返回該調用的返回值。

object[] Call(object[] args, Type[] returnTypes)

描述:

調用Lua函數,並指明返回參數的類型,系統會自動按指定類型進行轉換。

void SetEnv(LuaTable env)

描述:

相當於luasetfenv函數。

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類型| |-|-| |sbytebyteshortushortintuintdoublecharfloat|number| |decimal|userdata| |longulong|userdata/lua_Integer(lua53)| |bytes[]|string| |bool|boolean| |string|string|

複雜數據類型

|C#類型|Lua類型| |-|-| |LuaTable|table| |LuaFunction|function| |class或者 struct的實例|userdatatable| |methoddelegate|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的實例,將映射到Luauserdata,並通過__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");//映射到有對應字段的classby 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");//映射到LuaTableby 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. 重新調用requirerequire(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是不同的,僅僅是名稱的借用,來解決類似的問題。

 

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章