3008 Programming For Game Design
3008 Programming For Game Design
for Game
Design
A Hands-On Guide with Godot
—
Wallace Wang
Tonnetta Walcott
Programming for
Game Design
A Hands-On Guide with Godot
Wallace Wang
Tonnetta Walcott
Programming for Game Design: A Hands-On Guide with Godot
Wallace Wang Tonnetta Walcott
San Diego, CA, USA El Cajon, CA, USA
iii
Table of Contents
iv
Table of Contents
vi
Table of Contents
Firing a Projectile����������������������������������������������������������������������������������������������307
Removing Projectiles����������������������������������������������������������������������������������������313
Summary����������������������������������������������������������������������������������������������������������316
vii
Table of Contents
Index�������������������������������������������������������������������������������������������������503
viii
About the Authors
Wallace Wang has been writing computer books
for over 30 years, including Steal This Computer
Book, Microsoft Office for Dummies, Beginning
Programming for Dummies, Beginning iPhone
Development with SwiftUI, and The Structure
of Game Design, to name just a few. He created
the board game “Orbit War” for Steve Jackson
Games, which simulated satellite warfare in the
near future. He also writes screenplays and won
first place in Scriptapalooza’s 2023 screenwriting
competition.
ix
About the Technical Reviewer
Massimo Nardone has more than 27 years
of experience in security, web/mobile
development, and cloud and IT architecture.
His true IT passions are security and Android.
He has been programming and teaching how
to program with Android, Perl, PHP, Java, VB,
Python, C/C++, and MySQL for more than 27
years. He holds a Master of Science degree
in Computing Science from the University
of Salerno, Italy. He has worked as chief
information security officer (CISO), software
engineer, chief security architect, security executive, and OT/IoT/IIoT
security leader and architect for many years.
xi
CHAPTER 1
Why Learn
Programming
with the Godot Game
Engine?
Many people want to learn programming because the idea of creating a
program can be fun and exciting. Although programming is a skill that
anyone can learn, far too many beginner programming books and courses
forget about making programming fun right from the start.
Programming appeals to people because they want to create projects
that are interesting and show off their programming skills. Unfortunately,
the time for novices to gain the necessary skills to achieve their dreams can
take way too long. The end result is that too many programming students
give up because they fail to see how the skills that they gradually learn can
be useful in achieving the dreams that they want to achieve.
It doesn’t have to be that way. Persistency and patience are key.
Programming is about trial and error; however, it comes with the reward of
successfully completing a task.
2
Chapter 1 Why Learn Programming with the Godot Game Engine?
Because the Godot game engine is free, runs on all the major platforms
(Windows, macOS, and Linux), and runs on older, slower computers, the
Godot game engine offers a perfect introduction to both programming and
video game development.
You won’t learn how to develop the next massively multiplayer AAA
game title from this book, but you will learn programming principles
and basics in a fun and engaging manner. Although Godot uses its own
proprietary programming language called GDScript, it’s based on Python
and C. That means learning GDScript will prepare students to learn other
programming languages in the future.
So if you’re interested in both programming and video games, this
book is for you. We’ll start with the basics of a video game, then focus
on programming principles common in all programming languages.
Finally, we’ll focus on the specifics to making 2D video games. By the
time you complete this book, you should have a solid understanding of
programming principles and video game development.
This book will make programming fun by teaching video game design
in an accessible, fun, and interesting step-by-step manner. When you
finish this book, you’ll be well on your way to creating more sophisticated
programs and more complex video games. This book can help open
the doors to the fun and excitement of programming and video game
development. After this book, the entire world of programming and video
game development will be open to you no matter what programming
language or game engine you choose next.
As the Chinese proverb states, “A journey of a thousand miles begins
with a single step.” Let this book be your first step and you’ll find that
programming can be just as fun and exciting as you always thought it
should be.
3
CHAPTER 2
Getting to Know
Godot
The best way to learn any new skill is to start practicing it and not be
afraid of making a mistake. To learn programming, you need to spend
time practicing on your computer, so before you go any further, download
and install the Godot game engine (https://godotengine.org) on your
computer. Once you’ve installed Godot, you won’t need to install any
other programs to write programs in Godot. Remember, practicing takes
time and patience in order to be good at something. Learning Godot is a
fresh start!
The main steps to using Godot involve creating, editing, and running a
project. A project represents a complete video game. Each time you want
to create a different video game, you’ll need to create a separate project.
Godot isolates projects by storing them in different folders. The more
projects that you create, the more you’ll understand the mechanisms of
Godot. Still, it would be wise to focus on one project at a time to avoid
overload.
Although projects represent a complete video game, you may want to
create projects to test out different ideas. For example, you might want to
create a project to test out a combat system and another project to test out
an inventory system. Separate projects let you experiment with different
ideas in isolation, making them easier and faster to test without worrying
about integrating with the rest of an existing project.
While you can create as many projects as you wish, you’ll most likely
spend the bulk of your time editing an existing project rather than creating
new projects. Editing a project involves several tasks. The first way to edit
any project is to add assets such as graphic items to represent players,
obstacles, or background images. The best part of the project is that you
can be as creative as you want with your assets to your game. There are
different ways to create or obtain assets with enough research, so you don’t
have to make everything yourself.
Once you’ve added assets to a project, a second way to edit a project is
to modify the assets such as defining their position on the game field, their
size, their orientation, and their appearance. Assets have both a physical
appearance and a spatial location that you can define. Take as much time
as you need to modify and position your assets in order to build a fun,
playable, and functional game.
The appearance and position of assets create a static image. To make
a project interactive, you’ll need to write scripts that define how an asset
should behave when your project runs. A script is a code or program
that gives instructions to make assets function in a certain way. You will
learn more about scripts later on throughout this book. For example, a
cartoon car might need to avoid running into trees, telephone poles, and
other cars. If that should happen, then the cartoon car needs to change its
appearance to show the results of the crash. In addition, the cartoon car
should also behave differently after it’s been damaged.
In Godot, such scripts are written in a proprietary language called
GDScript, which resembles the Python programming language. Scripts
let assets in a project respond to user control through a keyboard or touch
screen and interact with other game assets.
A cartoon spaceship might fire lasers that can destroy asteroids in
the way. This might require a script to control and fire lasers from the
spaceship and another script for the asteroid to detect when it’s been hit
by a laser. Essentially, scripts make assets interactive and controllable by
the user.
6
Chapter 2 Getting to Know Godot
Editing a project lets you change the way the project looks and
behaves. To test if your project looks and works the way you want, you’ll
need to run the project periodically. These three steps (creating a project,
editing a project, and running a project) define the main actions you’ll take
while using Godot.
7
Chapter 2 Getting to Know Godot
8
Chapter 2 Getting to Know Godot
9
Chapter 2 Getting to Know Godot
Once you’ve created at least one Godot project, you can open
that project at a later time. To open an existing Godot project, follow
these steps:
10
Chapter 2 Getting to Know Godot
11
Chapter 2 Getting to Know Godot
Figure 2-5. Look for the project.godot file stored in your Godot
project folder
12
Chapter 2 Getting to Know Godot
13
Chapter 2 Getting to Know Godot
Figure 2-6. Every Godot scene gets stored in a file with the .tscn file
extension
14
Chapter 2 Getting to Know Godot
15
Chapter 2 Getting to Know Godot
16
Chapter 2 Getting to Know Godot
Figure 2-10. Typing in the Search text field filters out the list
of options
17
Chapter 2 Getting to Know Godot
18
Chapter 2 Getting to Know Godot
19
Chapter 2 Getting to Know Godot
20
Chapter 2 Getting to Know Godot
21
Chapter 2 Getting to Know Godot
22
Chapter 2 Getting to Know Godot
23
Chapter 2 Getting to Know Godot
You may wonder why the graphic image appears partially cut off in the
upper left corner of the (DEBUG) window. That’s because the upper left
corner is the default position every time you create a node. To see how to
view the contents of a scene, follow these steps:
24
Chapter 2 Getting to Know Godot
25
Chapter 2 Getting to Know Godot
26
Chapter 2 Getting to Know Godot
Figure 2-21. The Project Settings window lets you change the size of
the window when your project runs
27
Chapter 2 Getting to Know Godot
Figure 2-22. The Move icon displays an x axis and y axis arrow on
the selected node
28
Chapter 2 Getting to Know Godot
The Rotate icon lets you rotate a node in different positions, while the
Scale icon lets you shrink or enlarge an icon. To see how to use the Rotate
and Scale icons, follow these steps:
29
Chapter 2 Getting to Know Godot
10. Hold down the Shift key and drag either the red or
green square. Notice that when you hold down the
Shift key, both the width and the height of the image
expand or shrink at the same time.
When you want to keep the proportion of the width and the height
constant, hold down the Shift key before dragging one of the scaling
squares. By using the Rotate and Scale icons, you can modify the
appearance of an image displayed in the viewport.
Summary
You should store every Godot project in a separate folder. That will keep
files from one project from accidentally interfering with files used in a
different project. While each project creates a complete video game, don’t
be afraid to create projects to test ideas out or to learn different features
of Godot.
30
Chapter 2 Getting to Know Godot
31
CHAPTER 3
Writing Scripts
When you create a project with at least one scene that displays graphic
images on the screen, that scene will appear static no matter what the user
does. To make a project interactive, you need to write scripts that respond
when something happens to a specific node. Two common ways to make
a project interactive are to let the user control an object in a project and to
let the computer change the appearance of a project when collisions occur
between objects.
Scripts are essentially mini-programs written in one of two
programming languages:
1. C#
2. GDScript
In C#, you use curly brackets to define the start and ending of a block
of code such as follows:
if x > y
{
print("X is bigger than Y");
}
if x > y:
print(“X is bigger than Y")
34
Chapter 3 Writing Scripts
Creating a Script
When you create a Godot project, you need to create scenes and build
those scenes using nodes. Nodes are the basic building blocks used to
define a scene. You will add nodes as soon as you open up a scene. To
make a scene responsive, you need to attach scripts to nodes that make up
your scene. A node does not need a script, but if you do attach a script, you
may only attach one script to one node.
To attach a script to a node, you must select a node and then attach a
script to that selected node. Godot stores scripts in files that end with the
.gd file extension.
To see how to attach a script to a node, follow these steps:
35
Chapter 3 Writing Scripts
36
Chapter 3 Writing Scripts
37
Chapter 3 Writing Scripts
Writing a Script
Once you’ve created a script that’s attached to a node, you can then write
GDScript code to make that script work. Every script file contains one or
more functions where each function runs when a certain event occurs.
To see how a script works in a Godot project, follow these steps:
Figure 3-3. A script icon identifies nodes that have a script attached
38
Chapter 3 Writing Scripts
extends Sprite2D
func _ready():
print("Ready function here")
func _init():
print("Init function here")
Figure 3-5. All print commands display text in the Output pane
when a project runs
Notice that even though the ready() function appears first in the script,
the init() function actually runs first, and then the ready() function runs
second. The order you store functions in a script doesn’t matter. Functions
only run when certain events occur. In this case, the init() function
runs first, and then the ready() function runs next, but both only run
exactly once.
39
Chapter 3 Writing Scripts
The init() and ready() functions are useful for performing tasks when a
scene runs such as initializing a game by resetting the score to zero. Godot
offers several built-in functions that run when certain events occur. One of
the most common functions you’ll often use when creating video games is
the process(delta) function.
The process(delta) function runs continuously instead of just once like
the init() and ready() functions. This function can be useful for performing
repetitive tasks such as checking if the user presses a key or clicks
the mouse.
To see how the process(delta) function works, follow these steps:
extends Sprite2D
var speed = 2
func _process(delta):
rotation += speed * delta
40
Chapter 3 Writing Scripts
extends Sprite2D
var speed = 2
func _ready():
rotation += speed
Since the ready() function only runs once, the preceding code would
rotate the icon.svg just once and stop. By understanding when certain
functions run, you can put code in the right functions to make your project
interactive.
41
Chapter 3 Writing Scripts
The Online Docs icon opens your default browser to view the
documentation stored on the Godot website (https://docs.godotengine.
org). You can also access this online documentation by choosing Help ➤
Online Documentation from the Godot menu bar.
The Search Help icon opens a Search Help window to help you view
the different properties and methods available for each type of node as
shown in Figure 3-7. A property is a predefined variable for storing data,
and a method is a predefined function for performing a specific task.
42
Chapter 3 Writing Scripts
To see how to use the Search Help window, follow these steps:
43
Chapter 3 Writing Scripts
Figure 3-8. Searching for specific topics in the Search Help window
44
Chapter 3 Writing Scripts
45
Chapter 3 Writing Scripts
Rather than type in a term in the Search text field of the Search Help
window, another option is to move the cursor in a GDScript command that
you want more information about. Then choose Search ➤ Contextual Help
as shown in Figure 3-11.
46
Chapter 3 Writing Scripts
extends Sprite2D
var speed = 2
func _process(delta):
rotation += speed * delta
47
Chapter 3 Writing Scripts
At this point, you most likely won’t understand all the information
displayed in the documentation. Just be aware that you can search for
documentation within the Search Help window or on the documentation
web pages of the Godot website.
Oftentimes you may know what you want to do but have no idea how
to do it. That’s why Godot offers a special Questions and Answers web page
that you can access by choosing Help ➤ Questions and Answers.
This Questions and Answers page, as shown in Figure 3-12, lists
common problems that people run into while using Godot. By accessing
this page, you can find answers to common problems and even contribute
your own answers to problems that others might have.
48
Chapter 3 Writing Scripts
One fast way to get help on specific Godot keywords and predefined
functions is to hold down the Ctrl (Windows/Linux) or Command
(Macintosh) key and then move the mouse pointer over a Godot keyword
or function. If Godot recognizes a keyword or function, it will appear
underlined as shown in Figure 3-13.
49
Chapter 3 Writing Scripts
50
Chapter 3 Writing Scripts
Summary
This chapter introduced you to the basics of attaching a script to a node
and then writing GDScript code to respond to some event. You can
only attach one script to a node at a time. Scripts consist of one or more
functions that respond to specific events. The init() and ready() functions
run exactly once when the project starts. The process(delta) function
runs continuously. By storing GDScript code in functions, you can make a
project interactive.
Since you can’t memorize all possible commands available, Godot
provides help that you can access at any time. If you get stuck trying to
solve a particular problem, Godot offers a questions and answers section
where you can post a question and offer solutions to other people’s
problems.
51
Chapter 3 Writing Scripts
When you create a video game in Godot, you use nodes to define
the visual part of your game. Then you use the GDScript language
to write scripts to make those nodes respond to specific events. The
GDScript programming language is optimized to help you create projects
using Godot.
Now that you have a brief idea how scripts work, the next step is to
start learning basic programming principles so you can learn to control
different game objects on the screen.
52
CHAPTER 4
Storing
Data in Variables
The whole purpose of programming is to solve problems. To solve any
problem, you need to know the facts of the problem so you can figure out
how to use those facts to solve that problem. In computer programming,
facts represent data and the steps needed to solve a problem are called
algorithms. Computer programming and problem-solving are essential to
creating video games in Godot.
Suppose you wanted to convert a temperature from Celsius to
Fahrenheit. First, you need to know the temperature in Celsius, so let’s
assume it’s 20 0C. Once you know the temperature in Celsius, you can use
the following formula to convert that Celsius temperature to its equivalent
Fahrenheit temperature:
In this problem, the known data is the Celsius temperature (20 0C), and
the algorithm is the conversion formula. To convert 20 0C to Fahrenheit,
you can just plug in 20 0C into the conversion formula like this:
Fahrenheit temperature = 68
Problem-solving involves listing out the facts of the problem and then
making step-by-step instructions to find an answer. Once you know how
to solve a problem, the next step is to tell a computer how to solve that
problem by converting your instructions using a programming language.
In computer programming, a computer program is just that: a list of
instructions to perform certain tasks to solve a problem.
To store facts about a problem, programming languages temporarily
hold data in memory. To make it easy to retrieve data later, these memory
locations are given descriptive names and called “variables.”
Creating a Variable
Until you create (also called “declaring”) a variable, you can’t store any
data in your program. It is important to name and declare all variables
within a program; otherwise an error will occur, and the program may not
run. The two steps to creating or declaring a variable are
var x
var age
var first_name
var lastName
54
Chapter 4 Storing Data in Variables
You can give a variable any name you wish, but it’s best to use
descriptive names to make your code easier to understand. In the
preceding examples, a variable named “age” would likely contain a
number such as 37 or 24, while another variable named “first_name”
would likely contain text such as “John” or “Mary.”
Variable names can be as short as a single character (“x”) or consist
of multiple words smashed together. When combining multiple words to
make a variable name, Godot uses a convention known as “snake case”
where individual words are written in all lowercase and separated by an
underscore:
var snake_case_variable_name
When you create a variable, it initially has no value. If you try to use
that variable without a value stored in it, then your program will likely
crash. So when creating a variable by giving it a name, it’s common to also
store a value in that variable at the same time like this:
var age = 46
55
Chapter 4 Storing Data in Variables
extends Sprite2D
var age
func _process(delta):
print(age)
The general rule is that whenever you create a variable, never use it
until you actually store a value in that variable.
var age = 0
GDScript stores (or assigns) a value to a variable using the equal sign.
Whatever appears on the right of the equal sign gets stored in the variable
name that appears to the left of the equal sign.
Although you can assign values to a variable as often as you want, a
variable can only hold one value at a time. Each time you store a new value
in a variable, it erases the currently stored value. The following code stores
56
Chapter 4 Storing Data in Variables
three different values in the same variable, but by the end, only the last
value remains in the variable:
var age = 0
age = -15
age = 25
age = 137
The first line in the preceding code creates a variable called “age” and
stores an initial value of 0. The second line replaces the value of 0 with -15.
The third line replaces the value of -15 with 25. Finally, the last line replaces
the value of 25 with 137.
To retrieve the value stored in a variable, just use the variable name.
The simplest way to do that in GDScript is to use the print statement to
print the data out like this:
print(age)
This print command tells Godot to retrieve the value stored in the
“age” variable and print it out. To see how to store values in a variable and
retrieve those values, follow these steps:
extends Sprite2D
var age = 0
func _init():
print("First value = ", age)
age = -15
57
Chapter 4 Storing Data in Variables
First value = 0
Second value = -15
Third value = 25
Last value = 137
58
Chapter 4 Storing Data in Variables
extends Sprite2D
var age = 0
func _init():
print("First value = ", age)
age = "This is a string"
print("Second value = ", age)
age = 25.127
print("Third value = ", age)
age = 137
print("Last value = ", age)
59
Chapter 4 Storing Data in Variables
First value = 0
Second value = This is a string
Third value = 25.127
Last value = 137
Notice that the “age” variable initially contains a whole number (0),
then stores text (“This is a string”). Then it stores a decimal number
(25.127), and finally, it stores a whole number again (137).
Letting a variable contain all types of data (whole numbers, text, or
decimal numbers) can be convenient, but sometimes you may want to
restrict a variable to hold only certain types of data. For example, if you
create a variable to store someone’s name, you don’t want that variable to
hold non-text data such as -45.2 or 83 since a number wouldn’t make any
sense for someone’s name.
To restrict a variable to hold only one type of data, you need to
understand the different data types available as follows:
• Text (String)
GDScript calls whole numbers “int” (short for integer) and decimal
numbers as “float” (short for floating-point numbers). Text is called
“String” for text string. To restrict a variable to contain only one specific
type of data, you need to follow the variable name with a colon and the
data type you want to use such as follows:
60
Chapter 4 Storing Data in Variables
The first line creates a variable called “age” and specifies that it can
only hold integers (int). The second line creates a variable called “weight”
and specifies that it can only hold decimal numbers (float). The third line
creates a variable called “name” and specifies that it can only hold text
strings. Notice that all the data types (int and float) use lowercase letters
but String starts with an uppercase letter.
To see how declaring a specific data type works when creating a
variable, follow these steps:
extends Sprite2D
var age: int = 0
func _init():
print("First value = ", age)
age = "This is a string"
print("Second value = ", age)
age = 25.127
print("Third value = ", age)
age = 137
print("Last value = ", age)
61
Chapter 4 Storing Data in Variables
Notice that Godot won’t even let you run this program because it’s
trying to store a text string (“This is a string”) into the age variable as
shown in Figure 4-1.
Figure 4-1. Godot warns you when a variable tries to store a data
type it’s not designed to hold
To avoid problems in your code, it’s a good idea to assign initial values
to your variables when you create those variables. This prevents problems
trying to use a variable before it has a value in it.
A second way to avoid problems is to define variables to hold specific
data types. This helps ensure that your code doesn’t try to store the wrong
type of data in a variable, such as a number into a variable meant to
hold names.
62
Chapter 4 Storing Data in Variables
GDScript offers two ways to define specific data types for a variable.
One way is to define the variable name followed by a colon and the data
type it can hold like this:
A second way to declare a variable and define its data type at the same
time looks like this:
var legs := 4
var height := 18.25
var first_name := "John"
extends Sprite2D
var age := 0
var weight := 102.03
63
Chapter 4 Storing Data in Variables
Notice that the Output pane at the bottom of the Godot window
displays the print command’s output:
Age = 0
Weight = 102.03
Name = John
For clarity and readability, it’s often better to specify the data type
when declaring a variable like this:
64
Chapter 4 Storing Data in Variables
• Global
• Local
65
Chapter 4 Storing Data in Variables
extends Sprite2D
var global_variable = -56
func _init():
var local_variable = 79
print("Can access global variable in init
function = ", global_variable)
print("Can access local variable in init
function = ", local_variable)
66
Chapter 4 Storing Data in Variables
func _ready():
print("Can access global variable in ready
function = ", global_variable)
67
Chapter 4 Storing Data in Variables
var x: int = -3
var x: int = 41
If you try to give two variables the exact same name, Godot won’t run
your program and will display an error message as shown in Figure 4-4.
68
Chapter 4 Storing Data in Variables
Figure 4-4. You cannot give two variables the same name in the
same scope
extends Sprite2D
var x: int = -3
func _init():
var x: int = 41
print(x)
69
Chapter 4 Storing Data in Variables
70
Chapter 4 Storing Data in Variables
enum Obstacles {
ROCK,
SIGN,
POTHOLE
}
71
Chapter 4 Storing Data in Variables
An enumeration essentially acts like a data type, which means you can
assign an enumeration option to a variable like this:
The preceding code declares a variable called “player” that can only
hold data types defined by the Vehicles enumeration. Then it assigns that
data, which is defined by the enumeration name (Vehicles) followed by a
period and the actual enumeration option such as CAR.
As a shortcut, you could also declare the preceding variable without
defining the data type like this:
enum Obstacles {
ROCK,
SIGN = 45,
POTHOLE
}
72
Chapter 4 Storing Data in Variables
extends Sprite2D
const hard = 3
enum Vehicles {CAR = 78, TRUCK, MOTORCYCLE}
enum Obstacles {
ROCK,
SIGN,
POTHOLE = 64
}
func _ready():
var player = Vehicles.CAR
var enemy = Obstacles.POTHOLE
print("Player value = ", player)
print("Enemy value = ", enemy)
print("Number of obstacles = ", hard * 2)
Player value = 78
Enemy value = 64
Number of obstacles = 6
Notice that the two enumerations define a unique integer value for
CAR and POTHOLE. That means Vehicle.CAR actually represents the
integer 78 and Obstacles.POTHOLE actually represents the integer 64.
Assign the “player” variable to Vehicles.TRUCK and the “enemy” variable
to Obstacles.SIGN, and both of their values will be 1 since they appear as
the second item listed in the enumeration.
73
Chapter 4 Storing Data in Variables
Comments
Descriptive names for variables, constants, and enumerations can make
your code easier to understand, but sometimes you may need to add more
explanation. To make sure anyone looking at your code can understand
how your program works, GDScript lets you type comments directly into
your code.
A comment lets you add explanatory text, either on a separate line or
sharing the same line as code. To create a comment, type the # symbol
like this:
# This is a comment
Anything that appears to the right of the # symbol will be ignored by
the computer. That way you can type explanations in your code to explain
who wrote the code, when it was last modified, what assumptions the code
expects, what it’s doing, and why. The more comments you add to your
code, the easier it will be for you or someone else to understand your code,
so they can fix problems later or add new features.
Longer programs often result in “spaghetti code” making it hard to
process and containing more errors. Therefore, it is best to make the
program simple to follow and use comments to explain how a program
works. Comments are grayed out. They do not compile when running the
program but are simply used to help clarify what a program is doing and
how it works.
74
Chapter 4 Storing Data in Variables
extends Sprite2D
# This is a comment that appears on a separate line
func _ready():
print("Hello, world!") # This is a comment that
appears to the right of valid code
75
Chapter 4 Storing Data in Variables
your project again. However, a more convenient way to access and change
variables is to make those variables appear in the Inspector dock. That way
you can type in a different value and see how that changed value affects
the appearance of a project.
In GDScript, you can display a variable on the Inspector dock by
adding an @export in front of the variable declaration like this:
76
Chapter 4 Storing Data in Variables
Figure 4-6. Finding the Node2D node in the Create New Node
dialog box
77
Chapter 4 Storing Data in Variables
Figure 4-7. The Attach Child Node icon in the Scene dock
Figure 4-8. The Texture property of the Sprite2D can display the
icon.svg image
78
Chapter 4 Storing Data in Variables
extends Node2D
@export var x_position: int = 0
@export var y_position: int = 0
79
Chapter 4 Storing Data in Variables
func _process(delta):
$Sprite2D.position.x = x_position
$Sprite2D.position.y = y_position
16. Click the Node2D in the Scene dock. Notice that the
two @export variables (x_position and y_position)
now appear in the Inspector dock (see Figure 4-5).
80
Chapter 4 Storing Data in Variables
22. Click the Run Project icon (see Figure 4-10). Notice
that the icon now appears away from the left edge
(x_position = 550) and down from the top edge
(y_position = 250).
81
Chapter 4 Storing Data in Variables
Summary
To solve problems, you need to identify the facts of that problem
(data). Then using those facts, you can create step-by-step instructions
(algorithms) to find a solution. Finally, you need to translate the known
facts and instructions into a programming language so a computer can
solve that problem.
In programming, storing facts about a problem involves creating
variables to hold and retrieve data. To create a variable, you must use the
“var” keyword followed by a name for a variable. In GDScript, long names
typically consist of multiple words separated by an underscore such as
variable_name_here. Ideally, give each variable name a descriptive name.
A variable without a value may cause a program to crash, so it’s best to
give a variable an initial value. Since variables can hold any type of data,
this can cause problems if you want a variable to only hold certain types of
data such as numbers or text. For safety, variables can be defined to hold
only certain types such as integers (int), decimal numbers (float), or text
(String).
82
Chapter 4 Storing Data in Variables
83
CHAPTER 5
Mathematical
Operations
Nearly every nontrivial program needs to store data in variables. Once a
program stores data, it needs to manipulate that data to create a useful
result. Three ways to change data involve adding new data, deleting
existing data, or modifying existing data.
For example, a word processor lets you delete words, add new words,
or edit existing text. A spreadsheet lets you delete numbers, add new
numbers, edit existing numbers, or perform calculations on numbers. A
database lets you delete data, add new data, edit existing data, and search
and sort the data. Manipulating data and calculating new results from
existing data are two common ways programs use data to provide a useful
function.
So two common types of data that most programs store and
manipulate are
• Words (Strings)
Mathematical Operators
Every computer only understands binary numbers (0 and 1), so
manipulating numeric data is a common task for computers. A video
game, such as in Godot, needs to calculate where to move a player’s
character based on the player’s movement of a joystick or mouse, while
an accounting program needs to calculate financial formulas based
on money. Since the most common ways to manipulate data involve
numbers, every programming language offers mathematical operators
such as the following:
• + (addition)
• - (subtraction)
• * (multiplication)
• / (division)
• % (modulo)
• ** (exponentiation)
The addition operator (+) adds two numbers together like this:
var x = 4
x=x+6
The first line stores 4 in the “x” variable. The “4” that is stored is the
declaration of an integer or int variable. The second line may look a bit
odd since the “x” variable appears on both sides of the equal sign. This
essentially means add 6 to the value stored in “x” (4) and calculate 10.
Then store the value of 10 in the “x” variable, replacing (and erasing)
whatever current value may be stored in the “x” variable. Whatever
product comes after the “=” sign will usually be a String or integer of
some kind.
86
Chapter 5 Mathematical Operations
Rather than list the “x” variable twice on both sides of the equal sign,
GDScript offers a shortcut that looks like this:
x += 6
The += sign means “add 6 to the current value stored in x and store the
total sum as the new value in x.”
The subtraction operator (-), multiplication operator (*), and division
operator (/) work the same way like this:
var x = 4
x = x – 6 (or x -= 6)
var y = 4
y = y * 6 (or y *= 6)
var z = 4
z = z / 6 (or z /= 6)
The modulo (%) operator divides two numbers and returns the
remainder such as follows:
var a = 39
a = a % 5 (or a %= 5)
In this case, the % operator divides 39 by 5 which is 7 with a remainder
of 4, so the % operator returns a value of 4.
The exponentiation (**) operator multiplies one number several times
such as 2 ** 3, which means multiply 2 three times ( 2 * 2 * 2 = 8).
Besides adding two numbers together, the + operator can also be used
to combine two strings together such as follows:
“John” + “Doe”
87
Chapter 5 Mathematical Operations
When using the + operator to combine two strings, make sure you put
a space between the two strings or else the + operator will smash them
together.
To see how to use these different operators, follow these steps:
extends Sprite2D
var x = 39
var y = 5
var first_name = "John "
var last_name = "Doe"
func _init():
print("Addition = ", x + y)
print("Subtraction = ", x - y)
print("Multiplication = ", x * y)
print("Division = ", x / y)
print("Modulo = ", x % y)
print("Exponentiation = , x ** 2)
print("String concatenation = ", first_name +
last_name)
88
Chapter 5 Mathematical Operations
Addition = 44
Subtraction = 34
Multiplication = 195
Division = 7
Modulo = 4
Exponentiation = 1521
Creating Constants
When creating mathematical formulas, it’s common to use variables and
numbers such as this formula to calculate the circumference of a circle if
you know the circle’s diameter:
Circumference = 3.1415 * diameter
When code contains specific data such as numbers like 3.1415, these
are called “literals.” While there’s nothing wrong with using specific data
in a calculation, the meaning of that data may not be clear. To solve this
problem, programming languages allow you to create something called
“constants.”
A constant looks like a variable because you can give it a descriptive
name and assign it a value. The main difference is that you can store new
values in a variable over and over again, but with a constant, you can only
store a value in a constant exactly once.
89
Chapter 5 Mathematical Operations
const pi = 3.1415
For extra clarity, you can also define the data type of the constant
like this:
extends Sprite2D
var diameter: float = 3
var circumference: float = 0
90
Chapter 5 Mathematical Operations
Circumference = 9.4245
Understanding Precedence
When calculating simple formulas, you may only use one mathematical
operator such as 4 + 57 or 9.12 / 4.3. However, if you create more
complicated mathematical formulas that involve two or more operators,
you may run into a problem on how the computer calculates an answer.
Consider the following:
x=5+3*2
91
Chapter 5 Mathematical Operations
Does the computer first add 5 + 3 (8) and then multiply it by 2 to get
16? Or does the computer first multiply 3 * 2 (6) and then add this to 5
to get 11?
The order that calculation occurs is known as precedence. Precedence
defines which operators should be calculated first (higher precedence).
The following shows the highest to lowest precedence in operators:
• ** (exponentiation)
2*8/4
Since both multiplication (*) and division (/) have equal precedence,
the operator that appears first (furthest left) calculates first. That means
2 * 8 = 256, then 256 / 4 to calculate 64.
92
Chapter 5 Mathematical Operations
However, if you use parentheses, you could change the way this
calculation occurs like this:
2 * (8 / 4)
Now the parentheses tell the computer to calculate 8/4 first (2) and
then multiply this value to get 2 ** 2 which is 4.
To see how precedence works, follow these steps:
extends Sprite2D
func _init():
var result1 = 2 ** 8 / 4
var result2 = 2 ** (8 / 4)
print (result1)
print (result2)
64
4
93
Chapter 5 Mathematical Operations
• cos (cosine)
• log (logarithm)
• sin (sine)
• tan (tangent)
extends Sprite2D
func _init():
print("Absolute value = ", abs(-248))
print("Cosine = ", cos(1))
94
Chapter 5 Mathematical Operations
Cosine = 0.54030230586814
Logarithm = 1.09861228866811
Maximum = 23
Minimum = -5
Sine = 0.8414709848079
Square root = 5
Tangent = 1.5574077246549
95
Chapter 5 Mathematical Operations
randf()
96
Chapter 5 Mathematical Operations
randi() % 10
randi() % 250
extends Sprite2D
func _ready():
seed(123)
print(randf())
print(randi() % 10)
print(randi() % 10)
print(randi() % 10)
print(randi() % 10)
97
Chapter 5 Mathematical Operations
extends Sprite2D
func _ready():
randomize()
print(randf())
print(randi() % 10)
print(randi() % 10)
print(randi() % 10)
print(randi() % 10)
98
Chapter 5 Mathematical Operations
Unless you have a specific reason to create the same list of random
numbers over and over again, it’s best to use the randomize() function first
before calculating a random floating-point or integer number.
Manipulating Strings
It’s easy to understand mathematical operations on numbers, but
GDScript also offers different ways to manipulate strings as well. The most
common way to manipulate strings is concatenation, which simply adds
two strings together like this:
99
Chapter 5 Mathematical Operations
extends Sprite2D
func _ready():
var name = "Frank Parker Katz"
print (name.length())
print (name.to_upper())
print (name.to_lower())
print (name.to_snake_case())
100
Chapter 5 Mathematical Operations
17
FRANK PARKER KATZ
frank parker katz
frank_parker_katz
101
Chapter 5 Mathematical Operations
102
Chapter 5 Mathematical Operations
103
Chapter 5 Mathematical Operations
11. Click Button in the Scene dock and then click the
Node tab in the Inspector dock.
Figure 5-4. The Node tab appears next to the Inspector tab
104
Chapter 5 Mathematical Operations
func _on_button_pressed():
var window_size = DisplayServer.window_get_size()
randomize()
105
Chapter 5 Mathematical Operations
16. Click the Run icon. A dialog box asks if you want to
make the current scene the main scene.
18. Click the Button. Notice that each time you click
the Button, Godot displays the image at a random
position within the window as shown in Figure 5-6.
Figure 5-6. The Button lets you choose a random x and y value to
move the icon
106
Chapter 5 Mathematical Operations
Summary
Since computers only understand numbers, one of the most common
purposes for computer programs is to calculate a numeric result. You
can use common mathematical operations (addition, subtraction,
multiplication, and division), but for convenience, you can also use
built-in mathematical functions such as sqrt or sin to calculate common
mathematical results.
When using mathematical operators, be aware of precedence, which
defines which operators calculate first. For clarity, use parentheses to
define which operations you want to calculate first. Parentheses help
make your code easier to read while also clarifying how calculations work
as well.
In video games, random numbers are especially useful. To create
truly random numbers, you must understand that random numbers
require a seed value. If you define a fixed seed value, you’ll create the
same random numbers in a fixed order every time. That’s why it’s better to
use the randomize() function to calculate a seed value based on the time
the program runs. Since this will always be unpredictable, it will create
numbers that are as close to random as possible.
Mathematical calculations form the heart of most programs, so be sure
you know how to calculate results, use constants to represent fixed values,
and use built-in math functions. Calculating numeric results represents
the foundation of nearly every program you’ll need to write.
107
CHAPTER 6
Branching Statements
What makes every game fun to play are the choices players must make
to get closer to winning. The challenging yet intriguing part to making a
game fun is making choices that count and testing to see if the player made
the right choice. Since every video game offers choices, every program
controlling a video game must know how to offer choices and make
decisions based on what the user does. In the programming world, you
can create choices in a program through something called a branching
statement.
To make a decision, a branching statement needs to check if
something happened or not, such as the user pressing the space bar or
if a player’s character in a game has its strength reduced to 0 or less. To
make decisions, branching statements rely on another data type known as
Booleans. Branching statements and Boolean data types can make a game
respond to different situations.
A Boolean data type holds either a true or false value. Based on this
true or false value, a branching statement can make a decision on what to
do next. Godot can apply Boolean statements to determine if two objects
have collided, such as a player running into an obstacle. If a player did run
into an obstacle, then the game needs to determine what to do next such
as subtract a life from the player. To work with Boolean data types, you
need to know
To make sure a variable can only hold a Boolean value (either true or
false), you can declare a variable to hold Boolean data types like this:
While it’s possible to store a true or false value in a variable, it’s far
more common to calculate a true or false value instead using a comparison
operator. Essentially a comparison operator compares two values to
determine if the comparison is true or false.
The comparison operator is most effective when working with integers
or numbers. For example, if a variable to check a character’s health in a
game has been declared at “100,” a comparison operator can compare how
much health a player has before the health is depleted. If a player’s health
is less than 100, then the player loses health, and if it is below 0, then the
player dies and must respawn.
This is just an example of how comparison operators could be used
when working with games in Godot. Operators can also be used to
determine how far a player moves or the amount of items or weapons
used. The most common comparison operators are as follows:
110
Chapter 6 Branching Statements
• == (equal to)
6 > 2 (true)
47 < 5 (false)
29 == 29 (true)
7 != 7 (false)
y >= x + 42
z != 4
x <= y * z
111
Chapter 6 Branching Statements
events within a game. Some events may include the player losing health
or losing their life depending on how the game is programmed or other
events in a story-based game.
To see how these different comparison operators work, follow
these steps:
extends Sprite2D
var x = 8
var y = 4
var z = 2
func _ready():
print("x > y = ", x > y)
print("x < y = ", x < y)
print("x == y = ", x == y)
print("x >= z = ", x >= z)
print("x <= z = ", x <= y)
print("x != z = ", x != z)
112
Chapter 6 Branching Statements
x > y = true
x < y = false
x == y = false
x >= z = true
x <= z = false
x != z = true
Change the value of the x, y, and z variables and rerun the program
again to see how your changes alter the comparison operator calculations.
• and
• or
• not
Both the “and” and “or” logical operators compare two Boolean values
to calculate a single Boolean value. The “not” operator simply changes a
single Boolean value to its opposite.
The “and” logical operator only evaluates to true if both Boolean values
are true. Otherwise, the “and” logical operator evaluates to false like this:
113
Chapter 6 Branching Statements
The “or” logical operator works always evaluates to true unless both
Boolean values are false like this:
The “not” logical operator simply converts a true value to false (and a
false value to true) like this:
extends Sprite2D
func _ready():
print("true and true = ", true and true)
print("true and false = ", true and false)
print("false and true = ", false and true)
print("false and false = ", false and false)
print("true or true = ", true or true)
114
Chapter 6 Branching Statements
The if Statement
Every programming language offers multiple types of branching
statements, but the simplest one is called the if statement, which evaluates
a Boolean value. If this Boolean value is true, then it follows one or more
115
Chapter 6 Branching Statements
The key feature of the if statement is that it either runs one or more
commands (if its Boolean value is true) or does nothing at all (if its
Boolean value is false).
To see how the if statement works, follow these steps:
1. Make sure you have a Godot project that consists of
a Node2D and a child node Sprite2D that displays
the icon.svg image in a window.
2. Click the Sprite2D node in the Scene dock.
3. Edit the script attached to the Sprite2D node as follows:
extends Sprite2D
func _ready():
var x = 10
if x > 5:
print("x is greater than 5")
if x < 5:
print("x is less than 5")
x is greater than 5
116
Chapter 6 Branching Statements
Change the value of the “x” variable to -10 and run the program again.
Each time you change the value of the “x” variable, only one of the if
statements will run, depending on whether x > 5 is true or if x < 5 is true.
if x == 3:
commands
if x != 3:
other commands
While two separate if statements will work, it’s clumsy because it’s not
obvious that the second if statement will run only if the first if statement
does not run. A better solution is to use an if-else statement that offers two
different commands that can run. Depending on the Boolean value, either
the first set of commands will run or the second set of commands will run.
Unlike the if statement that only offers one set of commands that may
or may not run, the if-else statement offers exactly two sets of commands
where one set of commands will always run, depending on its Boolean
value. The if-else statement looks like this:
117
Chapter 6 Branching Statements
extends Sprite2D
func _ready():
var x = 1
if x > 5:
print("x is greater than 5")
else:
print("x is less than 5")
x is less than 5
Because the Boolean value of x > 5 is false (1 > 5), the else part of the
if-else statement runs. Change the value of the “x” variable to 10 and run
the program again. This will make the Boolean value of x > 5 (10 > 5) true,
so only the first command will run and print “x is greater than 5.”
118
Chapter 6 Branching Statements
Notice that the if-elif statement checks two Boolean values. First, it
checks if x > 15 is true. If so, then it prints “x is greater than 15.” If x > 15 is
false, then it checks the second Boolean value to see if x <= 15 is true or
not. If so, then it prints “x is less than or equal to 15.”
An if-elif statement can check multiple Boolean values such as follows:
if x == 5:
print("x is equal to 5")
elif x == 10:
print("x is equal to 10")
elif x == 15:
print("x is equal to 15")
elif x == 20:
print("x is equal to 20")
119
Chapter 6 Branching Statements
Notice that it’s possible that none of the Boolean values in the if-elif
statement will be true. In that case, no commands will run. To make sure
that at least one set of commands will run, it’s common to add a final else
part to the if-elif statement like this:
if x == 5:
print("x is equal to 5")
elif x == 10:
print("x is equal to 10")
elif x == 15:
print("x is equal to 15")
elif x == 20:
print("x is equal to 20")
else:
print("No Boolean value was true")
extends Sprite2D
func _ready():
var x = 15
if x == 5:
print("x is equal to 5")
elif x == 10:
print("x is equal to 10")
120
Chapter 6 Branching Statements
elif x == 15:
print("x is equal to 15")
elif x == 20:
print("x is equal to 20")
extends Sprite2D
func _ready():
var x = 7
if x == 5:
print("x is equal to 5")
elif x == 10:
print("x is equal to 10")
elif x == 15:
print("x is equal to 15")
elif x == 20:
print("x is equal to 20")
else:
print("No Boolean value was true")
121
Chapter 6 Branching Statements
if x == 5:
print("x is equal to 5")
elif x == 10:
print("x is equal to 10")
elif x == 15:
print("x is equal to 15")
elif x == 20:
print("x is equal to 20")
122
Chapter 6 Branching Statements
else:
print("No Boolean value was true")
match x:
5: print("x is equal to 5")
10: print("x is equal to 10")
15: print("x is equal to 15")
20: print("x is equal to 20")
_: print("No Boolean value was true")
extends Sprite2D
func _ready():
var x = 15
123
Chapter 6 Branching Statements
if x == 5:
print("x is equal to 5")
elif x == 10:
print("x is equal to 10")
elif x == 15:
print("x is equal to 15")
elif x == 20:
print("x is equal to 20")
else:
print("No Boolean value was true")
match x:
5: print("x is equal to 5")
10: print("x is equal to 10")
15: print("x is equal to 15")
20: print("x is equal to 20")
_: print("No Boolean value was true")
124
Chapter 6 Branching Statements
match x:
1, 3, 5, 7, 9: print("Odd number")
0, 2, 4, 6, 8: print("Even number")
_: print("Less than 0 or greater than 10")
If the number is less than 0 or greater than 10, the default (underscore)
part of the match statement will run, which will print “Less than 0 or
greater than 10.” In case you want to get the exact value that did not match
any of the earlier values, you can create a new variable like this:
match x:
1, 3, 5, 7, 9: print("Odd number")
0, 2, 4, 6, 8: print("Even number")
var new_variable: print("The value = ", new_variable)
extends Sprite2D
func _ready():
var x = 94
125
Chapter 6 Branching Statements
match x:
1, 3, 5, 7, 9: print("Odd number")
0, 2, 4, 6, 8: print("Even number")
var new_variable: print("The value = ",
new_variable)
126
Chapter 6 Branching Statements
127
Chapter 6 Branching Statements
128
Chapter 6 Branching Statements
extends Node2D
func _process(delta):
var window_size = DisplayServer.window_get_size()
if $Sprite2D.position.y > window_size.y / 2:
$RichTextLabel.clear()
$RichTextLabel.append_text("In bottom half")
else:
$RichTextLabel.clear()
$RichTextLabel.append_text("In top half")
func _on_button_pressed():
var window_size = DisplayServer.window_get_size()
randomize()
$Sprite2D.position.x = randi() % window_size.x
$Sprite2D.position.y = randi() % window_size.y
This exercise lets you see how different values can change a Boolean
value. Based on whether a Boolean value is true or false, an if-else
statement can decide to run one set of commands or an alternate set of
commands. In addition, you also learned how to display text on the user
interface by using a RichTextLabel node.
Summary
To make decisions, programs need to use Boolean data types that can hold
either a true or false value. One way to create a true or false value is by
using comparison operators to compare two values such as a variable with
a value or two variables. Depending on the value stored in the variable, the
value of the comparison operator will either be true or false.
The most common comparison operators are < (less than), > (greater
than), == (equal to), != (not equal to), <= (less than or equal to), and >=
greater than or equal to).
Another way to calculate a Boolean value is to use logical operators
that combine two Boolean values to calculate a single Boolean value. The
three logical operators are
• and
• or
• not
The “and” operator evaluates to true only if both Boolean values are
true such as x > 0 and x < 10 where x is 5. That means 5 > 0 is true and 5 < 10
is also true.
The “or” operator evaluates to false only if both Boolean values are
false such as x > 0 or x >= 80 where x is -7. That means -7 > 0 is false and -7
>= 10 is also true.
The “not” operator simply turns a false value to true and a true value
to false.
130
Chapter 6 Branching Statements
• if
• if-else
• if-elif
• match
The if statement checks a Boolean value, and if it’s true, then it runs
commands. If the Boolean value is false, it does nothing.
The if-else checks a Boolean value, and if it’s true, then it runs one set
of commands. If the Boolean value is false, then it runs the second set of
commands.
The if-elif statement checks multiple Boolean values until it finds one
that’s true. If none of these Boolean values are true, the if-elif statement
won’t do anything unless it includes an else part at the end.
The match statement is a shorter way to write an if-elif statement. A
match statement can match a single value or multiple values separated by
a comma.
131
CHAPTER 7
Looping Statements
A loop repeats one or more commands multiple times. When you play
a video game, the entire game represents a loop because as soon as you
finish (or die in the game), the game gives you a chance to play again.
That’s a loop. Within a game, you have smaller loops. Enemies may pop
up and move in a predictable pattern, which represents a loop. Whenever
a random enemy pops up, that is called spawning, and a program can run
a loop to spawn more enemies. Fighting a single enemy represents a loop.
Repeating the same game animation to fight an enemy is a loop. Any time
you have repetitive action, that’s a loop.
Looping statements let you repeat code. That way you can write
smaller programs that are easier to write and understand. In GDScript,
there are two types of loops:
• For loops
• While loops
The main difference between these two loops is that the for loop is
used most often when you know exactly how many times you want a loop
to repeat. On the other hand, a while loop is used most often when the
number of times the loop may repeat can vary, so it’s never a fixed number.
for x in 5:
print(x)
As simple as this for loop might look, there are actually several parts to
understand:
extends Sprite2D
func _ready():
for x in 5:
print(x)
134
Chapter 7 Looping Statements
0
1
2
3
4
for x in range(5):
print(x)
This is equivalent to
for x in 5:
print(x)
135
Chapter 7 Looping Statements
The preceding code starts counting at 5 and stops at the upper limit – 1
(11 – 1 = 10). To see how this for loop works, follow these steps:
extends Sprite2D
func _ready():
for x in range(5, 11):
print(x)
136
Chapter 7 Looping Statements
5
6
7
8
9
10
So far, we’ve created for loops that count from a lower value to a higher
value, incrementing by one. Rather than increment by 1, you can define a
number to increment by such as 2 or 3 like this:
137
Chapter 7 Looping Statements
extends Sprite2D
func _ready():
for x in range(5, 13, 2):
print(x)
5
7
9
11
138
Chapter 7 Looping Statements
extends Sprite2D
func _ready():
for x in range(13, 5, -1):
print(x)
13
12
11
10
9
8
7
6
139
Chapter 7 Looping Statements
for x in "Hello":
print(x)
Since the string “Hello” contains five characters, the preceding for loop
repeats five times. Besides defining how many times a for loop repeats, a
string can also define what value the for loop can retrieve. In the preceding
example, the for loop retrieves and prints each character of the string.
140
Chapter 7 Looping Statements
To see how a for loop can work with a string, follow these steps:
extends Sprite2D
func _ready():
for x in "Hello":
print(x)
H
e
l
l
o
141
Chapter 7 Looping Statements
Besides looping through a string, a for loop can also loop through an
array where the number of items in an array determines how many times
the for loop repeats and the contents of the array determine what a for loop
variable can retrieve like this:
The array contains five strings (["Hello", "Bye", "Cat", "Dog", "Bird"]), so
it repeats five times. Then it prints each item in the array.
To see how a for loop can use an array to define how many times to
repeat, follow these steps:
extends Sprite2D
func _ready():
for x in ["Hello", "Bye", "Cat", "Dog", "Bird"]:
print(x)
142
Chapter 7 Looping Statements
Hello
Bye
Cat
Dog
Bird
A for loop can repeat a fixed number of times or over a range where
that range can be two numeric values, a string, or an array. When you
know exactly how many times a loop should repeat, use a for loop. When
you don’t know how many times a loop should repeat, use a while loop
instead.
143
Chapter 7 Looping Statements
If you don’t create and store an initial value in a variable before the
while loop, the while loop’s Boolean value won’t be able to evaluate to
either true or false.
If you create a Boolean value that never evaluates to true, the while
loop will never run. If this Boolean value never evaluates to false, the while
loop will never stop running, creating an endless loop.
If you never change the variable within the while loop, the Boolean
value can never evaluate to false, creating an endless loop.
To see how the while loop works, follow these steps:
extends Sprite2D
func _ready():
var x = 0 # A variable that will change
within the loop
while x < 5: # A Boolean value that will
eventually be false
144
Chapter 7 Looping Statements
print(x)
x += 1 # A way to change the variable
within the loop
0
1
2
3
4
145
Chapter 7 Looping Statements
for x in 5:
print(x)
var x = 0
while x < 5:
print(x)
x += 1
Both loops count from 0 to 4, but the for loop is shorter and easier to
understand. On the other hand, the while loop is longer and harder to
understand with more ways the loop can work incorrectly. If you fail to
initialize a variable before the while loop, the while loop’s Boolean value
(x < 5) may never be true, so the loop never runs at all. If you fail to change
the “x” variable within the loop (x += 1), then the loop risks never stopping.
To see how to create equivalent for and while loops, follow these steps:
146
Chapter 7 Looping Statements
extends Sprite2D
func _ready():
for x in 5:
print("for loop = ", x)
var y = 0
while y < 5:
print("while loop = ", y)
y += 1
for loop = 0
for loop = 1
for loop = 2
for loop = 3
for loop = 4
while loop = 0
while loop = 1
while loop = 2
while loop = 3
while loop = 4
The while loop is best for repeating until a Boolean value becomes
false. Video games often use a loop to continue playing the game until the
user quits. So a while loop might look like this:
147
Chapter 7 Looping Statements
This while loop runs until the number of games played equals 5. Then
it changes its Boolean value to false, so the while loop eventually ends.
To duplicate this in a for loop, we need to use a “break” command
like this:
This for loop would normally continue looping 100 times, but the if
statement combined with the break command prematurely exits this for
loop after 5 times. Although the for loop may look shorter, there’s one huge
problem.
The while loop can continue looping indefinitely until its Boolean
value changes to false. With the for loop, we must choose an arbitrarily
large value (100) to make the for loop keep repeating. If this arbitrary value
is too low, the for loop could end too soon.
If the for loop controlled a video game, there’s no way of knowing how
many times someone might want to play a video game before stopping. No
matter what arbitrarily large value we define for the for loop, it may not be
high enough. In this case, the while loop does not need an arbitrary value
to define how many times to loop because a while loop repeats endlessly
until someone chooses to quit the game.
To see how the while and for loops compare when stopping when a
Boolean value changes, follow these steps:
148
Chapter 7 Looping Statements
extends Sprite2D
func _ready():
var play_game: bool = true
var games_played = 0
while play_game == true:
games_played += 1
print("While loop game ", games_played)
if games_played == 5:
play_game = false
149
Chapter 7 Looping Statements
When creating loops, choose between the for loop and the while loop.
Both have their advantages and disadvantages, so use the loop that’s best
for your particular needs.
150
Chapter 7 Looping Statements
extends Node2D
var degrees = 0
@export var final_angle = 0
func _process(delta):
while degrees <= final_angle:
$Sprite2D.rotation_degrees = degrees
degrees += 10
151
Chapter 7 Looping Statements
13. Click the Run icon. A dialog box asks for you to
choose a main scene.
15. Click Save. Godot runs your project. Notice that the
image appears rotated at the angle you defined in
the Final Angle property in the Inspector dock.
152
Chapter 7 Looping Statements
The while loop keeps rotating the image until it has rotated the image
beyond the value stored in the final_angle variable. Although we can’t see
the while loop running, we can see the results when the while loop finishes
running when it displays a rotated image in the (DEBUG) window.
Summary
A loop repeats one or more commands multiple times. This can make
programs more compact but also harder to understand. The two types of
available loops are for loops and while loops.
A for loop can run a fixed number of times. A while loop may never
run, but once it starts running, it keeps running until a Boolean value
changes to false.
When creating a for loop, you can define a fixed number to define how
many times to repeat the for loop, a range of values, and an increment
value. A for loop can also use a string or an array to define how many times
it repeats.
When creating a while loop, make sure you define a variable before the
loop and then change that variable somewhere inside the loop to make a
Boolean value change from true to false eventually. Failure to do this could
create an endless loop that freezes or hangs the program and keeps it from
working.
Both a for loop and a while loop can work identically although in most
cases, one loop will be simpler and easier to use than the other. Loops
simply give you a way to repeat code.
153
CHAPTER 8
Understanding Arrays
Every program needs to store data, and that data usually gets stored in one
or more variables. However, the more data a program needs to store, the
more variables you need to create. Rather than create multiple variables,
it’s much easier to use an array.
An array essentially acts like a single variable but with the ability
to store any number of items. A number of items are grouped together,
making arrays handy for storing data in one place that share a common
characteristic. A video game might use one array to store a list of supplies
such as a medical kit, ammunition for a rifle, and food and a second array
to store a list of weapons the player can use.
Another array can even be used to store the characteristics of a player
in an RPG-type game such as hair, torso, legs, etc. You can use an array for
just about anything when it comes to making a game. It just depends on
the type of game that you are making. Arrays are especially useful when
dealing with multiple variables.
Arrays represent the most commonly used way to store data besides
single variables. So it’s important to understand how to create arrays, fill
them with data, and retrieve data from them.
Using Arrays
A variable acts like a single box that can hold exactly one chunk of data.
To access the value in a variable, you just have to use the variable name.
The main limitation of a variable is that it can only hold one chunk of data
at a time.
To create a variable, just define three parts as shown in Figure 8-1:
• Variable name
156
Chapter 8 Understanding Arrays
Where a variable can only hold one chunk of data at a time, arrays can
hold multiple chunks of data in a single variable name. A variable acts like
a single box, but an array acts like a collection of boxes where each box can
hold a different chunk of data as shown in Figure 8-3.
var x = 4
print("The value in x = ", x)
However, to retrieve data stored in an array, you need both the variable
name and its position (also called an index value) within the array. The
leftmost position in an array is index value 0, the second position is index
value 1, and so on as shown in Figure 8-4.
157
Chapter 8 Understanding Arrays
Suppose you needed to track the health of two different characters. You
could create two separate variables like this:
var healthPerson = 40
var healthDog = 15
print(healthArray[0])
print(healthArray)
158
Chapter 8 Understanding Arrays
extends Sprite2D
func _ready():
var name_array = ["Tom", 42, 3.1415, "Pat"]
print("Index 2 = ", name_array[2])
print("Index 0 = ", name_array[0])
print("Index 3 = ", name_array[3])
print("Index 1 = ", name_array[1])
Index 2 = 3.1415
Index 0 = Tom
Index 3 = Pat
Index 1 = 42
159
Chapter 8 Understanding Arrays
To retrieve data from an array, you must specify the array name
followed by an index value enclosed in square brackets. So if you want to
retrieve the first item in an array, you would specify the array name with an
index value of 0 like this:
array_name[0]
0 “Tom”
1 42
2 3.1415
3 “Pat”
If you use the index value of 3, you’ll retrieve the data stored in the
index 3 position, which is “Pat.” However, if you use an index value of 4
or greater, there is no data stored in those index positions in the array, so
trying to retrieve nonexistent data will cause an error.
To see what happens if you try to retrieve nonexistent data in an array,
follow these steps:
160
Chapter 8 Understanding Arrays
extends Sprite2D
func _ready():
var name_array = ["Tom", 42, 3.1415, "Pat"]
print("Index 32 = ", name_array[32])
If you want to create an array but don’t want to define initial values,
you have two options:
• var pet_array = []
Both methods create an empty array. Whether you start with an empty
array or with an array containing one or more items, you can always add
new data to an array at any time. Two ways to add items to an array include
• append
• insert
161
Chapter 8 Understanding Arrays
The append command always adds a new item at the end of an array.
The insert command lets you define the position (index value) where you
want a newly added item to appear. This index value must be an existing
value, which means it already contains data. When you use the insert
command, it moves all existing data to the right as shown in Figure 8-5.
Figure 8-5. The insert command pushes existing data to the right,
increasing their index value by 1
To see how both the append and insert commands can add items to an
array, follow these steps:
extends Sprite2D
func _ready():
var number_array = [14, 23, 8]
number_array.append(75)
print("Append ", number_array)
number_array.insert(2, 61)
print("Insert ", number_array)
162
Chapter 8 Understanding Arrays
Notice that the append command puts 75 at the end (rightmost) side
of the array while the insert(2, 61) commands puts 61 at the third position
(index value 2) and pushes everything else to the right.
When using the insert command, be careful to only specify index
values that exist. If you try to insert an item into an array using an invalid
index number, the program will crash.
• is_empty
• size
• max/min
The is_empty command lets you know if an array is empty. The size
command returns the total number of items in an array. The max and min
commands retrieve the maximum or minimum items stored in an array.
163
Chapter 8 Understanding Arrays
For numeric values, the max command retrieves the largest value, and the
min command retrieves the smallest value.
To see how these commands work with arrays, follow these steps:
extends Sprite2D
func _ready():
var number_array = []
print("Is_empty = ", number_array.is_empty())
number_array.append(40)
print("Is_empty = ", number_array.is_empty())
number_array.append(25)
number_array.append(37)
number_array.append(94)
print(number_array)
print("Maximum value = ", number_array.max())
print("Minimum value = ", number_array.min())
print(number_array)
print("Size = ", number_array.size())
164
Chapter 8 Understanding Arrays
Is_empty = true
Is_empty = false
[40, 25, 37, 94]
Maximum value = 94
Minimum value = 25
[40, 25, 37, 94]
Size = 4
extends Sprite2D
func _ready():
165
Chapter 8 Understanding Arrays
Max = Wilma
Min = Barney
The preceding code creates an array with four strings and then prints
the string stored at index 1, which is “Barney.” One trouble with arrays
is that if you add or delete items from an array, the index values of the
data will constantly change. To make retrieving data from an array easier,
GDScript offers three commands:
166
Chapter 8 Understanding Arrays
• front
• back
• pick_random
The front command retrieves the first item in an array, which is always
at index 0. The back command retrieves the last item in an array. Since an
array can be of any size, trying to specify the index value of the last item
in an array can be dangerous since it will change when you add or delete
items from an array.
The pick_random command simply chooses an item from the array
at random. This can be handy for video games that use an array to store
playing card values or need to create other forms of random activity.
To see how to use these commands, follow these steps:
extends Sprite2D
func _ready():
var name_array = ["Fred", "Barney", "Wilma",
"Betty"]
print("Front = ", name_array.front())
print("Back = ", name_array.back())
print("Pick random = ", name_array.pick_random())
167
Chapter 8 Understanding Arrays
Front = Fred
Back = Betty
Pick random = Barney
Note that if you run the program again, the front (“Fred”) and back
(“Betty”) results will always be the same, but the pick_random command
will likely retrieve a different name.
The front and back commands retrieve an item from an array but do
not remove it from the array. Three other commands not only retrieve an
item from an array but remove it as well:
• pop_at
• pop_back
• pop_front
168
Chapter 8 Understanding Arrays
extends Sprite2D
func _ready():
var name_string = ""
var name_array = ["Fred", "Barney", "Wilma",
"Betty"]
print(name_array)
name_string = name_array.pop_front()
print("Pop front = ", name_string)
print(name_array)
name_string = name_array.pop_back()
print("Pop back = ", name_string)
print(name_array)
name_string = name_array.pop_at(1)
print("Pop at = ", name_string)
print(name_array)
169
Chapter 8 Understanding Arrays
Notice that each time a pop command runs, it retrieves and removes
an item from the array. Thus the array gradually shrinks each time another
pop command runs.
If a pop command tries to retrieve an item from an array that does not
exist, it will only return a <null> value. Change the array in the preceding
code to an empty array [] like this:
Then run the program again. The Output pane will display the
following:
[]
Pop front = <null>
[]
Pop back = <null>
[]
Pop at = <null>
[]
Remember that all pop commands retrieve and remove items from an
array. If you only want to retrieve an item without removing that item from
the array, use the front or back command, or access the name of the array
and an index value such as name_array[2].
Manipulating Arrays
You can store data in any order in an array, so two arrays can contain the
exact same data but arranged in different order. This chaotic way of storing
data can make arrays difficult to use since you never know where data
might be stored at any given time.
To change the order of data in an array, you can use the following
commands:
170
Chapter 8 Understanding Arrays
• Sort
• Reverse
• Shuffle
extends Sprite2D
func _ready():
var name_array = ["Fred", "Barney", "Wilma",
"Betty"]
name_array.sort()
print(name_array)
name_array.reverse()
print(name_array)
name_array.shuffle()
print(name_array)
171
Chapter 8 Understanding Arrays
number_array.reverse()
print(number_array)
number_array.shuffle()
print(number_array)
172
Chapter 8 Understanding Arrays
• bsearch
extends Sprite2D
func _ready():
var index_value = 0
var name_array = ["Fred", "Barney", "Wilma",
"Betty", "Daphne", "Shaggy"]
name_array.sort()
print(name_array)
if name_array.has("Shaggy"):
173
Chapter 8 Understanding Arrays
index_value = name_array.bsearch("Shaggy")
print("Shaggy is in the array at index = ",
index_value)
else:
print("Shaggy is not in the array")
if name_array.has("Melvin"):
index_value = name_array.bsearch("Melvin")
print("Melvin is in the array at index = ",
index_value)
else:
print("Melvin is not in the array")
The sort command first sorts the array. Then the first if statement uses
the “has” command to see if “Shaggy” is in the array. If so, then it uses
the bsearch command to find the index value of “Shaggy,” which is 4. The
second if statement also uses the ”has” command to see if “Melvin” is in
the array. Since “Melvin” isn’t stored in the array, the else part of the if-else
statement runs and prints “Melvin is not in the array.”
174
Chapter 8 Understanding Arrays
• clear
• erase
• remove_at
extends Sprite2D
func _ready():
var name_array = ["Fred", "Barney", "Wilma",
"Betty", "Daphne", "Shaggy"]
name_array.erase("Betty")
175
Chapter 8 Understanding Arrays
print(name_array)
name_array.remove_at(1)
print(name_array)
name_array.clear()
print(name_array)
176
Chapter 8 Understanding Arrays
177
Chapter 8 Understanding Arrays
178
Chapter 8 Understanding Arrays
extends Node2D
var array_position = []
func _on_button_pressed():
var window_size = DisplayServer.window_get_size()
var format_string = "(%d, %d)"
randomize()
$Sprite2D.position.x = randi() % window_size.x
$Sprite2D.position.y = randi() % window_size.y
var actual_string = format_string % [$Sprite2D.
position.x, $Sprite2D.position.y]
$Sprite2D/Label.text = actual_string
array_position.append(actual_string)
$ArrayLabel.text = (array_join(array_position))
179
Chapter 8 Understanding Arrays
17. Click the Run icon. A dialog box appears, asking you
to select a main scene.
180
Chapter 8 Understanding Arrays
Figure 8-8. The child node Label underneath Sprite2D displays the
current location of the image
Summary
Arrays let you store multiple items in a single variable name. The number
of items an array can hold can constantly expand or shrink. When storing
items in an array, you can identify that data by the array’s name and the
index value of that item where the first item in an array is stored at index 0,
the second at index 1, and so on.
Each time you add, delete, or rearrange items in an array, the index
values of data may change. Normally you can retrieve data by specifying
the array name and an index value, but you can also use the various pop
commands (pop_at, pop_front, and pop_back) to retrieve and remove data
from an array at the same time.
181
Chapter 8 Understanding Arrays
182
CHAPTER 9
Understanding
Dictionaries
Every program needs to store data and that data usually gets stored in one
or more variables. However, the more data a program needs to store, the
more variables you need to create. Even worse, multiple variables make it
hard to know which variables might be related to one another. This is why
it is important to declare variables early on and to make your program as
simple as possible.
Two ways to avoid using multiple variables are to use an array or a
dictionary. The main drawback of an array is that data can be stored in any
order, making it difficult to find and retrieve specific data.
A dictionary overcomes this drawback of arrays by using keys. Like
an array, dictionaries also store data in any order, but dictionaries always
store data with a key, known as a key-value pair. The value is the data you
want to store, and the key represents a way to identify the data so you can
retrieve it later.
Because you can always retrieve data using a key, the unordered state of
the dictionary doesn’t matter. Thus dictionaries are handy for retrieving data
quickly that’s not as simple to do with arrays. Two arrays can contain the
same data, but the index used to retrieve identical data can greatly differ. On
the other hand, the order that you store data in a dictionary doesn’t matter.
As long as you know the key associated with the data you want to retrieve,
you’ll always be able to retrieve that data from a dictionary at any time.
Creating Dictionaries
One advantage of a dictionary is that it can store data that can be related to
each other. Suppose a program needs to store someone’s name and phone
number. A phone number by itself means nothing without a name, but if
you store this data in two separate variables, it’s not clear they’re related to
each other like this:
• Dictionary name
• Curly brackets to enclose one or more key-value
pairs of data
184
Chapter 9 Understanding Dictionaries
“Frank” : “555-1234”
3.1415 : “Pi”
In this example, the key is 3.1415 and the value is “Pi.” When creating
a dictionary, you need to define a name for the dictionary and set it equal
to one or more key-value pairs enclosed within curly brackets, where each
key-value pair is separated by a comma like this:
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
"Amount" : 12.25
}
185
Chapter 9 Understanding Dictionaries
extends Sprite2D
func _ready():
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
"Amount" : 12.25
}
print(contacts)
This code creates a dictionary, stores three key-value pairs, and then
prints out the whole dictionary.
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
186
Chapter 9 Understanding Dictionaries
"Amount" : 12.25
}
In each key-value pair, the key comes first, followed by a colon and
its associated value. So to retrieve “555-1234,” you could reference the
dictionary name and the key linked to the data you want like this:
contacts["Frank"]
Rather than retrieve individual data from a dictionary, you can also
use a for loop to retrieve everything stored in a dictionary. Such a for loop
defines a variable that will retrieve each key-value pair from a dictionary.
Then the number of key-value pairs automatically defines how many times
the for loop repeats.
Inside the for loop, you can access each individual key-value pair by
specifying the dictionary name followed by the key inside square brackets
such as follows:
for x in contacts:
print(contacts[x])
extends Sprite2D
func _ready():
var contacts = {
"Frank" : "555-1234",
187
Chapter 9 Understanding Dictionaries
3.1415 : "Pi",
"Amount" : 12.25
}
print(contacts["Frank"])
print(contacts[3.1415])
print(contacts["Amount"])
555-1234
Pi
12.25
Now using a for loop
555-1234
Pi
12.25
The first three values appear by retrieving the data from the dictionary
using the key of each value. The second three values appear using a
for loop.
188
Chapter 9 Understanding Dictionaries
• is_empty
• size
• keys
• values
extends Sprite2D
func _ready():
var key_array = Array()
var value_array = []
189
Chapter 9 Understanding Dictionaries
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
"Amount" : 12.25
}
print(contacts)
if contacts.is_empty() == true:
print("Empty dictionary")
else:
print("Number of key-value pairs = ",
contacts.size())
key_array = contacts.keys()
value_array = contacts.values()
print(key_array)
print(value_array)
This code creates two empty arrays using two different methods:
Array() and []. Both methods are equivalent. After creating two empty
arrays, the code then creates a dictionary and stores three key-value pairs,
which prints out to display all three key-value pairs in the dictionary.
190
Chapter 9 Understanding Dictionaries
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
"Amount" : 12.25
}
If we wanted to change the value 12.25, we notice it’s linked to the key
“Amount.” Therefore, we just need to assign the dictionary name (contacts)
and the “Amount” key with new data like this:
contacts["Amount"] = 987
Since the “Amount” key originally contains the value 12.25, this value
gets replaced by 987. Now the “Amount” key is linked to the value 987. By
assigning new data to an existing key, you can replace data with new data
much like storing new data in a single variable.
When you want to delete data stored in a dictionary, you need to
use the key. If you don’t know the key but only know the data you want
to delete, you can use the data to find the key by using the find_key()
191
Chapter 9 Understanding Dictionaries
command. Once you know the key linked to the data you want to delete,
you can then use the erase command to delete the key and its linked data
from a dictionary.
To see how to change and delete data in a dictionary, follow
these steps:
extends Sprite2D
func _ready():
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
"Amount" : 12.25
}
print(contacts)
contacts["Amount"] = 987
print(contacts)
contacts.erase("Amount")
print(contacts)
192
Chapter 9 Understanding Dictionaries
extends Sprite2D
func _ready():
193
Chapter 9 Understanding Dictionaries
var contacts = {
"Frank" : "555-1234",
3.1415 : "Pi",
"Amount" : 12.25
}
print(contacts)
contacts.clear()
print(contacts)
The first print command shows the entire contents of the dictionary.
Then the clear command runs before the second print command. Since
the clear command deleted everything out of the dictionary, the second
print command reveals the dictionary is completely empty.
194
Chapter 9 Understanding Dictionaries
195
Chapter 9 Understanding Dictionaries
extends Node2D
var window_size = DisplayServer.window_get_size()
var x_dictionary = {
"Left" : 0,
"Middle" : window_size.x / 2,
"Right" : window_size.x
}
var y_dictionary = {
"Top" : 0,
"Middle" : window_size.y / 2,
"Bottom" : window_size.y
}
func _on_button_pressed():
var random_x = 0
var random_y = 0
var direction_x = ""
var direction_y = ""
randomize()
196
Chapter 9 Understanding Dictionaries
13. Click the Run icon. A dialog box appears, asking you
to select a main scene.
197
Chapter 9 Understanding Dictionaries
Summary
Dictionaries let you link data with a unique key in a key-value pair. By
using this key, you can retrieve the data you want. Unlike an array that
uses an index value to identify data, dictionaries use a key to identify data.
Therefore, key-value pairs can be more useful than arrays at times. Arrays
are sometimes nice for grouping variables together, but they have no
relation to each other. The main drawback is that the index value of data in
an array can constantly change as you add or delete items from that array.
On the other hand, data is always linked to a unique key in a dictionary.
To create a dictionary, define a dictionary name and store one or more
key-value pairs within curly brackets. Make sure each key-value pair is
separated by a colon and each key-value pair is separated from the other
key-value pairs by a comma (except for the last key-value pair).
The keys and the data can be of any data type such as integers, floating-
point numbers, or strings. The only restriction is that every key must
be unique.
Dictionaries give you another way to store data beyond arrays or
individual variables. While not as commonly used as arrays, the unique
key-value link makes dictionaries useful for storing related data together.
198
CHAPTER 10
Functions
You can attach a script to any item in Godot to make it interactive. To avoid
creating one massive script of code, it’s better to divide a large program
into smaller ones that work together. Such small programs that make up a
larger program are called functions.
Ideally, a function should perform one task and take up no more than
one page or screen. By keeping a function small, it’s easy to understand
how it works. Large amounts of code can be difficult to search to find
errors. Therefore it’s best to make code as small and simple as possible.
By making a function perform a single task, it makes it easy to know
what a function is supposed to do, so you know whether it’s working
correctly or not.
In the old days, computer programs were often simple and small
enough to understand. As programs got larger and more complicated,
understanding how an entire program worked became difficult. That’s why
programmers started dividing large programs into collections of smaller
ones called functions.
Functions act like building blocks. Each function should be as
independent as possible from the rest of a program. That way you can
modify a function without accidentally affecting any other part of a
program. Once a function proves it works, you can reuse it in another
project. Ultimately, this lets you create a library of proven functions that
you can reuse and create other programs faster.
Understanding Functions
You can create as many functions as you need, but Godot provides several
functions for every script you create. You can also access a function to a
node within a Godot project by selecting the node and then searching for
the function under the “Nodes” category under the Inspector. Some of
these built-in functions are
• _init()
• _ready()
• _process(delta)
All of these functions already exist, so you just need to add your own
custom code to make them work. The _init() and _ready() functions
automatically run every time a script starts running. The _init() function
runs first and is often used to load data.
The _ready() function runs second and starts only when the node that
its script is attached to has completely loaded.
The _process(delta) function runs continuously to respond to user
input such as pressing a key or clicking the mouse.
To see how these three built-in functions work, follow these steps:
extends Sprite2D
func _ready():
print("Ready function here")
200
Chapter 10 Functions
func _init():
print("Init function here")
extends Sprite2D
func _ready():
print("Ready function here")
func _init():
print("Init function here")
func _process(delta):
print("Process(delta) function here")
201
Chapter 10 Functions
• func keyword
• A function name
202
Chapter 10 Functions
a function when creating a game for Godot. Just make sure that your
functions are relative to the game so that it is easy to track what each
function does for the game to work. When you name your own functions,
it’s more common to omit an underscore character at the beginning of the
function name.
The parameter list is enclosed in parentheses and identifies any data
the function expects to receive. In both the _init() and _ready() functions,
this parameter list is empty, which means these functions can work
without receiving any outside data.
The _process(delta) function runs continuously, but it runs based on
frames displayed on the screen, which appear at slightly irregular intervals,
which is measured in seconds by the “delta” parameter.
To see how this delta value constantly changes each time the _process
function runs, follow these steps:
extends Sprite2D
func _process(delta):
print("Delta = ", delta)
203
Chapter 10 Functions
Delta = 0.01666666666667
Delta = 0.01111111111111
Delta = 0.00277555555556
Delta = 0.00833333333333
Delta = 0.00833333333333
Delta = 0.00833333333333
The actual values for delta will likely be different on your computer,
but you should see slight variations that show how the _process() function
runs continuously but at slightly irregular intervals.
Creating Functions
To create your own function, you must define a unique function name,
an optional parameter list, and code within the function to make it do
something. The simplest function has an empty parameter list and one
line of code such as follows:
func my_function():
print("My function running now")
Godot’s built-in functions run when certain events occur, but functions
that you create won’t run until they’re specifically called by name. To call
or run a function, you must specify the function name followed by its
parameter list like this:
my_function()
204
Chapter 10 Functions
Any code stored in that function now runs. Without functions, you
would have to type code throughout your program in multiple locations. If
you later needed to change that code to fix a problem or add new features,
you would have to modify it everywhere you used it in your program.
Any time you duplicate code that performs identical tasks, you risk
omitting one copy of the code that needs to be fixed. Over time, you could
wind up with several different versions of the same code.
By storing frequently used code in a function, you create a single copy
of the code. Now if you need to fix or modify that code, you change it in one
place, and those changes automatically appear throughout your program
wherever that function might be used. This saves time to correct a code in
one place rather than in multiple locations.
To see how to create and call a function, follow these steps:
extends Sprite2D
func my_function():
print("My function running now")
func _init():
print("Init function")
my_function()
func _ready():
print("Ready function")
my_function()
205
Chapter 10 Functions
Init function
My function running now
Ready function
My function running now
In the preceding code, the init() function runs first and runs two lines
of code. The first line of code prints “Init function.” Then the second line of
code calls my_function(). Now in the code within my_function(), the code
prints “My function running now.”
Then the ready() function runs its two lines of code. The first line of
code prints “Ready function.” Then the second line of code also calls my_
function(), which prints “My function running now.”
Notice that my_function() does the exact same thing each time it runs,
which is to print “My function running now.” In most cases, you want a
function to run slightly differently each time, and to do that, you need to
accept parameters.
func my_function(new_data):
206
Chapter 10 Functions
Each variable name within the parameter list can contain one chunk of
data. The preceding parameter list displays a variable (new_data). To call
this function and pass data, you have to use the function name and include
data to send to the function, listed within parentheses, like this:
my_function("Passed data")
This example passes a string (“Passed data”) to the function, but you
could also pass an integer or a floating-point number as well. If you want
to limit the parameter variable to a specific data type, you could do the
following:
The preceding code specifies that any data passed to the function must
be a String data type. If you wanted, you could also define an int or float
data type instead. Rather than pass one chunk of data, you can pass two
or more chunks of data by defining two or more variables in a function’s
parameter list like this:
To call this function, you would use the function name (another_
function) and pass in two integers like this:
another_function(-24, 95)
207
Chapter 10 Functions
extends Sprite2D
func my_function(new_data: String):
print("Called from this function = ", new_data)
func _init():
my_function("Init")
another_function(-24, 95)
func _ready():
my_function("Ready")
another_function(74, -827)
208
Chapter 10 Functions
extends Sprite2D
func big_function(my_name: String, age: int,
weight: float):
209
Chapter 10 Functions
func _init():
big_function("Randy", 38, 134.5)
func _ready():
big_function("Sally", 25, 124.8)
210
Chapter 10 Functions
Figure 10-1. Passing the wrong data type to a function will cause
an error
Optional Parameters
When functions define one or more parameters, calling that function must
provide the exact number of values in the right order. If a function expects
two different parameters, every function call must pass exactly two values.
Suppose you had a function like this:
To call this function, you must include an integer and a string inside a
parameter list like this:
my_function(34, "Hello")
211
Chapter 10 Functions
Since this function expects two parameters (an integer and a string),
it would not work if you only called the function with one value (either
an integer or a string), three or more values, or even two values but in the
wrong order (the integer must be first and the string must be second).
If you want the option of calling a function without specifying the exact
number of parameters, you can use something called optional parameters.
An optional parameter defines a default value for a parameter. That way
you can either call the function using all parameters or only the non-
optional parameters.
To define an optional parameter, set a default value to that parameter
like this:
• my_function(25, “Hello”)
• my_function(25)
The first method calls the function and passes it two parameters (25
and “Hello”). The second method calls the function but passes it only one
parameter (25). Since the second parameter is missing, the function will
use its default value (“Bye”).
When defining optional parameters, the optional parameters must
be last, so the following is invalid because a non-optional parameter
appears last:
You can also have two or more optional parameters as well such as
follows:
212
Chapter 10 Functions
The general rule is that when you use an optional parameter, all
parameters afterward must also be optional parameters.
To see how to use optional parameters, follow these steps:
extends Sprite2D
func _init():
my_function(15, "Hello")
Integer = 15
String = Hello
Because the function call includes two parameters (15 and “Hello”),
the function uses both parameters. Edit the function call as follows and
rerun the program:
my_function(-99)
213
Chapter 10 Functions
Notice that this function call omits the second parameter. As a result,
the function uses its default value for that second parameter, which is
“Default.” The result in the Output pane is now:
Integer = -99
String = Default
To call this function with two optional parameters, you have three
options:
The preceding three function calls are the only valid options for calling
a function with two optional parameters. If you do not pass a value to the
first parameter, you cannot pass values to any other parameters as well.
That’s because the function won’t know which default value to use for its
optional parameters. That’s why the following function call is invalid:
my_function("Frank")
214
Chapter 10 Functions
sqrt(x)
When you want to calculate the square root of a number, you can just
pass that number into the sqrt function. After the sqrt function calculates
the result, it stores that result in the function name, which represents that
single value. So if we wanted to calculate the square root of 9, we could call
the sqrt function and pass it a parameter of 9 like this:
sqrt(9)
print(sqrt(9))
215
Chapter 10 Functions
If you want a function to return a value, you must place the “return”
keyword on the last line in the function, followed by the data you want to
return. So if you wanted a function to return a string, the entire function
might look like this:
extends Sprite2D
func greeting(my_name: String, income: float):
const tax_bracket = 0.25
var tax_owed : float
tax_owed = income * tax_bracket
return "Hello, " + my_name + ". You owe " +
str(tax_owed) + " in taxes."
func _init():
print(greeting("Oliver", 125000))
func _ready():
print(greeting("Elsa", 79000))
216
Chapter 10 Functions
When a function returns a value, you can call that function by treating
it as if it were a single value. When a function does not return a value, you
can call that function as if it were a command.
217
Chapter 10 Functions
218
Chapter 10 Functions
219
Chapter 10 Functions
extends Node2D
func _on_button_pressed():
$Sprite2D.rotation = 0
$Label.text = ""
func _on_color_rect_mouse_entered():
$Label.text = "Mouse entered the color rectangle"
rotate_me(20)
func _on_color_rect_mouse_exited():
$Label.text = "Mouse exited the color rectangle"
rotate_me(-40)
220
Chapter 10 Functions
16. Click the Run icon. A dialog box appears, asking you
to select a main scene.
21. Click the Reset Button. The Label clears all text and
the Sprite2D straightens itself.
22. Click the close icon in the (DEBUG) window.
221
Chapter 10 Functions
Summary
A function lets you divide a large program into multiple smaller programs
like building blocks. Ideally, a function should be as completely
independent from the rest of the program as possible. That way it will be
easy to modify in the future without worrying if any changes might affect
the way another part of a program works.
A function consists of a name, a parameter list, and one or more lines
of code that do something. The simplest function has an empty parameter
list, so the function does the same thing over and over again. A more
flexible function accepts one or more parameters that can be defined to
hold only certain data types such as strings, integers, or floating-point
numbers.
Godot comes with built-in functions that run automatically, but when
you define your own functions, you’ll need to call them by name and pass
any values to their parameter list to make these functions run. A function
can run and complete a task, or a function can run and return a value by
using the “return” keyword on the last line of the function.
Ultimately, functions let you reuse and isolate code to help you write
more reliable programs.
222
CHAPTER 11
Object-Oriented
Programming
The main idea behind object-oriented programming is to help write more
reliable software. Smaller programs are easier to write and understand
than larger programs, which is why programmers divide large programs
into multiple, smaller functions. Every program consists of data to
manipulate and algorithms that provide step-by-step instructions for
manipulating that data.
One problem with functions is that they isolate algorithms, but each
function can potentially access data used by other functions. This can
make programs less reliable because you never know when data might
change. To fix this problem, computer scientists have created object-
oriented programming.
The idea behind object-oriented programming is to isolate both data
and the algorithms that manipulate them into self-contained, isolated
objects. Where functions isolated algorithms, objects isolate algorithms
and the data those algorithms directly affect.
Because data and the algorithms that manipulate them are stored
together, it’s easy to see which code might change data. Grouping together
data and the algorithms that change them is called encapsulation, which
represents one key advantage of object-oriented programming.
Creating a Class
The basis of object-oriented programming is classes. A class defines
variables (called properties) and functions (called methods) that are
related. Properties are simply variables defined in a class, and methods are
functions defined in a class.
To create a class, simply use the “class” keyword followed by a
descriptive name that usually begins with an uppercase letter such as
follows:
class GameObjects:
class GameObjects:
var x: int
var y: int
224
Chapter 11 Object-Oriented Programming
class GameObjects:
var x: int
var y: int
func move(x_position: int, y_position: int):
x += x_position + 2
y += y_position
A class by itself acts like a data type, so you need to create a variable to
create an object by specifying a new class like this:
225
Chapter 11 Object-Oriented Programming
extends Sprite2D
class GameObjects:
var x: int
var y: int
func move(x_position: int, y_position: int):
x += x_position + 2
y += y_position
func _init():
var car = GameObjects.new()
car.x = 0
car.y = 0
print("Car x position = ", car.x, " Car y
position = ", car.y)
car.move(3, 1)
print("Car x position = ", car.x, " Car y
position = ", car.y)
The init() function first creates an object using the GameObjects class
and the new() command. The next two lines define the x and y properties
as 0. Then it prints 0 for both the x and y positions of the car.
226
Chapter 11 Object-Oriented Programming
This bird object would have x and y properties that you could access
by referencing the object name followed by the property you want such as
follows:
bird.x = 12
bird.y = 35
Then you could call the move method for that bird object like this:
bird.move(2, 5)
When creating multiple objects from the same class, objects will share
the same properties and methods defined by the class. The object name
(car or bird) helps define the specific property and method you want to
access. So the combination of the object name (which should always be
unique) and the property or method name lets you access properties and
methods.
Since the name of the object is always separated from the property or
method name by a period or dot, the appearance of code like car.x or bird.
move(2, 5) is known as “dot-syntax.” Any time you see this dot-syntax in
code, chances are good that you’re looking at objects defined by a class.
227
Chapter 11 Object-Oriented Programming
Initializing Properties
When you create a class, you need to define one or more properties that
can hold specific data types. The simplest way to define properties is to
declare a name and the data type it can hold such as follows:
class GameObjects:
var x: int
var y: int
func _init():
x = -999
y = -111
Each time you create an object from this class, the x and y properties
will always start with these defined initial values (-999 for x and -111 for y).
To see how to assign initial values to properties, follow these steps:
228
Chapter 11 Object-Oriented Programming
extends Sprite2D
class GameObjects:
var x: int
var y: int
func _init():
x = -999
y = -111
func _init():
var car = GameObjects.new()
print("Car x position = ", car.x, " Car y
position = ", car.y)
Defining initial values for each property prevents your code from trying
to access a property that has no value at all, which will crash the program.
However, initial values likely won’t be useful, so you’ll need to take a
second step to assign valid values to each property. Each time you create
another object from the same class, this new object will always start with
the same initial values, which may not be what you want.
229
Chapter 11 Object-Oriented Programming
class GameObjects:
var x: int
var y: int
func _init(x_value, y_value):
x = x_value
y = y_value
This init() constructor method accepts two integer values and assigns
them to the x and y properties when creating an object. To create an object,
you must declare an object using the new command but also pass in values
for the x_value and y_value parameters like this:
230
Chapter 11 Object-Oriented Programming
extends Sprite2D
class GameObjects:
var x: int
var y: int
func _init(x_value, y_value):
x = x_value
y = y_value
func move(x_position: int, y_position: int):
x += x_position + 2
y += y_position
func _init():
var car = GameObjects.new(123, 456)
print("Car x position = ", car.x, " Car y
position = ", car.y)
When defining a class, you have three options as shown in Figure 11-1:
231
Chapter 11 Object-Oriented Programming
Inheriting Classes
A class lets you define common features shared among different items. A
video game might need to define the x and y positions of every item in the
game. However, a tree would be stationary, but a car would be mobile. So
even though both a tree and a car would need to define an x and y position,
a car would need additional code to make it move around.
You could create two separate classes, one for a plant and one for a car,
that define their x and y properties like this:
232
Chapter 11 Object-Oriented Programming
The preceding code tells the computer to copy all code defined in the
Plant class and reuse them in the Car class. Now whatever code is defined
in the Plant class can also be used in the Car class.
To see how to use parameters in a function, follow these steps:
233
Chapter 11 Object-Oriented Programming
extends Sprite2D
class Plant:
var x: int
var y: int
func _init():
x = 55
y = 66
func _init():
var ford = Car.new()
print("Ford x position = ", ford.x)
print("Ford y position - ", ford.y)
Ford x position = 55
Ford y position = 66
Notice that the Plant class defines an x and y property along with an
init() method that sets an initial value of 55 for x and 66 for y. Then the Car
class inherits from the Plant class. Even though the Car class is completely
empty (the “pass” command does nothing), the Car class inherits
everything defined in the Plant class. That means the Car class inherits a
value of 55 for its x property and a value of 66 for its y property.
234
Chapter 11 Object-Oriented Programming
The init() function creates a new object (ford) from the Car class. Then
it prints the x and y properties of the ford object to show that it inherited
the value of 55 for x and the value of 66 for y even though the Car class itself
does nothing. The Car class simply copied everything in the Plant class.
Simply inheriting everything defined in one class isn’t useful since you
might as well just use the class that defines everything. The more common
use for inheritance is to extend (hence the keyword “extends”) a class by
inheriting code from one class but adding new code as well.
Suppose you wanted to define a Car class with an x and y property to
define a location but also a speed property to define how fast it’s going.
A clumsy way would be to create a class that defines an x and y property
like this:
class Plant:
var x: int
var y: int
func _init():
x = 55
y = 66
Then you could copy that class and add a speed property like this:
class Car:
var x: int
var y: int
var speed: int
func _init():
x = 55
y = 66
235
Chapter 11 Object-Oriented Programming
As you can see, this duplicates code. A far better solution is to use
inheritance to define the Car class only with the properties and methods
that are unique to that class such as follows:
Notice that the Car class just adds a new speed property, but it inherits
all the properties and methods defined in the Plant class. By inheriting
code from one class while adding new code, a class can define custom
properties and methods quickly and easily.
To see how inheritance lets you create custom classes, follow
these steps:
extends Sprite2D
class Plant:
var x: int
var y: int
func _init():
x = 55
y = 66
236
Chapter 11 Object-Oriented Programming
func _init():
var ford = Car.new()
ford.speed = 123
print("Ford x position = ", ford.x)
print("Ford y position = ", ford.y)
print("Ford speed = ", ford.speed)
Ford x position = 55
Ford y position = 66
Ford speed = 123
Notice that the Car class defined a custom property (speed) that is
not available in the Plant class. If you create an object from the Plant class
and try to use the speed property, you’ll get an error because the speed
property is not defined in the Plant class.
Polymorphism
Inheritance lets one class virtually copy code from another class while also
adding its own unique code. One problem with inheritance is that it copies
all methods defined in one class. Polymorphism lets you rewrite code in a
method so that way you can use the same method name but replace it with
entirely different code.
237
Chapter 11 Object-Oriented Programming
For example, a video game might create an Animal class that defines
an x and y property along with a move method like this:
class Animal:
var x: int
var y: int
func move(x_position: int, y_position: int,
z_position: int):
x += x_position
y += y_position
Notice that this Bird class inherits the x and y properties from the
Animal class while adding its own z property as well. Also notice that the
move method contains a different code. Polymorphism lets you change
the code within a method, but you must keep the function name and
parameter list exactly the same. That’s why the Animal class defines
a move method with three parameters since that third parameter (z_
position) will be needed for the Bird class.
238
Chapter 11 Object-Oriented Programming
extends Sprite2D
class Animal:
var x: int
var y: int
func move(x_position: int, y_position: int,
z_position: int):
x += x_position
y += y_position
func _init():
var dog = Animal.new()
dog.move(2, 3, 4)
print("Dog x position = ", dog.x, " Dog y
position = ", dog.y)
239
Chapter 11 Object-Oriented Programming
Polymorphism lets you reuse method names and parameter lists while
replacing them with completely different code. That way you don’t get
stuck inheriting methods you don’t need.
240
Chapter 11 Object-Oriented Programming
241
Chapter 11 Object-Oriented Programming
To see how Godot uses objects to create the various nodes used to
define a scene, follow these steps:
Figure 11-2. The list of nodes in the Create New Node dialog box
242
Chapter 11 Object-Oriented Programming
243
Chapter 11 Object-Oriented Programming
244
Chapter 11 Object-Oriented Programming
245
Chapter 11 Object-Oriented Programming
Figure 11-5. Godot creates a new scene file in the FileSystem dock
246
Chapter 11 Object-Oriented Programming
Figure 11-6. Dragging and dropping the Player.tscn file into the
Main.tscn file three times
247
Chapter 11 Object-Oriented Programming
248
Chapter 11 Object-Oriented Programming
249
Chapter 11 Object-Oriented Programming
S
ummary
Object-oriented programming is a way to group data, and the algorithms
that manipulate that data, in one place. The first step to using object-
oriented programming is to create a class that defines one or more
properties (variables) and one or more methods (functions). After creating
a class, the second step is to create an object based on that class.
Three advantages of object-oriented programming include
encapsulation, inheritance, and polymorphism. Encapsulation means that
a class represents a self-contained entity that’s as independent as possible.
This lets you modify a class without affecting the rest of a program.
Inheritance lets one class virtually copy code from another class.
In this way, you can avoid physically duplicating code so only one copy
of code exists. That way you can modify this code, and the changes
automatically affect any other classes.
Polymorphism means that a class can inherit code from another class
but rewrite an inherited method to contain a completely different code.
This prevents a class from inheriting methods that aren’t needed.
Object-oriented programming is just one way to help write software
that’s easy to modify without affecting the rest of a program. In addition,
object-oriented program can make it easy to reuse code without physically
duplicating that code. Reusing tested code makes a program more reliable
and faster to write.
250
CHAPTER 12
Getting Input
from the User
All programs, such as word processors, spreadsheets, and databases, must
accept input from the user. In video games, players commonly control a
game object through the keyboard, mouse, joystick, or touch screen. By
defining how specific keys on the keyboard function, how different buttons
on a mouse or joystick work, and how to detect different types of touch
gestures, you can detect user input for your game’s particular needs.
Godot offers two ways to get input from input devices:
Input.is_key_pressed()
252
Chapter 12 Getting Input from the User
extends Sprite2D
func _process(delta):
var velocity = Vector2.ZERO
253
Chapter 12 Getting Input from the User
7. Click Save.
Detecting the left or right mouse button is just as easy. The GDScript
command for detecting when a mouse button is pressed is as follows:
Input.is_mouse_button_pressed()
254
Chapter 12 Getting Input from the User
extends Sprite2D
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_key_pressed(KEY_RIGHT) or
Input.is_key_pressed(KEY_D):
velocity = Vector2.RIGHT * speed
if Input.is_key_pressed(KEY_LEFT) or
Input.is_key_pressed(KEY_A):
velocity = Vector2.LEFT * speed
if Input.is_key_pressed(KEY_UP) or
Input.is_key_pressed(KEY_W):
velocity = Vector2.UP * speed
if Input.is_key_pressed(KEY_DOWN) or
Input.is_key_pressed(KEY_S):
velocity = Vector2.DOWN * speed
if Input.is_mouse_button_pressed
(MOUSE_BUTTON_RIGHT):
velocity = Vector2.RIGHT * speed
if Input.is_mouse_button_pressed
(MOUSE_BUTTON_LEFT):
velocity = Vector2.LEFT * speed
if Input.is_mouse_button_pressed
(MOUSE_BUTTON_MIDDLE):
velocity = Vector2.DOWN * speed
255
Chapter 12 Getting Input from the User
256
Chapter 12 Getting Input from the User
Figure 12-1. An Input Map can assign multiple input devices to the
same action
Once you’ve defined one or more ways to detect input (such as through
a keyboard, mouse, or joystick), you can assign equivalent inputs to the
same category. Now instead of checking for multiple pressed keys, mouse
buttons, or joystick buttons, your code can just check if the user selected a
specific input event such as move_left. This makes your code easier to read
and understand while also being shorter to write.
To see how to create and use an input map, follow these steps:
258
Chapter 12 Getting Input from the User
7. Click the Add New Action text field and type move_
left. Then click Add. Godot creates a new action
category.
8. Click the Add New Action text field and type move_
right. Then click Add.
9. Click the Add New Action text field and type move_
up. Then click Add.
10. Click the Add New Action text field and type move_
down. Then click Add. The Input Map displays four
categories as shown in Figure 12-3.
Figure 12-3. The Input Map with four empty categories displayed
259
Chapter 12 Getting Input from the User
14. Press the A key and click OK. Godot now assigns
the A key to the move_left event as shown in
Figure 12-5.
260
Chapter 12 Getting Input from the User
Figure 12-5. The Event Configuration dialog box listing two ways to
represent the move_left event
261
Chapter 12 Getting Input from the User
262
Chapter 12 Getting Input from the User
Figure 12-6. The Input Map tab in the Project Settings dialog box
listing two ways to represent each event
extends Sprite2D
var speed = 400
func _process(delta):
var velocity = Vector2.ZERO
263
Chapter 12 Getting Input from the User
if Input.is_action_pressed("move_left"):
velocity = Vector2.LEFT * speed
if Input.is_action_pressed("move_right"):
velocity = Vector2.RIGHT * speed
if Input.is_action_pressed("move_up"):
velocity = Vector2.UP * speed
if Input.is_action_pressed("move_down"):
velocity = Vector2.DOWN * speed
30. Click the Run icon at the top of the window. The
(DEBUG) window appears.
When assigning keys to an event, Godot gives you two choices. First,
you can select a key from the list of available options displayed in the Event
Configuration dialog box. Second, you can press a key. Either method lets
you choose all available keys to detect.
Besides letting you detect keys, the Input Map can also detect mouse
buttons and joystick actions. Any time you want to modify an action,
click the Edit icon (it looks like a pencil). Any time you want to remove
an action, click the Delete icon (it looks like a trash can) as shown in
Figure 12-7.
264
Chapter 12 Getting Input from the User
265
Chapter 12 Getting Input from the User
266
Chapter 12 Getting Input from the User
Figure 12-8. The modifier keys appear at the bottom of the Event
Configuration dialog box
267
Chapter 12 Getting Input from the User
extends Sprite2D
var speed = 400
var spin_speed = 5
func _process(delta):
var velocity = Vector2.ZERO
spin_speed = 0
if Input.is_action_pressed("move_left"):
velocity = Vector2.LEFT * speed
if Input.is_action_pressed("move_right"):
velocity = Vector2.RIGHT * speed
if Input.is_action_pressed("move_up"):
velocity = Vector2.UP * speed
if Input.is_action_pressed("move_down"):
velocity = Vector2.DOWN * speed
position += velocity * delta
if Input.is_action_pressed("rotate_left"):
spin_speed = -5
if Input.is_action_pressed("rotate_right"):
spin_speed = 5
rotation += spin_speed * delta
268
Chapter 12 Getting Input from the User
17. Hold down Shift and press the left arrow key. Notice
that the icon.svg image rotates to the left. The image
appears to roll to the left because the Shift+Left
arrow key rotates it but the Left arrow also moves it
to the left at the same time.
18. Hold down Shift and press the right arrow key.
Notice that the icon.svg image rotates to the right.
You can also use the modifier keys for non-keyboard input devices
such as with the mouse. To see how to use modifier keys with the mouse,
follow these steps:
269
Chapter 12 Getting Input from the User
270
Chapter 12 Getting Input from the User
13. Hold down the Shift key and hold the left mouse
button. The icon.svg image rotates to the left.
14. Hold down the Shift key and hold down the right
mouse button. The icon.svg image rotates to
the right.
271
Chapter 12 Getting Input from the User
Summary
Every video game needs to let the user control objects within the game
through the keyboard, mouse, joystick, or touch screen. You could write
code to detect specific input actions such as when the user presses the K
key or clicks the left mouse button. However, it’s more convenient to create
an Input Map instead.
An Input Map lets you define one or more event categories that
represent a particular type of movement such as up, down, left, or right.
Then within each category, you can detect multiple inputs through
different keys, the mouse, or a joystick.
Besides detecting individual keys, the Input Map can also detect
keystroke combinations through one or more modifiers such as the Shift,
Control, or Alt/Option modifier keys. By combining two or more modifiers
together, you can create keystroke combinations such as Control+Shift+F1
or Shift+Option+Left mouse button.
Once you know how to detect input from the user through a variety of
input devices (keyboard, mouse, joystick), you’ll be able to create a game
that can respond to the user in the way players like best.
272
CHAPTER 13
Shooting Projectiles
Moving is one of the most common tasks in any video game. The second
most common task is shooting a projectile to attack enemies. Even though
a player can move around in the game, it makes the game boring over
time. Therefore making more interactions such as shooting projectiles,
unlocking items, or other options can make a game more interesting.
Firing a projectile involves creating another object, aiming it, and
moving it in the direction it was aimed. Later, shooting a projectile also
means detecting if it collides with anything. It is important to check for
collision, or else shooting a projectile is void. If it hits something, then the
game must make the projectile disappear and respond to the projectile
hitting an object.
First you must create a projectile as a separate scene. This scene
defines the projectile’s appearance and must also include GDScript code
to make it move.
After you’ve created a projectile as a separate scene, the second step
is to link the projectile scene with the scene that defines your player in a
video game such as an airplane, tank, or gun. Once you’ve connected the
projectile scene with your player scene, you’ll need to write GDScript to
create the projectile at a specific location and a direction using a Marker2D
node. Wherever you place this Marker2D node is where your projectile will
appear, such as shooting out of the mouth of a cartoon dragon or out of the
tip of a laser cannon.
• Area2D (parent)
• Sprite2D (child) – Defines the image of the projectile in
the Texture property
274
Chapter 13 Shooting Projectiles
275
Chapter 13 Shooting Projectiles
4. Press Enter.
You can rename any node to give it a more descriptive name. For our
projectile, it’s enough just to change the Area2D name to Bullet, so it’s
easier to see what this scene represents in the game.
276
Chapter 13 Shooting Projectiles
Figure 13-4. A saved scene’s file name appears in the FileSystem dock
277
Chapter 13 Shooting Projectiles
278
Chapter 13 Shooting Projectiles
279
Chapter 13 Shooting Projectiles
280
Chapter 13 Shooting Projectiles
281
Chapter 13 Shooting Projectiles
282
Chapter 13 Shooting Projectiles
To see how to write GDScript code to make the projectile move, follow
these steps:
2. Click the Bullet node in the Scene dock and click the
Attach Script icon. An Attach Node Script dialog box
appears. Notice that the default script name is the
name of the root node (Bullet) followed by the .gd
file extension such as bullet.gd.
extends Area2D
@export var speed = 700
func _process(delta):
position += transform.x * speed * delta
The first line in the preceding code (extends Area2D) simply inherits
all code associated with the Area2D node. One particular property we want
to use from the Area2D node is the position property, which defines the
node’s position on the screen.
The second line (@export var speed = 700) defines a variable called
“speed” and sets its value to 700. The @export keyword in front of “var
speed” means this variable (speed) appears in the Inspector dock. That
means we can change the value of the speed variable either by modifying
the GDScript code or by changing the speed variable within the Inspector
dock as shown in Figure 13-9.
283
Chapter 13 Shooting Projectiles
284
Chapter 13 Shooting Projectiles
Each time you click the Run icon, you’ll see the icon.svg image move
to the right across the top of the (DEBUG) window. This lets you see that
the projectile looks and behaves the way we want. In the next chapter, we’ll
create a player and write GDScript code to make the projectile shoot out of
the player image as it moves and rotates around the screen.
Summary
Many video games create flying projectiles from the player (to hit enemies)
or from enemies (to hit the player). Shooting objects that fly across the
screen is a common mechanic used in nearly every video game, so it’s
important to know how to create a flying projectile.
A projectile consists of a single scene with an Area2D node as its root
node. Attached to this Area2D node are two child nodes: a Sprite2D node
and a CollisionShape2D node. The Sprite2D node lets you choose an
image to represent the projectile. The CollisionShape2D node lets you
define a collision boundary around your projectile.
To make a projectile move, you need to write GDScript code that
changes the position of the projectile along the x or y axis. Once you’ve
defined a projectile and made it move, you’ll be ready to add it to a player
scene. Creating separate scenes, made out of different nodes, and putting
them together to build larger scenes is what makes the Godot game engine
easy to use for creating all types of video games. Once you understand the
key concepts of shooting projectiles at enemies, you can also add health or
a scoreboard. Be as creative as you want when making a game.
285
CHAPTER 14
Adding Projectiles
to a Player
Creating a projectile and making it move is the first step. The second step
is making that projectile appear when and where you want it during a
game. In most cases, you want a projectile to appear where you aim, so
this chapter is about adding a projectile to a player that you can move
and rotate. No matter how you move or rotate the player, you can fire a
projectile from the top of the player image. This chapter focuses on adding
projectiles in case you want to make a simple 2D shooter game.
In Godot, a game consists of multiple scenes made up of nodes. You
can have as many scenes and modes as you want, but it’s best to keep the
project simple. Nodes act like building blocks to create scenes, and scenes
act as much larger building blocks to create more complex scenes. Once
you create a projectile as a scene, you need to create a new scene that
defines a player that you can control.
The most important part about shooting a projectile is defining
where it appears and what direction it will go. To do this, you need to
link a projectile to a special Marker2D node that defines the position and
direction where you want a projectile to appear around a player. In simpler
terms, the Marker2D will help with aiming and shooting at enemies in the
game that you are making.
288
Chapter 14 Adding Projectiles to a Player
The Sprite2D node lets you choose an image for the player. Ideally,
you should create custom images for your projectile, but any image
file will work. More importantly, the Marker2D node must be a child of
Sprite2D. This Marker2D node defines the position and direction where a
projectile will appear and move.
The CollisionShape2D node defines a collision boundary around the
player. This is useful for detecting when the player runs into objects or gets
hit by flying projectiles fired by enemy objects.
To see how to create a player, follow these steps:
289
Chapter 14 Adding Projectiles to a Player
290
Chapter 14 Adding Projectiles to a Player
4. Press Enter.
Figure 14-3. A saved scene’s file name appears in the FileSystem dock
291
Chapter 14 Adding Projectiles to a Player
292
Chapter 14 Adding Projectiles to a Player
293
Chapter 14 Adding Projectiles to a Player
Figure 14-4. Using the Move Mode icon to position a Marker2D node
6. Type -90 and press Enter. This rotates the x axis (red
arrow) to point vertically as shown in Figure 14-5.
This will define the direction that the projectile
will move.
294
Chapter 14 Adding Projectiles to a Player
• Up
• Down
• Left
295
Chapter 14 Adding Projectiles to a Player
• Right
• Rotate left
• Rotate right
• Shoot
Figure 14-6. The Input Map tab in the Project Settings window
4. Click the Add New Action text field, type up, and
press Enter.
5. Click the Add New Action text field, type down, and
press Enter.
6. Click the Add New Action text field, type left, and
press Enter.
7. Click the Add New Action text field, type right, and
press Enter.
296
Chapter 14 Adding Projectiles to a Player
10. Click the Add New Action text field, type shoot, and
press Enter. You should now have defined seven
different actions as shown in Figure 14-7.
297
Chapter 14 Adding Projectiles to a Player
298
Chapter 14 Adding Projectiles to a Player
6. Press the down arrow key and click OK. The Event
Configuration window displays the Down (Physical)
key under the down action.
10. Press the left arrow key and click OK. The Event
Configuration window displays the Left (Physical)
key under the down action.
11. Click the + (Add Event) icon that appears to the right
of the left action. An Event Configuration window
appears (see Figure 14-8).
13. Click the + (Add Event) icon that appears to the right
of the right action. An Event Configuration window
appears (see Figure 14-8).
14. Press the right arrow key and click OK. The Event
Configuration window displays the Right (Physical)
key under the down action.
299
Chapter 14 Adding Projectiles to a Player
15. Click the + (Add Event) icon that appears to the right
of the right action. An Event Configuration window
appears (see Figure 14-8).
These steps let you move the player up/down and left/right using
either the arrow keys or the WASD keys. Now the final steps involve
defining keys to rotate the player left and right and shoot the projectile.
To define keys to rotate left, rotate right, and shoot the projectile, follow
these steps:
300
Chapter 14 Adding Projectiles to a Player
Figure 14-9. Physical keys assigned to every action on the Input Map
301
Chapter 14 Adding Projectiles to a Player
302
Chapter 14 Adding Projectiles to a Player
extends Sprite2D
var spin = 5
func _process(delta):
spin = 0
if Input.is_action_pressed("rotate_left"):
spin = -5
303
Chapter 14 Adding Projectiles to a Player
if Input.is_action_pressed("rotate_right"):
spin = 5
rotation += spin * delta
This code constantly checks if the user has pressed any of the keys
associated with the “rotate_left” or “rotate_right” actions defined in the
Input Map. To test this script out, we need to first make this player.tscn the
main scene.
To define the player.tscn file as the main scene, follow these steps:
304
Chapter 14 Adding Projectiles to a Player
6. Click Close.
To test if you can rotate the player using the comma and period keys,
follow these steps:
305
Chapter 14 Adding Projectiles to a Player
Once you’re able to rotate the icon.svg image left and right, the next
step is to move the icon.svg image up/down and left/right along with
shooting a projectile.
To move the player, follow these steps:
extends CharacterBody2D
const SPEED = 300.0
func _physics_process(delta):
velocity = Vector2.ZERO
if Input.is_action_pressed("left"):
velocity = Vector2.LEFT * SPEED
if Input.is_action_pressed("right"):
velocity = Vector2.RIGHT * SPEED
306
Chapter 14 Adding Projectiles to a Player
if Input.is_action_pressed("up"):
velocity = Vector2.UP * SPEED
if Input.is_action_pressed("down"):
velocity = Vector2.DOWN * SPEED
position += velocity * delta
Firing a Projectile
Now that we can control the player (icon.svg image) by pressing the arrow
keys and the comma/period keys to rotate the image, it’s time to shoot a
projectile. Remember, when we defined different actions in the Input Map,
we also defined a “shoot” action that gets triggered by the space bar.
Firing a projectile involves several steps. The first and most important
step is to link the bullet.tscn within the player.tscn file. This involves
defining a variable using the @export keyword and declaring it to hold a
PackedScene data type like this:
307
Chapter 14 Adding Projectiles to a Player
The @export keyword lets the variable appear within the Inspector
dock where we need to drag and drop the bullet.tscn file. This allows us to
access the bullet.tscn file through the bullet_scene variable.
The second step is to detect when the user presses the space bar to
trigger the “shoot” action using the Input.is_action_just_pressed command
like this:
if Input.is_action_just_pressed("shoot"):
func shoot():
var b = bullet_scene.instantiate()
get_tree().root.add_child(b)
b.transform = $Sprite2D/Marker2D.global_transform
308
Chapter 14 Adding Projectiles to a Player
309
Chapter 14 Adding Projectiles to a Player
Once we’ve linked the bullet.tscn file to the player.tscn through the
Inspector dock, we now need to write GDScript code to detect when the
user presses the space bar to trigger the “shoot” action. We need to write a
function to create the bullet.tscn file.
To write GDScript code to create a projectile from the bullet.tscn file,
follow these steps:
310
Chapter 14 Adding Projectiles to a Player
if Input.is_action_just_pressed("shoot"):
shoot()
5. Add the following code at the end and indent all the
way to the left:
func shoot():
var bullet = bullet_scene.instantiate()
get_tree().root.add_child(bullet)
bullet.transform = $Sprite2D/Marker2D.global_
transform
extends CharacterBody2D
@export var bullet_scene : PackedScene
const SPEED = 300.0
func _physics_process(delta):
velocity = Vector2.ZERO
if Input.is_action_pressed("left"):
velocity = Vector2.LEFT * SPEED
if Input.is_action_pressed("right"):
velocity = Vector2.RIGHT * SPEED
if Input.is_action_pressed("up"):
velocity = Vector2.UP * SPEED
if Input.is_action_pressed("down"):
velocity = Vector2.DOWN * SPEED
position += velocity * delta
if Input.is_action_just_pressed("shoot"):
shoot()
311
Chapter 14 Adding Projectiles to a Player
func shoot():
var bullet = bullet_scene.instantiate()
get_tree().root.add_child(bullet)
bullet.transform = $Sprite2D/Marker2D.global_
transform
10. Press the comma and period keys to rotate the icon.
svg image.
11. Press the space bar. Notice that the icon.svg image
shoots out from the top of the player node no matter
which way it’s rotated.
If you have your own images, add them to the FileSystem dock and
substitute those images for the player and bullet. Depending on the size of
your images, you may need to adjust the collision boundaries around the
player and projectile. Then run your project again to make sure you can
still move, rotate, and shoot projectiles.
312
Chapter 14 Adding Projectiles to a Player
Removing Projectiles
One problem with creating and shooting a projectile is that it still exists
even after it exits off the edge of the game window and can no longer be
seen. Each projectile takes up memory and processing resources, so the
more projectiles created, the greater the load on the computer. Create too
many projectiles, and you risk slowing down the entire game.
The solution is to remove a projectile the moment it exits the game
window. To do this requires several steps:
To see the problem with not removing a projectile after creating it,
even after it’s no longer visible, follow these steps:
313
Chapter 14 Adding Projectiles to a Player
Figure 14-14. The Remote tab lets you view when scenes get created
(instantiated)
314
Chapter 14 Adding Projectiles to a Player
315
Chapter 14 Adding Projectiles to a Player
extends Area2D
func _process(delta):
position += transform.x * speed * delta
func _on_visible_on_screen_notifier_2d_screen_exited():
queue_free()
Summary
Creating a player involves several nodes: a CharacterBody2D parent node,
a Sprite2D node for defining an image to display, and a CollisionShape2D
to define the collision boundaries of the player. To fire projectiles, the
Sprite2D node also needs a child node defined by the Marker2D node.
This Marker2D node defines where a projectile appears and the direction
it travels.
316
Chapter 14 Adding Projectiles to a Player
You must define a projectile in a separate scene and then link it into
the scene that defines the player using GDScript code. To do this, you
need to use the @export keyword to define a variable that can hold a
PackedScene data type like this:
317
CHAPTER 15
Hitting Enemies
with Projectiles
Once you have a player you can control and a way to fire projectiles, the
next step involves hitting enemies with a projectile. This involves creating
an enemy, detecting when a collision occurs between a projectile and an
enemy, and then removing both the projectile and the enemy afterward.
By removing the enemy, the enemy is eliminated in the game.
First, you must create an enemy as a separate scene, which consists of
multiple nodes. One node defines the collision boundaries of the enemy,
while a second node defines the image used to represent the enemy.
Second, you must write GDScript code to detect when the projectile
hits an enemy and what to do when it detects a collision between a
projectile and an enemy. Typically, the game needs to remove the
projectile from the screen after a collision and then determine how much
damage the projectile caused on the enemy. In the simplest case, the
projectile removes the enemy after one hit, but in some games, you might
want to keep track of how many times an enemy gets hit before the game
finally removes it from the screen.
• CharacterBody2D (parent)
320
Chapter 15 Hitting Enemies with Projectiles
321
Chapter 15 Hitting Enemies with Projectiles
4. Press Enter.
322
Chapter 15 Hitting Enemies with Projectiles
323
Chapter 15 Hitting Enemies with Projectiles
324
Chapter 15 Hitting Enemies with Projectiles
325
Chapter 15 Hitting Enemies with Projectiles
326
Chapter 15 Hitting Enemies with Projectiles
327
Chapter 15 Hitting Enemies with Projectiles
Figure 15-5. Adding the player.tscn and enemy.tscn scenes into the
main_scene.tscn
328
Chapter 15 Hitting Enemies with Projectiles
329
Chapter 15 Hitting Enemies with Projectiles
Detecting Collisions
To detect collisions, both the projectile and enemy scenes have a
CollisionShape2D node that defines the boundaries of each object. Once
we’ve defined a CollisionShape2D node on every object that can collide
with the projectile, the next step is to take action when a projectile does
collide with an enemy.
Since our game will display multiple enemies, we need to detect when
a projectile hits any of those enemies. The simplest way to do this is to first
assign the enemy.tscn scene to a specific group name. Then we can use
GDScript code to detect whenever the projectile collides with any object
within a specific group.
To define the enemy.tscn scene in a group, follow these steps:
330
Chapter 15 Hitting Enemies with Projectiles
4. Click the Node tab and then click the Groups tab.
331
Chapter 15 Hitting Enemies with Projectiles
332
Chapter 15 Hitting Enemies with Projectiles
func _on_body_entered(body):
pass # Replace with function body.
333
Chapter 15 Hitting Enemies with Projectiles
extends Area2D
@export var speed = 700
func _process(delta):
position += transform.x * speed * delta
func _on_body_entered(body):
if body.is_in_group("Enemy"):
body.visible = false
hide()
334
Chapter 15 Hitting Enemies with Projectiles
S
ummary
The first step to detecting collisions is to add a CollisionShape2D node to
every scene that might collide with another scene. The CollisionShape2D
node lets you define the physical boundaries that trigger a collision around
an object.
Once you’ve added a Collisionshape2D boundary around an object,
the second step is to include one or more scenes to a group. A group is
simply an arbitrary name that you can define for one or more scenes. Once
you’ve defined a scene as part of a group, you can later detect if a collision
between two scenes includes a scene defined within a particular group.
The third step is to define an _on_body_entered(body) signal that
creates a function within the GDScript file of a scene to detect when it
collides with another scene.
Finally, within the _on_body_entered(body) function, you can define
what action to take when a collision occurs between a specific scene. The
most common action to take includes hiding both objects that collide.
335
CHAPTER 16
Displaying a User
Interface
One of the most important features of any project is the user interface. The
user interface is the visual display of a game. Every user interface serves
two purposes, depending on the needs of the program at the time:
To accept data, a user interface can let the user type in data such as text
or numbers or manipulate user interface controls that represent choices
such as sliders or lists of items. To display information back to the user, the
user interface can display text (either words or numbers) or other visual
images such as a health bar that lists a player’s current strength.
The main purpose of a user interface is to let users control a program
by giving it new data to accept and manipulate. After using this new
data to calculate a different result, the user interface then displays new
information to help the user determine what to do next.
• LineEdit
• TextEdit
The LineEdit control is designed for entering a single line of text, such
as a name. The TextEdit control works more like a text editor or word
processor, letting you type and edit multiple lines of text.
If you just want to display text without the ability to edit text, Godot
offers a Label control. By using a LineEdit or TextEdit to edit text and a
Label to display text, you can create a simple user interface to edit and view
text. You may want to use either LineEdit, TextEdit, or Label in Godot if you
would like to create something like a main menu or to display the player’s
name above a health bar.
To see how to store and display text using the LineEdit and Label
controls, follow these steps:
338
Chapter 16 Displaying a User Interface
339
Chapter 16 Displaying a User Interface
340
Chapter 16 Displaying a User Interface
10. Click the Search text field, type Label, and click
Create. Drag and resize the Label near the bottom
middle of the window as shown in Figure 16-4.
13. Click the Run icon. A dialog box appears, asking you
to choose a scene to display.
341
Chapter 16 Displaying a User Interface
Using Signals
Even though we can type and edit text in the LineEdit control, none of that
text appears in the Label control. Both the LineEdit and Label controls
store data in a text property, so we need to send the data stored in the
LineEdit’s text property into the Label’s text property.
One way to send data from the LineEdit to the Label is through a signal
where a signal detects when something happens within the LineEdit.
In this example, we’re going to use the text_changed signal that detects
whenever the text inside the LineEdit control changes. Each time the
LineEdit text property changes, it will send the contents of the LineEdit
control to the Label control.
A signal detects an event that occurs from a user interface control
(such as LineEdit) and creates a GDScript function to handle that event,
stored in a separate .gd file. That means before we can create a signal, we
must create at least one .gd file to store that Signal function.
To see how to use a signal to run each time the contents of the LineEdit
control changes, follow these steps:
342
Chapter 16 Displaying a User Interface
343
Chapter 16 Displaying a User Interface
Figure 16-6. The list of signals available in the Node tab of the
LineEdit control
344
Chapter 16 Displaying a User Interface
func _on_line_edit_text_changed(new_text):
pass # Replace with function body.
func _on_line_edit_text_changed(new_text):
$Label.text = new_text
345
Chapter 16 Displaying a User Interface
12. Click the LineEdit control and type text. Notice that
as you type, those changes automatically appear in
the Label control.
13. Press the Backspace key. Notice that each time you
delete text, the changed contents of the LineEdit
control also get sent to the Label control.
346
Chapter 16 Displaying a User Interface
347
Chapter 16 Displaying a User Interface
To see how to create and use both a TextEdit and Button control, follow
these steps:
348
Chapter 16 Displaying a User Interface
13. Click the Text property and type Send Text as shown
in Figure 16-10.
349
Chapter 16 Displaying a User Interface
Figure 16-10. The Text property lets you display text on a Button
func _on_button_pressed():
pass # Replace with function body.
func _on_button_pressed():
$Label.text = $TextEdit.text
350
Chapter 16 Displaying a User Interface
20. Click the TextEdit control and type some text. Notice
that unlike the LineEdit control, changes do not
automatically appear in the Label control.
21. Click the Send Text Button. Notice that the contents
of the TextEdit control now appear in the Label
control.
351
Chapter 16 Displaying a User Interface
Figure 16-12. A list of options appears only if the user selects the
Option Button
One problem with the Option Button is that you can’t see the list of
available choices until you first select the Option Button. To get around
this problem, an Item List displays all choices at all times as shown in
Figure 16-13.
352
Chapter 16 Displaying a User Interface
This makes it easy to see all possible choices, but the size of the Item
List expands to display every possible choice. If an Item List contains too
many choices, it will take up too much space.
To see how to use an Option Button, follow these steps:
353
Chapter 16 Displaying a User Interface
354
Chapter 16 Displaying a User Interface
355
Chapter 16 Displaying a User Interface
12. Click the Text property and type some text such as
Tadpole.
func _on_option_button_item_selected(index):
$Label.text = $OptionButton.get_item_text(index)
356
Chapter 16 Displaying a User Interface
357
Chapter 16 Displaying a User Interface
358
Chapter 16 Displaying a User Interface
At this point, we’ve just added choices to appear in the ItemList. Now
we need to detect which option the user selects so it can appear in the
Label control.
To make the ItemList work, follow these steps:
1. Click ItemList in the Scene dock.
359
Chapter 16 Displaying a User Interface
360
Chapter 16 Displaying a User Interface
361
Chapter 16 Displaying a User Interface
362
Chapter 16 Displaying a User Interface
At this point, we’ve created two CheckButtons. If you run this project,
you’ll be able to click each CheckButton to toggle it from an off to an on
state. However, we still need to write GDScript code to make both of these
CheckButtons actually do something.
To see how to write GDScript code to make the CheckButtons work,
follow these steps:
func _on_check_button_toggled(toggled_on):
if toggled_on:
$Label.text = "First check button selected"
else:
$Label.text = ""
363
Chapter 16 Displaying a User Interface
func _on_check_button_2_toggled(toggled_on):
if toggled_on:
$Label.text = "Second check button
selected"
else:
$Label.text = ""
14. Click the first check button. Notice that “First check
button selected” appears in the Label control.
364
Chapter 16 Displaying a User Interface
365
Chapter 16 Displaying a User Interface
func _on_h_slider_value_changed(value):
$Label.text = "The value of the slider = " +
str($HSlider.value)
366
Chapter 16 Displaying a User Interface
13. Drag the slider left and right. Notice that as you drag
the slider, the text in the Label control displays the
current value of the slider.
Summary
A user interface can accept data from the user and display information to
the user. The LineEdit and TextEdit controls can accept text data where the
LineEdit control is meant for short amounts of text and the TextEdit control
is meant for multiple lines of text.
Since users can type anything into a LineEdit or TextEdit control,
Godot offers two ways to offer choices for the user to select. An
OptionButton displays a pull-down menu of items the user can select,
while an ItemList displays all options in a list. The OptionButton takes up
the same amount of space no matter how many options it may contain,
while the ItemList expands in size the more options it displays.
A CheckButton can be toggled or not toggled. By using multiple
CheckButtons, users can select one or more options.
For inputting numeric data, Godot offers a slider. An HSlider appears
horizontally, while a VSlider appears vertically. Both types of sliders let
you define a minimum value and a maximum value. By dragging the
slider, users can define a numeric value that falls within the range of the
minimum and maximum values.
User interface controls rely on GDScript code to make them actually
work. By creating Signals for each user interface control, you can create
functions that respond to specific events that occur.
367
CHAPTER 17
Adding Physics
Physics in a video game lets objects collide, bounce, slide, or ricochet off
each other. In addition, physics lets objects work with gravity that forces
objects to fall down or even fall upward if you reverse gravity as a negative
number. By learning how to add physics to a game, you can create barriers,
platforms, walls, and collisions between different objects.
Physics can make any game more interactive and realistic. Gravity
creates downward (or upward) movement automatically, so there’s no
code needed to define this behavior. Collisions allow obstacles that force
players to avoid and move around to achieve their goals. Physics simply
makes objects on a screen behave as if they were physical objects in the
real world.
represent gravity pushing an object up. The Gravity Scale can range
from -128 to 128 where you can either choose a value by dragging the slider
left or right or by typing in a value.
370
Chapter 17 Adding Physics
10. Click the Add Child Node (+) icon. A Create New
Node window appears.
371
Chapter 17 Adding Physics
17. Click the Run icon at the top of the window. The
(DEBUG) window appears. Notice that the icon.svg
image drops down because of gravity.
372
Chapter 17 Adding Physics
20. Click the Run icon at the top of the window. The
(DEBUG) window appears. Notice that the icon.svg
image now rises up because of negative gravity.
23. Click the Run icon at the top of the window. The
(DEBUG) window appears. Notice that the icon.
svg image now remains stationary because a
Gravity Scale property of 0 means there is no gravity
affecting the object.
When the Gravity Scale is a positive number, objects fall downward like
in the real world. When the Gravity Scale is a negative number, objects fall
upward. This can be useful for creating floating objects such as balloons,
clouds, or birds. Objects falling downward can apply to a ball or items
needed for a game inventory.
Experiment with different values for the Gravity Scale, both positive
and negative numbers, so you can see how it affects the way an object falls
or rises. The greater the Gravity Scale value, the faster the object will move
downward (for large positive numbers) or upward (for small negative
numbers).
You can also define the strength of gravity and its direction as well. By
default, the strength of gravity is defined as 980 that pulls in the positive
y axis direction (down). However, you can change both the strength and
direction of gravity if you wish.
373
Chapter 17 Adding Physics
374
Chapter 17 Adding Physics
7. Click Close.
By changing the default gravity settings, you can create unique ways
individual objects interact within your game. Changing gravity settings
can be perfect for settings in outer space where gravity might be weaker
or different, but in most cases, you’ll probably never need to modify the
default gravity settings.
375
Chapter 17 Adding Physics
Adding Damping
When the Gravity Scale value is extremely high or low for an object, that
object will move much faster up or down. To alter the speed that an object
moves under the effect of gravity, you can change its Damp property. By
default, the Damp property is set to 0, which means there’s no effect on an
object moving under gravity.
The Damp property can vary from -1 to 100. When set to -1, any object
moves rapidly up or down under the influence of gravity. When the Damp
property is a nonzero positive number, the greater the value, the slower an
object will move.
To see how to damp the way an object moves under the influence of
gravity, follow these steps:
376
Chapter 17 Adding Physics
377
Chapter 17 Adding Physics
378
Chapter 17 Adding Physics
379
Chapter 17 Adding Physics
380
Chapter 17 Adding Physics
381
Chapter 17 Adding Physics
20. Click the Run icon. Notice that the top icon.svg
image drops because its Gravity Scale property is 1,
but the second icon.svg image remains stationary
because its Gravity Scale property is 0. The moment
the two icon.svg images collide, the top one keeps
falling, while the second one ricochets away
depending on the angle that they collide.
382
Chapter 17 Adding Physics
383
Chapter 17 Adding Physics
18. Click the Run icon. Notice that the bottom icon.svg
image in the StaticBody2D node remains stationary
even after other objects collide with it.
19. Click the close icon in the (DEBUG) window to
make it go away.
384
Chapter 17 Adding Physics
385
Chapter 17 Adding Physics
8. Click the Select icon on the far left (it looks like an
arrow) or press Q to select it.
386
Chapter 17 Adding Physics
387
Chapter 17 Adding Physics
4. Click the Select icon on the far left (it looks like an
arrow) or press Q to select it.
388
Chapter 17 Adding Physics
389
Chapter 17 Adding Physics
Thus you would want the player object (on Layer 1) to interact with a
treasure object and an enemy, so its mask would include Layers 2 and 3.
On the other hand, an enemy object (on Layer 3) would never interact with
a treasure object, so its mask would include only Layer 1. The Inspector
dock, under the CollisionObject2D category, defines the Layer and Masks
for an object as shown in Figure 17-11.
A node can appear on more than one Layer but often only appears on
a single layer. Nodes can list multiple numbers in the Mask category to
define all the different objects (Layers) it can interact with in a collision.
By default, Godot puts every node on Layer 1 with Mask 1. This means
that every node appears on Layer 1 and can interact with every node that
also appears on Layer 1. Since Godot offers up to 32 Layers and identifying
Layers by number can confusing, you can also give Layers a more
descriptive name such as “Player” or “Walls.”
390
Chapter 17 Adding Physics
Figure 17-12. The RigidBody2D nodes should appear above the two
StaticBody2D nodes
391
Chapter 17 Adding Physics
392
Chapter 17 Adding Physics
393
Chapter 17 Adding Physics
12. Click the Run icon. Notice that the top RigidBody2D
nodes bounce off the StaticBody2D node (on Layer
2) and then land on the second StaticBody2D node
(still on Layer 1). However, the middle RigidBody2D
node falls through the StaticBody2D node (on
Layer 2).
394
Chapter 17 Adding Physics
7. Click Close.
395
Chapter 17 Adding Physics
396
Chapter 17 Adding Physics
5. Click the Name text field and type a name for your
chosen layer.
6. Click Rename.
Restricting Movement
When creating a video game, you may want to restrict the movement of the
player within the boundaries of the game window. Otherwise, the player
could move off the screen and disappear. To restrict the movement of any
object within specific boundaries, Godot offers the clamp function that
looks like this:
Godot assigns the origin (0,0) to the upper left corner of the screen
where a positive x value moves to the right and a positive y value moves
down. Since every computer has different resolutions, you need to identify
the total size of a game window by using the following property:
get_viewport_rect().size
397
Chapter 17 Adding Physics
var screen_size
screen_size = get_viewport_rect().size
398
Chapter 17 Adding Physics
12. Click the Add Child Node (+) icon. A Create New
Node window appears.
399
Chapter 17 Adding Physics
extends CharacterBody2D
func _ready():
screen_size = get_viewport_rect().size
position.x = 400
position.y = 250
func _physics_process(delta):
move_and_slide()
400
Chapter 17 Adding Physics
Experiment with changing the Layer and Mask of different items to see
how this changes the way the different nodes collide. The clampi function
keeps the CharacterBody2D node from exiting the boundaries of the game
window, but part of it can still disappear from sight. Play with different
values in the clampi function to define the minimum and maximum
so no part of the CharacterBody2D node can disappear off the window
boundaries.
401
Chapter 17 Adding Physics
Summary
Gravity is one way to move items in a game. When objects hit one another,
they need to bounce off each other like in real life, which creates greater
realism. In Godot, the RigidBody2D node allows objects to interact with
gravity where gravity is defined within the 2D Physics category in the
Project Settings window.
Gravity typically pulls objects down, but you can define the strength
of gravity to make it stronger or weaker or have it affect objects in other
directions besides down. When objects fall, they collide with other objects
and bounce off them. If you don’t want this behavior, you can adjust a
node’s Layer and Mask.
Nodes can appear on one or more layers. Masks define the layer
that a node can identify. If a node does not recognize a layer and a node
appears on that layer, the two nodes will ignore each other. By using Layers
and Masks, you can define how different nodes can collide (or ignore)
each other.
Godot offers up to 32 Layers and Masks for each node. For your
convenience, you can give descriptive names to specific Layers to make it
easier to understand what nodes on each Layer represent.
Finally, use the clamp function to restrict movement within a game
window. The clamp function defines a minimum and maximum x and y
value where a node can move. By restricting movement, you can ensure
that a game object can’t accidentally disappear off the edge of the screen.
402
CHAPTER 18
Playing Audio
While video games are largely a visual medium, don’t overlook the
importance that sound can play in your project. Sometimes sound is
essential in a video game such as hearing enemies nearby to alert the
player or set the tone of a horror game. One type of sound might be
background noise such as the wind blowing, rain falling, or background
music playing. A second type of sound can give feedback such as when
a player picks up a treasure where a beep confirms that the player
successfully grabbed the treasure.
There can also be spoken dialogue between characters or explanatory
dialogue to help you better understand the game settings. Audio can
be music, sound effects, dialogue, or any noise that helps create greater
immersion into the game.
Although audio might seem unnecessary in a game, play your favorite
game with the sound turned completely off. Even without background
sound effects or music, the lack of sound can detract from a game and
make playing it less enjoyable.
• WAV
• Ogg Vorbis
• MP3
© Wallace Wang, Tonnetta Walcott 2024 403
W. Wang and T. Walcott, Programming for Game Design,
https://doi.org/10.1007/979-8-8688-0190-7_18
Chapter 18 Playing Audio
The WAV file format offers the greatest sound quality but at the
expense of storage space. Because a WAV file does not compress audio
in any way, which can reduce sound quality, WAV files are best when
you need the best sound quality possible. Just be aware that even simple
sounds, stored as a WAV file, can take up a large amount of storage space.
The Ogg Vorbis file format is a completely free and open source
standard for compressing audio into smaller files than WAV files. The main
drawback with compression is that the greater the compression (and the
smaller the audio file), the lower the audio quality.
Although the Ogg Vorbis file format is technically superior to the
MP3 audio compression file format, MP3 files are far more common
and popular. The main drawback with MP3 files is that the compression
standard is a proprietary format. This proprietary format of MP3 audio files
is the reason programmers created the alternative Ogg Vorbis file format.
Both the Ogg Vorbis and MP3 file formats are best for compressing
audio files to take up less space than a WAV file. The drawback is that the
greater the compression, the lower the audio quality.
It’s possible to convert sound from one audio file format to another.
While there are plenty of commercial audio software available for
recording and editing audio, one popular option is Audacity (https://
www.audacityteam.org), which runs on Windows, macOS, and Linux.
Even better, Audacity is completely free and easy to use, so anyone can
record and edit audio in multiple file formats including WAV, Ogg Vorbis,
and MP3.
To see how to play an audio file, follow these steps:
404
Chapter 18 Playing Audio
405
Chapter 18 Playing Audio
9. Drag the audio file from the FileSystem dock into the
Stream property in the Inspector dock. The Stream
property displays the name of your audio file as
shown in Figure 18-2.
406
Chapter 18 Playing Audio
407
Chapter 18 Playing Audio
Try the preceding steps using all three types of audio files (WAV,
Ogg Vorbis, and MP3) so you can see that they work identically. The
main difference is the trade-off between higher quality vs. larger storage
space. For the best audio quality, use WAV files. For large audio files,
use the compressed audio formats Ogg Vorbis or MP3. You may want to
experiment with your particular audio files to see whether they sound
better and compress more using Ogg Vorbis or MP3.
408
Chapter 18 Playing Audio
10. Click the first Button, click the Text property in the
Inspector dock, and type Start.
409
Chapter 18 Playing Audio
15. Click the Node tab in the Inspector dock and click
Signals. A list of different signals appears.
410
Chapter 18 Playing Audio
19. Click the Node tab in the Inspector dock and click
Signals. A list of different signals appears.
extends Node2D
func _on_button_pressed():
$AudioStreamPlayer.play()
func _on_button_2_pressed():
$AudioStreamPlayer.stop()
411
Chapter 18 Playing Audio
Pausing Audio
To start or stop the audio, we can just use the .play() and .stop() methods,
respectively, on the AudioStreamPlayer. However, what if we want to pause
the audio and start playing it again from the location where we last paused,
we need to take additional steps.
First, we need a float variable to store the location where an audio file
has paused. To get this location, we can use the .get_playback_position(),
which returns a decimal number defining the location where the audio
file paused.
After storing the position where the audio paused, we can then play it
back. Instead of using the ordinary .play() method, we need to include the
position in the audio file to start playing from such as .play(paused) where
“paused” represents a decimal value retrieved using the .get_playback_
position() method.
To see how to pause audio, follow these steps:
412
Chapter 18 Playing Audio
413
Chapter 18 Playing Audio
extends Node2D
func _on_button_pressed():
if paused == false:
$AudioStreamPlayer.play()
else:
$AudioStreamPlayer.play(save_position)
paused = false
func _on_button_2_pressed():
$AudioStreamPlayer.stop()
func _on_button_3_pressed():
save_position = $AudioStreamPlayer.get_playback_
position()
paused = true
$AudioStreamPlayer.stop()
12. Click the Start Button. The audio file defined in the
Stream property of the AudioStreamPlayer node
starts playing.
13. Click the Pause Button. The audio file stops playing.
414
Chapter 18 Playing Audio
14. Click the Start Button again. The audio file should
now start playing again where it left off.
To better understand this code, start with the two variables. The
“paused” variable is defined as a Boolean data type and initially set to false.
The “save_position” variable is defined as a float data type and initially has
no value:
func _on_button_3_pressed():
save_position = $AudioStreamPlayer.get_playback_
position()
paused = true
$AudioStreamPlayer.stop()
The code for the Start Button uses an if-else statement to determine
whether to play from the start of the audio file (.play()) or if the “paused”
variable is true to play from the location stored in the “save_position”
variable (.play(save_position)):
func _on_button_pressed():
if paused == false:
$AudioStreamPlayer.play()
415
Chapter 18 Playing Audio
else:
$AudioStreamPlayer.play(save_position)
paused = false
Essentially, pausing audio must let your code know where the audio
paused and if paused, where to start playing the audio again. Notice that
once the audio starts playing from the last paused position (save_position),
the code resets the “paused” variable to false.
For an even simpler solution, we can just use the stream_paused
property, which automatically stores the location of where an audio file
paused. The drawback is that we won’t know the exact location in the
audio file where it paused, but if that’s not important, then the stream_
paused property is much easier and more straightforward to use.
To see how to use the stream_paused property, follow these steps:
extends Node2D
func _on_button_pressed():
$AudioStreamPlayer.play()
func _on_button_2_pressed():
$AudioStreamPlayer.stop()
func _on_button_3_pressed():
$AudioStreamPlayer.stream_paused = not
$AudioStreamPlayer.stream_paused
416
Chapter 18 Playing Audio
Looping Audio
Most of the time, you want an audio file to play once such as the sound of
a gunshot, a lion roaring, or a glass window shattering. However, in some
cases, you want an audio file to keep playing in an endless loop such as
background music or background sound effects such as birds chirping.
The looping of sounds can indicate the type of environment taking place.
To loop audio, you need to follow three steps:
417
Chapter 18 Playing Audio
418
Chapter 18 Playing Audio
419
Chapter 18 Playing Audio
extends Node2D
func _on_audio_stream_player_finished():
$AudioStreamPlayer.play()
17. Click the Run icon. Notice that as soon as your audio
file ends, it repeats again.
420
Chapter 18 Playing Audio
To see how to play audio when detecting a collision, follow these steps:
421
Chapter 18 Playing Audio
18. Drag and drop the audio file into the Stream
property in the Inspector dock.
20. Click Save. Godot saves the scene under the name
player.tscn.
422
Chapter 18 Playing Audio
Now we need to create an obstacle scene so that way when the player
scene collides with the obstacle scene, it plays an audio file:
423
Chapter 18 Playing Audio
18. Click the Node tab in the Inspector dock and then
click Groups.
424
Chapter 18 Playing Audio
21. Click Save. Godot saves the scene under the name
obstacle.tscn.
3. Click the Search text field, type Node, and then click
Create. Godot creates a Node as the parent in the
Scene dock.
425
Chapter 18 Playing Audio
426
Chapter 18 Playing Audio
extends Area2D
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_key_pressed(KEY_RIGHT):
velocity = Vector2.RIGHT * speed
if Input.is_key_pressed(KEY_LEFT):
velocity = Vector2.LEFT * speed
427
Chapter 18 Playing Audio
if Input.is_key_pressed(KEY_UP):
velocity = Vector2.UP * speed
if Input.is_key_pressed(KEY_DOWN):
velocity = Vector2.DOWN * speed
We can now move the player around the screen, so the final step is to
detect a collision between the player and the obstacle. When that collision
occurs, we can then play the audio file stored in the Stream property of the
AudioStreamPlayer2D node.
To detect collisions and play audio, follow these steps:
428
Chapter 18 Playing Audio
func _on_area_entered(area):
if area.is_in_group("obstacle"):
$AudioStreamPlayer2D.play()
429
Chapter 18 Playing Audio
extends Area2D
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_key_pressed(KEY_RIGHT):
velocity = Vector2.RIGHT * speed
if Input.is_key_pressed(KEY_LEFT):
velocity = Vector2.LEFT * speed
if Input.is_key_pressed(KEY_UP):
velocity = Vector2.UP * speed
if Input.is_key_pressed(KEY_DOWN):
velocity = Vector2.DOWN * speed
func _on_area_entered(area):
if area.is_in_group("obstacle"):
$AudioStreamPlayer2D.play()
430
Chapter 18 Playing Audio
Summary
Audio can add background music or sound effects to a game. Audio can
also give feedback such as when players get die or pick up a treasure. The
next time you play a video game, turn off the sound, and then play it with
the sound turned on. You should notice that even if you’re not conscious of
the sound, you suddenly notice it when it’s missing.
The AudioStreamPlayer node can play sound, while the
AudioStreamPlayer2D node is useful for attaching sound to a game object
such as a player or enemy. You can play, pause, and stop audio as well as
change the volume and pitch of audio. By controlling sound through the
Inspector dock and through GDScript code, you can control how audio
works within your game.
431
CHAPTER 19
To do this, you must first create scenes that define the enemies or
obstacles you want to place inside a bigger scene. Once you’ve created an
enemy or obstacle, you need to follow several steps:
To see how to use GDScript code to add objects to a scene, we’ll first
need to create two .tscn scenes. One .tscn scene will represent the objects
to add, and the second .tscn scene will display the different objects on
the screen.
To create these two .tscn scenes, follow these steps:
434
Chapter 19 Creating and Using Scenes
The preceding steps have created a simple scene that we can randomly
place within a video game playing field. Now the next step is to create that
playing field by following these steps:
435
Chapter 19 Creating and Using Scenes
4. Click the Search text field, type Node, and then click
Create. Godot creates a generic Node as the parent
node in the Scene dock.
extends Node
436
Chapter 19 Creating and Using Scenes
437
Chapter 19 Creating and Using Scenes
Figure 19-2. Dragging a .tscn file from the FileSystem dock into the
Inspector dock
extends Node
func _ready():
const total_number = 10
screensize = get_viewport().get_visible_rect().size
for x in total_number:
var new_obstacle = obstacle_scene.instantiate()
add_child(new_obstacle)
new_obstacle.position = Vector2(randi_range(0,
screensize.x), randi_range(0, screensize.y))
438
Chapter 19 Creating and Using Scenes
20. Repeat steps 11 and 12. Notice that each time you
run the project, the exact location of each of the ten
objects changes because the randi_range function
calculates a different random number each time for
both the x and y positions.
439
Chapter 19 Creating and Using Scenes
440
Chapter 19 Creating and Using Scenes
441
Chapter 19 Creating and Using Scenes
442
Chapter 19 Creating and Using Scenes
3. Click the Search text field, type Node, and then click
Create. Godot creates a Node as the parent.
443
Chapter 19 Creating and Using Scenes
444
Chapter 19 Creating and Using Scenes
Any time you change the .tscn file, all instances of that file also change.
However, now we only want to change one instance of the enemy.tscn
file. To change just one instance without changing all of them, we need to
follow several steps:
445
Chapter 19 Creating and Using Scenes
446
Chapter 19 Creating and Using Scenes
447
Chapter 19 Creating and Using Scenes
• apply_torque_impulse(value)
448
Chapter 19 Creating and Using Scenes
At this point, we’ve just created a main scene and set it as the default
scene to run. Now we need to create two scenes. We’ll use the apply_force
function on one scene to make it move across the screen. Then we’ll use
the apply_torque_impulse function on the second scene to make it rotate
in place:
10. Click the Add Child Node (+) icon. A Create New
Node window appears.
449
Chapter 19 Creating and Using Scenes
extends RigidBody2D
func _physics_process(delta):
apply_force(Vector2(x_speed, y_speed))
450
Chapter 19 Creating and Using Scenes
24. Click the Run icon. Notice that the icon.svg image
moves diagonally down to the right.
451
Chapter 19 Creating and Using Scenes
10. Click the Add Child Node (+) icon. A Create New
Node window appears.
452
Chapter 19 Creating and Using Scenes
extends RigidBody2D
func _physics_process(delta):
apply_torque_impulse(rotation_speed)
453
Chapter 19 Creating and Using Scenes
22. Drag and drop the rotate_me.tscn file from the FileSystem
dock to the lower left corner of the main.tscn scene.
24. Click the Run icon. Notice that this newly added
icon.svg image rotates clockwise while the other
icon.svg image moves diagonally down to the right.
454
Chapter 19 Creating and Using Scenes
455
Chapter 19 Creating and Using Scenes
14. Click the Add Child Node (+) icon. A Create New
Node window appears.
15. Click the Search text field, type Area2D, and then
click Create. Godot creates an Area2D node as a
child of Node2D.
19. Click Area2D to select it and then click the Add Child
Node (+) icon. A Create New Node window appears.
456
Chapter 19 Creating and Using Scenes
extends Area2D
func _process(delta):
var velocity = Vector2.ZERO
var speed = 500
if Input.is_key_pressed(KEY_RIGHT):
velocity.x += speed
if Input.is_key_pressed(KEY_LEFT):
velocity.x -= speed
if Input.is_key_pressed(KEY_DOWN):
velocity.y += speed
if Input.is_key_pressed(KEY_UP):
velocity.y -= speed
457
Chapter 19 Creating and Using Scenes
The icon.svg image disappears from off the game window boundary
because we don’t have a camera to follow the player. With a camera, we
can follow the player wherever it moves.
To add a camera, follow these steps:
458
Chapter 19 Creating and Using Scenes
Turning Enabled off simply turns the camera off. The Drag Center
option in the Anchor Mode property keeps the player object in the center
of the game window at all times. The other option for the Anchor Mode
property is Top Left, which keeps the player object in the upper left corner
of the game window at all times. In most cases, you won’t need to change
either the Enabled or Anchor Mode properties.
459
Chapter 19 Creating and Using Scenes
Figure 19-12. A lower Zoom property value shows more of the game
playing field
Summary
Nodes represent building blocks, and a hierarchy of nodes defines a scene.
When creating a game, you’ll likely create separate scenes to represent
different parts of the game such as a player, enemies, obstacles, and the
user interface. By using GDScript code, you can make various objects
move or rotate.
A scene can define a single object such as a player or an obstacle, but
you can combine multiple scenes together to create larger items such as
460
Chapter 19 Creating and Using Scenes
the main game playing field along with enemies and obstacles. The main
idea behind Godot is that nodes offer specific features such as the Sprite2D
node used to define an image and the CollisionShape2D node used to
define the boundaries.
By combining nodes, you can create a scene to define a single object.
Then you can combine multiple scenes to create more complex scenes
such as a game playing field. By dividing a large project into multiple
smaller objects, Godot makes it easy to create a game one piece at a time.
461
CHAPTER 20
Using Signals
No matter what programming language you use, the main goal is to break
a large program into smaller parts known as functions. If the project is not
broken down, then the program will be difficult to follow. At the simplest
level, you can attach a GDScript to a node and divide that code into one
or more functions. Some functions can run automatically when certain
events occur, such as when the game starts. Other functions will only run
when specifically called from other GDScript codes stored in the same file.
Calling functions within the same file is easy since you just have to
specify the function name to run. However, one file may need to call a
function stored in a different file. To call functions stored in another file,
there are two possibilities as shown in Figure 20-1:
464
Chapter 20 Using Signals
6. Click the Search text field, type Label, and then click
Create. Godot creates a Label node as a child of
HUD (Control).
465
Chapter 20 Using Signals
466
Chapter 20 Using Signals
extends Control
func update_score(score):
$Label.text = str(score)
467
Chapter 20 Using Signals
4. Click the Search text field, type Node, and then click
Create. Godot creates Node as the parent node in
the Scene dock.
468
Chapter 20 Using Signals
469
Chapter 20 Using Signals
At this point, we’ve created two separate scenes. Now it’s time to
combine the hud.tscn into the main.tscn and call the update_score
function stored in the hud.gd file:
Figure 20-6. The node hierarchy of the main.tscn scene after adding
the hud.tscn scene
470
Chapter 20 Using Signals
extends Node
func _on_button_pressed():
score += 1
$HUD.update_score(score)
471
Chapter 20 Using Signals
Signaling Up
In the previous example, we saw how to call down to run a function
stored in a node lower in the hierarchy. However, sometimes you may
need to call a function stored in a node higher in the hierarchy. You can’t
simply specify the node name and function name such as $HUD.update_
score(score) because nodes lower in a hierarchy can’t directly access
nodes that are higher in the hierarchy.
Instead, we have to use signals. First, we need to define the function
name we want to access using the Signal keyword. Second, we need to
call that function by using the emit_signal command. Third, we need to
connect the signal from one .gd file to another higher up in the hierarchy.
Signals are sometimes needed to track certain events within a game.
To see how to use signals, we’ll create a simple game that keeps track of
collisions. If a player collides with treasure, they get a point. Then the game
displays the number of collisions on the screen.
Tracking collisions involves several steps:
472
Chapter 20 Using Signals
8. Click the Search text field, type Label, and then click
Create. Godot creates a Label node as a child of
Control as shown in Figure 20-7.
473
Chapter 20 Using Signals
474
Chapter 20 Using Signals
extends Node
func update_score(score):
$Control/Label.text = str(score)
475
Chapter 20 Using Signals
18. Click the text field, type treasure, and press Enter.
476
Chapter 20 Using Signals
The preceding steps have created a simple scene that we can randomly
place within a video game playing field. Now the next step is to create a
player scene that we can move around. To create a player scene, follow
these steps:
477
Chapter 20 Using Signals
4. Click the Search text field, type Node, and then click
Create. Godot creates a generic Node as parent node
in the Scene dock.
478
Chapter 20 Using Signals
8. Click Create.
extends Node
13. Drag and drop the hud.tscn file from the FileSystem
dock on to Main in the Scene dock.
479
Chapter 20 Using Signals
Figure 20-8. Adding a player and three treasures into the main.
tscn scene
extends Area2D
func _process(delta):
var velocity = Vector2.ZERO
var speed = 500
480
Chapter 20 Using Signals
if Input.is_key_pressed(KEY_RIGHT):
velocity.x += speed
if Input.is_key_pressed(KEY_LEFT):
velocity.x -= speed
if Input.is_key_pressed(KEY_DOWN):
velocity.y += speed
if Input.is_key_pressed(KEY_UP):
velocity.y -= speed
481
Chapter 20 Using Signals
func _on_area_entered(area):
if area.is_in_group("treasure"):
area.hide()
emit_signal("update_display")
482
Chapter 20 Using Signals
signal update_display
extends Area2D
signal update_display
func _process(delta):
var velocity = Vector2.ZERO
var speed = 500
if Input.is_key_pressed(KEY_RIGHT):
velocity.x += speed
if Input.is_key_pressed(KEY_LEFT):
velocity.x -= speed
if Input.is_key_pressed(KEY_DOWN):
velocity.y += speed
if Input.is_key_pressed(KEY_UP):
velocity.y -= speed
func _on_area_entered(area):
if area.is_in_group("treasure"):
area.hide()
emit_signal("update_display")
483
Chapter 20 Using Signals
484
Chapter 20 Using Signals
485
Chapter 20 Using Signals
extends Node
486
Chapter 20 Using Signals
func _on_player_update_display():
score += 1
$HUD.update_score(score)
Summary
Functions can be stored in separate files. When calling a function within
the same .gd file, you just need to use the function name. When you want
to call a function stored in a separate file, you have two options:
487
Chapter 20 Using Signals
488
CHAPTER 21
Creating a Simple
Tic-Tac-Toe Game
So far, you’ve learned some of the important elements used to make a
basic video game such as shooting projectiles and writing GDScript to
make those projectiles possible. With Godot, making any game is possible,
even the game Tic-Tac-Toe.
Tic-Tac-Toe is a classic game that was created in 1884. Traditionally,
the game is played on paper with a 3x3 grid for two players. One player
is an “X,” while the other is an “O.” The goal of Tic-Tac-Toe is for one
of players to either get three Xs or three Os in a row, in a column, or
diagonally. Tic-Tac-Toe plays quick, yet it’s simple and fun at the same
time. Though it is usually played on paper, it can also be played online by
computer. In this chapter, you’ll learn how to make a Tic-Tac-Toe game
using Godot.
To make the Tic-Toe-Game through Godot, you’ll need three images as
shown in Figure 21-1:
You can create these three images using a graphics editor such as
Photoshop, or simply search the Internet for a 3 by 3 grid, an X and an O or
any other images you want to use.
To see how to display the Tic-Tac-Toe grid, follow these steps:
490
Chapter 21 Creating a Simple Tic-Tac-Toe Game
491
Chapter 21 Creating a Simple Tic-Tac-Toe Game
To see how to detect the position of a mouse click, follow these steps:
extends Node2D
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and
event.pressed:
print(event.position)
492
Chapter 21 Creating a Simple Tic-Tac-Toe Game
if event is InputEventMouseButton
Then “func _input(event)” checks if the left mouse button has been
pressed or clicked:
493
Chapter 21 Creating a Simple Tic-Tac-Toe Game
To see how to create symbols for our game, follow these steps:
494
Chapter 21 Creating a Simple Tic-Tac-Toe Game
495
Chapter 21 Creating a Simple Tic-Tac-Toe Game
We created two separate scenes (x.tscn and o.tscn). The Scene dock
should contain nothing but Board as the parent node and Sprite2D that
displays the playing board. Now we need to make the X and O symbols
appear when we click the board.
496
Chapter 21 Creating a Simple Tic-Tac-Toe Game
497
Chapter 21 Creating a Simple Tic-Tac-Toe Game
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT
and event.pressed:
# print(event.position)
createSymbol(player, event.position)
updatePlayer()
func updatePlayer():
if player == 1:
player = 2
else:
player = 1
498
Chapter 21 Creating a Simple Tic-Tac-Toe Game
extends Node2D
func _ready():
screensize = get_viewport().get_visible_rect().size
position = screensize/2
offset = position
499
Chapter 21 Creating a Simple Tic-Tac-Toe Game
extends Node2D
func _ready():
screensize = get_viewport().get_visible_rect().size
position = screensize/2
offset = position
500
Chapter 21 Creating a Simple Tic-Tac-Toe Game
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and
event.pressed:
print(event.position)
createSymbol(player, event.position)
updatePlayer()
func updatePlayer():
if player == 1:
player = 2
else:
player = 1
12. Click the board. Notice that each time you click,
the game alternates between displaying an X and
O image.
501
Chapter 21 Creating a Simple Tic-Tac-Toe Game
Summary
Godot lets you make any game that you like from RPG, shooter, and
fighting games to card games like UNO and Go Fish! This chapter showed
how to create a simple Tic-Tac-Toe game with Godot and demonstrated
several features of Godot:
502
Index
A looping, 417–420
pausing, 412–416
addition operator (+), 86
starting/stopping, 408–411
another_function, 209
AudioStreamPlayer2D node, 420,
apply_force function, 448, 449
422, 428, 430, 431
apply_torque_impulse function,
AudioStreamPlayer node, 405, 408,
448, 449, 452
411, 417, 418, 431
Area2D node, 274–276, 283, 285,
420, 434, 475, 477
Arrays B
create and retrieve big_function, 210
data, 159–161 Boolean data types, 109, 110,
creating/adding items, 161–163 130, 415
data structures, 182 Booleans, 109
deleting data, 175, 176 Branching statements
exercise, 176–180 boolean data type, 109
get information, 163–166 boolean values, 126–131
manipulating, 170–172 comparison operators, 110–112
retrieving data, 166–170 if-elif, 119, 121, 122
searching data, 173, 174 if-else statement, 117, 118
variable, 156–158 if statement, 115, 116
video game, 155 logical operators, 113–115
Audio match statement, 122–126
background noise, 403
detecting collision, 420–430
feedback, 403 C
file formats, Godot, 403–405, C#, 33
407, 408 Camera2D node, 458, 459
504
INDEX
505
INDEX
M inheritance, 250
inheriting class, 232–237
Marker2D node, 273, 287–289,
initializing properties, 228–231
293–295, 308, 316, 317
nodes, 243
Mathematical operators
polymorphism, 237, 238, 240
built-in math functions, 107
smaller programs, 223
constants, creating, 89–91
Objects isolate algorithms, 223
manipulate strings, 99–101
Ogg Vorbis file format, 404
math functions, 94, 95
_on_body_entered(body)
+ operator, 88
function, 335
precedence, 92, 93
_on_button_3_pressed()
randomizing X/Y
function, 415
position, 101–106
on_button_3_pressed()
random numbers, creating, 96, 98
function, 417
spreadsheet, 85
_on_button_pressed() function,
modulo (%) operator, 87
178, 411
my_function(), 206
on_button_pressed() function, 350
_on_player_update_display()
N function, 487
new() method, 225, 230
“not” logical operator, 114 P
Pane, 46
O Physics
Object-oriented programming adding damping, 376–378
advantages, 250 gravity, 369–375, 402
creating class, 224–227 layers/masks, 389–397, 402
Godot work polygons, 385–389
color dialog box, 249 restricting movement, 397–401
FileSystem dock, 246 static/rigid bodies, 378–384
modulate property, 248 Pitch Scale property, 407
Node2D/Node3D, 241 Player, adding projectiles
nodes, 242–244, 246 changing name node, 290, 291
player.tscn file, 247 control player, 295–301
visibility property, 248 creating player scene, 288–290
506
INDEX
507
INDEX
508