3. Scripting with C# in Godot: Common Tasks – Moving from Unity to Godot: An In-Depth Handbook to Godot for Unity Users

© Alan Thorn 2020
A. ThornMoving from Unity to Godothttps://doi.org/10.1007/978-1-4842-5908-5_3

3. Scripting with C# in Godot: Common Tasks

Alan Thorn1 
(1)
High Wycombe, UK
 

Godot supports many scripting languages unofficially through add-ons and patches. You’ll find people who’ve used GDScript, Python, Visual Scripting, and JavaScript. This book and chapter, however, focus on C#. C# is natively supported by Godot and is the natural choice for Unity developers where C# is used almost exclusively. Here, we’ll write our first Hello World script file with C# and see how to code common gameplay tasks with C# through the Godot API (Application Programming Interface). If you’ve coded with C# in Unity, you’ll be used to a whole string of commands and classes, like Transform, GetComponent, Time.deltaTime, Update, and lots more. Now, while Godot often uses very different names for its classes and functions, you’ll be pleased to know that Godot has Unity equivalents and is remarkably similar sometimes. Table 3-1 shows some common mappings between Unity and Godot classes and function calls. Figure 3-1 demonstrates how to download the Godot engine with C# support natively included.

Note

To use C# in Godot, you’ll need to download the Godot Mono Build from the Godot home page: https://​godotengine.​org/​download/​.

Figure 3-1

Downloading Godot with Mono Support

Table 3-1

Terminology: Unity C# vs. Godot C#

Unity

Godot

Print

GD.Print

Update()

_process()

Start()

_ready()

Prefab

Scene

Hierarchy

Scene Tree

Project Panel

File System

Inspector

Inspector

Empty Object

Spatial Node

Asset

Resource

Tag

Group

.NET and Build Problems with C#

With Unity, you can normally just install the engine and start using C# right away. This isn’t always the case with Godot, however. Many people report compilation issues or other problems preventing them from effectively writing and compiling C# code out of the box. Let’s start by seeing what issues arise, in case they happen for you, and then how to address them. When creating a Godot project for the first time after a fresh install, it’s a good idea to check that your C# code will compile successfully for you. To do this, simply create a new Script file. Right-click your mouse over the res:// item in the FileSystem panel, and choose New Script from the context menu. See Figure 3-2.
Figure 3-2

Creating a New Script Resource

Next, select C# for the scripting language, and inherit from the Spatial class, since we’ll be adding this script to a Spatial object. I’ve named the script HelloWorld to print a simple message to the console for a Hello World program, coded later in this chapter. See Figure 3-3. When you’ve chosen these settings, simply click Create. The script file will be generated and added to the current Godot project.
Figure 3-3

Creating a New Script File… If Class Name Is Left Blank, Godot Will Name the Class After the Filename

After the C# file is generated, it’ll appear inside the FileSystem panel, as shown in Figure 3-4. And just as a Unity script file is populated with default code, derived from MonoBehaviour, Godot also generates a code file, as shown in Listing 3-1.
Figure 3-4

Script Files Are Added to a Godot Project and Appear in the File System Panel

using Godot;
using System;
public class HelloWorld : Spatial
{
    // Declare member variables here. Examples:
    // private int a = 2;
    // private string b = "text";
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
    }
//  // Called every frame. 'delta' is the elapsed time since the previous frame.
//  public override void _Process(float delta)
//  {
//
//  }
}
Listing 3-1

Default Generated Godot Script File

Now just click Build at the top-right corner of the Godot Editor interface. See Figure 3-5. Clicking this button begins the C# compilation process. The result will indicate whether you’ll successfully compile C# code at all using the current install. You may see a compilation progress bar briefly, as shown in Figure 3-5.
Figure 3-5

Compiling a C# Script File

After compiling, if the Godot engine returns silently and the output window (at the bottom of the interface) doesn’t display any errors, then everything probably compiled successfully. To confirm, press the Play button from the toolbar to run the application. If it runs successfully, the project compiled successfully and you’re good to continue to the next section. However, if the Output window displays and prints an Error message, then there’s a problem with your C# setup. See Figure 3-6.
Figure 3-6

Examining a Compilation Error

The printed Error Message will usually be related to a mismatch in the .Net Framework version. Godot is unable to compile your C# for the targeted .Net framework. To solve this, first ensure your Godot project is targeting the MS .Net framework, as opposed to the Mono Framework. You can set this by choosing EditorEditor Settings from the application menu to display the Editor Settings Window. Then, choose MonoBuilds from the Editor menu. Select VS Build Tools for the Build Tools drop-down. See Figure 3-7.
Figure 3-7

