使用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是不同的,仅仅是名称的借用,来解决类似的问题。

 

 

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

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