Unity中 Xlua使用整理(一)

1.安装:

从GitHub上下载Xlua源码

Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. (github.com)

下载Xlua压缩包,并解压将Aseet文件夹中的Xlua和Plugins文件夹复制到Unity工程中

复制到工程之后,菜单栏就会出现Xlua项

新建脚本运行第一个程序:

cs 复制代码
public class Test : MonoBehaviour
{
    void Start()
    {
        LuaEnv luaenv = new LuaEnv();
        luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
        luaenv.Dispose();
    }
}

结果:

2.加载Lua脚本

1.读取字符串:

使用DoString函数,输入lua的字符串代码执行

cs 复制代码
LuaEnv luaenv = new LuaEnv();
luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
luaenv.Dispose();

2.默认loader加载:

使用DoString函数,执行require函数加载Lua脚本

cs 复制代码
 LuaEnv luaenv = new LuaEnv();
 luaenv.DoString("require 'main'");
 luaenv.Dispose();

require加载的路径:Resources文件夹、StreamingAssets文件夹、CustomLoader自定义Loader加载。下图中用红色框选的都是require的加载路径

如果放在Resources文件夹,因为Resource只支持有限的后缀,放Resources下的lua文件得加上txt后缀

如果放在StreamingAssets文件夹或者放在与Asset同级目录以及编辑器安装目录,则可以使用.lua作为后缀。

3.自定义loader加载:

通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。

新建Lua文件夹,存放lua文件

加载代码:

cs 复制代码
void Start()
{
    LuaEnv luaenv = new LuaEnv();
    luaenv.AddLoader(customLoader);
    luaenv.DoString("require 'main'");
    luaenv.Dispose();
}

    public byte[] customLoader(ref string filepath)
    {
        /*加载Lua代码*/
#if UNITY_EDITOR
        return AssetDatabase.LoadAssetAtPath<TextAsset>($"Assets/Lua/{filepath}.lua.txt").bytes;
#endif
        return null;
    }

3.C#调用Lua:

1.使用LuaEnv.Global获取一个全局基本数据类型:

使用Get<T>(string name)方法获取全局变量

C#代码:

cs 复制代码
    int hp = luaenv.Global.Get<int>("hp");
    bool isPlay = luaenv.Global.Get<bool>("isPlay");
    string heroName = luaenv.Global.Get<string>("heroName");
    Debug.Log($"HP:{hp},isPlay:{isPlay},heroName:{heroName}");

Lua代码:

Lua 复制代码
hp = 100
isPlay = false
heroName = "Owen"

结果:

2.访问一个全局的table

1.映射到普通class或struct

注:这种方式下xLua会new一个实例,并把对应的字段赋值过去。

table的属性可以多于或者少于 class的属性,可以嵌套其它复杂类型, 这个过程是值拷贝,如果class比较复杂代价会比较大,而且修改class的字段值不会同步到table,反过来也不会。这个功能可以通过把类型加到GCOptimize生成降低开销。

使用Get<T>(string name)方法获取全局变量

C#代码:class和struct中的字段访问都是公开的(publish)

cs 复制代码
 public class PlayerInfo
 {
      public int id;
      public string name;
      public int level;
 }

 public struct EventMsg
 {
      public int eventId;
      public string param;
 } 
 //映射到有对应字段的class,by value 
 PlayerInfo info = luaenv.Global.Get<PlayerInfo>("playerInfo");
 EventMsg msg = luaenv.Global.Get<EventMsg>("eventMsg");
 Debug.Log($"PlayerInfo:{info.id},{info.name},{info.level}");
 Debug.Log($"EventMsg:{msg.eventId},{msg.param}");

Lua代码:

Lua 复制代码
playerInfo = {
    id = 1001,
    name = "Owen",
    level = 100
} 

eventMsg = {
    eventId = 101,
    param = "aaaaaaaaaaa"
}

结果:

2.映射到一个interface

需要先生成代码(如果没生成代码会抛InvalidCastException异常),

代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数

C#代码:

