4. Making a 2D Game – 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_4

4. Making a 2D Game

Alan Thorn1 
(1)
High Wycombe, UK
 

Godot has a truly amazing 2D feature set for quickly and easily building 2D games. These games will normally export smoothly across platforms and run impressively. In this chapter, we’ll explore Godot’s 2D feature set by building a simple yet functional 2D game. Furthermore, we’ll explore ways of working that Godot expects, and which differ from Unity. One of the biggest differences is philosophical, as we’ll see. For example, in Unity, there’s only one type of Scene, namely, a 3D Scene. You can, of course, build 2D games and 2D worlds in Unity. But such worlds are, in truth, just flat objects aligned to the camera in a 3D scene, only making it appear 2D. Godot by contrast does distinguish between scenes and world spaces. Godot supports both 3D scenes and 2D scenes, and these do differ significantly. A 2D scene in Godot is not simply a flat object aligned to the camera in a 3D world. It’s truly a hierarchy of 2D objects, positioned and measured in 2D space. This offers us both advantages and disadvantages as a developer. It makes 2D worlds intuitive and simpler to navigate during development but poses new challenges whenever we want to create mixed worlds featuring both 2D and 3D elements.

The 2D game we’ll create in this chapter will be a top-down coin collection game. The player will move a 2D toon-sprite character around a tile-set world to collect coin pickup objects before the timer expires. To achieve this, we’ll use a broad range of 2D features. Let’s get started. See Figure 4-1 to see the completed game in action. The completed game is included in the book companion files.
Figure 4-1

Building a Top-Down 2D Collection Game

Configuring a 2D Project

To get started, we’ll need to create a project and configure it appropriately. Simply create a new project with OpenGL ES 3.0 activated, choose a folder location for saving, and assign the project a suitable name, such as “top-down collector.” Once created, you’ll begin inside the Godot Editor. From here, access the Project Options by choosing ProjectProject Settings from the application menu. Let’s start by setting the Application Window size, that is, the complete dimensions of the game window in pixels. To do that, select DisplayWindow, and then enter values of 1024 x 600 for the pixel width and height, respectively. See Figure 4-2.
Figure 4-2

Setting Window Width and Height

Next, scroll down to the Stretch Section of the Project Settings Window. This determines how Godot should automatically render your scene when the window size changes from the intended resolution. This may be because the user resized the window or because the game is now running on a device with a different resolution. By default, Godot will maintain pixel sizes. This will be problematic if you want your 2D game to auto-resize or scale to a different resolution. To fix this, change the Mode drop-down to 2D and the aspect to keep_width. See Figure 4-3.
Figure 4-3

Setting the Stretch Properties

Our game will use Tilemaps to build the world, based on 2D tile images arranged in a grid. On some graphics cards – such as legacy NVIDIA cards – you may experience flickering from your tiles, especially as the camera moves around the scene. To avoid this bug, let’s switch to the RenderingQuality tab and move to the Depth Section. From here, remove the check mark from the HDR field. See Figure 4-4.
Figure 4-4

Disabling HDR for Rendering Optimizations in 2D

Now let’s set up the keyboard input for up, down, left, and right controls. These span the Horizontal and Vertical axes, corresponding to the keys WASD (and the corresponding arrow keys). Chapter 3 explores how to configure these. Figure 4-5 shows the final key configuration for our 2D game.
Figure 4-5

Setting Input for the 2D Collection Game

Importing Assets

For our 2D game, we’ll use the freely available public domain assets from kenney.nl. For character sprites, we’ll use www.kenney.nl/assets/toon-characters-1 and for environment tile sets www.kenney.nl/assets/topdown-shooter. Later, we’ll also use www.kenney.nl/assets/generic-items for pickups. Kenney is an excellent online source for quick prototypical 2D game art. Other great resources include https://opengameart.org/, https://texturehaven.com/, and hdrihaven.com. Each pack contains a selection of images in PNG format. And within each pack, there will be a single sprite sheet, with each smaller image composed into a single larger atlas image. We’ll import the larger sprite sheet into Godot, and these are included in the book companion files too. See Figure 4-6.
Figure 4-6

Importing PNG Image Textures into the Project

