Build a complete game with the Godot game engine

Tutorial – Gaming with Godot

Article from Issue 244/2021
Author(s):

Creating a game requires a wide set of skills to combine graphics, animations, sound, double-clicks, and meticulous coding. The free and open source Godot game engine provides you with all the tools you need to get started.

Writing a game from scratch is hard, and that's why nobody does it anymore. Game creators instead use "engines" that combine a framework and a comprehensive set of tools that let you skip the drudgery and get to the creative parts right away. Godot [1] is one of the most popular free and open source game engines, and, after a couple of weeks playing with it, I can see why.

The Concept

One of Godot's creators, Juan "reduz" Linietsky, stated that the name "Godot" is a reference to the homonymous gentleman in Samuel Beckett's play. Godot, in the play, never arrives and, in a similar manner, Linietsky says the Godot game engine will never be entirely finished, as it can always be improved and expanded.

After six years of active development, Godot has grown to include a huge variety of tools. The best way to demonstrate Godot's capabilities is simply to build a game from beginning to end. So let's make a game of tactical interstellar warfare that … Who am I kidding? It's Space Invaders; we're making Space Invaders, people (Figure 1).

Figure 1: You will be making a Space Invaders clone with Godot.

Tower Defense

The documentation says Godot development is scene-based, and the first thing you have to grok is that a "scene," in Godot parlance, is a bunch of nodes (more about nodes in a minute) that work together.

Take the turret defending Earth in a Space Invaders video game. The image (or images, in case of an animation) of the turret, the collision map that lets you detect when it touches another game element, the sound it makes when it fires, etc., are all the different nodes that give life to the turret entity. This is what Godot calls a scene (Figure 2).

Figure 2: A "scene" is a bunch of related nodes that work together.

I'll start by building a turret that the player can move left or right across the bottom of the screen and that animates when the player hits the fire button.

Start up Godot and choose New Project from the Projects tab in the Projects Manager dialog. Give your game a name, select an empty folder to store all the stuff in it, and click Create and edit.

The first screen in the designer looks very exciting, with a 3D plane extending off into infinity. Unfortunately you won't be using that, as Spaced Marauders is a 2D game. Your workspace will look more like Figure 3. To get to that 2D editor, click on the 2D label in the top center of the screen (Figure 3, section 6).

Figure 3: Anatomy of Godot's editor: workspace (1), Scene dock(2), Inspector/Node tabs (3), Filesystem dock, (4) playback toolbar (5), editor modes (6), Animations pane (7), and main menu (8).

In the default layout, on the top left you have the Scene dock (Figure 3, section 2) that will contain the nodes for your current scene. Below that you have the FileSystem dock, where you can see your assets – that is, your files containing code, images, sounds, etc. (Figure 3, section 4). Center stage is the workspace (Figure 3, section 1) showing an image of the assets linked to the current node (if there are any) on the playing field. This view changes to a text editor when you need to start coding. On the right is the Inspector (Figure 3, section 3), which will show the properties of the currently selected node. Another tab behind Inspector called Nodes will show the signals you can leverage for the selected node and options for grouping similar nodes together. The usefulness of both will become clear later.

Go back to the Scene dock (Figure 3, section 2). If you haven't done anything yet, it will be showing several suggestions of nodes to add. Click on + Other Node to open a list of available nodes. There are a lot, but you can filter the options using the search function at the top of the dialog. Type area in the search box and chose Area2D from the list.

The node's names are pretty good descriptions of what they are; Area2D is an area that contains a 2D object. Usually, all the things that have to change when they "touch" each other (like the turret being hit by bullets or aliens) will be Area2D nodes. Once picked and added to your scene, you will see that Godot places a yellow warning icon next to the empty Area2D node. If you click on the warning icon, Godot tells you that an Area2D is not useful until it contains a collision shape. Double-click on the node's title of the node and edit the text to Turret. Press Ctrl+S to save the scene, and Godot will suggest Turret.tscn. That name is fine.

Before adding a collision shape, you will need to know what to base the shape on, namely the picture of the turret. You can use something like what you can see in Figure 4. Go to the directory where you are saving your Godot project and create a new subdirectory called art. Copy your image in there. If you would like to use the stuff I drew for this project, all art, code, and sounds are available online [2].

Figure 4: The turret is actually a sprite sheet containing six frames in one PNG image.

