Características de C#

Esta página provee un resumen acerca de las características comunmente utilizadas en C# y Godot y cómo son usadas.

Conversión de tipo y casting

C# es un lenguaje estáticamente tipado, por lo tanto, no puedes hacer lo siguiente:

var mySprite = GetNode("MySprite");
mySprite.SetFrame(0);

El método GetNode() retorna una instancia Node. Se debe convertir explícitamente al tipo derivado deseado, Sprite en este caso.

Para esto, hay varias opciones en C#.

Casting & Chequeo de Tipo

Lanza InvalidCastException si el valor retornado no puede ser convertido a Sprite. Se utiliza en lugar del operador as si se está seguro que no fallará.

Sprite mySprite = (Sprite)GetNode("MySprite");
mySprite.SetFrame(0);

Utilizando el operador AS

El operador as retorna null si el nodo no puede ser convertido a Sprite, y por esta razón no puede ser usado en tipos "valor".

Sprite mySprite = GetNode("MySprite") as Sprite;
// Only call SetFrame() if mySprite is not null
mySprite?.SetFrame(0);

Utilizando métodos genéricos

Los métodos genéricos se proveen para hacer la conversión de tipo transparente.

GetNode<T>() realiza un casting del nodo antes de retornarlo. Lanzará una InvalidCastException si el nodo no puede ser convertido al tipo deseado.

Sprite mySprite = GetNode<Sprite>("MySprite");
mySprite.SetFrame(0);

GetNodeOrNull<T>() usa el operador as y retornará null si el nodo no puede ser convertido al tipo deseado.

Sprite mySprite = GetNodeOrNull<Sprite>("MySprite");
// Only call SetFrame() if mySprite is not null
mySprite?.SetFrame(0);

Chequeo de tipo usando el operador IS

Para comprobar si un nodo puede ser convertido a Sprite, puedes usar el operador is. Este operador retorna false si el nodo no puede ser convertido a Sprite, de otro modo, retorna true.

if (GetNode("MySprite") is Sprite)
{
    // Yup, it's a sprite!
}

Para tipos avanzados de comprobaciones, puedes mirar Pattern Matching.

Señales en C#

Para ejemplos completos de C#, ver la sección Manipulando una señal en el tutorial paso a paso Lenguaje de Scripting.

Una señal en C# se declara mediante el atributo [Signal] en un delegate.

[Signal]
delegate void MySignal();

[Signal]
delegate void MySignalWithArguments(string foo, int bar);

Estas señales pueden ser conectadas en el editor o desde el código con "Connect". Si quieres conectar una señal en el editor, necesitas (re)construir los ensamblajes del proyecto para ver la nueva señal. Esta construcción puede ser activada manualmente haciendo clic en el botón "Build" en la esquina superior derecha de la ventana del editor.

public void MyCallback()
{
    GD.Print("My callback!");
}

public void MyCallbackWithArguments(string foo, int bar)
{
    GD.Print("My callback with: ", foo, " and ", bar, "!");
}

public void SomeFunction()
{
    instance.Connect("MySignal", this, "MyCallback");
    instance.Connect(nameof(MySignalWithArguments), this, "MyCallbackWithArguments");
}

Las señales se emiten con el método EmitSignal.

public void SomeFunction()
{
    EmitSignal(nameof(MySignal));
    EmitSignal("MySignalWithArguments", "hello there", 28);
}

Tener en cuenta que se puede hacer referenciar a una señal con la palabra clave nameof (aplicado al delegate).

Es posible vincular valores al establecer una conexión pasando un array de Godot.

public int Value { get; private set; } = 0;

private void ModifyValue(int modifier)
{
    Value += modifier;
}

public void SomeFunction()
{
    var plusButton = (Button)GetNode("PlusButton");
    var minusButton = (Button)GetNode("MinusButton");

    plusButton.Connect("pressed", this, "ModifyValue", new Godot.Collections.Array { 1 });
    minusButton.Connect("pressed", this, "ModifyValue", new Godot.Collections.Array { -1 });
}

Las señales soportan parámetros y valores vinculados de todos los tipos built-in y clases derivadas de Godot.Object. Consecuentemente, cualquier Node o Reference será compatible automaticamente, pero objetos de datos personalizados deberán extender de Godot.Object o alguna de sus subclases.

public class DataObject : Godot.Object
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
}

Finalmente, las señales pueden ser creadas llamando AddUserSinal, pero tener en cuenta que debe ser ejecutado antes de la utilización de dicha señal (con Connect o EmitSignal).

public void SomeFunction()
{
    AddUserSignal("MyOtherSignal");
    EmitSignal("MyOtherSignal");
}

Defines de preprocesador

Godot tiene un conjunto de definiciones que le permiten cambiar su código C# dependiendo del entorno en el que esté compilando.

Nota

Si creaste tu proyecto antes de Godot 3.2, tienes que modificar o regenerar tu archivo csproj para usar esta característica (compara <DefineConstants> con un nuevo proyecto 3.2+).

Ejemplos

Por ejemplo, puedes cambiar el código basado en la plataforma:

    public override void _Ready()
    {
#if GODOT_SERVER
        // Don't try to load meshes or anything, this is a server!
        LaunchServer();
#elif GODOT_32 || GODOT_MOBILE || GODOT_WEB
        // Use simple objects when running on less powerful systems.
        SpawnSimpleObjects();
#else
        SpawnComplexObjects();
#endif
    }

O puedes detectar en qué motor está tu código, útil para hacer bibliotecas de motores cruzados:

    public void MyPlatformPrinter()
    {
#if GODOT
        GD.Print("This is Godot.");
#elif UNITY_5_3_OR_NEWER
        print("This is Unity.");
#else
        throw new InvalidWorkflowException("Only Godot and Unity are supported.");
#endif
    }

A continuación está la lista completa de hints

  • "GODOT" siempre está definido para los proyectos de Godot.

  • Uno de GODOT_64 o GODOT_32 se define dependiendo de si la arquitectura es de 64 o 32 bits.

  • Uno de GODOT_X11, GODOT_WINDOWS, GODOT_OSX, GODOT_ANDROID, GODOT_IOS, GODOT_HTML5, o GODOT_SERVER, dependiendo del sistema operativo. Estos nombres pueden cambiar en el futuro. Se crean desde el método get_name() del singleton :ref:OS <class_OS>, pero no todos los sistemas operativos posibles que el método devuelve son sistemas operativos en los que funciona Godot con Mono.

Cuando se exporta, también se puede definir lo siguiente dependiendo de las características de la exportación:

  • Una de GODOT_PC, GODOT_MOBILE, o GODOT_WEB dependiendo del tipo de plataforma.

  • Uno de GODOT_ARM64_V8A o GODOT_ARMEABI_V7A en Android sólo dependiendo de la arquitectura.

  • Uno de GODOT_ARM64 o GODOT_ARMV7 en iOS sólo dependiendo de la arquitectura.

  • Cualquiera de GODOT_S3TC, GODOT_ETC y GODOT_ETC2 dependiendo del tipo de compresión de textura.

  • Cualquier característica personalizada añadida en el menú de exportación se escribirá en mayúsculas y con el prefijo: foo -> GODOT_FOO.

Para ver un proyecto de ejemplo, revisa la demo de pruebas de SO: https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test