After importing the textures, select each one from the FileSystem panel and then move to the Import tab above. This allows you to change the import settings for the selected texture. For the Character sprite sheet – and for any objects that’ll move or rotate during gameplay – ensure Filter is enabled. For environmental assets, and tile-sets, and other 2D assets that don’t move during gameplay (walls, floors, tables, etc.), ensure Filter is deactivated. Don’t forget to click the Reimport button if you make any changes. See Figure 4-7.
Figure 4-7

Reimporting Textures

Creating the Player Character

Let’s start development by creating the player character. The character will be able to move around the environment using the WASD keys, collide with objects like walls and props, and collect power-up items. In Godot, each distinct gameplay element should be created in its own scene. For this reason, we’ll need at least a player scene, an environment scene, and a collectible scene. During gameplay, these scenes will be merged, or linked, together to create a master scene containing our combined gameplay elements. Scenes, in this sense, then, should be conceived as stand-alone, self-contained gameplay units. Let’s create the player scene by choosing SceneNew Scene from the application menu and then click 2D Scene from the Scene tab to create the initial starting node for the scene. See Figure 4-8.
Figure 4-8

Creating a 2D Scene for the Player Character

The player root node should be a KinematicBody2D, because it’ll be player controlled and should react with physical objects. Right-click the scene root, and then choose Add Child Node. From here, create a KinematicBody2D. See Figure 4-9.
Figure 4-9

Creating a Kinematic Body As the Player Root

You can make any node the scene root by right-clicking the node from the Scene Tree and then selecting Make Scene Root from the context menu. For this example, go ahead and make our newly created Kinematic Body the scene root.

The KinematicBody2D has no physical or visible presence when created on its own. It must be complemented by two additional child nodes: one to define the character’s volume and size physically and the other to define its appearance to the player. To achieve this, let’s create two child nodes: first, the CollisionShape2D node and, second, the AnimatedSprite node. See Figure 4-10 for the completed scene tree.
Figure 4-10

Creating a Kinematic Body As the Player Root

Let’s proceed by defining the Sprite Appearance through the AnimatedSprite node, that is, the 2D appearance of the player character. We’re using an AnimatedSprite node, as opposed to a regular Sprite node, because actions like walking should display a spite animation. Select the AnimatedSprite from the Scene Tree, and from the Frames field in the Object Inspector, choose New SpriteFrames . See Figure 4-11.
Figure 4-11

Creating New Sprite Frames

By clicking the newly created SpriteFrames Resource in the Inspector, you’ll display the SpriteFrames Window at the bottom center of the Godot interface. This lets you edit the SpriteFrames resource, adding animations and frames from a pixel-based sprite or sprite sheet. See Figure 4-12. To create our first animation, click and rename the default to Idle. And then click the Add Frames from Sprite Sheet button. From here, you can select a texture asset (character_maleAdventurer_sheet.png) from our available resources.
Figure 4-12

Adding Frames from a Sprite Sheet

Clicking the Add Frames from Sprite Sheet button displays the Select Frames Window, where you can slice the sprite sheet into even tiles, based on rows and columns. For our character sprite image, a horizontal slice of 9 and a vertical slice of 5 works. This divides the image successfully into evenly sliced tiles. Once sliced, simply hold down the Ctrl key (or Cmd key on a Mac) to left-click and select all frames to be included in the Idle animation. The Idle animation defines how the character will animate when standing still. See Figure 4-13. When done, click the Add Frames button.
Figure 4-13

Selecting Animation Frames

Now you can use the Sprite Frames Window to arrange the frames and define a frame rate for the animation. Use the Copy and Paste buttons to duplicate frames where needed and the Move Before and Move After buttons to rearrange the order of frames. See Figure 4-14.
Figure 4-14

Defining an Idle Animation Using the Sprite Frames Window

Repeat this process now for creating a Walk animation, which will play whenever the player character moves. Simply hit the New Animation button to add a Walk animation, and then add frames via the preceding method. See Figure 4-15.
Figure 4-15

Creating a Walk Animation

You can preview each animation directly in the viewport. To do this, select the AnimatedSprite node, and from the Inspector, enable the Playing check box and select the relevant animation to preview from the Animation field. See Figure 4-16.
Figure 4-16

Previewing Character Animations

