XLua Lua调用C
阅前须知
由于该内容为本人的个人笔记,再加上本人喜欢将重要的说明放在注释中,所以只有一些特别重要的内容才会写在代码块外部,整体的内容都是以代码为主
对于一些简单的代码并没有全部给出(太占篇幅了)
对于Xlua来说Lua如何调用C#代码是要比上一篇C # 如何调用Lua更重要的,一般我们会在C#使用一个require将逻辑转移到lua
using UnityEngine;
/*
1.lua没有办法直接访问C#
2.需要先使用C#脚本调用lua
*/
public class Main : MonoBehaviour
{
void Start()
{
LuaMgr.GetInstance().Init();
//等效于LuaDoString("require('Main')"),但注意要用自定义Loader加载你的lua脚本(如果在Resource文件夹下记得加上.txt后缀)
LuaMgr.GetInstance().DoLuaFile("Main");
}
}
Lua调用C#类
主要方式为
CS.命名空间.类名
对于Unity类即为:CS.UnityEngine.GameObject- 对于成员方法需使用
: 调用,对于静态方法要使用 . 调用
local go4=GameObject.Find("ColdPlay")
go4.transform:Translate(Vector3.right)代码如下:
- XLua默认情况(不使用反射)下只支持调用有约束、有参数的泛型函数,对于AddComponent我们可以:
go5:AddComponent(typeof(CS.LuaCallCsharp))因为Unity提供了如下API·
lua代码
print("*********XLua调用C#类***********")
--[[
CS.命名空间.类名
对于Unity类即为:CS.UnityEngine.GameObject
]]
local go1=CS.UnityEngine.GameObject()
local go2=CS.UnityEngine.GameObject("ColdPlay")
--节约性能,定义全局变量存储(本质上就是Table)(别名)
GameObject=CS.UnityEngine.GameObject
Debug=CS.UnityEngine.Debug
Vector3=CS.UnityEngine.Vector3
-- 创建一个GameObjecet
local go3=GameObject("ColdPlay2")
--静态对象、方法,使用.调用
local go4=GameObject.Find("ColdPlay")
Debug.Log( go4.transform.position);
print(go4.transform.position)
--使用对象中的成员方法 必须使用:调用 传入self
go4.transform:Translate(Vector3.right)
Debug.Log(go4.transform.position)
--使用自定义无命名空间的类(不继承Mono)
local test=CS.Test()
test:Speak("speak")
--使用自定义有命名空间的类(不继承Mono)
local test2=CS.ColdPlay.Test2()
test2:Speak2("speak2")
--自定义类(Mono)
local go5=GameObject("AddComponent")
--使用AddComponent加入
--Xlua不支持无参泛型,需利用Xlua提供的typeof添加Mono脚本
go5:AddComponent(typeof(CS.LuaCallCsharp))
Lua调用C# 枚举
- 使用枚举不需要实例化
- 枚举转换使用
__CastFrom
lua代码
print("*********XLua调用C#枚举***********")
--别名
GameObject=CS.UnityEngine.GameObject
Debug=CS.UnityEngine.Debug
Vector3=CS.UnityEngine.Vector3
PrimitiveType=CS.UnityEngine.PrimitiveType
--使用Unity自带枚举
local go=GameObject.CreatePrimitive(PrimitiveType.Cube)
--自定义枚举
--不存在实例化
E_MyEnum=CS.E_MyEnum
local c=E_MyEnum.Idle
print(c)
--枚举转换
local a =E_MyEnum.__CastFrom(1)
print(a)
local b =E_MyEnum.__CastFrom("Atk")
print(b)Lua调用C# 集合
- 使用数组、List等不要使用
# 要使用Length、Count - 创建数组
local array=CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)- 创建List
--lua创建List对象
--老版本:
-- List`1代表CLR中的List<T> 其中`1代表有一个泛型参数 后续[]中就是实际的类型
-- 创建字典:local dict1 = CS.System.Collections.Generic["Dictionary`2[System.String,System.Int32]"]()
local list1=CS.System.Collections.Generic["List`1[System.String]"]()
print(list1)
list1:Add("123")
print(list1[0])
--新版本(lua>2.1.12):
local List_String=CS.System.Collections.Generic.List(CS.System.String)
local list2=List_String()
print(list2)
list2:Add("321")
print(list2[0])- 遍历Dictionary需要使用pairs(遍历不确保顺序)
-- 遍历字典需要使用pairs
-- ipairs只遍历数组部分(更高效) 也就是table的数组部分
-- pairs 会遍历所有的部分 数组+哈希部分
-- 在xlua中List会被映射数组部分
for key, value in pairs(dic) do
print(key,value)
endlua代码:
print("*********XLua调用C# Array List Dic***********")
local obj= CS.Learn3()
--Array
--长度 不能使用#
--C#中的数组 索引从0开始
print(obj.array.Length)
print(obj.array[0])
for i=0,obj.array.Length-1 do
print("i:" .. obj.array[i])
end
--lua创建C#数组
local array=CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
print(array)
print("array.Length:" .. array.Length)
--List
obj.list:Add(1)
obj.list:Add(2)
obj.list:Add(3)
print(obj.list)
print(obj.list.Count)
for i = 0, obj.list.Count-1, 1 do
print(obj.list[i])
end
--lua创建List对象
--老版本:
-- List`1代表CLR中的List<T> 其中`1代表有一个泛型参数 后续[]中就是实际的类型
-- 创建字典:local dict1 = CS.System.Collections.Generic["Dictionary`2[System.String,System.Int32]"]()
local list1=CS.System.Collections.Generic["List`1[System.String]"]()
print(list1)
list1:Add("123")
print(list1[0])
--新版本(lua>2.1.12):
local List_String=CS.System.Collections.Generic.List(CS.System.String)
local list2=List_String()
print(list2)
list2:Add("321")
print(list2[0])
--Dictionary
obj.dic:Add(1,"123")
print(obj.dic[1])
for key, value in pairs(obj.dic) do
print(key,value)
end
--lua创建Dic
local Dic_String_Vector3=CS.System.Collections.Generic.Dictionary(CS.System.String,CS.UnityEngine.Vector3)
local dic=Dic_String_Vector3()
dic:Add("2",CS.UnityEngine.Vector3.right)
print(dic)
-- lua中创建的字典 无法使用[]访问
-- 使用 dic:get_Item("123")
print(dic["2"])
--两个返回值 bool和 out value(lua直接多返回值即可)
print(dic:TryGetValue("2"))
print(dic:get_Item("2"))
dic:set_Item("2",CS.UnityEngine.Vector3.left)
print(dic:get_Item("2"))
--由于是Vector3是struct,所以有默认值
dic:set_Item("2",nil)
print(dic:get_Item("2"))
-- 遍历字典需要使用pairs
-- ipairs只遍历数组部分(更高效) 也就是table的数组部分
-- pairs 会遍历所有的部分 数组+哈希部分
-- 在xlua中List也会被映射数组部分
for key, value in pairs(dic) do
print(key,value)
endLua调用C#静态扩展方法
- 给静态扩展类添加LuaCallCsharp特性
静态扩展方法
print("*********XLua调用C# 静态拓展方法***********")
Learn4=CS.Learn4
Learn4.Eat()
local obj=Learn4()
obj:Speak("speak")
--静态拓展方法和成员方法调用方式相同
obj:Move()Lua调用C#使用ref和out的方法
- 对于ref和out可以直接使用lua的多返回值的方式接收即可
- ref必须初始化、out无需初始化
C #代码
public int RefFunc(int a, ref int b, ref int c, int d)
{
b = a + b;
c = c + d;
return 100;
}
public int OutFunc(int a, out int b, out int c, int d)
{
b = a;
c = d;
return 200;
}
public int RefOutFunc(int a, out int b, ref int c, int d)
{
b = a * 100;
c = a * d;
return 300;
}lua代码
print("*********XLua调用C# ref out***********")
Learn5 = CS.Learn5
local obj = Learn5()
-- ref会以多返回值返回
-- a为函数返回值,b,c为ref
-- ref参数需要初始化
local a, b, c = obj:RefFunc(1, 0, 0, 1)
-- local a, b, c = obj:RefFunc(1, 1) 后面的参数会默认用0补
print('call ref' .. ':' .. a .. ':' .. b .. ':' .. c)
-- out参数可以不初始化
-- 传入的为 a和d
local d, e, f = obj:OutFunc(1, 1)
print('call out' .. ':' .. d .. ':' .. e .. ':' .. f)
-- 传入的为 a,c,d
local g, h, i = obj:RefOutFunc(20, 1, 200)
print('call ref out' .. ':' .. g .. ':' .. h .. ':' .. i)
Lua调用C#重载方法
- C#和lua语言精度不一致(float)
- 使用如下代码的时候
print(obj:Calc(1))
print(obj:Calc(1.2))我们会发现输出为(如果我们先调用Calc(1.2)在调用Calc(1),浮点数部分的输出仍然为0)
解决办法
--解决 借助反射,效率低(尽量不用)
--得到指定函数信息
local m1=typeof(CS.Learn6):GetMethod("Calc",{typeof(CS.System.Int32)})
local m2=typeof(CS.Learn6):GetMethod("Calc",{typeof(CS.System.Single)})
--使用xlua提供的方法转换为方法,一般一个方法转化一次
local f1=xlua.tofunction(m1)
local f2=xlua.tofunction(m2)
-- LUA: 10.199999809265 语言float精度不同
print(f1(obj,10))
print(f2(obj,10.2))C #代码
public int Calc()
{
return 100;
}
public int Calc(int a, int b)
{
return a + b;
}
public int Calc(int a)
{
return a;
}
public float Calc(float a)
{
return a;
}lua代码
print("*********XLua调用C# overload func***********")
Learn6=CS.Learn6
local obj =Learn6()
print(obj:Calc())
print(obj:Calc(1,1))
-- 虽然支持调用C#重载函数
-- lua只有number
-- 对于C#多精度的重载函数支持不好
print(obj:Calc(1))
print(obj:Calc(1.2))
--解决 借助反射,效率低(尽量不用)
--得到指定函数信息
local m1=typeof(CS.Learn6):GetMethod("Calc",{typeof(CS.System.Int32)})
local m2=typeof(CS.Learn6):GetMethod("Calc",{typeof(CS.System.Single)})
--使用xlua提供的方法转换为方法,一般一个方法转化一次
local f1=xlua.tofunction(m1)
local f2=xlua.tofunction(m2)
-- LUA: 10.199999809265 语言float精度不同
print(f1(obj,10))
print(f2(obj,10.2))
Lua调用C# 委托和事件
- 第一次使用使用C#某个委托需要先
obj.del=func(此时del为nil)才能使用obj.del=obj.del+func - 对于event需要使用(需注意的是event只能在声明他的类去调用、清空,我可以在C#部分封装一个调用、清空的方法)
obj:eventAction('+',func2)
obj:eventAction('-',func2)
obj:DoEvent()Lua代码
print('lua call C# delegate')
local obj=CS.Learn7()
local func = function ()
print('lua func')
end
-- delegate
--第一次添加委托引用的函数,不能使用 -- obj.del=obj.del+func (del为nil)
obj.del=func
obj.del=obj.del+func
-- 不建议 不方便减少引用
obj.del =obj.del + function ()
print('temp')
end
obj.del()
obj.del=obj.del-func
obj.del=obj.del-func
obj.del()
obj.del=nil
obj.del=func
obj.del()
local funcf= function (f)
print('float' .. f)
end
obj.del2=funcf
obj.del2(2)
-- event
print('lua call C# event')
local func2 = function ()
print('lua func2')
end
--调用事件使用 :
--增加
obj:eventAction('+',func2)
obj:eventAction('+',func2)
obj:eventAction('+' ,function ()
print('func')
end)
obj:DoEvent()
obj:eventAction('-',func2)
obj:DoEvent()
-- obj.eventAction=nil 不能使用(事件不能在外部置空)
print('清空事件')
obj:ClearEvent()
obj:DoEvent()
Lua使用Unity的UI
GameObject = CS.UnityEngine.GameObject
UI = CS.UnityEngine.UI
local slider = GameObject.Find('Slider')
print(slider.name)
local sliderScript = slider:GetComponent(typeof(UI.Slider))
print(sliderScript)
sliderScript.onValueChanged:AddListener(
function(p)
print(p)
end
)
Lua使用Unity携程
- 需使用xlua.util
util = require("xlua.util")
-- 不能直接将lua函数直接传入
-- 借助xlua.util
-- 注意要添加 CsharpCallLua typeof(System.Collections.IEnumerator)
b=mono:StartCoroutine(util.cs_generator(fun))lua代码
print('lua call mono coroutine')
--使用xlua.util
util = require("xlua.util")
GameObject = CS.UnityEngine.GameObject
WaitForSeconds = CS.UnityEngine.WaitForSeconds
local obj = GameObject("Coroutine")
local mono = obj:AddComponent(typeof(CS.LuaCallCsharp))
fun = function()
local a = 1
while true do
--不能使用C# yield return
--每一秒打印一次a
coroutine.yield(WaitForSeconds(1))
if a>10 then
-- 关闭携程
mono :StopCoroutine(b)
end
print(a)
a = a + 1
end
end
-- 不能直接将lua函数直接传入
-- 借助xlua.util
-- 注意要添加 CsharpCallLua typeof(System.Collections.IEnumerator)
b=mono:StartCoroutine(util.cs_generator(fun))
Lua调用C#泛型方法
- 默认情况只支支持有约束有参数的泛型方法
- 内部类使用
local child = CS.Learn12.TestChild()- 使用反射调用无约束或无参数的泛型方法,需注意mono和il2cpp的区别
C#代码
public class Learn12
{
public interface ITest
{
}
public class TestFather
{
}
public class TestChild : TestFather,ITest
{
}
public void TestFun1<T>(T a, T b) where T : TestFather
{
Debug.Log("有参数有约束的泛型");
}
public void TestFun2<T>(T a)
{
Debug.Log("有参数无约束的泛型");
}
public void TestFun3<T>() where T : TestFather
{
Debug.Log("无参数有约束的泛型");
}
public void TestFun4<T>(T a) where T : ITest
{
Debug.Log("有参数有约束(接口)的泛型");
}
}lua代码
print('lua call generic func')
local obj = CS.Learn12()
-- 内部类
local child = CS.Learn12.TestChild()
local father = CS.Learn12.TestFather()
-- 支持有参数有约束泛型
-- 作为参数时候变相相当于传type
obj:TestFun1(child, father)
obj:TestFun1(father, child)
-- 不支持没有约束的泛型
-- obj:TestFun2(child)
-- 不支持没有参数的泛型
-- obj : TestFun3()
-- 不支持非class约束
-- obj:TestFun4(child)
--[[
如何使用不支持的泛型 (反射,少用,效率低),
且有使用限制
1.Mono可以使用
2.Il2cpp 支持T为引用类型
如果为值类型,必须是C#已经使用过同类型的泛型参数,lua才能使用
原因:Il2cpp 为AOT(引用类型 使用共享代码,所有引用类型用一份泛型实现,对于值类型只有目标泛型实例使用过,才会被生成)
Mono为JIT(即时编译)
]]
--[[
1.得到通用函数
2.设置实际的类型
]]
--获取通用函数
local testFun2=xlua.get_generic_method(CS.Learn12,"TestFun2")
--设置类型
local testFun2_Real=testFun2(CS.System.Int32)
--调用
testFun2_Real(obj,12)
其他特殊情况
对于一些没有办法修改的(如DLL)库应该怎么添加LuaCallCsharp和CsharpCallLua特性
用一个静态类的静态列表即可
public static class Learn10
{
//dll等不可修改类加特性
[CSharpCallLua]
public static List<Type> csharpCallLuaList = new List<Type>()
{
typeof(UnityAction<float>),
typeof(System.Collections.IEnumerator),
};
//包括LuaCallC#
[LuaCallCSharp]
public static List<Type> luaCallCsharpList = new List<Type>()
{
typeof(GameObject)
};
}C#的null不等于lua的nil
仅对于xlua2.1.15版本(其他版本本人还未使用过)
对于以下情况
local obj = GameObject("测试添加组件")
local rig= obj:GetComponent(typeof(Rigidbody))
print(rig)
-- nil和null无法进行
if rig==nil then
print('== add rigidbody')
rig=obj:AddComponent(typeof(Rigidbody))
end我们会发现nil不等于null
解决办法
- 静态扩展方法
ublic static bool IsNull(this UnityEngine.Object obj)
{
return obj == null;
}- 使用Equals
function IsNull(obj)
--这里借助了短路的特性 既obj==nil就不会在判断obj:Equals(nil),变相放置了对于lua中的nil调用Equals
if obj == nil or obj:Equals(nil) then
return true;
end
return false
endLua使用C# 的二维数组
# Lua使用C#二维数组
- **GetLength(0):row GetLength(1):col 中的0,1,2就代表维度**
```C#
print('lua use two-dimensional array')
local obj=CS.Learn8()
-- GetLength(0):row GetLength(1):col 0,1,2就代表维度
print("row:" .. obj.array:GetLength(0) .. " col:" .. obj.array:GetLength(1))
-- 不能使用 obj.array[0][0] 或 obj.array{0,1}
print(obj.array:GetValue(0,0))
for i=0,obj.array:GetLength(0)-1 do
for j=0,obj.array:GetLength(1)-1 do
print(obj.array:GetValue(i,j))
end
end
```本文详细介绍了在XLua框架下,Lua脚本如何调用C#代码的方法和注意事项。主要内容包括:
- 基本调用方式:通过CS.命名空间.类名访问C#类和Unity内置类,区分成员方法(:)和静态方法(.)的调用语法。
特殊处理:
- 泛型方法的调用限制及解决方案
- 枚举的使用和转换(__CastFrom)
- 集合(List/数组)的正确遍历方式(避免使用#)
实际示例:
- 创建GameObject和组件
- 调用自定义类方法
- 枚举转换应用
- 集合创建和操作
性能优化建议:
- 使用全局变量存储常用类引用
- 类型转换的最佳实践
文章采用代码注释为主的讲解方式,强调XLua与C#交互时的关键点和常见问题解决方案。














1 条评论
果博东方客服开户联系方式【182-8836-2750—】?薇- cxs20250806】
果博东方公司客服电话联系方式【182-8836-2750—】?薇- cxs20250806】
果博东方开户流程【182-8836-2750—】?薇- cxs20250806】
果博东方客服怎么联系【182-8836-2750—】?薇- cxs20250806】