Setting the VS Build Tools

Next, you’ll need to either (1) change the targeted .NET framework for your Godot project to the version matching the latest system one or (2) install the .NET Framework 4.5 targeting pack for Visual Studio. This chapter explores the second option. To get started, you’ll need to install Visual Studio Community. You can download it from here: https://​visualstudio.​microsoft.​com/​downloads/​.

Note

To view steps for the first option, check out my free BeIndie.Biz tutorial on YouTube here: www.​youtube.​com/​watch?​v=​KyqBKq_​wQQw.

While running the Visual Studio Installer, select the Individual Components tab, and from the .NET group, enable the .NET Framework 4.5 retargeting pack. See Figure 3-8. Then continue or complete the installation. After installation, restart your computer, and your Godot project should now compile successfully. Great!
Figure 3-8

Installing the .NET Retargeting Pack

Building a Hello World Program

Hello World programs simply print the message “Hello World” to an output, like a screen, or a printer, or (in our case) the Output Window . Printing the message “Hello World” isn’t especially useful itself – certainly not. But the process of being able to send a human-readable string message to the output is incredibly helpful. It’s useful notably as a Debugging tool. You can print messages to show the order of code execution, the value of variables, and other data. In this section, we’ll write a Hello World program for Godot. To start, create a Hello World C# script file, as demonstrated in the previous section. Then double-click the script file from the FileSystem Panel to open it inside your code Editor, such as Visual Studio Code. See Chapter 1 for details. See Figure 3-9 for a default code file open in an Editor, ready to code.
Figure 3-9

Preparing to Code a Hello World Program

Note

You can change the Visual Studio Code Color Scheme by choosing FilePreferencesSettings from the application menu. Then select Workbench ➤ Appearance. And set the Color Theme to your preference. Visual Studio Light is used in Figure 3-9.

In Godot, the C# function GD.Print prints a string to the output console for reading in the editor interface while the game is running. Our Hello World application should print the message “Hello World” to the console at application startup. For this reason, we’ll need to use the _ready event. _ready is comparable to Unity’s Start event. _ready is called after a Node is created and when it is added to the scene tree, that is, when the object enters the scene hierarchy. Look at Listing 3-2 to see an example of a Hello World program in C#.
using Godot;
using System;
public class HelloWorld : Spatial
{
    // Declare member variables here. Examples:
    // private int a = 2;
    // private string b = "text";
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        GD.Print("Hello World");
    }
//  // Called every frame. 'delta' is the elapsed time since the previous frame.
//  public override void _Process(float delta)
//  {
//
//  }
}
Listing 3-2

A Hello World Program

Note

Unity also has the Awake event, which is called before Start. Godot has a similar event called _init. The _init function is invoked when a node is created in memory but before it is added to the scene tree, becoming a “thing in the world.” The _init event always happens before _ready and is useful for initializing private member variables, as well as for running private member functions. The _ready event should be preferred, however, for any initialization code that references external nodes or objects or which accesses the scene tree. The sequence in which _ready events are invoked across all nodes in a scene should never be assumed. For this reason, every _ready event should assume that all other nodes are already initialized as a result of their _init event called earlier.

After writing your Hello World code, as shown in Listing 3-2, you should attach to it a Spatial node in a 3D scene. To do this, select a spatial node in the scene, and from the Object Inspector, expand the Script group. From the Script drop-down field, select the Load option. See Figure 3-10.
Figure 3-10

Adding a Script to a Node

From the Load menu, you may choose any .cs script file to attach to the selected node where it’ll be instantiated and run in Play Mode, just as it does in Unity. In our case, simply select the newly created HelloWorld.cs file from the file list and then click the Open button at the bottom-left corner. See Figure 3-11.
Figure 3-11

Attaching a Hello World C# Script to the Selected Node

After clicking Open, simply confirm the Script has been added to the Node by checking the Script field from the Inspector, which should now display the associated filename. Further, always click the Build button, from the top-right corner of the interface, before playing your project. This ensures you’re always testing with the latest compiled code. See Figure 3-12.
Figure 3-12

Always Build the Latest Code Before Playing Your Project

After compiling, click the Play Scene button (F6) from the toolbar to run your game in an application Window. See Figure 3-13.
Figure 3-13

