Scripting entre linguagens

O Godot permite que você misture e combine linguagens de script para atender às suas necessidades. Isso significa que um único projeto pode definir nós tanto em C# quanto em GDScript. Esta página passará pelas possíveis interações entre dois nós escritos em diferentes linguagens.

Os dois scripts a seguir serão usados como referências ao longo desta página.

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_n_times(msg : String, n : int) -> void:
    for i in range(n):
        print(msg)

Instanciando nós

Se você não estiver usando nós da árvore da cena, provavelmente vai querer instanciar os nós diretamente do código.

Instanciando nós C# a partir do GDScript

Usar C# a partir do GDScript não precisa de muito trabalho. Uma vez carregado (veja Classes como recursos) o script pode ser instanciado com 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

Aviso

Ao criar scripts .cs você deve sempre ter em mente que a classe que o Godot usará é a nomeada como o próprio arquivo .cs. Se essa classe não existir no arquivo, você verá o seguinte erro: Chamada inválida. Função inexistente `new` na base.

Por exemplo, MyCoolNode.cs deve conter uma classe chamada MyCoolNode.

Você também precisa verificar se seu arquivo .cs é referenciado no arquivo .csproj do projeto. Caso contrário, o mesmo erro ocorrerá.

Instanciando nós GDScript a partir do C#

Do lado C#, tudo funciona da mesma maneira. Uma vez carregado, um GDScript pode ser instanciado com GDScript.New().

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

Aqui estamos usando um Object mas você pode usar conversão de tipo como explicado em Conversão de Tipos e Casting.

Acessando campos

Acessando campos C# a partir do GDScript

Acessar os campos do C# no GDScript é simples, você não deve ter nada com que se preocupar.

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

Observe que não importa se o campo é definido como uma propriedade ou um atributo, mas tentar definir um valor em uma propriedade que não tem um setter resultará em um crash.

Acessando campos do GDScript no C#

Como C# é estáticamente tipado, acessar os campos de GDScript a partir do C# é um pouco mais complicado, você terá que usar Object.Get() e Object.Set(). O primeiro argumento é o nome do campo que você quer acessar.

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

Tenha em mente que ao definir um valor de campo você só deve usar tipos conhecidos pelo GDScript. Essencialmente, você precisa trabalhar com tipos embutidos descritos em Básicos do GDScript ou classes que estendam Object.

Chamando métodos

Chamando métodos C# a partir do GDScript

Novamente, chamar métodos de C# no GDScript deve ser simples. O processo de marshalling fará o seu melhor para converter seu argumento para bater com as assinaturas de funções. Se isso for impossível, você verá o seguinte erro: Chamada inválida. Função inexistente `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

Chamando métodos de GDScript no C#

Para chamar os métodos de GDScript a partir do C# você precisará usar Object.Call(). O primeiro argumento é o nome do método que você quer chamar. Os próximos argumentos serão passados para esse método.

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

Aviso

Como você pode ver, se o primeiro argumento do método chamado é um array, você precisará convertê-lo para object. Caso contrário, cada elemento de seu array será tratado como um único argumento e a assinatura da função não corresponderá.

Herança

Um arquivo GDscript não pode herdar de um script em C#. De forma semelhante, um script C# também não pode herdar de um arquivo GDscript. Devido a complexidade de sua implementação, essa limitação tem poucas chances de permanecer no futuro. Veja esse problema na plataforma GitHub para mais informações.