Click on your Turret node to highlight it, and either click on the plus (+) sign in the upper left-hand corner of the dock or press Ctrl+A to add a new subnode. As you can see in Figure 4, the turret image is really a sequence of images (i.e., an animation containing six frames in one image). This is what Godot calls a sprite sheet. The idea is that every time you fire, the cannon will recoil before firing again.

If you start typing anima into Create New Node's search bar, the first option that pops up is AnimatedSprite and that is exactly what you need. Double-click on it to add it under your Turret node. The AnimatedSprite node comes with another yellow warning icon, this time telling you that you need to add an image before you will be able to see anything.

Cross over to the right of the screen to the Inspector (Figure 3, section 3) and notice how the Frames property says it is [empty]. Click on the arrow pointing down in the box and pick New SpriteFrames from the drop-down menu. A new horizontal panel, SpriteFrames, will open across the bottom of the workspace (Figure 3, section 7). Locate the Add Frames from Sprite Sheet button in the Animation Frames toolbar (second from left; it looks like a grid).

A file browser dialog opens. Navigate to the art directory you created earlier and pick the image containing the frames shown in Figure 4. It is important that all the frames are the same size and shape, as Godot will now ask you how you want to split the frames.

Change the value in the Horizontal text box to 6 (meaning there are 6 frames along the horizontal axis), change the value in the Vertical text box to 1 (meaning there is only one row of frames), and click Select/Clear All Frames to select all the frames. A set of six blue boxes will surround each frame. Click on Add 6 Frames at the bottom of the dialog to add them to your node. The frames will appear in the SpriteFrames pane at the bottom of the window and the sprite will take the shape of the first frame and show up in the upper left corner of the playing field (Figure 5). You can now rename your animation "fire," move frames around, or copy and paste frames into different positions.

Figure 5: Adding a sprite sheet shows the frames in the SpriteFrames panel and the first frame on the playing field in the upper left-hand corner.

Looping is fine for things like walk cycles or spinning wheels, but with your turret, you want the cannon to retract once and then stop the animation until the fire button is pressed again. So deactivate looping by switching off the Loop toggle button in the bottom left of the panel, and change the frames per second (FPS) to something like 40, so that it doesn't look like the cannon is recoiling in slow motion.

In the properties, changing the value of Frame will show the corresponding frame on your turret in the editor, and ticking the Playing checkbox will play the animation once (because I deactivated looping). If you need to change anything else about the animation, click on the arrow pointing down in the Frames drop-down and choose Edit from the list of options. Use the arrow pointing left at the top of the Inspector dock to get back to the AnimatedSprite's main property list when you're done editing the animation.

Before you continue, it is a good idea to lock all the graphical nodes in your scene together so you don't pick one up by mistake and move it separately from the others. Click on the top Turret node to select it, and then click on the tool that keeps the subnodes from being selected separately. It is the 12th icon from the left above the workspace (Figure 3, section 1) and looks like the icon shown in the green box in Figure 2.

Once you have locked all your nodes together, pick up the image of your turret by clicking and dragging on it in the workspace and move it somewhere near the center of the playing field so you can see clearly what happens next. The playing field is shown as a faint purple rectangle in the workspace.

One of Godot's tenets is that you should be able to test each scene separately, without having to run the complete project. This helps isolate problems later on, when you have put it all together. You have now reached a point in which you can "play" your first scene.

At the top right of the screen, there is a series of buttons (Figure 3, section 5). You can use the Play button (an icon with an arrow pointing to the right) or press F5 to play the whole project, but as we haven't defined the main scene, you can't do that just yet. Two icons to the right of the Play button is another one that looks like a movie clapper. Click it (or press F6), and it will play only the selected scene.

When you hit the Play Scene button, not much happens. Indeed, it looks like nothing happens: Your turret pops into existence on the playing field wherever you dragged it to and just sits there, no animation, no nothing. But that is fine, as you have not yet told Godot under which circumstances you want to animate the turret.

Getting Animated

This is where you need to start coding. Godot supports several programming languages, including a visual node-based one. But the best option is probably GDScript [3], a language similar to Python but designed specifically for Godot.

Although Godot does let you write scripts that are independent from the nodes, most will be associated with nodes. Such is the case with Turret, as you are going to link a script with its Area2D node. Select the node from the Scene dock by clicking on it and then click on the Attach Script icon in the toolbar (Figure 2, yellow box).