Playing the Open Scene

On running your game, the _ready function executes immediately for every Node in the scene. By viewing the Output Window at the bottom center of the Editor interface, you’ll now see the complete message “Hello World” printed straight from the _Ready function. Printing messages like this is great – it’s an effective strategy for debugging your code, seeing its flow as the application executes. See Figure 3-14.
Figure 3-14

Printing Hello World to the Output Window

Working with Nodes

A Godot scene is, fundamentally, a hierarchical tree of Nodes (the SceneTree). The SceneTree has one Root node – the topmost node – from which all others are children. They are children either directly by being a child of the root node – or indirectly by being a grandchild of the root node – or by having an even more distant connection. Each Node in the scene may contain a maximum of only one attached Script file. In Unity, by contrast, a GameObject may have multiple Scripts attached, each becoming a Component. Godot however expects you to attach only one script to a Node wherever you need custom behavior. More complex behaviors – requiring multiple systems – are created not by adding more scripts to a node, but by building a hierarchy of Nodes with different Scripts attached to each, or, in some cases, by using C# inheritance to customize a single Script on a Node. This may seem like a limitation compared to Unity; but really, it only requires a different way of thinking and a different approach, as we’ll see. Each Node, therefore, is an instantiation of a single class only, one that derives from the Node class directly or indirectly by one of Node’s descendant classes. In the previous section, for example, a Hello World class was derived from the Spatial class, which in turn derives from the Node class. The Godot API Reference Documentation provides a solid description of each native class – such as Node, Spatial, Camera, Light, and more – including its member variables and functions. Each class gets its own dedicated documentation page. You can see any class’ inheritance connection to its ultimate ancestor Node class by viewing the Inherits section of the class’ documentation page. See Figure 3-15. Knowing this connection is important for understanding the C# variables and methods supported by any given node in the SceneTree.
Figure 3-15

Class Hierarchy Can Be Viewed from the Godot API

Iterating Through Child Nodes

Being able to navigate the SceneTree effectively by code is critically important for creating gameplay, for understanding how a scene is working, and for accessing different objects in a scene. Let’s start by looking at how a script on any Node can cycle through all direct children, one by one. There are two common methods given in Listings 3-3 and 3-4, respectively. Cycling through child nodes is a valuable and powerful technique if you structure your scene tree cleverly. For example, by cycling through children, you can make inventory systems that keep track of all items the player has collected. You can count how many enemies are remaining in the scene. And you can determine which rooms are on the same floor. And lots more! Listings 3-3 and 3-4 achieve the same result. When you attach the scripts to nodes in the scene, it’ll print the names of all children to the Output Window.
using Godot;
using System;
public class NodeTraverse : Node
{
    public override void _Ready()
    {
        //Print all child node names, Method 1
        for(int i=0; i<GetChildCount(); i++)
        {
            Node N = GetChildOrNull<Node>(i);
            if(N != null)
                GD.Print(N.Name);
        }
    }
}
Listing 3-3

Printing Child Object Names – Method 1

using Godot;
using System;
public class NodeTraverse : Node
{    public override void _Ready()
    {
        //Print all child node names, Method 2
        Godot.Collections.Array ChildArray = GetChildren();
        foreach(Node N in ChildArray)
            GD.Print(N.Name);
    }
}
Listing 3-4

Printing Child Object Names – Method 2

Finding Nodes by Name

Unity features the GameObject.Find function. This searches the scene hierarchy for the first object matching the specified name. Godot has an equivalent function. It’s the FindNode function. This function searches all child nodes recursively for a matching node. Unlike the Unity equivalent of GameObject.Find, however, Godot’s FindNode function only searches the entire scene tree if executed from the scene’s root node. Otherwise, it only searches from the parent downward to all children. Finding nodes by name is useful when you need access to specific singular objects in the scene, like the player character, or the GameManager, or a Respawn Location. Listing 3-5 searches all children recursively for an object named “Player”.
Node PlayerNode = FindNode("Player", true);
if(PlayerNode != null)
    GD.Print("Player Node found!");
else
    GD.Print("Player Node not found!");
Listing 3-5

Finding Child Nodes by Name