cs 复制代码
    [CSharpCallLua]
    public interface IPlayerPosition
    {
        int x { get; set; }
        int y { get; set; }
        int z { get; set; }

        void add(int x, int y, int z);
        void sub(int x,int y ,int z);
    }

    public void GetInterface()
    {
        //映射到interface实例,by ref,这个要求interface加到生成列表,否则会返回null,建议用法
        IPlayerPosition pPos = luaenv.Global.Get<IPlayerPosition>("playerPosition");
        Debug.Log($"{pPos.x},{pPos.y},{pPos.z}");
        pPos.add(10,1,23);
        Debug.Log($"{pPos.x},{pPos.y},{pPos.z}");
        pPos.sub(1,-1,11);
        Debug.Log($"{pPos.x},{pPos.y},{pPos.z}");
    }

Lua代码:

Lua 复制代码
playerPosition = {
   x = 10,
   y = 0,
   z = 10
}

function playerPosition:add(x0,y0,z0)
       self.x = self.x + x0
       self.y = self.y + y0
       self.z = self.z + z0
end

function playerPosition:sub(x0,y0,z0)
       self.x = self.x - x0
       self.y = self.y - y0
       self.z = self.z - z0
end

结果:

注意:

C#侧和Lua侧的属性、方法、字段必须名字一样大小写一致。如果将C#侧的add方法写成Add,就会报错,如下:

3.映射到Dictionary<>,List<>

lua侧table和C#侧的key和value类型必须一致,List只会映射table的数组部分,Dictionary只会映射非数组部分。

C#代码:

cs 复制代码
      //映射到Dictionary<string, double>,by value
      Dictionary<string, double> d = luaenv.Global.Get<Dictionary<string, double>>("Item");
      Debug.Log(d.Count);

      //映射到List<double>,by value
      List<double> l = luaenv.Global.Get<List<double>>("Item"); 
      Debug.Log(l.Count);

Lua代码:

Lua 复制代码
Item = {
  10001,1002,content = 10000,20001,3300,
}

结果:

4.映射到LuaTable类

这种方式好处是不需要生成代码,但也有一些问题,慢,比2要慢一个数量级,没有类型检查。

访问字段属性时需要用Get<T>(string name)方法访问。

C#侧代码:

cs 复制代码
//映射到LuaTable,by ref
LuaTable info= luaenv.Global.Get<LuaTable>("playerInfo");
Debug.Log($"PlayerInfo:{info.Get<int>("id")},{info.Get<string>("name")},{info.Get<int>("level")}");
cs 复制代码
  [CSharpCallLua]
  public delegate void AddMethod(LuaTable self,int x,int y, int z);

  [CSharpCallLua]
  public delegate Action addAc(LuaTable t, int x, int y, int z);

  //映射到LuaTable,by ref
  LuaTable info= luaenv.Global.Get<LuaTable>("playerPosition");

  AddMethod LD = info.Get<AddMethod>("add");
  LD(info,2, 3, 4);
  Debug.Log($"playerPosition :{info.Get<int>("x")},{info.Get<int>("y")},{info.Get<int>("z")}");

  var ac = info.Get<Action<LuaTable,int,int,int>>("add");
  ac(info, 2, 3, 4);
  Debug.Log($"playerPosition :{info.Get<int>("x")},{info.Get<int>("y")},{info.Get<int>("z")}");

  var aac = info.Get<addAc>("add");
  aac(info, 2, 3, 4);
  Debug.Log($"playerPosition :{info.Get<int>("x")},{info.Get<int>("y")},{info.Get<int>("z")}");
cs 复制代码
 //映射到LuaTable,by ref
 LuaTable info= luaenv.Global.Get<LuaTable>("playerPosition");
 var LF = info.Get<LuaFunction>("add");
 LF.Call(info,1,2,3);
 Debug.Log($"{info.Get<int>("x")},{info.Get<int>("y")},{info.Get<int>("z")}");

结果:

注意:在LuaTable中获取对象中方法需要用.Get<LuaFunction>(""),或者.Get<Action<type...>>("")或者使用.Get<delegate>("")。

3.访问一个全局的function

