UH2S2L18——特殊问题

二维数组遍历

先在C#脚本内声明如下内容

1
2
3
4
5
6
7
8
9
10
public class Lesson8
{
public int[,] array = new int[2, 3] { { 1, 2, 3 },
{ 4, 5, 6 } };

public void SetValue(int[,] array, int value, int index1, int index2)
{
array[index1, index2] = value;
}
}

获取长度

二维数组的获取某个列或者行的长度是通过GetLength()​来获取的,Lua中也可以调用该方法

1
2
3
4
5
local obj = CS.Lesson8()

--获取长度
print("行: " .. obj.array:GetLength(0))
print("列: " .. obj.array:GetLength(1))

image

获取元素

虽然在C#中二维数组可以使用[数字, 数字]​来获取某行某列的值
但是Lua中不支持这种语法,且 [数字][数字] 也是不可行的,都会报错

不过,除了通过索引器去获取值,C#还提供了GetValue​这个成员方法,可以通过它来获取元素

1
2
3
4
5
6
7
8
print(obj.array:GetValue(0, 0))
print(obj.array:GetValue(1, 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

image

null和nil比较

C#中的null​和Lua中的nil​不能直接比较,因此有三种方法来解决这个问题

  1. Equals判空:对象.Equals(nil)​,前提是对象是C#中的object​而非Lua中的对象,否则会报错
  2. 全局方法判空:自行声明一个IsNull​方法,先判断是不是nil​在用对象.Equals(nil)​判断
  3. 拓展方法判空:在C#中拓展一个判空方法,让Lua调用

Equals判空

假设一个情景,我们需要往场景对象上添加一个脚本,如果存在就不加,如果不存在再加

1
2
3
4
5
6
7
8
9
10
11
12
-- 往场景对象上添加一个脚本 如果存在就不加 如果不存在再加
GameObject = CS.UnityEngine.GameObject
Rigidbody = CS.UnityEngine.Rigidbody

local obj = GameObject("测试加脚本")
local rig = obj:GetComponent(typeof(Rigidbody))
print(rig)
if rig == nil then
print("进入判断内")
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)

然而,对象上并没有添加组件,检查输出也发现并没有进入判空逻辑

image

这是因为,Lua中的****​nil不等同于C#的****​null,他们两个不能直接使用 ==来比较
因此我们需要使用C#的****​object
提供的****​Equals方法,传入****​nil,使用这种方法来判空

1
2
3
4
5
6
7
8
local obj = GameObject("测试加脚本")
local rig = obj:GetComponent(typeof(Rigidbody))
print(rig)
if rig:Equals(nil) then
print("进入判断内")
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)

image

全局方法判空

但是以上的方法也有问题,如果rig​是Lua里的对象而非C#的对象,则很有可能报错,因为不能调用object​提供的Equals

因此,我们可以写一个全局的判空函数,写到Main脚本内,这样即可到处调用该函数用于判空

1
2
3
4
5
6
7
8
9
print("主Lua脚本启动")

--判断对象是否为空
function IsNull(obj)
if obj == nil or obj:Equals(nil) then
return false
end
return true
end
1
2
3
4
5
6
7
8
local obj = GameObject("测试加脚本")
local rig = obj:GetComponent(typeof(Rigidbody))
print(rig)
if IsNull(rig) then
print("进入判断内")
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)

C#脚本拓展方法判空

还有第三种方法,就是在为Unity的Object​拓展一个判空方法

1
2
3
4
5
6
7
8
9
10
//为Object 拓展一个方法
[LuaCallCSharp]
public static class Lesson9
{
//拓展一个为Object判空的方法 主要是给lua用 lua没法用null和nil比较
public static bool IsNull(this UnityEngine.Object obj)
{
return obj == null;
}
}
1
2
3
4
5
6
7
8
local obj = GameObject("测试加脚本")
local rig = obj:GetComponent(typeof(Rigidbody))
print(rig)
if rig:IsNull() then
print("进入判断内")
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)

让系统类型和Lua能互相访问

解决方法就是:
声明静态类,再声明静态Type类型列表,为其添加[CSharpCallLua]或者[LuaCallCSharp],将想要生成代码的类的类型添加到列表即可

自己声明的委托或者接口要映射Lua中的方法或者表,需要添加[CSharpCallLua]
在类里声明拓展方法时,装载拓展方法的类需要添加[LuaCallCSharp]​,当然所有会被Lua调用的自定义类都可以添加该特性,可以优化性能

但是系统类和第三方库代码(例如UGUI)时,我们是无法添加这两个特性的,这可能会导致出现问题

假设我们要通过Lua代码来监听UI界面上一个Slider的改变

1
2
3
4
5
6
7
8
9
10
GameObject = CS.UnityEngine.GameObject
UI = CS.UnityEngine.UI

local slider = GameObject.Find("Slider")
print(slider)
local sliderScript = slider:GetComponent(typeof(UI.Slider))
print(sliderScript)
sliderScript.onValueChanged:AddListener(function(f)
print(f)
end)

以上代码直接运行就会报错,因为xLua会要求我们对UnityAction​添加[CSharpCallLua]​特性(Unity自己声明的委托)
但是这是Unity内部的代码,我们无法为其添加特性

我们需要去声明一个静态类,在里面声明类型为Type的列表,添加 [CSharpCallLua] 特性
声明时直接添加要调用Lua方法或者表的类型(如果有泛型参数则必须要提前填入),之后执行XLua的Generate Code
这样XLua就会根据我们列表里添加的类型,自动生成对应代码,而不需要我们手动添加特性

本例我们需要监听silder的值,因此会用到UnityAction<float>​这个委托,在声明时添加

1
2
3
4
5
6
7
public static class Lesson10
{
[CSharpCallLua]
public static List<Type> csharpCallLuaList = new List<Type>() {
typeof(UnityAction<float>)
};
}

此时拖动silder,逻辑正常执行

可见,这是一种非常好用的方法,它不仅可以为我们不能修改代码的系统类和第三方库类,
以前自己声明的委托和接口也可以获取其类型然后添加到列表内,这样我们不必再自行添加特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static class Lesson10
{
[CSharpCallLua]
public static List<Type> csharpCallLuaList = new List<Type>()
{
typeof(UnityAction<float>),
typeof(UnityAction<int>),
//... 向列表内添加想要生成代码的类的Type
};

public static List<Type> luaCallCSharpList = new List<Type>()
{
typeof(GameObject),
//... 向列表内添加想要生成代码的类的Type
};
}