You can always search the entire scene for an object by name by simply searching from the root node. If your script isn’t attached to the root node, you can always get access to it by using the Node.GetTree function. This function returns the complete scene hierarchy, allowing you access the root node. See Listing 3-6 to search the scene hierarchy for an object by name via the Root Node.
SceneTree ST = GetTree();
Node PlayerNode = ST.CurrentScene.FindNode("Player");
if(PlayerNode != null)
    GD.Print("Player node found");
Listing 3-6

Finding Child Nodes by Name from the Root Node

Finding Nodes by Path

An excellent feature of Godot is that each Node can be referenced by both an absolute and a relative path. You can select a Node from a C# script by using the GetNode function or by its GetNodeOrNull version. The former throws a game-breaking exception if the specified Node isn’t found and the latter simply returns NULL in the same event. Listing 3-7 retrieves a root node called root_level inside the _ready event and prints its name to the Output. The scene hierarchy is shown in Figure 3-16.
public override void _Ready()
{
    Node N = GetNodeOrNull<Node>("root/root_level");
    if(N!=null)
        GD.Print(N.Name);
}
Listing 3-7

Getting Access to the Root Node, Called root_level

Figure 3-16

Root Node in the Scene Tree

Note

Remember, if your root node is named differently from root_level, you’ll need to specify it by name to access it via the GetNode or GetNodeOrNull functions.

You can also access nodes by using relative paths with the GetNode or the GetNodeOrNull functions. For example, Listing 3-8 uses the path “../Player” to get access to a sibling node named Player. The prefix (..) refers to the Parent node.
public override void _Ready()
{
    Node N = GetNodeOrNull<Node>("../Player");
    if(N!=null)
        GD.Print(N.Name);
}
Listing 3-8

Accessing a Sibling Node by Name and Path

Godot Groups vs. Unity Tags

If you’re familiar with Unity, you’ll remember that it has a Tag feature. This lets you label related objects like power-ups, enemies, or weapons. Tagging objects helps you find them easily in C# code because you can quickly search for objects with a matching tag. Most objects in Unity are Untagged, but some – like the Main Camera and the Player – are tagged by default. See Figure 3-17.
Figure 3-17

Unity Tags Group Together Related Objects

Godot supports equivalent tagging functionality through Groups. However, the Godot concept of Groups is more versatile and powerful than Tags, as we’ll see. In Godot, a Group is a collection of objects, but an object may also belong to multiple groups. To create a new group, select any node in the scene and switch to the Node Inspector – it’s next to the Object Inspector and it’s easy to miss! See Figure 3-18.
Figure 3-18

Unity Tags Group Together Related Objects

Click the Groups tag to show the Godot Group options. From there, click the Manage Groups button to show the Group Editor Window. From here, you can create and remove groups. See Figure 3-19.
Figure 3-19

Accessing the Group Editor

From the Group Editor, type in a meaningful Group Name and click the Add button to create a new empty group. See Figure 3-20. Meaningful names may include power-ups, enemies, treasure, weapons, waypoints, doors, elevators, and more. It depends on your project. Your scene should have as many groups as needed. They’re excellent for organizing nodes, and they make coding easier too.
Figure 3-20

Adding Groups Using the Group Editor

After creating a Group in the Group Editor, you can add nodes to the group from the Nodes Not in Group column. Select the nodes to add and click the Add button (you can remove nodes using the Remove button). Remember, you can add Nodes to multiple groups if needed, for example, Enemies and NPCs groups or Weapons and Equipment groups. See Figure 3-21.
Figure 3-21

Adding Ogre NPCs to the NPC Group

Now that you can add nodes to groups in Godot, you can efficiently process those nodes in C# code. Specifically, the SceneTree.GetNodesInGroup function returns a list of all nodes in the specified named group. Listing 3-9 retrieves all nodes in the NPC group. The node list is returned as a Godot.Collections.Array. More information on the Array class can be found at the Godot API documentation here: https://​docs.​godotengine.​org/​en/​3.​1/​classes/​class_​array.​html.
//Getting nodes in group named 'NPC'
Godot.Collections.Array GroupedNodes = GetTree().GetNodesInGroup("NPC");
//Cycle through all nodes in group, if any
foreach(Node N in GroupedNodes)
{
    GD.Print(N.Name);
}
Listing 3-9

Finding All Nodes in a Specified Group

You can also find all the groups to which a single Node belongs by calling the Node.GetGroups function. See Listing 3-10. This listing prints the names for all groups associated with the first found Ogre node.
//Find first child node featuring 'Ogre' in the name
Node N = FindNode("*Ogre*");
//Get all groups to which this node belongs
Godot.Collections.Array Groups = N.GetGroups();
//Print all group names
foreach(string S in Groups)
    GD.Print(S);