注意:用该类访问Lua函数会有boxing,unboxing的开销,为了性能考虑,需要频繁调用的地方不要用该类。建议通过 table.Get<ABCDelegate> 获取一个 delegate 再调用(假设 ABCDelegate 是 C# 的一个 delegate)。在使用使用 table.Get<ABCDelegate> 之前,请先把ABCDelegate加到代码生成列表。

1.映射到delegate:

使用delegate获取lua方法时需要先生成代码

Lua 复制代码
  [CSharpCallLua]
  public delegate void test1(int x);

  [CSharpCallLua] 
  public delegate Action test2(int x);
  
  test1 LD = luaenv.Global.Get<test1>("test");
  LD(100);

  var ac = luaenv.Global.Get<Action<int>>("test");
  ac(0);

  var aac = luaenv.Global.Get<test2>("test");
  aac(19);

Lua代码:

Lua 复制代码
function test(a)
    print(a)
end

结果:

2.映射到LuaFunction

cs 复制代码
var lf = luaenv.Global.Get<LuaFunction>("test");
lf.Call(10);

结果:

4.使用建议(官方手册)

  1. 访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。

  2. 如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。

4.Lua调用C#:

1.new C#对象

lua里头没有new关键字;所有C#相关的都放到CS 下,包括构造函数,静态成员属性、方法

使用lua new一个C#侧的GameObject对象

Lua代码:

Lua 复制代码
local newGameObj = CS.UnityEngine.GameObject()

结果:

xlua支持重载,使用重载new一个构造函数带参的GameObject

Lua代码:

Lua 复制代码
local newGameObj = CS.UnityEngine.GameObject("test")

结果:

2.访问C#静态属性,方法

c#代码:使用打标签(LuaCallCSharp)需要先生成代码再使用,如果不生成代码会使用性能较低的反射方式来访问。

cs 复制代码
    //此类是否是static都可以
    [LuaCallCSharp]  
    public static class GameCfg
    {
        public static int times = 0;
        public static string url = "www.baidu.com";

        public static void CalcValue()
        {
            Debug.Log("cccccccc");
        }
    }

1.获取静态属性:

Lua代码:

Lua 复制代码
local url = CS.GameCfg.url
print(url)

结果:

2.写入静态属性:

Lua代码:

Lua 复制代码
CS.GameCfg.url = "zzzzzz"
print(CS.GameCfg.url)

结果:

3.调用静态方法:

Lua代码:

Lua 复制代码
CS.Test.GameCfg.CalcValue()

结果:

3.访问C#成员属性,方法

C#侧代码:

cs 复制代码
    [LuaCallCSharp]
    public class Actor
    {
        public int id;
        public float hp;
        public string name;
        public float baseAtk;
        public float baseDef;

        public virtual void callAtk()
        {
            Debug.Log("atk");
        }

        public virtual void callWalk()
        {
            Debug.Log("walk");
        }
     
        public void PrintActorInfo()
        {
            Debug.Log($"ActorInfo:{id},{hp},{name},{baseAtk},{baseDef}");
        }

        public void Test2(int v, ref int c, out int d)
        {
            d = v + c;
            c++;
        }

        public void Test2(int v, ref int c)
        {
            c += v;
        }

        public void Test(int v , Action action,ref int c,out int d,out Action<int,int> testFunc)
        {
            d = v + c;
            c++;
            action();
            testFunc = (c,d) => { Debug.Log($"{c},{d}"); };
        }
    }

    [LuaCallCSharp]
    public class Player : Actor
    {
        public float Atk;
        public float Def;
        
        public delegate void testDelegate();

        public override void callAtk()
        {
            Debug.Log("Player Atk");
        }

        public override void callWalk()
        {
            Debug.Log("Player walk");
        }
     
        public bool testPass;

        public static Player operator +(Player a, Player b)
        {
            Player player = new Player();
            player.Atk = a.Atk + b.Atk;
            return player;
        }
     
        public void TestParam(int a, params int[] p)
        {
           Debug.Log($"a:{a},param len:{p.Length}");
        }

        public void TestDefualtValue(int a, string b ,bool c,Player p)
        {
           Debug.Log($"a:{a},b:{b},c:{c},p:{p}");
        }
    }

   [LuaCallCSharp]
   public class Monster : Actor
   {
     public event Action<string> testEvent;

     public override void callAtk()
     {
         Debug.Log("Monster atk");
     }

     public override void callWalk()
     {
         Debug.Log("Monster walk");
     }

     public MonsterType getMonsterType(int value)
     {
         value = Mathf.Min(++value, 3);
         return (MonsterType)value;
     }
   }

1.调用成员方法:

调用成员方法,第一个参数需要传该对象,建议用冒号语法糖。

Lua代码:

Lua 复制代码
local actorObj = CS.Test.Actor()

actorObj:callAtk()
actorObj:callWalk()

--或者使用如下方式执行成员方法
--actorObj.callAtk(actorObj)
--actorObj.callWalk(actorObj)

结果:

2.写入成员属性:

Lua代码:

Lua 复制代码
local actorObj = CS.Test.Actor()

actorObj:PrintActorInfo()

actorObj.id = 1001
actorObj.hp = 100
actorObj.name = "wukong"
actorObj.baseAtk = 500.6
actorObj.baseDef = 800

actorObj:PrintActorInfo()

结果:

3.获取成员属性:

Lua代码:

Lua 复制代码
local actorObj = CS.Test.Actor()

actorObj.id = 1001
actorObj.hp = 100
actorObj.name = "wukong"
actorObj.baseAtk = 500.6
actorObj.baseDef = 800

print(actorObj.id)
print(actorObj.hp)
print(actorObj.name)
print(actorObj.baseAtk)
print(actorObj.baseDef)

actorObj:PrintActorInfo()

结果:

4.父类属性,方法

Xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法。

Lua代码:

Lua 复制代码
local playerObj = CS.Test.Player()

playerObj.id = 1001
playerObj.hp = 100
playerObj.name = "wukong"
playerObj.baseAtk = 500.6
playerObj.baseDef = 800

playerObj:PrintActorInfo()

结果:

5.参数的输入输出属性(out,ref)

Lua调用侧的参数处理规则:C#的普通参数和带有ref参数算输入形参,带有out的参数不算形参,然后从左往右对应lua 调用侧的实参列表;

Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)、带有ref的参数和带有out参数算返回值,然后从左往右对应lua的多返回值。