Excellent! We’ve now got an animated character, complete with two distinct animations. Of course, the Idle animation should be the default, as it’ll always play unless the character is moving. However, the character still doesn’t have any physical size, shape, or volume. Let’s address that by selecting the CollisionShape2D node from the Scene Tree panel. With this object selected, click the Shape field from the Object Inspector, and choose New CircleShape2D from the context menu. See Figure 4-17.
Figure 4-17

Creating a Collision Shape for the Player Character

After creating a new CircleShape2D, you’ll be able to resize the radius to enlarge or shrink the circle, approximating the character shape and size. You can resize the circle by adjusting the Radius field or by directly dragging the circle gizmo in the editor. See Figure 4-18. As you adjust the Radius, you’ll notice the AnimatedSprite is probably centered on the object pivot point, making the character pelvis or mid-region centered at the origin. This is not ideal. So, we’ll change that next.
Figure 4-18

Creating a Collision Shape for the Player Character

Select the AnimatedSprite character, and then deactivate the Centered check box. Next, use the Offset X and Y fields to recenter the character, so his feet touch the origin instead. This’ll make it easier to predict where the character will appear when we set his position during gameplay. See Figure 4-19.
Figure 4-19

Recentering the Player Character

Now, reselect the CollisionShape2D, and adjust the collision shape to match the character’s body. You don’t need to enclose the head, because when seen from a top-down perspective in a regular 2D level, it’s only the body that will collide with walls, doors, and objects. See Figure 4-20.
Figure 4-20

Sizing the Circle Collider

This is great. We’ve got a fully configured player character. And now, finally, we can code player controls. To do this, select the root KinematicBody2D node, and create a new C# script from the Script field in the Inspector. The script should inherit from KinematicBody2D and be called PlayerControl. See Figure 4-21.
Figure 4-21

Creating a New C# Script for Player Controls, Attached to the KinematicBody2D

The purpose of the PlayerControl script is to respond directly to user input – up, down, left, and right – and to ensure the player interacts with physical obstacles in the scene, like walls and doors, and props. The complete source code for the player character is given in Listing 4-1. Comments follow.
using Godot;
using System;
public class PlayerControl : KinematicBody2D
{
    [Export]
    public float MoveSpeed = 2f;
    private Vector2 InputDir = Vector2.Zero;
    private AnimatedSprite AnimSprite = null;
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        AnimSprite = GetNode("AnimatedSprite") as AnimatedSprite;
    }
    public override void _Process(float delta)
    {
        InputDir.x = -Input.GetActionStrength("Left") + Input.GetActionStrength("Right");
        InputDir.y = Input.GetActionStrength("Down") + -Input.GetActionStrength("Up");
        if(InputDir.x > 0) AnimSprite.FlipH = false;
        if(InputDir.x < 0) AnimSprite.FlipH = true;
        InputDir = InputDir * MoveSpeed;
        if(InputDir.LengthSquared() <= 0)
            AnimSprite.Play("Idle");
        else
            AnimSprite.Play("Walk");
    }
    public override void _PhysicsProcess(float delta)
    {
        MoveAndSlide(InputDir);
    }
}
Listing 4-1

Player Controls

Listing 4-1 contains the full and complete PlayerControl script. The _Ready function executes on startup and finds the AnimatedSprite node. The _Process function runs on each frame to read input from the keyboard, to flip the sprite left or right if needed, and to trigger the appropriate animation in the AnimatedSprite node. The collected input is expressed as a 2D velocity in the Vector InputDir, which is used inside the _PhysicsProcess event by the MoveAndSlide function. To draw a parallel, Unity divides a “frame” across both Update (on each rendered frame) and FixedUpdate (each physics step). Godot, by contrast, uses _Process for rendered frames and _PhysicsProcess for physics steps. This means that all physics-based functions – like MoveAndSlide – must happen inside _PhysicsProcess, if they are always to behave as intended. Calling a physics-based function outside the _PhysicsProcess event may mean collisions aren’t always detected, characters could pass through solid objects, and physics collisions could result in jitter and stutter.

Now, to complete the player character, let’s add a follow camera. This will track the player as they move around the scene, ensuring the player is always visible. Right-click the KinematicBody2D node and add a Camera2D node. See Figure 4-22.
Figure 4-22