Listing 3-10

Get All Groups Associated with a Selected Node

Note

You can also check if a node belongs to a specific named Group using the Node.IsInGroup function. This returns a Boolean, indicating whether the node is in the named group.

A really great feature is calling a named Function for all Nodes in a Group. This is achieved using the SceneTree.CallGroup function. This lets you run a function on all nodes within a named group. This is great for initiating group behaviors – like charging or fleeing NPCs who must act together at the same time – or for saving and loading scene data, for example, saving the state of a game for all objects in the scene. The SceneTree.CallGroup function uses reflection internally, and so it must be used as sparingly as possible for best performance. Avoid calling this function inside the _process event. See Listing 3-11.
public override void _Ready()
{
    //Save data for all NPC characters
    GetTree().CallGroup("NPC", "SaveData");
}
Listing 3-11

Invoking the Method SaveData on All Objects in the NPC Group

Accessing Variables in the Inspector

In Unity, the public keyword can be prefixed to a variable in C# – like an Int or a Float or a String – to make it fully readable and writeable in the Object Inspector. This means the public variable is shown in the inspector and can be edited from there as well as from code. This is convenient for many reasons; notably, you can edit a variable’s starting value without having to recompile your code, and you can visually observe a variable changing during gameplay to debug any potential problems. Godot supports similar functionality but doesn’t use the scope specifier – public, protected, or private – to determine variable visibility. Instead, you must use the [Export] attribute for each variable that should appear in the Inspector. See Listing 3-12 for variable declarations and then the corresponding result in Figure 3-22.
public class Ogre : Spatial
{
    [Export]
    private string NPCName;
    [Export]
    public float NPCSpeed;
    [Export]
    public float NPCHealth;
}
Listing 3-12

Making Variables Accessible from the Inspector

Figure 3-22

Exposing Variables in the Inspector

Variables As Properties – GetComponent?

OK, so you’ve created an [export] variable, as shown in the previous section. An Export variable becomes a Property of the Node. You can both see and edit properties from the Object Inspector. Each property gets its own unique identifier within the Godot tree – just like Nodes. For example, the following path /root/Spatial/Ogre:Health refers to the Health variable on an object named Ogre. You can find the Property name of any variable by simply hovering your cursor over its name in the Inspector. The Property name will display in a pop-up context menu. See Figure 3-23.
Figure 3-23

Viewing Property Names

Excellent! But wait, how can one script access the variables of another? That is, how can two different script instances (attached to different nodes) communicate and interact effectively with each other at runtime? In Unity, you’d often call GetComponent or GetComponents to retrieve direct references to other script instances. Godot is different. To access a script on any node – and its variables – you can use the Typecast method or the Property Access method. Let’s see these in turn starting with Typecasting. See Listing 3-13. Here, we simply typecast the node to our intended data type (in this case, NPC) using the as keyword.
public override void _Ready()
{
    //Get NPC Object Named Ogre, with an NPC script
    Node N = GetTree().Root.GetNodeOrNull("/root/Spatial/Ogre");
    //Typecast object as type NPC to get script access
    NPC OgreNPC = N as NPC;
    //Set health of Ogre NPC
    OgreNPC.Health = 100f;
}
Listing 3-13

Typecasting Nodes

The other method for accessing variables is simple, doesn’t involve any typecasting, and feels intuitive. But it’s far from optimal and can lead to poor performance when used frequently and often. See Listing 3-14. This involves using the Object.Set function for setting a named variable.
public override void _Ready()
{
    //Get NPC Object Named Ogre
    Node N = GetTree().Root.GetNodeOrNull("/root/Spatial/Ogre");
    //Set variable value
    N.Set("Health", 50f);
}
Listing 3-14

Setting Properties

NodePaths and Node References

We’ve seen already how functions like GetNode, FindNode, and GetGroups can programmatically search for, and retrieve, references to specific nodes in a scene. This works well when you’re retrieving nodes in code. However, you’ll sometimes want to specify nodes from the Inspector through a variable property, such as referencing the player character, or the game manager, or the UI system. In Unity, you’d do this by declaring a public GameObject or Transform variable in your script file, and then you’d drag and drop your GameObject from the Scene View into the associated Inspector slot. However, in Godot, you must use the NodePath object type instead, declaring this as an Export type. The NodePath doesn’t reference a Node directly. Rather, it represents a fully qualified path to a node, which gets resolved dynamically when needed. See Listing 3-15.
using Godot;
using System;
public class ExportVar : Node
{
    [Export]
    public NodePath LinkToPlayer = null;
    //Actual reference to player
    private Node PlayerNode = null;
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        //Resolve the link and find the object
        PlayerNode = GetNode(LinkToPlayer);
    }
}
Listing 3-15