Lua代码:

Lua 复制代码
local actorObj = CS.Test.Actor()
local c,d = actorObj:Test2(10,1)
print(c,d)

结果:

6.重载方法

直接通过不同的参数类型进行重载函数的访问,Xlua只一定程度上支持重载函数的调用,调用顺序是生成代码中排前面的那个最先调用,也就是同样参数数量的方法,在生成代码中最先生成的符合参数数量的函数被执行。

Lua代码:

Lua 复制代码
local actorObj = CS.Test.Actor()
local c,d = actorObj:Test2(10,1)
print(c,d)

结果:

因为C#侧的两个参数的Test2的重载方法最先生成代码,所以执行的是两个参数的Test2

7.操作符

支持的操作符有:+,-,*,/,==,unary-(++,--,+,-,!,~,(T)x,await,&x *x),<,<=, %,[]

C#代码:

cs 复制代码
 public static Player operator +(Player a,Player b)
 {
     Player player = new Player();
     player.Atk = a.Atk + b.Atk;
     return player;
 }

Lua代码:

Lua 复制代码
--操作符
local player1 = CS.Test.Player()
player1.Atk = 100
local player2 = CS.Test.Player()
player2.Atk = 210
print((player1+player2).Atk)

结果:

8.参数带默认值的方法

和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。

C#代码:

cs 复制代码
 public void TestDefualtValue(int a, string b ,bool c,Player p)
 {
     Debug.Log($"a:{a},b:{b},c:{c},p:{p}");
 }

Lua代码:

Lua 复制代码
local p = CS.Test.Player()
p:TestDefualtValue(2,"aaaaaaaa",false)
p:TestDefualtValue(2,"aaaaaaaa")
p:TestDefualtValue(2)

结果:

9.可变参数方法

C#代码:

cs 复制代码
 public void TestParam(int a, params int[] p)
 {
     Debug.Log($"a:{a},param len:{p.Length}");
 }

Lua代码:

Lua 复制代码
local p = CS.Test.Player()
p:TestParam(10,"hp",false,{"ccc",1,2})
p:TestParam(10,"hp",false)

结果:

10.使用Extension methods,泛型(模版)方法(本身不支持,使用扩展方法实现)

扩展方法:扩展方法 - C# | Microsoft Learn

C#代码:

cs 复制代码
public static class ActorExtendMethod
{
    public static void TestExtend(this Player p)
    {
        Debug.Log("这是Player的扩展方法");
    }

    public static void TestExtend(this Actor a)
    {
        Debug.Log("这是Actor的扩展方法");
    }