A dialog box pops up asking what language you want to use for the script (Godot has no problem letting you use different languages for different nodes), what it inherits (you usually won't want to change this), the template you want to use (Default is fine), whether the script will be built-in (don't choose that, otherwise you will not be able to edit the script with an external editor), and the path where you want to store the script in your resources directory.

Once you click Create, a scroll icon representing the script will appear to the left of the name of the node (Figure 2, red box). Click on the scroll icon and the script will open in the script editor, showing you the default template (Listing 1).

Listing 1

GDScript's Default Node Template

01 extends [Node Type]
02
03 # Declare member variables here. Examples:
04 # var a = 2
05 # var b = "text"
06
07 # Called when the node enters the scene tree for the first time.
08 func _ready():
09   pass # Replace with function body.
10
11 # Called every frame. 'delta' is the elapsed time since the previous frame.
12 #func _process(delta):
13 #  pass

Things to note:

  • When you associate a script with a node, GDScript treats the node like a class, and your script extends the class. Hence, line 1 in the script for our Turret scene will read extends Area2D. If you change the type of node later on, you will have to change line 1 by hand to the node's new type, or Godot will be unable to run the scene.
  • In GDScript, like in Python, indentation matters. When you create a function, start a loop, or establish a conditional structure, you must indent its contents.
  • The Godot project publishes a style guide [4] that you can ignore, but you would be advised to follow it to keep your code nice and organized.

The template provides you with two ready-made functions that are very common in many node-attached scripts: _ready() and _process().The _ready() function is called automatically when the object (node) is instantiated for the first time (i.e., when it is pulled in as part of the game). When an alien pops into existence at the beginning of a level, Godot looks for the alien's _ready() function first. You can use _ready() to set the node's properties when it starts. You could, for example, set the coordinates of an alien on the playing field.

The _process() function is called every game frame to update the node. If the node contains an animation, it will update the frame; if the node is moving, it will update its position.

The _process() function's delta variable provides what is called "game time," a way to calculate the state of things taking into account how long has passed since the node's _process() function was last called. For example, say a node's sprite is moving at 400 pixels per second horizontally across the screen, and the last time the node's _process() function was called was half a second ago (delta = 0.5). To find out where to paint the sprite next, you just have to multiply the sprite's speed by the delta (400 x 0.5), and Godot will show the sprite 200 pixels from its prior position.

In _process() is where you are going to do the first modification. Get rid of all the quoted lines and unquote line 12 (func _process(delta):) and change line 13 from pass to $AnimatedSprite.play("fire"). Note that this line has to be indented.

Using a dollar sign ($) plus the node's name is the way you refer to nodes in the node tree. In this case, you want to call $AnimatedSprite's play() method. How do you know $AnimatedSprite has a play() method? Because as soon as you type the point (.) after $AnimatedSprite, the GDScript editor pops up a useful list of the methods and attributes you can use with that particular node, and play() is on said list. The GDScript documentation also has a list of all the types of objects/nodes [5], and that list tells you what methods you can use with which node. Look for AnimatedSprite, and you will learn that the play() method plays the animation you pass it as a parameter. In this case, the fire animation you made earlier plays. The final script looks like what you can see in Listing 2.

Listing 2

Turret.gd (v1)

01 extends Area2D
02
03 func _ready():
04   pass
05
06 func _process(delta):
07   $AnimatedSprite.play("fire")

Now run the scene. The turret will animate, as if firing bullets out of its cannon. You can also see the scene working in the editor if you want. Add a line that says tool to the beginning of your script and save it, and the scene will play out as you edit. You only want the firing animation to play when the player hits the fire button. To do that, you have to be able to read input from the player.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Introduction

    This month in Linux Voice.

  • FOSSPicks

    This month Graham looks at Godot 4, PostRunner, LeanCreator, lurk, Cubic, SuperStarfighter, and more!

  • FOSSPicks

    This month Graham reviews PeaZip, LibreSprite, NeoChat, Beaker, Giada, Thrive, Kurve, and much more!

  • Animation with OpenToonz

    OpenToonz is a professional animation tool for comic and manga artists.

  • Tutorials – Natron

    Natron gives you the power to apply sophisticated effects to your videos, but its node-based interface can be a bit confusing. This tutorial will help you get a grasp on the basics.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News