Getting a Node from a Reference

Listing 3-15 features the Export variable LinkToPlayer. This is of type NodePath. A NodePath variable doesn’t line to a node directly. It must be resolved in code. For this reason, the _Ready event uses the GetNode function to convert the path to a node reference. See Figure 3-24, which shows a NodePath in the inspector where a node reference can be specified.
Figure 3-24

Referencing a Node via a NodePath

Set an Object’s Position

The Spatial class is the base for objects that exist spatially in a 3D scene. This includes meshes, particle systems, cameras, lights, and others. When working with Spatial objects like these, you’ll commonly want to set their position in the scene in terms of X, Y, and Z. In Unity, you achieve this using the Transform.Position variable. In Godot, you achieve this similarly with the Transform.Origin variable. The Transform class in Godot is accessed by value rather than by reference. See Listing 3-16 for a script that sets an object’s position based on a Vector3 PosRestrict variable. Figure 3-25 illustrates what this class will look like in the Inspector when attached to a node.
using Godot;
using System;
public class PostionRestrict : Spatial
{
    [Export]
    public Vector3 PosRestrict = Vector3.Zero;
  // Called every frame. 'delta' is the elapsed time since the previous frame.
  public override void _Process(float delta)
  {
      //Get access to the object’s transform
      Transform T = Transform;
      //Set the position
      T.origin = PosRestrict;
      //Apply the changes
      Transform = T;
  }
}
Listing 3-16

Restricting an Object’s Position

Figure 3-25

Restricting an Object to a Position

Note

Remember, the Transform member variable will always refer to the transform of the Node to which the script is attached.

Make an Object Move Smoothly

The previous section demonstrated how to set an object’s position absolutely. Now let’s make an object move smoothly. That is, let’s change its position over time. To be specific, every object faces in a specific direction, namely, its forward direction. When an object moves, it normally moves in the direction it’s facing. This ensures an object always moves forward, no matter how it’s orientated. To achieve this, we’ll use the Transform.Origin variable along with some clever Vector math. Specifically, we’ll offset the object’s position along its forward vector, which is expressed by the variable Transform.basis.z. This is equivalent to Unity’s transform.forward variable. Consider Listing 3-17, which moves an object forward continuously.
using Godot;
using System;
public class Mover : Spatial
{
    [Export]
    public float Speed = 5f;
    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(float delta)
    {
        Transform T = Transform;
        T.origin += T.basis.z * Speed * delta;
        Transform = T;
    }
}
Listing 3-17

Moving an Object in C#

Note

The Godot delta parameter of function _Process is equivalent to Time.deltaTime in Unity. It represents the amount of time, measured in seconds, since the previous frame completed.

Make an Object Rotate Smoothly

Now let’s make an object rotate continuously and smoothly around its central axis, like a spinning power-up coin or a rotating sign. This approach is like making an object move, as demonstrated in the previous section. To make an object rotate, you’ll need to rotate an object per frame by a rotational speed. Many rotation functions in Godot expect angles to be specified in Radians and not Degrees – or Euler Angles. Consequently, if you’re specifying angles in degrees, you’ll need to convert them using the Mathf.Deg2Rad function. See Listing 3-18.
using Godot;
using System;
public class Rotator : Spatial
{
    [Export]
    public float RotateSpeed = 90f;
    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(float delta)
    {
        Transform T = Transform;
        T = T.Rotated(Vector3.Up, Mathf.Deg2Rad(delta * RotateSpeed));
        Transform = T;
    }
}
Listing 3-18

Rotating an Object in C#

Detecting When an Object Enters a Trigger

Detecting when an object – like the player or an NPC – enters a 3D volume is useful. It can let us know when an object enters a room or an area in the scene or when the player falls into a lava pit and other scenarios. In Unity, the event OnTriggerEnter will fire for every MonoBehaviour script when a physical object enters a 3D volume. In Godot, the process for detecting collisions or intersections is different from Unity. Let’s start by configuring a 3D volume marking out an area in the scene. We’ll be detecting to see if an object enters that area. To start, right-click the root Node in a new scene from the Scene Tree Panel. Then choose Add Node to add a node. See Figure 3-26.
Figure 3-26

