跨语言脚本
Godot 允许混合、匹配使用脚本语言以满足你的需求,也就是说,一个项目可以同时用 C# 和 GDScript 定义节点。本页将用不同语言编写的两个脚本来介绍这两种语言之间可进行的交互。
以下两个脚本在整个页面中用作参考。
GDScriptC#
extends Node
var my_field: String = "foo"
signal my_signal
func print_node_name(node: Node) -> void:
print(node.get_name())
func print_array(arr: Array) -> void:
for element in arr:
print(element)
func print_n_times(msg: String, n: int) -> void:
for i in range(n):
print(msg)
func my_signal_handler():
print("The signal handler was called!")
using Godot;
public partial class MyCSharpNode : Node
{
public string myField = "bar";
[Signal] public delegate void MySignalEventHandler();
public void PrintNodeName(Node node)
{
GD.Print(node.Name);
}
public void PrintArray(string[] arr)
{
foreach (string element in arr)
{
GD.Print(element);
}
}
public void PrintNTimes(string msg, int n)
{
for (int i = 0; i < n; ++i)
{
GD.Print(msg);
}
}
public void MySignalHandler()
{
GD.Print("The signal handler was called!");
}
}
实例化节点
如果不想通过场景树来使用节点,则可能需要直接通过代码来实例化节点。
在 GDScript 中实例化 C# 节点
在 GDScript 中使用 C# 脚本并不麻烦,C# 脚本经加载后(见 类作为资源)便可使用 new() 来进行实例化。
var my_csharp_script = load("res://Path/To/MyCSharpNode.cs")
var my_csharp_node = my_csharp_script.new()
警告
创建 .cs
脚本时,要始终记住:Godot 会使用和这个 .cs
文件名相同的类进行相关操作。如果文件中不存在该类,那么你将会看到以下错误: Invalid call. Nonexistent function `new` in base
。
例如:MyCoolNode.cs 应该包含一个名为 MyCoolNode 的类。
C# 类也需要获取 Godot 类,如GodotObject
,否则将触发同样的错误。
你还需要检查在项目的 .csproj
文件中是否引用了该 .cs
文件的内容,否则将触发同样的错误。
在 C# 中实例化 GDScript 节点
在 C# 侧的实例化方式与 GDScript 的基本相同,加载 GDScript 脚本后,该 GDScript 脚本就可以使用GDScript.New()实例化。
GDScript MyGDScript = GD.Load<GDScript>("res://path/to/my_gd_script.gd");
GodotObject myGDScriptNode = (GodotObject)MyGDScript.New(); // This is a GodotObject.
在这里我们用 Object 进行转型,但是也可以使用 Godot 所提供的类型转换语法转型,见类型转换和强制转换章节。
访问字段
从 GDScript 中访问 C# 字段
从 GDScript 访问 C# 字段非常简单,大可不必担心会出现什么问题。
print(my_csharp_node.myField) # bar
my_csharp_node.myField = "BAR"
print(my_csharp_node.myField) # BAR
从 C# 中访问 GDSscript
由于 C# 是静态类型语言,因此在 C# 脚本里访问 GDScript 字段会有点复杂,此时必须使用 Object.Get() 和 Object.Set() 来读写字段,其首个参数为要访问的字段的名称。
GD.Print(myGDScriptNode.Get("my_field")); // foo
myGDScriptNode.Set("my_field", "FOO");
GD.Print(myGDScriptNode.Get("my_field")); // FOO
需要牢记:在给字段赋值时只能使用 GDScript 已知的类型,实际上指的就是 GDScript 的内置类型 GDScript 参考 或 Object 的子类型。
调用方法
在 GDScript 中调用 C# 方法
在 GDScript 里调用 C# 方法同样非常简单,调用 C# 脚本的方法时将尽可能地将你的参数类型进行强制转型以匹配函数签名。如果调用失败,则会看到以下错误 Invalid call. Nonexistent function `FunctionName`
。
my_csharp_node.PrintNodeName(self) # myGDScriptNode
# my_csharp_node.PrintNodeName() # This line will fail.
my_csharp_node.PrintNTimes("Hello there!", 2) # Hello there! Hello there!
my_csharp_node.PrintArray(["a", "b", "c"]) # a, b, c
my_csharp_node.PrintArray([1, 2, 3]) # 1, 2, 3
从 C# 中 调用 GDScript 方法
在 C# 脚本中调用 GDScript 脚本的方法时需要使用 Object.Call() 函数,该方法的第一个参数为想要调用方法的名称,之后的其他参数会顺次传递给被调用的方法。
myGDScriptNode.Call("print_node_name", this); // my_csharp_node
// myGDScriptNode.Call("print_node_name"); // This line will fail silently and won't error out.
myGDScriptNode.Call("print_n_times", "Hello there!", 2); // Hello there! Hello there!
string[] arr = new string[] { "a", "b", "c" };
myGDScriptNode.Call("print_array", arr); // a, b, c
myGDScriptNode.Call("print_array", new int[] { 1, 2, 3 }); // 1, 2, 3
// Note how the type of each array entry does not matter as long as it can be handled by the marshaller.
警告
如你所见,若被调用方法的第一个参数为数组类型,则需要强制转为 object
类型,否则该数组内的每个元素都将会被当做单个参数传入,导致与被调用的参数不匹配。
Connecting to signals
Connecting to C# signals from GDScript
Connecting to a C# signal from GDScript is the same as connecting to a signal defined in GDScript:
my_csharp_node.MySignal.connect(my_signal_handler)
Connecting to GDScript signals from C
Connecting to a GDScript signal from C# only works with the Connect
method because no C# static types exist for signals defined by GDScript:
myGDScriptNode.Connect("my_signal", Callable.From(MySignalHandler));
继承
GDScript 脚本无法继承 C#脚本。同样,C#脚本也无法继承 GDScript 脚本。由于该机制实现起来非常复杂,故未来不太可能取消此限制,详见 该 GitHub issue 。