Adding a Camera2D Node to Become a Follow Camera

After adding a Camera2D node as a child of the KinematicBody2D, enable the Current boolean setting from the Inspector. This ensures the Camera is always the main active camera in the scene. In addition, enable Smoothing to create a smoothed camera motion as the camera tracks its target, and set its Process mode to Physics, since the camera motion is based entirely on player movement, which uses the physics system. See Figure 4-23. Great work! We’ve now got a fully functional player.
Figure 4-23

Configuring the Camera to Follow the Player

The player is done! Save your changes. Now create a new master scene, if you haven’t already, and drag the player scene into it. The Master Scene will contain all top-level gameplay units of our game, including the player and the level. See Figure 4-24.
Figure 4-24

Setting Up a Master Scene

Building a Level – Tilemaps and Tilesets

Our 2D Collection game needs a world – or an environment – where the player can explore and find things to collect. In 2D, worlds can be made easily using a Tilemap, which is a grid of equally sized image tiles, arranged in rows and columns, and reused optimally to form a single complete world. Many famous games were constructed in this way, including the early Zelda and Final Fantasy games. Nearly every top-down RPG game is made from a tilemap, where each level one is simply one big tilemap. A Tilemap in Godot is composed from a Tileset, which is a palette of tile images. To build our level, let’s create a new 2D scene and add a Tilemap node. See Figure 4-25.
Figure 4-25

Creating a Tilemap Object

Unity also supports TileMaps and offers feature equivalence to Godot. However, Godot also supports GridMaps, which are tilemaps for 3D scenes and meshes.

Newly created Tilemaps are generated empty. To start using them, you’ll need to create a Tileset. A Tileset represents the palette or images, or the raw materials, from which you build a Tilemap. With the Tilemap object selected in the Scene Tree, click the Tileset drop-down from the inspector and select New Tileset from the context menu. This creates a new Tileset Resource, and this can be edited in the Tileset Editor Window, shown by default at the bottom center of the interface or whenever you click the Tile Set field in the Inspector. See Figure 4-26.
Figure 4-26

Creating a Tileset Resource for the Tilemap

The Tile Set Editor is used to create new Tile sets. To start, drag and drop a tile sheet texture from the FileSystem panel into the texture list of the TileSet Editor. For our level, we’ll use the spritesheet_tiles.png file included in the book companion files and which are part of Kenney Assets. See Figure 4-27. To confirm the drag and drop was successful, the Tileset Window will populate with the sprite sheet image.
Figure 4-27

Building a New Tileset

Let’s make our first two tile types, standard tiles for a wooden floor. First, click the New Single Tile button from the top menu of the Tile Set Window. Click and drag a region in the texture sheet area and then expand the Snap settings from the Inspector. If you don’t see the Snap settings, be sure to enable Tile Snapping in the toolbar. This makes selecting and working with Tiles easier. I have chosen a tile width and height of 64 and a pixel separation value of 10. This simply means that each tile in the image is 64 pixels wide and high, and there is a gap of 10 pixels between tiles, both horizontally and vertically. See Figure 4-28.
Figure 4-28

Starting Our First Tile

Now select the tile image to be used for our first tile, which will be wooden floorboards. To do this, click the floorboard tile image and assign the selected tile a name from the Inspector. This name will display in the Tilemap object, when shown in the tile palette. See Figure 4-29.
Figure 4-29

Configuring the First Floor Tile

You’ve created your first tile. Excellent. Now jump back to the Godot Tilemap node just by selecting it from the Scene Tree panel. On selecting it, the Tile Palette will be displayed in the Inspector, showing your newly created tile. See Figure 4-30.
Figure 4-30

A New Tile Map Node with Our First Tile

Just by selecting the tile from the Inspector and then by clicking and dragging in the viewport like a brush, you can draw many instances of the tile in the scene to create a world. Right-clicking a tile erases it. You can also use the rotate left and rotate right controls to rotate your brush, drawing different tile variations. See Figure 4-31.
Figure 4-31

Drawing a Scene from a Tile