    public static void TestExtend(this Monster m)
    {
        Debug.Log("这是Monster的扩展方法");
    }
}

Lua代码:

Lua 复制代码
local c1 = CS.Test.Actor()
local c2 = CS.Test.Player()
local c3 = CS.Test.Monster()

c1.TestExtend()
c2.TestExtend()
c3.TestExtend()

结果:

11.枚举类型

枚举类支持**__CastFrom**方法,可以实现从一个整数或者字符串到枚举值的转换

C#代码:

cs 复制代码
[LuaCallCSharp]
public enum MonsterType
{
    None,
    Normal,
    melee,
    remote
}

Lua代码:

Lua 复制代码
local m = CS.Test.Monster()
print(m:getMonsterType(2))
print(CS.MonsterType.__CastFrom(1))

结果:

12.delegate使用(调用,+,-)

C#的delegate调用:和调用普通lua函数一样,+操作符 :对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。-操作符:和+相反,把一个delegate从调用链中移除。

C#代码:

cs 复制代码
  public delegate void testDelegate2(string content);
  public testDelegate2 test2 = (string content) =>
  {
      Debug.Log(content);
  };

Lua代码:

Lua 复制代码
local p = CS.Test.Player()

p.test2 = p.test2 + testD 
p.test2("add")

p.test2 = p.test2 - testD
p.test2("remove")

结果:

注:由于使用的是多播委托,所以初始时需要给多播委托一个值,后续增加委托才能用"+"/"-",如果直接使用"+",

cs 复制代码
 public delegate void testDelegate2(string content);
 public testDelegate2 test2;

会报如下的错误

13.event

C#代码:

cs 复制代码
public event Action<string> testEvent;

public void testCALL(string content)
{
     testEvent?.Invoke(content);
}

Lua代码:

Lua 复制代码
local function testD(content)
   print("LUA TESTD...."..content)
end

local m = CS.Test.Monster()

m:testEvent('+', testD)
m:testCALL("add");


m:testCALL("remove")
m:testEvent('-', testD)

结果:

注:

event使用 对象:事件名(+,方法),事件名(-,方法)来注册事件,所以在调用的时候不能使用对象:事件名()来执行事件,如果使用就会报下面的错,

因为使用对象:事件名()相当于参数只传了一个对象自身,"+"/"-"和方法并没有传入,得到的gen_param_count就是1,然后gen_delegate就是null,然后就会出现上边的报错。

由上,要执行事件可以再写一个函数来执行事件函数。

Lua代码中不要使用中文,

会报以下错:

14.64位整数支持

Lua53版本64位整数(long,ulong)映射到原生的64位整数,而luajit版本,相当于lua5.1的标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdata,支持在lua里头进行64位的运算、比较、打印,支持和lua number的运算、比较,在64扩展库中,实际上只有int64,ulong也会先强转成long再传递到lua,而对ulong的一些运算,比较,采取和java一样的支持方式。

15.C#复杂类型和table的自动转换

对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等。

C#代码:

cs 复制代码
[LuaCallCSharp]
public class TestObj
{
    public void test(testStruct t)
    {
        Debug.Log($"{t.a},{t.c}");
    }
}

public struct testStruct
{
    public int a;
    public string c;
}

Lua代码:

Lua 复制代码
local m = CS.TestObj()
m:test({a=10,c="ccccccc"})

结果:

16.获取类型

Lua代码:

Lua 复制代码
print(typeof(CS.Test.Monster))

结果:

17.强制类型转换

lua没类型,所以不会有强类型语言的"强转"。

Lua代码:

Lua 复制代码
cast(calc, typeof(CS.Tutorial.Calc))
什么时候用到?

有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问。

注意:

1.如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能。

Lua 复制代码
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
  1. 像上边这种Lua调用C#中某个类中的属性、字段、方法,调用方式是CS.命名空间.类名,如果没有命名空间则就是CS.类名,要注意的是,如果这个类包含在一个类中,就需要CS.类名.类名,比如

GameCfg包含在Test类中,如果用如下Lua代码,就会出现这个类的获取错误的问题

Lua 复制代码
print(CS.GameCfg.url)
CS.GameCfg.url = "zzzzzz"
print(CS.GameCfg.url)
CS.GameCfg.CalcValue()

