Cross-language scripting

Godot allows you to mix and match scripting languages to suit your needs. This means a single project can define nodes in both C# and GDScript. This page will go through the possible interactions between two nodes writen in different languages.

The following two scripts will be used as references throughout this page.

extends Node

var str1 : String = "foo"
var str2 : String setget ,get_str2

func get_str2() -> String:
    return "foofoo"

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_x_times(msg : String, n : int) -> void:
    for i in range(n):
        print(msg)
public class MyCSharpNode : Node
{
    public String str1 = "bar";
    public String str2 { get { return "barbar"; } }

    public void PrintNodeName(Node node)
    {
        GD.Print(node.GetName());
    }

    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);
        }
    }
}

实例化节点

如果不使用场景树中的节点,则可能需要直接从代码实例化节点。

在 GDScript 中实例化 C# 节点

从GDScript中使用C#并不麻烦。加载后(见:ref:doc_gdscript_classes_as_resources)脚本就可以使用:ref:`new() <class_CSharpScript_method_new>`进行实例化。

var my_csharp_script = load("res://path_to_cs_file.cs")
var my_csharp_node = my_csharp_script.new()
print(my_csharp_node.str2) # barbar

警告

创建 .cs 脚本时,应始终记住 Godot 将使用的类是和这个 .cs 文件名称相同的类。如果文件中不存在该类,您将看到以下错误:Invalid call. Nonexistent function `new` in base

比如,MyCoolNode.cs 应该包含一个名为 MyCoolNode 的类。

您还需要检查在项目的 .csproj 文件中引用了该 .cs 文件的内容。否则,将发生相同的错误。

在C#中实例化GDScript节点

From the C# side, everything work the same way. Once loaded, the GDScript can be instantiated with GDScript.New().

GDScript MyGDScript = (GDScript) GD.Load("res://path_to_gd_file.gd");
Object myGDScriptNode = (Godot.Object) MyGDScript.New(); // This is a Godot.Object

Here we are using an class_Object but you can use type convertion like explained in 类型转换和强制转换.

访问字段

从 GDScript 中访问 C# 字段

从GDScript访问 C# 字段很简单,没什么可担心的。

print(my_csharp_node.str1) # bar
my_csharp_node.str1 = "BAR"
print(my_csharp_node.str1) # BAR

print(my_csharp_node.str2) # barbar
# my_csharp_node.str2 = "BARBAR" # This line will hang and crash

Note that it doesn’t matter if the field is defined as a property or an attribute, but trying to set a value on a property that does not define a setter will result in a crash.

从 C# 中访问 GDSscript

As C# is statically typed, accessing GDScript from C# is a bit more convoluted, you will have to use Object.Get() and Object.Set(). The first argument is the name of the field you want to access.

GD.Print(myGDScriptNode.Get("str1")); // foo
myGDScriptNode.Set("str1", "FOO");
GD.Print(myGDScriptNode.Get("str1")); // FOO

GD.Print(myGDScriptNode.Get("str2")); // foofoo
// myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything

Keep in mind that when setting a field value you should only use types the GDScript side knows about. Essentially, you want to work with built-in types as described in GDScript 基础 or classes extending class_Object.

调用方法

在GDScript中调用C#方法

Again, calling C# methods from GDScript should be straightforward. The marshalling process will do its best to cast your the arguments to match function signatures. If that’s impossible you’ll see the following error: 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!

// When dealing with functions taking a single array as arguments, we need to be careful.
// If we don't cast it into an object, the engine will treat each element of the array as a separate argument and the call will fail.
String[] arr = new String[] { "a", "b", "c" };
// myGDScriptNode.Call("print_array", arr); // This line will fail silently and won't error out.
myGDScriptNode.Call("print_array", (object)arr); // a, b, c
myGDScriptNode.Call("print_array", (object)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 。否则数组的每个元素将被当做单个参数传入,与函数的结构不匹配。