Next, we’ll create a second tile (much like the first), which is a slightly different variation of the floorboard tile. This simply adds variety and diversity in the scene. For a third tile, we’ll create an Autotile, which adds lots of interesting flexibility for wall tiles, tracks, roads, and other kinds of winding tiles that other connect together. An Autotile lets you select a collection of related tiles (e.g., different areas of wall) and will intelligently paint your tile into the level to connect seamlessly with surrounding areas. These kinds of tiles are best understood by trying them out. To get started, click the Tileset asset from the Inspector to return to the TileSet Window. Then click the New Autotile button. See Figure 4-32.
Figure 4-32

Autotiles Let You Create Integrated Walls, Roads, and Other Elements

Now click and drag over a tile set region to select all tiles to be included as part of the autotile. Be sure to set your tile snap settings from the Inspector to 64x64 and a separation of 10 pixels, as we did earlier. The purpose of our autotile will be to create wall elements for the environment: corner sections, intersections, and straight sections. So I’ll click and drag a complete rectangular region around all the wall elements. See Figure 4-33.
Figure 4-33

Select an Auto-Tile Region. This Should Contain All Tiles Related to a Specific Architectural Element: Walls, Floors, Roads, Fences, and Others

Now click the Bitmask button from the Tileset toolbar. From the Inspector, choose 3x3 (minimal) for the Autotile Bitmask field. Then set 64x64 for the Subtile field, X and Y. Once these settings are provided, we’re ready to complete our auto-tile by drawing out the “walkable” regions, that is, the continuous, inside regions that follow the direction of the wall. See Figure 4-34.
Figure 4-34

Preparing to Select the Autotile Walkable Region

Next, click and drag over the “empty” region of the wall tiles. These will be marked in red. Ensure you cover all areas that are “walkable.” See Figure 4-35.
Figure 4-35

Drawing the Walkable Regions of an Autotile

Name your Autotile, and then switch to the Icon tab. From there, click to select the most appropriate icon. In this case, I selected the completely enclosed wall section. See Figure 4-36.
Figure 4-36

Setting the Auto-Tile Icon

Now you’re ready to start drawing interconnected walls with the auto-tile brush. Jump over to the Tilemap node in the scene, and then select the auto-tile brush, which is represented in the palette by your selected icon. You’ll notice that by clicking and dragging the brush around the scene, inside the viewport, the walls will be drawn and connected together correctly, automatically, and seamlessly. See Figure 4-37.
Figure 4-37

Drawing an Auto-Tile Wall

Great! Now use your three tiles (two regular tiles and one auto-tile) to build a complete level. A floor layout surrounded by walls. See Figure 4-38.
Figure 4-38

Designing a Complete Level

World Collisions

We’ve built both a character and a world. Let’s test them. Open the Master Scene file created earlier – or create a new master scene, if you haven’t already – and drop both the player scene and the level01 scene together into the master. You may notice that the level overlaps, and hides, the player character. If you don’t, check out the Scene Tree Panel. By default, higher objects are rendered behind lower objects.
Figure 4-39

Lower-Order Nodes in the Hierarchy Render on Top of Higher-Order Nodes

We could solve the render order problem by rearranging the nodes, moving the player below the level node. However, this order really shouldn’t matter when it comes to rendering. So, we can tweak it easily instead by jumping to the level scene and selecting the Tilemap node. We should set its Z Index to –1000, leaving Z as Relative check box enabled. When enabled, the Z Order of the Tilemap will always be set relative to its parent. See Figure 3-40. And now the level appears behind the player.
Figure 4-40

Setting a Node’s Render Order (Z Index)

The main problem however is that our game, when played, doesn’t support any collisions even though our player controller uses the MoveAndSlide function inside the _PhysicsProcess event. By pressing Play or Play Scene, the player will happily walk through walls and any solid objects. See Figure 4-41.
Figure 4-41

The Player Walks Through Walls!

To fix this, we’ll need to create Collision information for the level. This information defines the borders and extents of solid, impassable objects. Open up the Level Scene file and create a new StaticBody2D node. See Figure 4-42. Static Bodies represent solid objects that never move during gameplay. Typically, this includes walls, ceilings, floors, trees, pillars, and others.
Figure 4-42

Create a New Static Body for Non-movable Objects

