C# features

This page provides an overview of the commonly used features of both C# and Godot and how they are used together.

Type conversion and casting

C# is a statically typed language. Therefore, you can’t do the following:

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

GetNode() returns a Node instance. You must explicitly convert it to the desired derived type, Sprite in this case. For this, you have various options in C#. Casting and Type Checking InvalidCastException if the returned node cannot be cast to Sprite. You would use it instead of the as operator if you are pretty sure it won’t fail.

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

Using the AS operator as operator returns null if the node cannot be cast to Sprite, and for that reason, it cannot be used with value types.

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

Using the generic methods Generic methods are also provided to make this type conversion transparent. GetNode<T>() casts the node before returning it. It will throw an InvalidCastException if the node cannot be cast to the desired type.

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

GetNodeOrNull<T>() uses the as operator and will return null if the node cannot be cast to the desired type.

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

Type checking using the IS operator is operator. The is operator returns false if the node cannot be cast to Sprite, otherwise it returns true.

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

Pattern Matching.

C# signals

Handling a signal section in the step by step Scripting languages tutorial. [Signal] attribute on a delegate.

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

Connect. If you want to connect a signal in the editor, you need to (re)build the project assemblies to see the new signal. This build can be manually triggered by clicking the “Build” button at the top right corner of the editor window.

  1. 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");}

EmitSignal method.

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

nameof keyword (applied on the delegate itself). It is possible to bind values when establishing a connection by passing a Godot array.

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

built-in types and Classes derived from Godot.Object. Consequently, any Node or Reference will be compatible automatically, but custom data objects will need to extend from Godot.Object or one of its subclasses.

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

AddUserSignal, but be aware that it should be executed before any use of said signals (with Connect or EmitSignal).

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

Preprocessor defines

Godot has a set of defines that allow you to change your C# code depending on the environment you are compiling to. Note <DefineConstants> with a new 3.2+ project).

Examples

For example, you can change code based on the platform:

  1. 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 }

Or you can detect which engine your code is in, useful for making cross-engine libraries:

  1. 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 }

Full list of defines

  • GODOT is always defined for Godot projects.
  • GODOT_64 or GODOT_32 is defined depending on if the architecture is 64-bit or 32-bit.
  • GODOT_X11, GODOT_WINDOWS, GODOT_OSX, GODOT_ANDROID, GODOT_IOS, GODOT_HTML5, or GODOT_SERVER depending on the OS. These names may change in the future. These are created from the get_name() method of the OS singleton, but not every possible OS the method returns is an OS that Godot with Mono runs on. exporting, the following may also be defined depending on the export features:
  • GODOT_PC, GODOT_MOBILE, or GODOT_WEB depending on the platform type.
  • GODOT_ARM64_V8A or GODOT_ARMEABI_V7A on Android only depending on the architecture.
  • GODOT_ARM64 or GODOT_ARMV7 on iOS only depending on the architecture.
  • GODOT_S3TC, GODOT_ETC, and GODOT_ETC2 depending on the texture compression type.
  • foo -> GODOT_FOO. https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test