当前位置: 首页 > news >正文

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项

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

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的字符串代码执行

LuaEnv luaenv = new LuaEnv();
luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
luaenv.Dispose();

2.默认loader加载:

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

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

加载代码:

void Start()
{LuaEnv luaenv = new LuaEnv();luaenv.AddLoader(customLoader);luaenv.DoString("require 'main'");luaenv.Dispose();
}public byte[] customLoader(ref string filepath){/*加载Lua代码*/
#if UNITY_EDITORreturn AssetDatabase.LoadAssetAtPath<TextAsset>($"Assets/Lua/{filepath}.lua.txt").bytes;
#endifreturn null;}

3.C#调用Lua:

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

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

C#代码:

    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代码:

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)

 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代码:

playerInfo = {id = 1001,name = "Owen",level = 100
} eventMsg = {eventId = 101,param = "aaaaaaaaaaa"
}

结果:

2.映射到一个interface

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

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

C#代码:

    [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代码:

playerPosition = {x = 10,y = 0,z = 10
}function playerPosition:add(x0,y0,z0)self.x = self.x + x0self.y = self.y + y0self.z = self.z + z0
endfunction playerPosition:sub(x0,y0,z0)self.x = self.x - x0self.y = self.y - y0self.z = self.z - z0
end

结果:

注意:

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

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

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

C#代码:

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

Lua代码:

Item = {10001,1002,content = 10000,20001,3300,
}

 结果:

4.映射到LuaTable类

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

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

C#侧代码:

//映射到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")}");
  [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 refLuaTable 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")}");
 //映射到LuaTable,by refLuaTable 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方法时需要先生成代码

  [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代码:

function test(a)print(a)
end

结果: 

2.映射到LuaFunction

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代码:

local newGameObj = CS.UnityEngine.GameObject()

结果:

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

Lua代码:

local newGameObj = CS.UnityEngine.GameObject("test")

结果: 

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

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

    //此类是否是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代码:

local url = CS.GameCfg.url
print(url)

结果:

 

2.写入静态属性:

Lua代码:

CS.GameCfg.url = "zzzzzz"
print(CS.GameCfg.url)

结果:

3.调用静态方法:

Lua代码:

CS.Test.GameCfg.CalcValue()

结果:

 

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

C#侧代码: 

    [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代码:

local actorObj = CS.Test.Actor()actorObj:callAtk()
actorObj:callWalk()--或者使用如下方式执行成员方法
--actorObj.callAtk(actorObj)
--actorObj.callWalk(actorObj)

结果:

2.写入成员属性:

Lua代码:

local actorObj = CS.Test.Actor()actorObj:PrintActorInfo()actorObj.id = 1001
actorObj.hp = 100
actorObj.name = "wukong"
actorObj.baseAtk = 500.6
actorObj.baseDef = 800actorObj:PrintActorInfo()

结果:

3.获取成员属性:

Lua代码:

local actorObj = CS.Test.Actor()actorObj.id = 1001
actorObj.hp = 100
actorObj.name = "wukong"
actorObj.baseAtk = 500.6
actorObj.baseDef = 800print(actorObj.id)
print(actorObj.hp)
print(actorObj.name)
print(actorObj.baseAtk)
print(actorObj.baseDef)actorObj:PrintActorInfo()

结果:

4.父类属性,方法

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

Lua代码:

local playerObj = CS.Test.Player()playerObj.id = 1001
playerObj.hp = 100
playerObj.name = "wukong"
playerObj.baseAtk = 500.6
playerObj.baseDef = 800playerObj:PrintActorInfo()

结果:

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

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

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

Lua代码:

local actorObj = CS.Test.Actor()
local c,d = actorObj:Test2(10,1)
print(c,d)

结果:

6.重载方法

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

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#代码:

 public static Player operator +(Player a,Player b){Player player = new Player();player.Atk = a.Atk + b.Atk;return player;}

Lua代码:

--操作符
local player1 = CS.Test.Player()
player1.Atk = 100
local player2 = CS.Test.Player()
player2.Atk = 210
print((player1+player2).Atk)

结果:

8.参数带默认值的方法

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

C#代码:

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

Lua代码:

local p = CS.Test.Player()
p:TestDefualtValue(2,"aaaaaaaa",false)
p:TestDefualtValue(2,"aaaaaaaa")
p:TestDefualtValue(2)

结果:

9.可变参数方法

C#代码:

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

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#代码:

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代码:

local c1 = CS.Test.Actor()
local c2 = CS.Test.Player()
local c3 = CS.Test.Monster()c1.TestExtend()
c2.TestExtend()
c3.TestExtend()

结果:

11.枚举类型

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

C#代码:

[LuaCallCSharp]
public enum MonsterType
{None,Normal,melee,remote
}

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#代码:

  public delegate void testDelegate2(string content);public testDelegate2 test2 = (string content) =>{Debug.Log(content);};

Lua代码:

local p = CS.Test.Player()p.test2 = p.test2 + testD 
p.test2("add")p.test2 = p.test2 - testD
p.test2("remove")

结果:

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

 public delegate void testDelegate2(string content);public testDelegate2 test2;

会报如下的错误

13.event

C#代码:

public event Action<string> testEvent;public void testCALL(string content)
{testEvent?.Invoke(content);
}

Lua代码:

local function testD(content)print("LUA TESTD...."..content)
endlocal 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#代码:

[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代码:

local m = CS.TestObj()
m:test({a=10,c="ccccccc"})

结果:

16.获取类型

Lua代码:

print(typeof(CS.Test.Monster))

结果:

17.强制类型转换

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

Lua代码:

cast(calc, typeof(CS.Tutorial.Calc))
什么时候用到?

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

注意:

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

local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')

2. 像上边这种Lua调用C#中某个类中的属性、字段、方法,调用方式是CS.命名空间.类名,如果没有命名空间则就是CS.类名,要注意的是,如果这个类包含在一个类中,就需要CS.类名.类名,比如

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

print(CS.GameCfg.url)
CS.GameCfg.url = "zzzzzz"
print(CS.GameCfg.url)
CS.GameCfg.CalcValue()

结果:

查看GameCfg生成的代码如下:

是通过获取Test.GameCfg的类型来注册的,所以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#代码:

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_EDITORreturn AssetDatabase.LoadAssetAtPath<TextAsset>($"Assets/Lua/{filepath}.lua.txt").bytes;
#endifreturn null;}
}[Hotfix]
public class TestHotFix
{public void TestPrint(){Debug.Log("Before Hotfix");}
}

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
decimaluserdata
long,ulonguserdata/lua_Integer(lua53)
bytes[]string
boolboolean
stringstring

2.复杂数据类型

C#类型Lua类型
LuaTabletable
LuaFunctionfunction
class或者 struct的实例userdata,table
method,delegatefunction

 

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)

相关文章:

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压缩包&#xff0c;并解压将Aseet文件夹中的Xlua和Plugins文件夹复制到Unit…...

在调用 borrowObject 方法时,Apache Commons Pool 会根据连接池的配置触发一系列相关的方法

在调用 borrowObject 方法时&#xff0c;Apache Commons Pool 会根据连接池的配置触发一系列相关的方法 1. GrpcChannel 的概念 GrpcChannel 是 gRPC 客户端与服务器之间通信的核心组件。它是基于 HTTP/2 的连接&#xff0c;支持多路复用&#xff0c;即通过单个通道可以发送多…...

【数据结构与算法:八、排序】

第8章 排序 排序是计算机科学中最基本且最常用的操作之一。本章详细介绍了排序算法的概念、分类、每种算法的定义、图示、代码实现及其应用场景。 8.1 基本概念和排序方法概述 8.1.1 排序的基本概念 排序是指将一组无序的记录按照某种指定的顺序重新排列的过程。 排序的目…...

Unity学习笔记(六)使用状态机重构角色移动、跳跃、冲刺

前言 本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记 整体状态框架(简化) Player 是操作对象的类&#xff1a; 继承了 MonoBehaviour 用于定义游戏对象的行为&#xff0c;每个挂载在 Unity 游戏对象上的脚本都需要继承自 MonoBehaviour&#x…...

搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程

参考文章&#xff1a; 安装protoc、protoc-gen-go、protoc-gen-go-grpc-CSDN博客 一、简单介绍 本文开发环境&#xff0c;均为 windows 环境&#xff0c;mac 环境其实也类似 ~ ① 编译proto文件&#xff0c;相关插件 简单介绍&#xff1a; protoc 是编译器&#xff0c;用于将…...

策略模式(strategy)

一.策略模式是什么 策略模式是一种行为型对象模式&#xff0c;它定义了一系列算法&#xff0c;并将每一个算法封装起来&#xff0c;使它们可以相互替换。这样&#xff0c;算法可以独立于使用它的客户端而变化‌‌。 策略者模式的核心思想是将一系列的算法封装到一系列的策略类里…...

Centos源码安装MariaDB 基于GTID主从部署(一遍过)

MariaDB安装 安装依赖 yum install cmake ncurses ncurses-devel bison 下载源码 // 下载源码 wget https://downloads.mariadb.org/interstitial/mariadb-10.6.20/source/mariadb-10.6.20.tar.gz // 解压源码 tar xzvf mariadb-10.5.9.tar.gz 编译安装 cmake -DCMAKE_INSTA…...

如何在 VSCode 中配置 C++ 开发环境:详细教程

如何在 VSCode 中配置 C 开发环境&#xff1a;详细教程 在软件开发的过程中&#xff0c;选择一个合适的开发环境是非常重要的。Visual Studio Code&#xff08;VSCode&#xff09;作为一款轻量级的代码编辑器&#xff0c;凭借其强大的扩展性和灵活性&#xff0c;受到许多开发者…...

信息安全、网络安全和数据安全的区别和联系

1. 前言 有次有朋友问我 信息安全、网络安全和数据安全&#xff0c;这三个词平时写文档时怎么用&#xff1f; 我想很多人都说不清。这次我查阅了资料&#xff0c;尽量讲清楚这三者之间的区别和联系。 2. 信息安全 2.1 定义 信息安全是指为数据处理系统建立和采用的技术和管…...

路由组件与一般组件的区别

路由组件与一般组件的区别 1. 基本概念 1.1 路由组件 路由组件是指通过路由规则映射的组件&#xff0c;通常放在 pages 或 views 文件夹中。 1.2 一般组件 一般组件是指通过 import 导入后直接使用的组件&#xff0c;通常放在 components 文件夹中。 2. 主要区别 2.1 存…...

【微服务】4、服务保护

微服务架构与组件介绍 单体架构拆分&#xff1a;黑马商城早期为单体架构&#xff0c;后拆分为微服务架构。跨服务调用与组件使用 服务拆分后存在跨服务远程调用&#xff0c;如下单需查询商品信息&#xff0c;使用openfeign组件解决。服务间调用关系复杂&#xff0c;需维护服务…...

6_TypeScript 函数 --[深入浅出 TypeScript 测试]

在 TypeScript 中&#xff0c;函数是编程的核心组成部分之一。TypeScript 不仅继承了 JavaScript 的所有函数特性&#xff0c;还添加了静态类型检查和其他一些增强功能&#xff0c;使得函数更加安全和易于理解。以下是关于 TypeScript 函数的一些关键点和两个具体的示例&#x…...

Apifox=Postman+Swagger+Jmeter+Mock

A. 开发人员接口管理使用(Swagger 工具管理接口) B. 后端开发人员通过Postman 工具&#xff0c;一边开发一边测试 C. 前端开发人员需要Mock 工具提供前端调用 D. 测试人员通过(Postman、Jmeter)等工具进行接口测试 为了后台开发、前端开发、测试工程师等不同角色更加便捷管理…...

升级 Spring Boot 3 配置讲解 —— Spring Boot 3 核心源码专讲

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; Spring Boot 3 是 Spring 生态中的重要里程碑&#xff0c;它不仅全面支持 Java 17&#xff0c;还引入了许多新特性&#xff0c;如对 GraalVM 原生镜像的支持、改进的性能优化以及更灵活的…...

接口开发完后,个人对于接下来接口优化的一些思考

优化点 入参的合法性和长度范围&#xff0c;必填项的检查验证 因为没有入参&#xff0c;所以不需要考虑。 批量思想解决N1问题 // 假设要查询100个订单及其对应的用户信息 List<Order> orders orderMapper.selectList(new QueryWrapper<>().last("limit …...

jenkins 使用 ssh-agent向windows进行部署

背景&#xff1a; jenkins在linux的docker环境内&#xff0c;应用服务部署在windows。需要使用jenkins实现自动化部署。 实现方式&#xff1a; jenkins上构建pipeline任务&#xff0c;脚本如下&#xff1a; 遇到问题&#xff1a; 1、问题&#xff1a;jenkins 调用部署bat脚…...

音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现

一、引言 通过FFmpeg命令可以获取到PS文件/PS流的视频压缩编码格式、色彩格式&#xff08;像素格式&#xff09;、分辨率、帧率信息&#xff1a; ./ffmpeg -i XXX.ps 本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 二、视频压缩编码格式 &#xff08;…...

如果Adobe 退出中国后怎么办

最近听说Adobe要退出中国了?那咱们的设计师们可得好好想想怎么搞到正版软件了。别急&#xff0c;今天教大家一个超酷的福利——Edu邮箱&#xff01; Edu邮箱是什么&#xff1f;有什么好处&#xff1f; Edu邮箱就是学校给学生和老师们发的邮箱&#xff0c;一般结尾是.edu。有了…...

欧几里得距离在权重矩阵中的物理意义

欧几里得距离在权重矩阵中的物理意义 目录 欧几里得距离在权重矩阵中的物理意义**衡量神经元差异程度**:**反映模型变化程度**:**聚类和分组的依据**:自然语言处理中的模型更新:**神经网络聚类分组**:欧几里得距离在权重矩阵中的物理意义衡量神经元差异程度: 在神经网络中…...

玩转大语言模型——ollama导入huggingface下载的模型

ollama导入huggingface模型 前言gguf模型查找相关模型下载模型 导入Ollama配置参数文件导入模型查看导入情况 safetensfors模型下载模型下载llama.cpp配置环境并转换 前言 ollama在大语言模型的应用中十分的方便&#xff0c;但是也存在一定的问题&#xff0c;比如不能使用自己…...

Linux-----进程通讯(管道Pipe)

目录 进程不共享内存 匿名管道 通过匿名管道实现通讯 有名管道 库函数mkfifo() 案例 进程不共享内存 不同进程之间内存是不共享的。是相互独立的。 #include <stdio.h> #include <stdlib.h> #include <errno.h>int num 0;int main(int argc, char con…...

【C++11】列表初始化、右值引用和移动语义、引用折叠、完美转发

C11 一.C的发展历史二.列表初始化1.C98的{}2.C11的{}3.C11中的std::initializer_list 三.右值引用和移动语义1.左值和右值2.左值引用和右值引用3.引用延长生命周期4.左值和右值的参数匹配5.右值引用和移动语义使用场景1.左值引用使用场景2.移动构造和移动赋值3.右值引用和移动语…...

Openssl1.1.1s rpm包构建与升级

rpmbuild入门知识 openssh/ssl二进制升级 文章目录 前言一、资源准备1.下载openssh、openssl二进制包2.安装rpmbuild工具3.拷贝源码包到SOURCES目录下4.系统开启telnet&#xff0c;防止意外导致shh无法连接5.编译工具安装6.补充说明 二、制作 OpenSSL RPM 包1.编写 SPEC 文件2.…...

递归思想的深度理解——汉诺塔问题和青蛙跳台阶问题

递归的深度理解——汉诺塔问题and青蛙跳台阶问题 青蛙跳台阶问题汉诺塔问题 青蛙跳台阶问题 问题&#xff1a;一只青蛙可以一次跳一级台阶&#xff0c;也可以一次跳两级台阶&#xff0c;如果青蛙要跳n级台阶&#xff0c;共有多少种跳法&#xff1f; 解答&#xff1a;我们可以先…...

从数据到诊断:朴素贝叶斯算法助力肿瘤预测之路

1.案例概述 肿瘤性质的判断影响着患者的治疗方式和痊愈速度。传统的做法是医生根据数十个指标来判断肿瘤的性质&#xff0c;预测效果依赖于医生的个人经验而且效率较低&#xff0c;而通过机器学习有望能快速预测肿瘤的性质。 2.数据集 本次肿瘤预测使用的数据集共有569组样本…...

Element-UI:如何实现表格组件el-table多选场景下根据数据对某一行进行禁止被选中?

如何实现表格组件el-table多选场景下根据数据对某一行进行禁止被选中&#xff1f; 在使用 Element UI 的 Table 组件时&#xff0c;如果你想要禁用某一行的选中&#xff08;特别是在多选模式下&#xff09;&#xff0c;可以通过自定义行的 selectable 属性来实现。selectable …...

Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py

此脚本的主要功能是可视化点云数据文件&#xff08;.pcd 文件&#xff09;&#xff0c;并通过键盘交互选择演示数据的起始帧和结束帧&#xff0c;生成片段标记文件 (clip_marks.json) 主要流程包括&#xff1a; 用户指定数据目录&#xff1a;检查目录是否存在并处理标记文件 -…...

JWT理解

前言 随着互联网的快速发展&#xff0c;身份验证和授权成为了许多应用的重要需求。JWT&#xff08;JSON Web Token&#xff09;作为一种轻量级的身份验证和授权机制&#xff0c;得到了广泛的应用。本文将为您详细介绍JWT的原理、结构和优点&#xff0c;帮助您更好地理解和应用…...

一种融合联邦学习和大模型特点的全新系统架构

一种融合联邦学习和大模型特点的全新系统架构 以下是一种融合联邦学习和大模型特点的全新系统架构设计: 分层分布式架构 底层 - 数据采集与预处理层:由大量的边缘设备和终端节点组成,如智能手机、物联网传感器等。这些设备负责采集本地数据,并在本地进行初步的数据预处理,…...

html表格table导出excel,主从表格式,带样式.自动分列

html的table导出成excel, vue模板 项目使用xlsx-js-style 源代码从https://github.com/gitbrent/xlsx-js-style/releases/tag/v1.2.0 下载 用里面的dist目录下的文件即可. 复制到vue项目的public目录下的XLSX目录下. 在index.hml中引入js脚本, 为啥要在这里引入? 是因为这里…...

U8G2库使用案例(stm32)

目录 一、小球在 OLED 屏幕平面内运动并碰撞反弹的效果 二、 简单的波形生成和显示程序: 三、三维三角形旋转展示 四、正方形平面内顺时针旋转 五、带有旋转点的空心圆圈应用 六、字幕滚动效果 七、下雪动画效果 八、进度条动画效果 自己移植的U8g2库&#xff0c;OLED库…...

067B-基于R语言平台Biomod2模型的物种分布建模与数据可视化-高阶课程【2025】

课程培训包含&#xff1a;发票全套软件脚本学习数据视频文件导师答疑 本教程旨在通过系统的培训学习&#xff0c;学员可以掌握Biomod2模型最新版本的使用方法&#xff0c;最新版包含12个模型&#xff08;ANN, CTA, FDA, GAM, GBM, GLM, MARS, MAXENT, MAXNET, RF, SRE, XGBOOST…...

【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜

AI的两次寒冬&#xff1a;从感知机困局到深度学习前夜 引用&#xff08;中英双语&#xff09; 中文&#xff1a; “第一次AI寒冬&#xff0c;是因为感知机局限性被揭示&#xff0c;让人们失去了对算法可行性的信心。” “第二次AI寒冬&#xff0c;则是因为专家系统的局限性和硬…...

141.《mac m系列芯片安装mongodb详细教程》

文章目录 下载从官网下载安装包 下载后双击解压出文件夹安装文件名修改为 mongodb配置data存放位置和日志log的存放位置启动方式一方式二方式二:输入mongo报错以及解决办法 本人电脑 m2 pro,属于 arm 架构 下载 官网地址: mongodb官网 怎么查看自己电脑应该下载哪个版本,输入…...

【Linux】sed编辑器

一、基本介绍 sed编辑器也叫流编辑器&#xff08;stream editor&#xff09;&#xff0c;它是根据事先设计好得一组规则编辑数据流。 交互式文本编辑器&#xff08;如Vim&#xff09;中&#xff0c;可以用键盘命令交互式地插入、删除或替换文本数据。 sed编辑器是根据命令处理…...

unity3d-搞个场景漫游如何实现Alpha

要处理两个问题&#xff1a; 如何设置地面人不掉下去 方法一、 游戏物体加刚体&#xff0c;将游戏物体和地面加collider。如果是地形&#xff0c;可以使用 Terrain Collider&#xff1b;如果是简单的平面&#xff0c;可以添加 Box Collider 或者 Mesh Collider&#xff08;如果…...

概率基本概念 --- 离散型随机变量实例

条件概率&独立事件 随机变量 - 离散型随机变量 - 非离散型随机变量 连续型随机变量奇异性型随机变量 概率表示 概率分布函数概率密度函数概率质量函数全概率公式贝叶斯公式 概率计算 数学期望方差协方差 计算实例 假设有两个离散型随机变量X和Y&#xff0c;它们代…...

oscp备考 oscp系列——Kioptix Level 1靶场 古老的 Apache Vuln

目录 前言 1. 主机发现 2. 端口扫描 3. 指纹识别 4. 目录扫描 5. 漏洞搜索和利用 前言 oscp备考&#xff0c;oscp系列——Kioptix Level 1靶场 Kioptix Level 1难度为简单靶场&#xff0c;主要考察 nmap的使用已经是否会看输出&#xff0c;以及是否会通过应用查找对应漏…...

【简博士统计学习方法】3. 统计学习方法的三要素

3. 统计学习方法的三要素 3.1 监督学习的三要素 3.1.1 模型 假设空间&#xff08;Hypothesis Space&#xff09;&#xff1a;所有可能的条件概率分布或决策函数&#xff0c;用 F \mathcal{F} F表示。 若定义为决策函数的集合&#xff1a; F { f ∣ Y f ( X ) } \mathcal{F…...

UnionTech OS Server 20 网页无法访问yum源地址

统信yum地址 https://euler-packages.chinauos.com/server-euler/fuyu/1060/everything/sw_64/Packages/ 浏览器访问401报错无权限&#xff0c;查看linux uos环境下yum配置的用户名和密码 cat /etc/yum/vars/auth_* 然后自己组装生成Basic Authorization def generate_basic_…...

WPF区域导航+导航参数使用+路由守卫+导航日志

背景&#xff1a;使用ContentControl控件实现区域导航是有Mvvm框架的WPF都能使用的&#xff0c;不限于Prism 主要是将ContenControl控件的Content内容在ViewModel中切换成不同的用户控件 下面是MainViewModel&#xff1a; private object body;public object Body {get { retu…...

jvm基础

jvm的基本结构‌‌ ‌类加载器&#xff08;ClassLoader&#xff09;‌&#xff1a;加载class文件到内存中进行使用。 ‌运行时数据区&#xff08;Runtime Data Area&#xff09;‌&#xff1a;这是JVM在运行Java程序期间管理的内存区域&#xff0c;包括方法区&#xff08;Meta…...

kaggle竞赛:纽约出租车行程时间NYC Taxi Trip Duration

1.引言 作为一名&#xff08;坦白说有点懒的&#xff09;图像处理方向的研究生&#xff0c;说实话最近新开一个坑&#xff0c;可能是因为要寒假了比较无聊&#xff0c;这次带来的系列是kaggle数据处理竞赛的经典例题&#xff1a;纽约出租车行程时间问题。希望大家多多支持&…...

Python提取目标Json键值:包含子嵌套列表和字典

目标&#xff1a;取json中所有的Name、Age字典 思路&#xff1a;递归处理字典中直接包含子字典的情况&#xff0c; import jsondef find_targ_dicts(data,key1,key2):result {}if isinstance(data, dict):if key1 in data and key2 in data: # 第一层字典中包含key1和key2re…...

<div>{{ $t(“collectionPlan“) }}</div> 中的$t是什么

$t是Vue I18n插件提供的一种方法&#xff0c;用于根据当前应用的语言环境来获取相应的翻译文本。 以下是一个简单的示例&#xff0c;展示如何在Vue I18n中定义消息&#xff1a; const i18n new VueI18n({locale: en, // 设置默认语言messages: {en: {collectionPlan: Collec…...

医学图像分析工具01:FreeSurfer || Recon -all 全流程MRI皮质表面重建

FreeSurfer是什么 FreeSurfer 是一个功能强大的神经影像学分析软件包&#xff0c;广泛用于处理和可视化大脑的横断面和纵向研究数据。该软件由马萨诸塞州总医院的Martinos生物医学成像中心的计算神经影像实验室开发&#xff0c;旨在为神经科学研究人员提供一个高效、精确的数据…...

win32汇编环境,在对话框中画五边形与六边形

;运行效果 ;win32汇编环境,在对话框中画五边形与六边形 ;展示五边形与六边形的画法 ;将代码复制进radasm软件里,直接编译可运行.重要部分加备注。 ;下面为asm文件 ;>>>>>>>>>>>>>>>>>>>>>>>>>&g…...

小白学Pytorch

小白学Pytorch 发现一个比较好的教程&#xff0c;对于自己来说比较合适&#xff0c;适合从零开始的教程。 1、搭建一个简单的网络 https://www.cnblogs.com/PythonLearner/p/13587092.html 搭建网络这步说的比较清楚&#xff1a; 我们使用nn包中的Sequential搭建网络&#…...

[A-25]ARMv8/v9-GIC的系统架构(中断的硬件基础)

ver0.1 前言 我们在观看很多的影视剧过程中,尤其是军旅体裁类型的布景中,经常会看见高级干部的办公桌上都会有几部电话机。这样的电话可不能小看,重要的事情尤其是突发和紧急的情况都要通过这几部电话第一时间通知给决策者。这几部电话,必须举报几个特点:及时性好、稳定…...

毕业项目推荐:基于yolov8/yolov5的行人检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…...