As with the KinematicBody2D created earlier for the player character, the StaticBody2D has no extension or volume by default. We need to create its size and shape using a CollisionPolygon2D. To do this, select the newly created StaticBody2D node, and create a child CollisionPolygon2D node. Once added, click the Create Points icon in the toolbar, and then click inside the viewport to start placing points around the level walls. See Figure 4-43.
Figure 4-43

Build a Collision Polygon for the Scene Static Body

By default, point placement is free and loose, and you’ll likely want to snap points to the world grid to ensure they align with the walls easily. To do this, click the Toggle Grid Snap icon from the top toolbar. A Grid is then placed over the complete map and drawn in the viewport. It will not, of course, be visible to the player – it’s for our reference only. See Figure 4-44.
Figure 4-44

Enabling Grid Snap for Precise Point Placement

The created grid will typically be tightly packed, spaced to 8 pixels per grid line. We can change this to suit our tiles better. To do this, click the Snap Options button (the three dots beside the Snap Toggle), and choose Configure Snap from the menu. See Figure 4-45.
Figure 4-45

Accessing the Grid Snap Options

For our TileSet, let’s use a value of 32x32 pixels for X and Y. These will realign the gridlines and match our tile sizes while also giving us appropriate steps within the grid. Excellent. See Figure 4-46.
Figure 4-46

Setting Grid Increments

Now you can easily click inside the level to place points for the CollisionPolygon2D, building a Collision Shape that represents the scene walls. Be sure to add a point at every corner. Take a look at Figure 4-47, which shows a half-completed wall arrangement.
Figure 4-47

Building the Wall Collision Polygon

Great. You’ve now constructed a full collision polygon that surrounds the walls. By opening the Master Scene and running the game, the player character will not pass through the walls and will remain within the scene boundaries. This is good progress, but there’s more to do still. See Figure 4-48. Now, let’s see how to progress in the next section!
Figure 4-48

Supporting Player Collisions

2D Lighting

In this section, we’ll add drama to the scene with 2D lighting. Specifically, the room will begin dark, and the player will carry a torch, illuminating any areas he moves to. The light will also support full shadow casting, allowing different 2D objects to cast shadows and obscure light. To get started, let’s open up our environment level scene. To make it dark, add a CanvasModulate node. And from the Inspector, set its Color property to a very dark gray, close to black. Suddenly, the entire level darkens. The CanvasModulate node control multiplies the color values for all Canvas-based objects, including 2D objects and user interface elements. See Figure 4-49.
Figure 4-49

Supporting Player Collisions…

To create Shadows and to ensure the world responds correctly to any lights added, let’s create a LightOccluder2D node. This works like the CollisionPolygon2D node created in the previous section to define the collision extents of the walls. By comparison, the Occluder node defines which objects will “block” light. Since light won’t travel through walls, the Occluder polygon should be the same as the wall collision. Take a look at Figure 4-50.
Figure 4-50

Building Light Occluder Polygons

Now let’s switch over to the player scene. Select the root node and add a new Light2D child. This represents a light object. It requires a 2D texture to define the shape, scale, and brightness. Let’s use the light texture provided by Godot, in its Light2D tutorial, here: https://docs.godotengine.org/en/3.2/tutorials/2d/2d_lights_and_shadows.html. The image is Light.png, which is also included in the course companion files for your convenience. Please do check out this tutorial, and others, on the excellent Godot website: https://docs.godotengine.org/en/3.2/tutorials/2d/. In this section, we’ll import the light.png texture into the Godot FileSystem panel. And then assign it to the Texture slot of the Light2D. See Figure 4-51.
Figure 4-51

Configuring a Light2D Texture

Next, let’s configure Light Shadow casting to interact with the world. To do this, select the Light2D node from the Scene Tree and then expand the Shadows tab from the Inspector. From there, click the Enabled check box. And now test the MasterScene. See Figure 4-52. Great, the Light2D now interacts with, and casts shadows from, the main environment walls.
Figure 4-52

Operational Shadows! Great Work…

Pickups

In our collection game, the player needs to collect something! The idea is that the player must collect all pickups before the time expires. In our case, the player will collect gamepad sprites, and these can be downloaded from the excellent texture resource, Kenney Assets, available for free here, as part of the Generic Items pack, www.kenney.nl/assets/generic-items, specifically the PNG image file genericItem_color_082.png. See Figure 4-53.
Figure 4-53