Creating a New Node

Next, create a new Area node by typing “Area” into the Search field of the Add Node Window. The Area node is useful for attaching physical forces, interactions, and collision detections to specific areas within a scene. Areas are invisible to the game camera; they simply mark out regions of 3D space. See Figure 3-27.
Figure 3-27

Creating an Area Node

By default, Areas lack width, height, and depth. They represent a physics anchor point in the scene and nothing more. To associate an Area with a Volume, you must create a CollisionShape child node. A Godot CollisionShape is equivalent to a Unity Collider. To create a CollisionShape as a child of an Area, right-click the newly created Area node from the Scene Tree Panel and then choose Add Node. From the Node creation menu, search for CollisionShape, and add a Collision Shape object. See Figure 3-28.
Figure 3-28

Adding a Collision Shape Node

Next, you’ll need to choose the Volume to use for the collision area. This specifies the 3D volume inside which collision can be detected. To do this, select the CollisionShape object from the Scene Tree and click the Shape drop-down in the Inspector. From there, select a 3D volume. For our example here, let’s select a Cube by choosing New BoxShape from the menu. See Figure 3-29.
Figure 3-29

Assigning a New Box Shape to the Shape Field

When a fully configured CollisionShape features as a child of an Area Node, you’ll end up with a complete Collision Detection area. See Figure 3-30.
Figure 3-30

Completing a Cube Collision Area

Now, simply configure a test physics object, such as a RigidBody box, which can fall or enter the Collision Area. To create a physics box, simply add a new RigidBody node and then add a Box Collision shape as a child node. Then finally, add a Box MeshInstance as a child of the Box Collision object. This creates a RigidBody node network that can interact with other objects. See Figure 3-31.
Figure 3-31

Creating a Rigid Body Object

Great. You’ve now configured the scene, ready to detect a collision. You have both a physics Rigid Body (a Cube) and a Collision Area. To detect when the rigid body enters the area, start by adding a new script onto the Area node. This script will handle the collision event. Next, select the Area object from the Scene Tree. Once selected, click the Node tab from the Object Inspector to view the Signals tab. See Figure 3-32.
Figure 3-32

Accessing the Signals Tab

Signals in Godot equate to Events from Unity – or Delegates in C# – that you can connect to different functions in code, which execute when events happen. To detect when rigid bodies enter an area, first double-click the body_entered event from the Signals list. This event will execute once whenever a Rigid Body first enters a collider. It corresponds to Unity’s OnTriggerEnter event. When you double-click the body_entered event, the Signal Connector Window appears. See Figure 3-33.
Figure 3-33

The Signal Connector Window

From the Signals Connector Window, select the Area object from the Connect to Script list. This node should be selectable. If it’s not, check that it has a script attached. After selecting the Area node, enter a suitable name for the function that is to become the Event, using the Receiver Method field. Your associated script file should feature a function of a matching name, which will be called by Godot automatically whenever a Rigid Body first enters the area. You may need to add the function manually. See Listing 3-19 to see how a script file should be configured to support a body_entered event. Figure 3-34 demonstrates how the node connection should look when configured.
using Godot;
using System;
public class CollisionDetect : Spatial
{
    public void _on_Area_body_entered(PhysicsBody Node)
    {
        GD.Print("entered");
    }
}
Listing 3-19

Detecting Collisions in C# Script

Figure 3-34

Handling the Area Entered Event

Great! Your Area is now configured to detect collisions with physical objects. Let’s quickly create a RigidBody to react with, for testing purposes. To do this, add new Rigidbody node and then a cube collision shape as a child, and then finally add a child MeshInstance node, created as a Cube. Basically, that’s three nested nodes: a RigidBody, a CollisionShape, and then a Cube MeshInstance. See Figure 3-35.
Figure 3-35

Configuring a Rigid Body Object

Now just hit Play on the toolbar and watch your rigidbody cube fall into the area below during gameplay. You may need to set up a scene camera first to get an ideal view of the interaction. You’ll also see a message printed to the console when the collision happens. See Figure 3-36.
Figure 3-36

Printing a Message on Object Entry

Viewing Spatial Nodes