结果:

查看GameCfg生成的代码如下:

是通过获取Test.GameCfg的类型来注册的,所以Lua代码需要修改为:

Lua 复制代码
print(CS.Test.GameCfg.url)
CS.Test.GameCfg.url = "zzzzzz"
print(CS.Test.GameCfg.url)
CS.Test.GameCfg.CalcValue()

结果:

5.HotFix:

使用xLua 的代码逻辑替换掉原有的 C# 程序逻辑, 以实现热补丁。不支持静态构造函数。目前只支持 Assets 下代码的热补丁,不支持引擎,C# 系统库的热补丁

1.使用

1.开启HotFix

按照官方文档的步骤:

执行HotFix Inject In Editor时,

会出现以下错误:

解决方案:将Xlua源工程中Tools文件夹复制到现工程于Asset文件同级目录即可

----->

如果

2.开始使用

C#代码:

cs 复制代码
public class HotFixTest : MonoBehaviour
{
    LuaEnv luaenv = null;

    Action onDestroy = null;

    void Start()
    {
        TestHotFix testHotFix = new TestHotFix();
        luaenv = new LuaEnv();
        luaenv.AddLoader(customLoader);
        testHotFix.TestPrint();
        luaenv.DoString("require 'main'");
        testHotFix.TestPrint();
        luaenv.Dispose();
    }

    public byte[] customLoader(ref string filepath)
    {
        /*加载Lua代码*/
#if UNITY_EDITOR
        return AssetDatabase.LoadAssetAtPath<TextAsset>($"Assets/Lua/{filepath}.lua.txt").bytes;
#endif
        return null;
    }
}

[Hotfix]
public class TestHotFix
{
    public void TestPrint()
    {
        Debug.Log("Before Hotfix");
    }
}

Lua代码:

Lua 复制代码
xlua.hotfix(CS.TestHotFix, 'TestPrint', function(self)
    print('After HotFix')
end)

结果:

在释放LuaEnv时出现了一个错误,官方文档FQA解答如下:

使用Xlua提供的工具,并在lua侧增加一个函数,并映射到c#端

C#侧:

Lua侧:

可以查看到

也就是HotFix打补丁的函数和lua增加的函数OnDestroy函数占用,所以将二者释放即可。

注: 下图中使用的util文件就是在文件夹下的文件,为为了方便我将它移动到这里

2.建议

对所有较大可能变动的类型加上 Hotfix 标识;

建议用反射找出所有函数参数、字段、属性、事件涉及的 delegate 类型,标注 CSharpCallLua;

业务代码、引擎 API、系统 API,需要在 Lua 补丁里头高性能访问的类型,加上 LuaCallCSharp;

引擎 API、系统 API 可能被代码剪裁调(C#无引用的地方都会被剪裁),如果觉得可能会新增 C# 代码之外的 API 调用,这些 API 所在的类型要么加 LuaCallCSharp,要么加 ReflectionUse

6.类型映射

1.基本数据类型

|----------------------------------------------------|-----------------------------|
| 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 |

2.复杂数据类型

|-------------------|----------------|
| 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个参数。

参考链接:

介绍 --- XLua (tencent.github.io)

相关推荐
avi91114 小时前
[AI相关]Unity的C#代码如何简写
unity·c#·语法糖
虾球xz11 小时前
游戏引擎学习第114天
学习·游戏引擎
虾球xz12 小时前
游戏引擎学习第109天
学习·游戏引擎
心疼你的一切12 小时前
C# 中关于补位的写法 PadLeft,PadRight 函数
开发语言·unity·c#·游戏引擎·csdn·心疼你的一切
沐沐森的故事16 小时前
Unity for Python —— 强大的 Python 脚本支持提升 Unity 编辑器效率
python·unity·编辑器·pythonrunner·pythonengine
xklcy18 小时前
Unity通过Vosk实现离线语音识别方法
unity·语音识别
虾球xz20 小时前
游戏引擎学习第113天
学习·游戏引擎
老朱佩琪!1 天前
在Unity中用简单工厂模式模拟原神中的元素反应
unity·简单工厂模式
虾球xz2 天前
游戏引擎学习第111天
学习·游戏引擎
程序猿多布2 天前
预定义委托(C# and Unity)
unity·c#