Importing a Gamepad Texture for the Pickup

Next, create a new 2D Scene. This will be used for the Pickup object. Then create an Area2D node and make this the scene root. The Area2D node behaves like a Unity Trigger collider. It defines an area or a volume within the scene, inside which events can be detected. But, it doesn’t represent a physical, solid object like a StaticBody or a Kinematic body. Areas are useful for lava pits, water, poisonous atmospheres, and cut-scene triggers – whenever you must detect the entry or exit of objects in areas. See Figure 4-54 for the original setup.
Figure 4-54

Make an Area2D Node the Scene Root

Let’s proceed by adding both a Sprite Node and a CollisionShape node. The former defines the object appearance and the latter its volume. We’ve seen this process before when creating both the player and the world. Consider Figure 4-55 for the final configuration.
Figure 4-55

Configuring a Pickup

Great. Finally, let’s add the Pickup to a “Pickup” group and use the Nodes tab from the Inspector (see Chapter 2 on how to use Groups), and then we can add some code to the pickup. The code will make the object disappear when it touches the player and will check to see if there are any remaining collectible objects. If not, the level is completed. See Listing 4-2. This code should be attached to the topmost Area2D node of the Pickup scene, and it assumes the player character belongs to the Player Group.
using Godot;
using System;
public class Pickup : Area2D
{
    public void _on_Pickup_body_entered(Node N)
    {
        if(!N.IsInGroup("Player"))return;
        //Check pickups remaining
        if(GetTree().GetNodesInGroup("Pickup").Count <= 1)
        {
            //You completed the level
            GD.Print("Level Completed");
        }
        CallDeferred("RemoveObject");
    }
    public void RemoveObject()
    {
        GetParent().RemoveChild(this);
    }
}
Listing 4-2

Responding to Pickup Collection

Now jump back to the Level Scene and add multiple instances of the Pickup object around the level for the player to collect. See Figure 4-56.
Figure 4-56

Placing Pickups

Now you can play the Master Scene and watch the player collect all items. This is excellent. We’re nearly there. Only one more feature left to create: the timer! See Figure 4-57 for the completed level with collectibles.
Figure 4-57

Catching Those Pickups!

Timers and Countdowns

So, the final step is adding a timer countdown. When the timer expires, the level will restart. It’s that simple, and Godot makes it incredibly easy to add timers. To start, open the Level Scene, since the countdown is part of the level and not the player. Although the countdown applies to the player, in terms of game mechanics, its properties are specific to the level. So, let’s add a Timer node. See Figure 4-58.
Figure 4-58

Adding a Timer Node

Set the Timer Process Mode to Physics, to update along with the physics cycle, and specify a Wait Time of 20 seconds – we can easily change this – which represents the total time available for the player to collect the pickups. Enable One Shot and Autostart to begin the timer as the level begins. See Figure 4-59.
Figure 4-59

Configuring a Timer

Now we’ll create a new C# script file to handle the level restart behavior for when the timer actually expires. It’s pretty simple code. This script should be attached to the Timer node. Take a look at Listing 4-3.
using Godot;
using System;
public class LevelReload : Timer
{
    public void _on_Timer_timeout()
    {
        GetTree().ReloadCurrentScene();
    }
}
Listing 4-3

Level Reloading for Expired Timers

We should connect this code to the Timer expired signal and allow the code to execute when the timer expires. To do this, select the Timer node and switch to the Node tab on the Inspector. From there, double-click the Timeout signal and connect it to our _on_Time_timeout function, as we’ve seen before. In the end, your signal panel will look as shown in Figure 4-60.
Figure 4-60

Responding to Timeout Behaviors

And that’s it! You’ve now completed a simple, but functional collection game using the Godot engine. Excellent. Go ahead; take it for a test run and behold your work! See Figure 4-61.
Figure 4-61

The Completed Game!

Summary

Great work. In this chapter, we created a complete, comprehensive, and fun 2D game with a variety of behaviors, showcasing many Godot 2D features. Godot has an amazing and easy-to-use 2D feature set, which is easy to pick up, use, and reuse. Godot’s clever hierarchical scene framework makes it easy to create gameplay elements, like levels and player characters, in isolation and then bring them together seamlessly into a single world.