Godot’s Spatial Node is, in many ways, equivalent to Unity’s Empty Object. It marks a location in 3D space, has no visibility, and can be used – like a folder – to contain child nodes for organization. But, by default, Spatial nodes have no editor visibility either. This makes it difficult for developers to see them in the viewport and to select them by clicking. To select a Spatial node, you must always resort to clicking the object by name from the Scene Tree panel. This can be inconvenient. Instead, if you need to view and select a Spatial Node from the view, you should use a Position3D node. To create one, simply right-click the root node from the Scene Tree Panel, choose Add Node to display the Node Creation Window, and then search for Position3D to add a new Position3D node. See Figure 3-37.
Figure 3-37

Creating a Position 3D Node…

After creating a Position3D Node, you’ll always be able to see it inside the viewport and click it to select it. See Figure 3-38.
Figure 3-38

Selecting a Position3D Node in the Viewport

Reading Player Input

There are many ways to read input from multiple devices in Godot, including the keyboard, mouse, and gamepad. The cleanest method, and the closest to Unity’s, is through the Input Mapping System. Let’s see how this works. To start, access the Input Map settings by choosing ProjectProject Settings from the application menu. This displays the general project settings. See Figure 3-39.
Figure 3-39

Accessing Project Settings from the Application Menu

From the Project Settings menu, choose the Input Map tab. See Figure 3-40. This displays a list of custom input axes, each associated with different input devices and buttons. Its purpose is to assign different buttons from different device types to a single unified input map that works effectively.
Figure 3-40

The Input Map Associates Buttons with input axes

From the Input Map tab, let’s create a new axis to read input from the keyboard, detecting when the space bar key is pressed. This is useful for handling character-jump or character-shoot events. To start, type a new Axis name into the Action type field at the top of the Input Map tab. Let’s call this Axis fire. Once entered, click the Add button to a new axis with the matching name. See Figure 3-41.
Figure 3-41

Adding a New Fire Axis

After adding the fire axis, click the + icon to associate a new key to it. From the context menu, select Key. See Figure 3-42. After selecting Key, simply press the relevant key on the keyboard to assign (here, we’ll press the space bar).
Figure 3-42

Adding a New Key to the Fire Axis…

Great! You’ve now created a new Axis associated with a space bar press. You can then use C# to detect important axis events. Specifically, the Input.IsActionPressed function will return true for as long as the space bar is being held down. The Input.IsActionJustPressed function will return true once only on the first occasion of the space bar being pressed and the Input. IsActionJustReleased function will return true once only on the first occasion of the space bar being released. Listing 3-20 prints a message when the space bar is first pressed.
public override void _Process(float delta)
{
    if(Input.IsActionJustPressed("fire"))
      GD.Print("Fired!");
}
Listing 3-20

Detecting When the Space Bar Is First Pressed

This is excellent. You can now read input from specific button presses, such as jumps, shoots, interacts, and other forms of Boolean interactions. However, you’ll also want to read analog data across a range of values in an axis. A classic example is left-right and up-down motion of a character. In Unity, you can use the Input.GetAxis function to read Horizontal and Vertical data with smoothed values to drive character movement left and right and up and down. You can do the same in Godot too. To achieve this, add additional actions in the Input Map for Left, Right, Up, and Down. You can associate them with keyboard presses for WASD and arrows. See Figure 3-43.
Figure 3-43

Configuring Left, Right, Up, and Down Actions

Next, you can read Horizontal and Vertical input values using Listing 3-21. The Horizontal Axis can return –1 (meaning left is being pressed), or 1 (meaning right is being pressed), or 0 (meaning neither direction is pressed). Similarly, for Vertical Input, 1 means Up is pressed, –1 means Down is pressed, and 0 means neither is pressed.
public override void _Process(float delta)
{
    float Horizontal = -Input.GetActionStrength("left") + Input.GetActionStrength("right");
    float Vertical = -Input.GetActionStrength("down") + Input.GetActionStrength("up");
    GD.Print("Vertical: " + Vertical + " Horizontal: " + Horizontal);
}
Listing 3-21

Reading Horizontal and Vertical Input

Summary

This chapter demonstrates the fundamentals of using C# in Godot to achieve critically important gameplay tasks. This includes searching for named nodes, traversing the node hierarchy, converting node paths to node references, exposing variables in the inspector, detecting collisions and reading input from the player, and others. By exploring the range of coding tasks illustrated here, you can now put together complex scenes with important gameplay behaviors.