Transcripts
1. Intro: Welcome to my course
on the essentials of game development and Gado. This course is a continuation of introduction to three D game
development and Gado but can be followed and applied
to any project that contains a character the player can control to move through
multiple levels. You're welcome to join
our discord server to work on this course
alongside your peers. In this course, we will
cover essential elements that are common to almost
any game of any genre. Building a title scene, menus, smoothly transitioning
between scenes, moving a character between different levels of your
game, background music, settings, and data
persistence between scenes, levels, and play sessions. When you're done,
you'll have a good basic structure of
a game that you can further develop into something of your own
design of any genre. You'll also learn useful skills for working with the
Gada game engine, organizing and designing your projects to
be more scalable. You will be learning
how to code with GD script with everything
explained in detail. We will apply object
oriented design principles in our scripts, inheritance, encapsulation and abstraction,
to keep them organized, customizable, and reusable
for a project of any size. All of the project files will also be available on GitHub, if you need to review
the project as it was after completing
each lesson. These videos were recorded
using Gadot version 4.2 0.2. The project starts
with assets from Ka Kits Character and Dungeon remastered Packs
made by Kay Lauberg. In this course, I'll
also be adding assets from Basic Guy Bundle
made by Penzilla, and music in the
Barns Music pack made by Eric the Funny Baron. All are available to download
for free on H dot IO.
2. Pause: Hello, friends. Before
we get started, you should have a game
scene in your project, which contains a character
that the player can control and a level they
can walk around in. You should have at least two or three different level scenes ready to connect together when the player walks
past a threshold. Throughout this course, we will add elements that
are essential to any type of game you can
think of, including menus, scene and level transitions, fading both visuals and audio, settings, credits,
and data persistence. You can use your own project to follow along with this
course or download a starter project from my
Github titled Godot Essentials course Main branch. Your project can be
two D or three D and almost any game
genre that involves moving a character
between different scenes. Let's start by allowing the
player to pause the game. Opening the project settings and switching to the input Map tab, we can add a new action for pause and click on
the Add button. Scrolling down, finding
our new pause action, we can add events
which will trigger this pause action by
clicking on the Plus button. I'll use the Escape key and the start button
on my controller. We've been using the
player node to handle our game's input through
the attached players script and relaying the
appropriate information to move the character or the
spring arm and camera. Like we did with the
run and jump buttons, we can check if the pause
button was pressed. But we don't want to pause
the character or the camera. We want to pause
the entire scene. We can grab a reference
to the scenes root node by exporting
another variable. Let's call it underscore GM, short for game manager. But the value of this variable shouldn't ever really
need to be changed. It should always be the root
node of the game scene. Instead of at export, we could use at on ready to set the value of our
GM variable to get parent, since the player node is
a child of the game node. But we may not want to rely on this exact structure in order for our script
to work properly. We may, for instance,
want the player node to be a child of the character
node for other reasons. So to make this reference always point directly to the
Scenes root node, we can use the dollar
sign annotation. Starting the node path
with slash root slash, we'll start at the
Scene trees root. But it's important to note that this is not yet the
game scene node. We also need to add the
name of the root node to this node path root slash gain. If we run our current scene, then switch to the remote
view in the scene panel, we can see that the Scene trees root node is named a root, and currently the roots only
child node is the game node. Now that our player node has a direct reference to
the scenes root node, let's call a function
on it that we haven't written
yet, Toggle pause. Attaching a new script
to our game node. This script will be responsible for managing the
scene as a whole. We'll call it game manager. Much like a manager
in a workplace, this scripts job will be to open the scene
when it first starts. Make sure all the
employees or child nodes have everything that they need to do their individual jobs, then close the scene
when it's finished. Since our simple game only
has one mode of play, we only need one game manager. But a more complex game with multiple game scenes might
have an exploration manager, battle manager, puzzle manager, and so on, for different
gameplay scenes. In our new game manager script, we can add the toggle
pause function we called from the
player script. Pausing the game is
actually quite simple. We will start by
getting a reference to the entire scene tree with a
built in function, get tree. The return type of this
method is a scene tree, which contains a variable named
paused that we can access with p. The type of the
paused variable is a Boolean, which has a default
value of false. Setting the value to
true pauses to gain. We can switch the value back and forth between true
and false every time this toggle pause function is called using the
naught logic operator, represented with an
exclamation point. Setting the value of
paused to naught false the first time and then not true the second and repeating. If we test it out, we
can pause the game, but we can't unpause yet. This is because the player node, which is trying to receive the
input to unpause the game, is also paused and can't
receive or process input. Some specific nodes we might not want to pause while
the game is paused. Selecting the player
node in the scene tree, look in the inspector under node and expand the
process section. The mode property is
currently set to inherit, which means that it will inherit the same process mode as
its parent, the game node. Since the game node is paused, the player node will also pause. We can change this
value to always, so the player node will always process even when
the game is paused. Note that we can also make
nodes pai have some nodes only process while the game is paused or disable processing from certain nodes altogether. Now when we run the scene, we can pause and
unpause the game since the player node is always
running regardless. But this creates a new problem. If we pause the game and
press the jump button, then unpause, the
character jumps. If we do not want this behavior, we need to return to
the player's script. Let's first move the check for the pause button to the
top of the input function. Then if the tree is paused, ignore any further inputs by returning and do the same in the process function to ignore the movement and
camera controls too. Now when the game is paused, the only input the player can give is to unpause the game. Everything else will be ignored. Lastly, I would like
to display a menu on the screen with some buttons
while the game is paused. But before we do anything
involving the user interface, we should configure the
game's window size. Open the project settings and
switch to the general tab. Selecting window, we can set the size property to set the
window size of our game. Optimal window size will depend
on your target platform. I'll use 12 80 by 720 pixels. So far, everything in
our scene tree is being observed by the camera to
produce a two D image, which is what we see in our
window when we play the game. This two D image
being rendered by our camera is layer zero
of the two D display. We can add additional
layers to our games display after the camera has finished its rendering using
a new node type, a Canvas layer node. The default layer of a
Canvas layer node is one, so it will be drawn
over layer zero. If we want, we can
add any number of layers to draw in front
of or behind our game. Let's rename the Canvas
layer node to UY, short for user interface. Right clicking on the UI node and selecting Add child node. Let's add a label
node to the Canvass. This switches our view to two
D. So far we've only been working with default nodes like the player node and three D
nodes for everything else. Our Canvas will use
a different family of nodes called Control nodes, which have green icons. I'll rename this node to title and set its property
to say pause. Next, let's repeat the
process to add a button. And name the button continue. Then edit the buttons
text to say the same. Zoom and pan, so you can see the blue rectangle
to the right of the green y axis and
below the red x axis. This is the viewport
of the game window. Our nodes were added
at the scene origin, so they will be rendered in the top left corner of the
screen if we play the game, but they are overlapping. Let's organize these nodes
using another control node, a vertical box container. A vertical box container
doesn't actually draw anything, but we'll organize its
children to align vertically, making organization faster than if we were to manually
position each node. Selecting both title
and continued nodes, we can click and drag
to re parent them to the vertical box node and see that they are
automatically reorganized. Let's also duplicate the
continued button to create another button and
name this one exit. Then edit the text
property to match. The vertical box automatically adjusted itself to
accommodate the extra button. Let's rename the vertical
box to pause menu. With the pause menu
node selected, expand the layout section. The anchors preset value is
currently set to top left. We can easily anchor
the pause menu to any corner edge or the
center of the screen. The anchors are represented in the preview by these
four green pins, which we can individually click and drag
around the screen. The anchors determine the
notes position or size, in relation to the window size we set in the project settings, expressed as a percentage. We can also use custom anchors if we want to, for example, use only the upper 10% of
the screen for a menu bar, or maybe use the middle third of the screen to
display a scoreboard. I'll anchor my pause menu to
the center of the screen, and also set the titles
horizontal alignment to be centered to
match the buttons. With the pause menu created, let's set its visible
property to false by default by clicking on the icon
beside it in the scene tree. Then in the game manager script, we can use a ready to get a
reference to the Pause menu. Using $1 sign to
specify a node path, using quotation marks because
the name contains a space, going through the UI node to
access the pause menu node. When the game is
paused or unpaused, we can set the
visible property of the pause menu to match the
paused value of the tree. Now we have a pause
menu with buttons, but how do we know when
the buttons are pressed, and what do we do when
they are pressed? If we select the exit
button in the scene tree, we can switch the inspector
tab to the node tab, which will display
signals by default. If groups are displayed, you can switch to signals
by clicking here. Here we can see a
variety of signals that this node will emit under
different conditions. The button node will emit a
signal when it is pressed. All we have to do is connect
this signal to a function, either by double clicking on it or right clicking and
selecting connect. In the window, we can select
any node in our scene tree, which we'll listen for
the signal to be emitted. If we select the
game manager node, the receiver method field
will say on Exit pressed, which is the name of the
function which will be created for us when
we click Connect. Our view will
automatically switch to script and display the
game manager script, which now contains the on
exit pressed function. This function will now be called automatically when the exit button is pressed by the player. A green connection icon
is displayed next to the function
definition to tell us that this function is
being called by a signal, and the exit button in the scene tree has
a signal icon to tell us that this node can emit a signal which is
connected to a receiver. For now, let's just
print to the output, a message saying,
return to Title scene. Both so we know it works and to remind us to implement
this feature later. Selecting the continued button, we can also connect
its pressed signal to the game manager node. But this time, click
on the pick button and pick the toggle pause
function we wrote earlier. Lastly, we need this node to process while
the game is paused. So let's change its
process mode to when paused. Let's try it out. We can pause the game, clicking
the exit button displays our message and pressing the continued button,
pauses the game. Once the menu
buttons have focus, we can also use the
arrow keys to navigate between the buttons and the
space bar to press them. Let's make this work
with our game controller as well with just a
couple more simple steps. Opening the project settings, the input Map tab, click on the show
built in actions toggle to see the
actions that were used. The Controllers DP is already
listed under UI left, right up and down for
navigating menus. But we need to add a
controller input to the UI accept action to be
able to press the buttons. I'll use the bottom
action button, which is A on my Controller. Now we just need the menu to grab focus when it is opened. Adding a script to
the pause menu node, we'll just name it menu. We'll export a variable
to hold the menu item, which should grab
focus by default when the menu is opened
of type control. I'll assign the
continued button to be the default focus item in the inspector by clicking and
dragging it into the field. Creating a public function,
let's call it open. We can set the visible
property to true and also tell the default
focus item to grab focus. And another function close, will set the visible
property back to falls. Back in the game manager script. Instead of setting
the visible property of the pause many directly, we can call either open or closed based on the value of paused, using
an if statement. Running the scene, using
only a controller, we can pause the game, navigate the menu, and
press the buttons. We now have a pause menu that is presented to the player
when they press the pause. In the next lesson, we'll build a title scene
for our game. I'll see you in the next lesson.
3. Title: Hello, friends. In
the previous lesson, we created a pause menu
for our game scene. In this lesson, we'll build a title scene that connects
to the game scene. For that, we'll need
a background image and some basic UI assets. I'll be using basic Guy bundle made by Penzila on it shot IO. Let's start by
creating a new scene. You can select scene from the
menu bar, then new scene. Use the shortcut Control
N or Command N or right click in the File System tab
and select Create New Scene. Right clicking on a
specific folder will create the scene in that folder and allow us to give it
a name right away. It doesn't matter if this
scene is two D or three D, since we're only interested
in UY for this lesson. Let's make this scenes root node a Control node by
picking User Interface. And the root node
is automatically renamed to match the
name of the scene title. If we create the scene
through the menu, we will be asked to select
the root node type first. Then name it and sort it into a folder the first
time it is saved. Selecting the root node,
we can see that this is a control node with its anchor
presets set to full wreck, covering the entire game window, but not actually
drawing anything. Like in the previous lesson, let's add a label node
and some buttons. L et's name the label note title and set the text property
to the title of our game. Then likewise, name
our buttons and edit their text
properties to new game. Continue. Settings, credits, and exit or any other buttons you would like to have on
your game's title screen. Let's also organize
these buttons using a vertical box container like we did with the Pause menu and rename it to menu buttons. We can quickly and
easily organize these items on our
screen using anchors. For example, I would like
the title to be centered horizontally on the screen and one third down from
the top of the screen. With the title label
note selected, expanding the layout section, I'll change the anchor
presets to custom. Anchors are defined
as numbers 0-1, representing the percentage of the parents width or height. Positioning the title in
the middle of the screen horizontally can be done by setting the left
anchor to 0.5, and the right anchor will
automatically update to match since the right anchor
can't be less than the left. Likewise, positioning the title
one third from the top of the screen would mean setting
the top anchor to 0.333. This isn't quite position
the way I want yet because the anchors are in the top
left corner of the label. The text just is played to the right and below
the anchor point. Expanding the grow
direction subsection, we can set the horizontal and vertical growth
directions to both. Epending the anchor
offset subsection, we can reset them all to zero, and now the anchors are
centered within the label. I'll also position the button container in the same manner. But with its vertical
anchor at two thirds down, so it appears below
the game title. You can also position any of
your control nodes manually, but remember that using
anchors will make your layouts more flexible and fit
different screen resolutions. Let's add a manuscript to the Mini buttons node and assign a default focus
item in the inspector. Like our game scene has
a game manager script attached to its root node, we likewise need a
title manager script attached to the Title
scenes root node. And here we can
grab a reference to the menu buttons script during the ready phase
using add on ready. Then tell the menu buttons to
open in the ready function. The menu was already visible, but telling it to
open, we'll also tell the default focus
item to grab focus. Selecting each of
the button nodes, we can add an pressed
function call to our title manager
script for all of them. For now, we'll only be concerned with the
new game button. The rest can just
print strings to tell us to implement
these features later. The purpose of the new game
button will be to switch our game from the title
scene to the game scene. We can accomplish this by getting a reference
to the scene tree, then accessing a method
called change scene to file. This method takes
a string argument, which is a file path to
the scene we want to load. The suggestions contain a list of all the scenes
in our project, so we can select the
game scene from here. Alternatively, looking
in the file system tab, we can find the game
scene and right click, then select Copy path, and paste it as text. But remember that strings must be enclosed in
quotation marks. So far, we've been using the run current scene button
to test our game. But you've probably
accidentally pressed the run project button
at least once by now, which will prompt the setting of our project's main scene. This is the first
scene that will be loaded when we
start the game, which should be this
scene, the title scene. If you haven't set this yet, you can click on the
select current button to set the title scene as
the game's main scene. The new game button grabs
focus when the game starts. Clicking each of the buttons
prints out our statements, and clicking on the
new game button immediately switches
to the game scene. We can also edit the project's
main scene anytime from the project settings
in the general tab under application run Min scene. Our title scene is
now functional, but it doesn't look very good. First, we can add a splash
background using a text node. Let's name it background. In the layout section, setting the anchors
preset to full wreck, will ensure that the background
covers the entire window. We can edit the texture property to display whatever
two D image we want. Using the properties
in the inspector, we can alter how the image
is displayed from keeping its native size
or fitting inside the anchor points either
by width or height. We can also specify that
the image should maintain its aspect ratio to avoid the image being stretched
to fit the window. Lastly, we'll sort it at the
top of the scenes children, so it is drawn first,
behind everything else. Next, let's take a look at the title label
nodes properties. Most of the label
settings have to do with justification
and wrapping, which isn't really
relevant for our title. The settings we want to change
are under the properties inherited from control in
the theme override section. Here we can adjust
the font color. Add a shadow or an outline which can also be adjusted in the
constants subsection. We can change the font size, and also the font itself. I've downloaded a couple of
free fonts from dafont.com. Make sure any fonts you use
in your games are free. Da font displays the licensing for their fonts above
the download button. This works fine for
our game title, which is meant to stand out, but editing individual
properties in this way for everything in
our project would be tedious. For editing our menu buttons, we should instead
create a common theme to use throughout
the game project. Selecting the button
container node. This time, expand
the theme section. The theme field is empty. Clicking on it, we can select new theme to create a new theme
resource for our project. Clicking on the theme resource
will add a new tab to the bottom panel of our
window, a theme editor. The theme editor
contains a preview of a variety of
different control nodes, a panel, playable,
different types of buttons, toggles, drop down, color picker, text fields, scroll bars, separators,
tabs, and a tree. All using the default theme
that is provided by Gadot. Looking back at our theme resource in the inspector panel, we can edit the default font and font size for everything
in our theme. Let's change the font
and adjust the size. We can see the effects both in the preview and in our
buttons of our title menu. This doesn't affect
the game title label since the theme has not
been applied to that node. It has only been applied to
the menu buttons container. Even if the theme were
applied to the title node, the theme overrides we just applied would
be used instead. The buttons inherit their theme from their parent
in the scene tree. In the theme editor, we
can add new settings for any control node by
clicking on the Plus button. Since our menu contains
only button nodes, let's add settings
for button nodes. There are a lot of different
settings for adjusting the font and icon colors during different
states of the button. Default, disabled,
focused, hovered, hovered pressed, and pressed, as well as an outline color. Before we can adjust
any of these settings, we need to add them to the theme by clicking on the plus
button beside them. Adjusting any of these settings, we can see their
effects in the preview, including the hover, focus, and press states by
clicking on the buttons. There are additional
settings and other tabs for
adjusting constants, fonts and font size. The font and font
size have already been set by the theme
resource itself. We don't need to
adjust them here. We can also change the look of the button itself
using style boxes. Each of the button states can be assigned to
different style box. Let's start with normal.
Clicking on the plus button, we can use a flat
style box to just create a flat colored
rectangle for the button, or we can use a
texture style box to use an imported two D image. Clicking on the style box
resource will open it in the inspector panel
where we can populate the texture field with
a two D image file. You may want to add some padding to the
edges of the button, so the button text doesn't overlap with the
edges of the image. Expanding the content
margin section, we can adjust the
size of the margins to better fit the text
inside the image. Adding a pressed style box, we can switch the texture that is used to draw the button. Copying the same settings weed
for the normal style box, but increasing the top padding to make the text move
down when pressed. Adding more style boxes for disabled hover and
focused states. You may not have
different textures for all your different
button states. But we can still create new
style boxes for these states. Use the same normal
texture and settings, but add color
modulation to create a different appearance for
the button in this state. We can multiply the textures
colors by something like yellow to make it
look noticeably different while being covered. Or we can just use
another image in a different color
to emphasize focus. When disabled, we can multiply the colors Alpha to
add some transparency. We can test all of these
different style boxes easily from the preview
in the theme editor. Now the remain
menu looks better, let's also edit the Pause menu in the game scene to match. Instead of adding a new theme to the Pause menu and
redoing all of the edits, we can instead return
to the title scene, find the resource, and
click on the Dropdown. Select Save and save the theme in our projects
resource folder. Now, in the game scene, we can assign this theme
to the pause menu, and the buttons
will automatically be updated to match
the new theme. Opening the game manager script. When the player presses
the exit button, we can also change
to the title scene. Running our game, we
start in the title scene. The new game button
switches to the game scene. Pausing the game and selecting exit returns to the title scene. We now have a title
scene and can switch back and forth
between our two scenes. In the next lesson, we'll smooth the scene transition
by fading to black. I'll see you in the next lesson.
4. Transition: Hello, friends. In
the previous lesson, we created a title scene and
connected to the game scene. In this lesson, we
make the transition smoother with a visual fade. Most games start
with a black screen. Let's add a new node
to our title scene, a color wrecked node. Let's call it Fade.
In the Inspector, we can set the color
property to black. Then set the anchor precets to full wreck to cover
the entire window. When the game starts,
we can gently fade this object to
be fully transparent, revealing the title
scene behind it. Even when an object in
our scene is transparent, mouse events will only be triggered on the
first thing they hit, which means moving the
mouse or clicking on our buttons has been
blocked by the fade. In the properties inherited from control, in the mouse section, we can change the filter
settings of our fade to ignore, which will allow
the mouse events to pass through the fade to
the objects behind it. Now the buttons work
with the mouse again. While we're working on
our scene in the editor, it would be better to be
able to see everything else. I'd like to have the fade
being visible by default. Before we attach a script, let's start organizing
our scripts into more specific folders by
creating a subfolder named UI. We can move the manuscript into the subfolder since it is
used to control UI elements. But when moving resources
around like this, make sure the scenes that
use it are open first. Otherwise, the scene files
might lose track of them. If that happens, just move the resource back to
where it was originally and make sure that
the scenes using the resource are open
before moving it. Now we can attach a script
to the Fad node and use the browser to sort it
into the UI scripts folder. Unlike our manuscript, which could be relevant to
any control node, since it was only affecting
the visible property, which is inherited from control. This FAD script will be altering the color property of
the color wrecked node, so it will only be usable by color nodes or its inheritors. Let's start in the
ready function. Setting the visible property of the fa true when the
scene first starts, making the window
completely black. Then we can declare a public
function called to clear. Unlike most functions
in most scripts, this is something that
will need to happen gradually over time,
spanning several frames. We can accomplish this with a
new variable type, a tween. This can be a private variable, only applicable to
this script locally, and I'll just name it
tween for simplicity. The name of this
class comes from the English words in between, since it is most
often used to find points in between a starting
point and an end point. We can create a tween anytime by calling a built in
method, create tween. This method returns
a tween object, which we can assign to
our variable to store it. A tween is a complex class
with a variety of methods, but the one we want to use
is called tween property, which requires
several arguments. The first argument is the node that this tween
will be affecting, which is the same node that
this script is attached to. We can get a reference to this node using
the keyword self. The next is the node
property we want to change, a property path as a string. We will be changing the
color property of this node. If you're unsure of
the property path, we can look in the inspector. Hovering over any property will show us the property
path to access it, or we can right click to copy the property path and
paste it as text. The next argument is
the end value we want this property to be set to
after the tween is finished. Since the property is a color, we will be setting this to a
custom color we will define. Let's call it clear. And the last property is the amount of time that
the tween will last, measured in seconds,
expressed as a float. Let's set it to 1
second for now. The value of clear will be something that we can
define but never changes. Instead of declaring a variable, we can define a constant
using the keyword constant. Name it clear of type color. It's conventional to name
constants in all uppercase. Then assign the value of
this constant as a color. Writing the class name
followed by brackets, accesses a constructor method, allowing us to
construct the color by specifying all the
required properties. A color is defined
by four floats, red, green, blue, and
Alpha for transparency. A as numbers 0-1. Our clear color will
simply be zero, red, zero, green, zero, blue, and zero Alpha, making it black, but also completely transparent. Calling our two
clear method will transition the color property
from its current value, which is black to clear over
the duration of 1 second. We can also write
another function to black to do the opposite. However, we don't need
to define a constant for black since the color
class already has one. We can access it through the
class name using a period, and we can see that there are a large number of
predefined colors. The one we want
right now is black. Black is defined as zero, red, zero, green, zero,
blue, but one Alpha. Let's switch over to the title manager script for a moment and grab a reference to the FAD Noe during the ready
phase using at ready. When the scene starts, we can tell the note
to Fate to clear. Then before switching
to the game scene, we can tell it to fade to black. But the process of fading
to black takes time, and this function will run in one frame immediately
switching to the game scene, so we won't see it happen. We can tell this
function to pause and wait for a signal
before proceeding, using a new keyword, a weight. A weight requires a
signal to wait for, which we can return from
the fade to black function. Back in the FAD script, we can specify a return type for our functions
before the colon by adding a hyphen an arrow,
followed by a type. In this case, we want
to return a signal. The tween class contains
a signal that is emitted automatically when
the tween is finished, so we can return that. Now the title manager
will wait for the FAD node to fade to black
before changing scenes. Butt's also copy this format
into the two clear function. Even though it is
returning a signal, the title manager
does not need to accept this return or
do anything with it. In this case, the tween
will be started and the remainder of the function
will proceed immediately. We can copy this FAD node
into the game scene. Then edit the game
manager script to get a reference to the FAD node
during the ready phase. Fade to clear when the
scene first starts, and await fade to black before switching to
the title scene. But this function
on exit pressed, is being called while
the game is paused. Therefore, the F node is also paused and will not tw
its color as a result. We can allow the FAD node to always process to get around. But it would also
be a good idea to unpause the game before
switching scenes. Let's try it out. Now the game starts from a black screen
fading into our title screen, pressing the new game
button fades to black, switches to the game scene,
which fades back in. Pausing and pressing the
exit button, fades to black, switches to the title scene, which fades back in as well. There's a flicker between
switching scenes, however. We can edit the project settings under rendering environment, and change the default
color to black. This is a much more
gentle transition than immediately
switching scenes, and if our game
gets large enough to require longer loading times, we can change this
to a loading screen. However, there may be
some problems with starting a tween before the
previous one has finished. Back in the FAD script, we can check if the tween
exists by comparing it to null. In Godo script, we can do this implicitly with just if tween. If it does exist, also
check if it's running. In that case, we should kill the tween before
creating a new one. This will prevent any chance of multiple tweens existing and
interfering with each other, since the most recent tween
will simply take over. Our two clear and two
black functions are very similar with only the color
value different between them. It will be good practice to instead declare a
private function here. Let's name it two color, and accept a color
as a parameter. We can then do all
this only once. Passing the color to the
twin property method, and having the two clear
and two black functions called this two color
function instead. Passing the return
signal through the chain or managers
can await it. This way, making any changes to this behavior will require only changing one block of code. You may want to have the
script to be more adaptable. Let's export a variable
for the duration of the tween and assign it a
default value of 1 second. Then replace the 1 second
argument with our variable, and now the timing of the can be easily adjusted
in the inspector. Let's see how the FAD looks with the duration of 2 seconds. We can make this even
more flexible by allowing the two clear and two
black functions to accept an optional parameter to
override the specified duration. Let's call the parameter
duration without an underscore, making it different
from the variable which does have an underscore. And assign it a default value
of our underscore duration. We then need to pass this value to the two color function. And add it to the
list of parameters. Then use the parameter in the tween property method
instead of our variable. Our manager scripts
can still use these functions without
specifying a duration. In that case, the
exported value will be. But they can also specify a different duration
and use that instead. Let's have the exported
de duration be 1 second, but allow the title
manager to override that and fade in over a duration
of 2 seconds instead. We now have a more gentle visual transition
between our two scenes. In the next lesson, we'll pass information between scenes. I'll see you in the next lesson.
5. Autoload: Hello, friends. In
the previous lesson, we faded our scenes in and out
for a smoother transition. In this lesson, we'll
add extra nodes to the scene tree that are
accessible from any scene. If we run our game and switch
to the remote scene tree. We can see that the scene
trees root has only one child, which is the title scene. When switching to
the game scene, the title scene is removed from the tree and replaced
with the game scene. If we want to pass
information between scenes, we can add extra children to the root node
of the scene tree, which will remain there as
long as the game is running. These are called auto loads, and we can create them
by writing scripts. First, let's add a
special folder inside the scripts folder for holding
our auto load scripts. Then right click on this
folder to create a new script. The purpose of the script
will be to save load, and provide access to data. I'd like to name
this script file. Since this doesn't represent anything that needs to be drawn, it can inherit from node. This script will
have two variables containing separate
sets of information, the game's current settings, and the player's current
progress through the game. You may want to have a
third variable for storing progress information associated
with a user account, but not tied to a
specific save slot, such as unlocking difficulty
levels or achievements. These variables will
be publicly accessible to any other script in
our game in any scene. Opening the project settings, switch to the auto load tab. Click on the folder icon beside the path field to
open a browser, then navigate to
the file script. The node name field
will automatically be populated to match
the name of the script. Click the Add button to add this script to the
list of auto loads. Now when we run the game, we can see in the
remote scene tree that the root node has another
child node named file. Clicking on this node,
we can see that it has two variables named
settings and progress. Switching to the game scene, the title scene is removed and replaced with
the game scene, but the file node remains, meaning we can use it to pass
information between scenes. But I haven't
specified a type for the variables
because we will want to create our own types to hold complex collections
of information. Let's create another
sub folder in the scripts folder and
name it custom resources. Then right click to
create a new script. Let's name it settings. Instead of inheriting
from a node type, this time, the script will
inherit from resource. Then we can create another one. Let's call this one progress. In the resource scripts, we can add any number of
variables of different types, but they must be built in types, not references to other things. For example, we can store
a Boolean variable, which is either true
or false to set whether the camera
controls on the x axis are inverted and another
for the Y axis. Numbers can be stored as floats, which allow decimal points. You might use this to save the game's volume setting
as a number between zero, muted or one full volume. Numbers can also be
stored as integers, a number that does not
contain decimal points, but can be positive or negative. We might use this in
the progress script to store the players money or. We can also use strings to
store sequences of characters, perhaps giving the player save slot or protagonist a name. Any other built in types like vectors or colors
can also be used? Collections of any of
these types can also be stored in either an
array or a dictionary, such as an array of Booleans to contain whether
or not the player has completed each level. Each of these variable
types has a default value. Booleans are false, integers
and numbers are zero. Strings, arrays, and
dictionaries are all empty. When a resource is created, it will automatically call a built in function named in it. Short f initialize. Overriding this
function, we can specify whatever default values we
want for our variables. Perhaps starting our volume
setting as 0.5 for one, or giving the protagonist
a default name. To create our resource, we
need to specify a class name, which is done at the
top of the script. Usually in the same line
as the extends keyword. With the keyword class name, we can give this resource
class a name that will be used by other scripts to refer
to this class as a type. We can name this
class settings with a capital S, and this one, progress with a P.
Conventionally, classes are named in Pascal case with the first letter of
each word capitalized. Back in the file script, we
can now specify the types of our variables as
settings, and progress. For now, we can just create these resources inside
the ready function. Using their class name
followed by dot Neu. This will also trigger
the init function, initializing the variables
inside the resource. Running the game, we can see these variables in the
remote scene tree. Click on them, see and edit
their property values. Let's change their
values around. Switch to the game
scene and confirm that the changes remain
through the scene transition. Any script in our game
can easily access these resources thanks
to the file auto load. Let's open the spring arm script that is responsible for
rotating the camera. Its current behavior is
using inverted x and y axes. But now we have variables in our settings resource which
should toggle this on or off. Note that rotating
the camera about the x axis is a
vertical rotation, which players will understand
as the y rotation, and rotating about the Y axis is the horizontal rotation
or x rotation. We can write an if
statement before each of these
calculations to check our file dot settings to see if the appropriate invert
variable is true or false. Then multiply the
formula by negative one, if it's false, to switch the inverted behavior
to not inverted. This means having almost the same formula copied twice with only a minor difference of being multiplied by
negative one or not. We can do this more efficiently
using an re statement. Returning to the
original format first, then multiplying by in brackets, one, if file settings invert else, negative one. And repeating this with
the horizontal rotation, changing y to x.
Turnary statements are effective anytime
you want to change one value based on a
simple bullying condition. L et's try it out. Tilting
the right analog stick, the cameras x rotation is no
longer inverted by default, but the y rotation is inverted. Opening the remote scene tree, we can access and
edit the settings. And returning to the simulation, the camera controls
are now opposite. We've now created a
few of our scripts that inherit from
different note types and also from resource. But we can also write scripts that inherit from our
own custom classes. Taking a quick look
at the FAD script, we combined the behavior of two clear and two black
into one combined function. We can combine similar behaviors from different scripts
in a similar way, creating a super class
and using inheritance to allow different scripts
to access the same behavior. Let's use our manager
scripts as an example, since they both now use
the FAD transition. It would be a good idea to put these in a separate
scripts folder. Let's call it managers. Moving both the game manager and title manager
into this folder. We can also create
a new third script, let's call it scene Manager. In the scene Manager script, we'll give the script a class
name in pass bell case. Anytime you give a
script a class name, you'll need to save before
the engine will recognize it. Then in the game Manager
and title Manager scripts, we can have these extend the behavior of the
scene Manager class. Now, anything we put in the
Scene Manager script will be used by both the game
manager and title manager. Let's move the FAD variable into the scene Manager script. Then in the game
and title managers, since they inherit
the Fad variable from the scene manager class, they are not allowed to declare another variable
of the same name. We can delete the variable, since it is now inherited
from the scene manager class. But the node path is different between the game scene
and the title scene. We can easily get around this by exporting the variable
instead of using at on ready. We will need to assign
the value of fade in both scenes in
the inspector panel. If the variable
isn't showing up, you may need to close and reopen Godot to get the class
inheritance working. Functions are also inherited, but subclasses can redefine inherited functions
overriding their behavior. This is why every class
we have written with a re or process function
has a blue icon beside it, since it is overriding the inherited definition that already exists in
the node class. If our scene manager class has a ready function that tells
the fade to fade to clear, this behavior will
be inherited by both the game manager
and the title manager. We can remove this function
from the game manager, but the title manager is
also opening the menu. Allowing the title
manager to override the ready function definition inherited from Scene Manager, we don't need to
override all of it. Our subclasses can access the inherited definition of a function using
the keyword super. Since title manager inherits
from Scene Manager, Scene Manager is the superclass. By calling super dot ready, we are telling the
Fade to fade to clear. But then we also tell the
menu to open as well. The structure of
superclasses and inheritance will
become more useful if we have more scene managers or more common behaviors
of all our scene managers. We don't need to
rewrite the same code across multiple scripts. We now have an auto loaded
node in our scene try that can store information about
game settings and progress. In the next lesson, we'll add background
music to our game. I'll see you in the next lesson.
6. Music: Hello, friends. In
the previous lesson, we created auto
loaded nodes that can be used to pass information
between different scenes. In this lesson we'll add background music that can change with each scene of our game. For this lesson, I'll be using Barn's music pack made by Eric the Funny Barn
on itch dot IO. Whatever background music you choose to use for your game, make sure that it is
designed to loop. Since audio files are
typically very large, I recommend only
importing them into your project as you
decide to implement them. Once you have them imported and sorted into a music folder, select one and switch the scam
panel to the import panel. You may need to edit
the import settings for your audio files before
they will be able to loop. Depending on the file type, the loop setting
may be a checkbox or a drop down with
multiple options. In that case, you'll want to
change it to linear looping. Let's start by opening the title scene and
adding a new node to it. On audio stream player node. Make sure not to use the
two d or three D variants, as these are meant to simulate hearing sounds in two
D or three D space. The normal audio stream
player node will not be affected by proximity or
anything else in the scene, and just play the audio as is. We can set the audio
stream property in the inspector to one
of our imported songs. Taking a look at the
audio stream player notes properties
in the inspector, you'll notice that the volume
is measured in decibels, afloat with a default value of zero ranging from
negative 80 to 24. It's important to
note that this number doesn't scale liarly. Increasing from 1 decibel to 2 decibels is not twice as loud. But don't worry if you
don't understand how decibels work because we
won't be using them directly. We can tell the audio
stream player node to autoplay when
the game starts. Running the game, the music
starts at full volume, and when we transition to the game scene, it
stops suddenly. Combining what we've learned
in the previous two lessons, we can create a
persistent audio player for playing background music, and also fade music
in and not smoothly. Since background music is often something games
will have in every scene, it would make sense for there
to be an auto loaded node which can remain
in the scene tree permanently to handle it. So we can delete the audiotrem player node in our scene tree. And create a new script
in the auto lodes folder. Let's call it music. This auto loaded node can inherit
from audio stream player, giving the music node access to all the properties
and methods of an audio stream player node. Since we can't access
the properties of an auto loaded node
in the inspector, we will have to set its
default property values in the scripts ready function. I would like the volume to start at its lowest possible value, which is negative 80 decibels. But we can also use linear
volume if we want to, which is easier for most
people to understand. Using a built in function, linear to dB, short
for decibels, which will convert a linear
scale into decibel scale, allowing us to set the volume
to zero in linear terms, which means no sound. If you want the music to also play while the game is paused, we will need to change the
process mode of this node. Using the suggestions,
we can set it to node, dot, process always. Thank you. Okay. Thinking first about how we would want
this node to be used, we probably only need
two public functions. One to play a music track
and another to fade it out. Play track will take a track as a parameter of
type audio stream. While fade out doesn't
require any parameters. In the Play track function, we can access any properties inherited from audio
stream player node, including the stream that
is currently playing, which has a default
value of null. We can set the value of stream to the track
we want to play. Then tell the audio
stream player to play. But the volume is
currently inaudible. Let's use a tween
like we did with the visual fade to gradually
adjust the volume over time, declaring a new tween variable
at the top of the script. We'll first check if the
tween already exists, and if it does exist, check if it is already running. And if so, kill it. We can then create a new
tween. And tween a property. Accessing this node itself, the volume property, gradually changing it to file dot setting dot volume over
the duration of 1 second. But remember that the volume
doesn't scale linearly, since it is measured
in decibels. Instead of tweening the
volume property directly, we can tween a method instead. Tween method doesn't
require a node reference, so we can remove
the first argument. The property path is
now a callable method. Let's write a function
called set linear volume, which takes a float parameter
of the linear volume. This function will set
the volume property after converting it from
linear to decibels. We can use this in our ready
function for consistency. And also use this as our callable method in
the tween method call. Note that when using
a method as a cable, it doesn't have
brackets at the end. Method also needs not
only a final value, but also a starting value, since this isn't a property
of the node anymore. It has no way of knowing
what the current value is. The starting and ending values are actually an
argument that will be passed to the callable
method, the linear volume. Using the opposite
built in function, we can convert decibels
to linear to change the current decibel volume into a linear volume as
the starting value. We can specify that
the linear volume of our music will be
tweened from whatever the volume currently is up to file that setting stop
volume over 1 second. Let's also return the
tween finished signal. Then the fade out function will want to do the exact
opposite of this. Tweeting the linear volume
from whatever it's at to zero. Waiting for the tween
to be finished, stopping the node from playing, and setting the
track back to null. Like we did with the color Fade, we can combine these into
one private function. Let's call it volume. Taking only the target linear
volume as a parameter. Twining the volume from
whatever it's currently at to the target volume over
the duration of 1 second. And now both fading in and fading out use the
same function. Opening the project settings, switching to the auto load tab. We can add the music script to the list of
auto loaded nodes. In the scene manager script, we can export a variable to hold the background music
track for each scene, since every scene
manager will inherit the variable and be able to
assign its own value to it. Our scene Manager
script can now easily access the music auto load
node using its node name, telling the music node to play the desired
track for this scene. L et's assign a
different music track for each of the title
and game scenes. And also make sure to fade out the music anytime
we're changing scenes. Let's try it out. The title
scene music fades in, pressing the new game
button fades the music out, and the game scene fades
its own music back in. Pausing and pressing
exit fades the music out and the title
music fades back in. This works fine as long as we are only playing
one music track per scene and are diligent at always fading out before
requesting a new track. But what if we wanted to
tell the music node to play a different track while it is already playing or
even the same track? It would be a good idea to add some extra conditions to
the play track function. Let's start with checking if the music node is
already playing. Then also check if
what the node is playing is the same as
what we want to play. If that's the case,
then we don't need to change the stream
or tell it to play. We can just return the
tween finished signal that is returned by
fading the volume. If a different track
is already playing, we should fade out that track before switching to this one. Wait for the twin to finish, then proceed with the
rest of this function. Note that this will
now be performing two executions of fad volume, each within 1 second duration. It will take 2 seconds to
change tracks as a result. Let's make this more flexible by exporting the duration
like we did with the FAD and give it a
default value of 1 second. Then allow the play track and fade out functions to accept an optional duration
parameter with a default value of the
exported variable. Passing the duration parameter to the fade volume function. We can use it in the tween
method call as an argument. Taking a look at the game
and title manager scripts, O on new game pressed and on exit pressed functions are
looking pretty similar now. Let's write an
inherited method for changing scenes in our
scene Manager script. Named change scenes. We can tell the
music to fade out. Wait for the Fade
to fade to black, unpause the game
if it is paused, then change the scene
of the tree to a file. Passing the file
path as an argument. In order to get the
results we are expecting, we will need to make sure
that the signal being awaited is the one with
the longer duration. The await statement must also be the last one if we want all the tweens to
happen in parallel. If for any reason, you need
to reorganize the order of these statements so
that the signal being awaited is not the one returned, you can also store it in a variable and await it
on a different line. Now, the title manager
can do all of this by calling its inherited
change scene method, passing the game scenes
path as an argument. Likewise, the game manager can also do this changing
to the title scene. Also, don't forget that
subclasses can always override these behaviors if you want a specific scene to
behave differently. We now have background
music that smoothly fades in and out with
each scene in our game. In the next lesson, we'll add a settings menu for
changing the game settings. I'll see you in the next lesson.
7. Settings: Hello, friends. In
the previous lesson, we added background music
that gently fades in and out. In this lesson, we'll add a
settings menu to our game. Let's start in the title scene. Unlike the buttons and
menus we've created so far, which are just floating
in front of everything, I would prefer the game settings to be enclosed inside a panel. There are two different
control notes we can add to our scene, which
behave differently. A panel is just a basic
window that we can resize as we want and rearrange its children
in any fashion. A panel container
will automatically resize itself to
encapsulate its children, similar to a horizontal or
vertical box container. Both have advantages
and disadvantages. I like to use a panel container to save me the
time and effort of sizing and reorganizing
child nodes while I'm building out
my user interface. But I might switch
to a panel if I want more flexibility when it comes to polishing the
finished project. Since our settings class
has some Boolean variables. We can add either a check box or a check button
for each variable, allowing the player to set their value to true
or false accordingly. Let's add one for camera invert x and another for camera invert. I'll add one of each,
so I can see what they look like and decide which
one I like better later. The volume setting
is a float variable, which is often controlled
in menus by a slider. Since a slider will inherently enforce a minimum
and maximum value. Each of these settings
will also need a label node to tell the
player what they're for. Much like we've used horizontal and vertical box containers to organize nodes automatically. We can also use a
grid container to automatically organize the
nodes into a grid pattern. The grid nodes only property is the number of
columns in the grid. Rows are automatically
added to and removed from the grid container
to accommodate the number of children it has. Setting this to two, we
can easily organize all of the label nodes into
the left column and the interactable control
nodes on the right side. The grid container will compress all of its children
as much as it can. But we can specify minimum
sizes for any of them. I'll give the volume slider a minimum x value of 100 pixels, which forces the right column of the grid container to expand. Now I think the
check boxes would look better centered
within the column. Expanding the layout section and the container
sizing subsection, I'll set the horizontal
value to shrink center. Now the check box is centered horizontally within the column. And I'll do the same
for the check button. Now that we can see our slider, let's take a look at its
properties in the inspector. We can set its minimum
and maximum values, as well as the step value, which is a distance between values to which the
slider can be set. Using our linear
volume settings, the minimum value
should be zero. A maximum value of
one is fine as one in linear terms will
equate to 0 decibels. But we can also allow the
volume to go above this. Since we know that the
maximum decibel value of the audio stream player
node is 24 decibels. We can actually type built in
functions into this field, using db to linear, passing 24, we get 15.849 is
the actual maximum value. But this may cause a
decrease in audio quality. Setting the maximum
linear volume to one is fine for most cases. Something as fluid as volume can usually be adjusted to
a very small degree. Since my slider is 100 pixels, I'll set its step value to 0.01 with each pixel representing
a 1% change in volume. We'll also need a title
label for this menu. And a button that closes it. But these don't belong
as part of the grid. We can sort the title
grid container and close button inside a
vertical box container. The title label and
closed button are stretched to fit the same
width as the grid container. I'll center the title using
its alignment property. The button can be set to shrink begin to put it in the
lower left corner. Shrink center to center it or shrink end to put it
in the lower right corner. If you don't want to
use these containers or find them too restricting, you can always use a
normal panel and manual we position each of the child nodes to get the arrangement you want. We can reuse the theme resource for our settings
menu if we want, but you may want to use
different button styles for different parts
of your game. In that case, you can easily
create a new theme resource. I'll just add a panel container setting to the existing them. Change its style
box to a texture. And give it some content
margins of 16 pixels. The check boxes are inheriting
their styles from button, so we can override that
by adding a check box to the theme and replace
all of its style boxes. I'll just use empty style boxes. Then repeat the process
for the check button. Okay. I think the title should have a larger font
size than the other labels. I'll override its font size in the theme override section. Now that I've seen both
the checkbox and check button and can compare them
in the context of the menu. I think I prefer
the checkbox and I'll switch the camera
invert y to match. With the menu built, let's anchor it to the
center of the screen. We've already written
a menu script that handles showing and hiding a control node and can also give focus when
a menu is opened. Let's attach this to
the settings menu node. And set its default focus
item to the first menu item, the camera invert X checkbox. But in order to connect the control signals to our script, it will need to be
a different script than the ones used
by other menu. We can give the
manuscript a class name then create a new script in the UI scripts folder,
inheriting from menu. Let's call it settings menu. Dragging this script onto
the settings menu node, it replaces the manuscript. But we will need to repopulate
the default focus item. In our new script, we will
need to initialize each of our control nodes
to match the values that are already inside
the settings resource. So we'll need to grab
references to each of our control nodes
using At on ready. Then in the ready function, we can set the values to the matching variable in
the file settings resource. With our setting controls sorted into the menu
and initialized. We can now connect signals from each control node to the
settings manuscript. Calling a function anytime a setting has been
changed by the player. The check box and check button nodes will connect
the toggled signal, telling us whether it's
set true or false. And the slider will connect
the value changed signal, giving us the new value
it was just set to. We can set the values
of the variables inside our file dot settings
to match any of the values that have been changed using the control nodes. Since the camera
controls are reading directly from the settings to
determine how it functions. No further actions are necessary to get these
settings to work. But the volume setting
is not going to affect the music
node automatically. We will need to adjust its
volume setting manually. When the volume setting changes, we can call linear volume passing new value
as an argument. Let's run this scene
and try adjusting the volume setting to hear the immediate difference
with music playing. A completed game will likely
have more audio nodes, each with their own
volume setting. We may not want to
adjust them all individually from inside
the settings menu. To anticipate this need, we can emit a
signal here to tell the entire scene that the volume setting
has been changed. Then any node that
is listening for this signal to be
emitted can react to it. At the top of the script, we can declare signals the
same way we declare variables, using the keyword signal. Let's name volume changed. It's conventional to name
signals in past tense, stating what it is that happened that other scripts
should react to. Similar to a function,
we can add brackets to the end of the
signal declaration to add parameters to it. We can not only emit that
the volume was changed, but also emit the new
volume value as a float. When connecting this
signal to a function, the receiving function
should accept a float parameter to
match the signal. Next, let's connect the
closed buttons pressed signal to the menu classes closed
method to close the menu. Most games will allow changing the settings from both
title and game scenes. Let's save the settings menu
branch as its own scene. Then copy the settings menu node and paste it into
the game scenes I. It's always a good
idea to make sure the fade node is in front
of everything else, so I'll sort the
settings menu above it and set its visible
property de false. We can easily add a settings
button to the pause menu. Since both scenes have the settings menu,
much like the Fade, we can export a variable from
the scene manager script, so both the title manager
and gain manager can use it. And in both scenes,
the settings menu is accessed from another menu. Either the Title scenes main menu or the game
scenes pause menu. We can create a very simple
Breadcrumbs implementation within our manuscript, so anytime a menu is
opened from another menu, closing it will return to the previous menu automatically. In our manuscript, we can
declare another variable. Let's call it underscore
Breadcrumb of type menu. Then add an optional
parameter to the open function also
called Breadcrumb. If breadcrumb is anything other than null when the
menu is opened, then we should save
it in the variable, and close that menu
before opening this one. Then when the menu is closed, if the breadcrumb is not null, that menu should be opened
after closing this one, then it can be set back to null. Both the title manager and game manager scripts can
now open the settings menu, passing the menu buttons or pause menu as the bread crumb. Opening the settings menu scene. Let's make sure that the
default focus item is set and that these nodes will process always even
when the game is paused. Let's try running the game. Changing the game settings, and switching to the game scene. Both the volume and camera
controls remain changed, and we can change them
again from the pause menu. We now have a settings
menu that can change the volume and camera
controls in real time. In the next lesson, we'll
add transition events for moving our character
between different levels. I'll see you in the next lesson.
8. Exit: Hello, friends. In
the previous lesson, we added a settings menu. In this lesson, we'll detect
when the player enters a doorway and get ready to transition to a
different level. Let's start in
whichever level is currently loaded
into the game scene. For me, that is a room three. There are two doorways
leading out of this room. When the player enters
either of these doorways, they should transition to a
different level of my game. In a similar way that
our character body is colliding with the
floor static body, we can detect collisions with empty space to trigger
something to happen. The node that does this is
called an area three D node. And just like a physics body, it requires a collision
shape before it can work. Let's add a collision shape three D node and
make it a box shape. To make it fit with the
size of my floor tiles, I'll make it 4 meters in
length width and height. Let's rename the area three
D node to transition. Then make it its own scene
by clicking and dragging it into the scenes folder
in the filesystem tab. Then open the new transition
scene for editing. I'd like to have the transitions origin rest clearly
on the floor. We'll move the collision
shape up along the y axis, half of its height of 4
meters, which is 2 meters. In order to react to
collisions with the player, the transition node
will need a script. Let's make a new
script by clicking on the scroll icon. Okay. Make sure it is saved
in the scripts folder. The default name and
inheritance are fine. The area three D
node has a bunch of different signals for
reacting to collisions. Since our character
body qualifies as a physics body and we want to react to the character
entering this area, the signal we want
is body entered. Connecting this signal
to the transition node itself creates the on
body entered function, which accepts the
body that entered this area as a parameter
of type node three D. We can see the types of the parameters
in the list of signals. For now, let's just print body
to see our collision work. Running the game scene,
entering the transition area, name of the character body
three D note is printed, but also the names of static
body nodes in our scene. We don't want just
any physics body entering this area to
trigger a reaction, only the player character. We can check if the body
is of a certain class. Or check the node's name to see if this is the
player character. But those solutions
aren't very flexible. An easier and cleaner
method is to make sure only the player
character can actually collide with this area
using collision masking. First, let's open the
character scene and select the character
body three D node. Looking under the properties inherited from collision
object three D, expand the collision section. There are two sets of numbers here going all the way up to 32. These are collision layers, which we can use to
control which objects in our game can collide with
each other and which can't? So far, everything in our game exists in the default Layer one, and masks or collides
with only Layer one. Since layer one is the default and everything collides
with it by default. Let's have layer one
be the game's terrain. The way these layers are
displayed in blocks of eight, I like to think of each block as layers that are
grouped together. The first eight layers
of my game might be reserved for the world
or environment layers. Our character can exist on
a different unique layer. Let's use layer
nine as an example. This way, the entire
second block of layers can be reserved for collision
layers related to the player. Opening the project settings,
in the general tab, under layer names, we
can change the names of each of these 32 layers that exist for three D physics. I'll name layer one terrain
and layer nine player. Hovering over the layers in the inspector will
display their name. In the transition scene, since nothing needs
to be able to detect or collide with our area, it actually doesn't need
to exist on any layer. Feel free to set aside a collision layer for transitions
if it helps you though. The transition
only needs to have a collision mask to detect
collisions with the player. Turning off layer one and only masking layer
nine, the player. We can further enforce
this by changing the properties of the
area three D node, telling it to monitor for
other physics bodies, but not be monitorable
by other physics bodies. Now, the only possible way that this area
three D can trigger a collision is if it detects a physics body on layer nine. It won't look for
any other layers, and it will be invisible
to everything else. The transition area
can be positioned to cover the empty space
beyond the doorway, making it impossible for the player character to exit this room without
colliding with it. Opening all of my
different level scenes. I can see that this transition is meant to connect to Room one. So let's name it two room one. Duplicating the node
will automatically rename the duplicate
to two room two, and I can reposition
it to the other door. T While my small levels, each only have two transitions. You can imagine how a large complex level might have many more that are
hard to keep track of. So we should group these nodes together to sort them
inside the scene tree. Let's add a new node, just a normal node, and name it transitions. Then re parent the
transition nodes to it. This will make them
easier to find and use not only in our scene
tree in the editor, but also from our scripts later. We can copy and paste
these nodes into our level scenes and reposition them to cover every doorway. Renaming them so that we
know where they lead. Opening up the progress script, we can change the variables
or add new ones to store the name of the level the player is currently
in as a string. I'll initialize it
to be Room one. For this method to
work is intended, the value of this
variable must be the exact spelling of the scene name of the level
in the file system tab. In the transition script. We can then set this variable as the player transitions
between levels. Let's export a variable
to hold the name of the level this transition
leads to as a string. Then when the character
body enters this area, we can set the value of file
dot progress dot level. To be the level
name. Next, we'll want to tell the game manager
to switch to this level. Like we did in the
player script, we can always access the
scenes root node by starting our node path with
dollar sign slash root, then get the game
manager with game. Let's just tell the
game manager to call a function we haven't written
yet named Load level. Since the name of the level is already stored in the
progress resource, there's no need to pass
it as an argument. Since the only time
we're trying to access the game manager is in this
one singular instance, there's no need to store it in a variable or use add on ready. Especially since logically, only one transition event can possibly happen per level
loaded into the game. So every transition declaring
the variable and accessing the game manager when only one actually needs it would
be more wasteful. In the game manager script, let's first grab a reference to the level that already
exists in the scene. We can click and
drag the node into the script to automatically
produce its node path, or even hold down the control
or command key while we release the mouse button to automatically store
it in a variable. I'll rename the variable
to current level. Then declare the
load level function we called from the
transition script. First, we'll need to know if there is already a level loaded by checking if this variable contains a value
or if it is null. If it does contain a value, then we should first
await f to black. We can then unload the
current level from our game scene using the
built in function Q. We'll go overloading the next
scene in the next lesson. For now, let's just await
Fate to clear and try it out. If the player
enters the doorway, the scene fades to black. The current level is removed
from the scene tree, and then we fade back
in to see our character and free fall since there
is no longer any level. Our Debug panel is
giving us two warnings. We've declared a variable that was never used in the script, and there's an unused parameter in our collision function. We can prefix unused
parameters with an underscore to flag them for the engine so
they can be ignored. Switching to the
transition script, the body parameter
is no longer used. We can add the prefix
to the name of the variable to
satisfy this warning. In the scene Manager script. We have declared the settings manu variable in this script, but it is not being used
inside this script. We have done this on purpose because the settings
many variable is inherited and used by all
of this class's inheritors. We have no intention
of ever actually using the scene manager
script directly, but are using it to contain all the similar properties and behaviors of a group
of related scripts. In other languages,
this would be known as an abstract class. This pattern of using
inheritance in this way is known in object oriented
programming as abstraction. We can choose to
ignore any warning by adding another line before the line that is triggering the warning using
at warning ignore. Then specify the warning
we want to ignore. In this case, we have declared an unused private variable. With both debug
warnings addressed, running the game will no
longer present any warnings. We now have the current
level unloading when the player
enters a doorway. In the next lesson, we'll
load the next level and position the player
in the corresponding doorway on the other side. I'll see you in the next lesson.
9. Enter: Hello, friends. In
the previous lesson, we had the player exiting
the level through a doorway. In this lesson, we'll load the next level and position
the player inside it. Before loading the next level, we have first
checked if there is a current level and if
so, we are unloading it. We can then assign
a new value to the current level variable
since it is now empty. Using a built in function load, we can load any resource
from our file system tab, including scenes by specifying
a file path as a string. To keep our scenes
better organized, let's create a sub folder inside the scenes folder named levels. Then put each of the games
levels inside this folder. Selecting any of
the level scenes, we can click and dragon into the load functions brackets to populate it with
the file path. Since all of our games levels are located in the
levels folder, and they all end with
the dot TSCN extension, we can separate these
parts of the string using the Append
operator, the plus sign. Then replace the name
of the level scene with the ones stored inside
the progress resource. The load function
returns a resource. But the current level
variable is a node three d. We first need to call a function on
the resource to turn it into a node, dot instantiate. Then add it to the scene tree with the function add child. Since this is the
game manager script attached to the game
scenes root node, the level node will be added
as a child of the game node. But what about when
the game first starts? Or if the player
loads a safe file? We can start our game scene
without a level node in it. Deleting the level node
from the game scene, We can remove the add on ready from the current
level variable. Let's override the ready
function inherited from Scene manager and call
load level right away. Before running the superclass definition from Scene Manager. This will allow us to load
any level in our game, simply by setting
its file name inside the progress resource
and calling load level. It will cleanly transition
between levels by first fading to black and
unloading the current scene, then loading the next
level and fading back in. If this is the
first time a level is being loaded and there
is no current level, it will skip that step and
immediately load the level. But we will need to position the player inside
the new level and also make sure the position is tied to the path they
entered the level from. Let's start in the
transition scene, adding a new node which
does nothing more than simply marking a
position in three D space, a marker three D node. The only difference between
a marker three D node and a node three D is
that the marker will draw a Gizmo and the editor
showing where it is. Let's rename this node entrance and move it forward
along the Z axis, so it is outside the
area node and far enough away that the player character will not collide with the area. In the transition script, we can drag this in
and hold control or command to add it as a
variable without ready. This will be the position
where the player will be spawned into the level when entering through
this transition. In our level scenes, we can rotate any of our
transitions to point the Z axis and the
entrance marker to an appropriate spot
inside the level. If for any reason,
you need to move an individual entrance marker without affecting
other transitions, you can always right click on a transition node and toggle
on editable children. This will allow you to move a
specific entrance marker to account for obstacles or variations in floor
height, for example. Before we can figure out
how to position the player, we will need the
level scenes to keep track of their transitions
with the script. Let's add a new script
to the levels root node named level in
the scripts folder. If we drag in the
transition node, we can store it in a
variable with add on ready. But we don't want this node. We want all of its children. Using a built in
function get children, we will store a list
of all the child nodes to this transition node
in an array of nodes. The order of the nodes
in the array will be the same as the order
in the scene tree. We can access the
contents of an array by specifying an index number
starting from zero. Let's write a function
in our level script that gives the transitions index number and returns coordinates where the player
character should be placed. Let's call it get entrance, accepting the transition index
as an integer parameter. And returning a vector three, the position in three D space for the character to be placed. We can access the
transitions array, putting the index number
in square brackets. If the index number is zero, then that will give
us the top node, followed by one, two, et cetera. Next, we'll get the
entrance marker from the transition and get the global position property of the entrance
marker three D node. Since the notes position
is relative to its parent. We want to know the
global position as it exists within the entire
scene, so we can return this. We will need to attach
the level script to the root note of
every level in our game. There's also a
possibility, however, unlikely that when loading the level before
repositioning the character, the character just happens to already be inside
a transition area. In the transition scene, we should disable the
monitoring property by default. Then in the level script, when a level is loaded and the character is
positioned properly, we can call a function to
activate all the transitions. A very useful feature of
arrays is our ability to iterate through each of
their elements using a loop. Starting with the keyword four, we can specify an alias, we will use inside the loop to refer to the current element. Followed by the keyword in, then the name of the
array and a colon. Anything we put
inside the loop block will run on each
element of the array, and we can refer to the current
element using the alias. For each transition
in transitions, we can set its
monitoring property to true to activate all of the
transitions in this level. In the game manager script, we can grab a reference
to the player character. Then when a level is loaded, we can position the player
at the levels entrance. Let's use zero as the
argument for now. Now that the character
has been positioned, we can activate all the
transitions in the level, since we know that the character is not touching any of them. Finally, we need to
set the values of every transition in every
level in the inspector, so each transition knows
which level it connects to. Running the game scene, the
game now starts in room one, and the player is positioned
in front of the doorway. The top child of the
transitions node. Enering the any doorway, the level is unloaded, and the game scenes, which
is to the correct level. But no matter which
doorway we pass through, the player is always positioned in front of the same doorway. Let's add another variable to the progress resource named
transition ID of type int. Then also add this variable to the transition script exported. When the player character
enters this transition, we can set the value inside the progress resource to
match the exported variable. And in the game manager script, use this as the
argument to select the transition where the
player will be placed. Starting in Room one, let's take a look at the
transition to room two, which now has a
transition ID variable with a value of zero. This will be the index of the transition node that returns to the scene
from room two. We can see that
the transition to Room one is the top child node, which is index zero. So that's correct. Taking a look at the transition from
room two to Room three. Let's switch to the
Room three scene, and we can see that
the transition from Room three to room
two is Index one. So we will need to change
the transition ID of the transition from room
two to Room three to one. And the transitions leaving room three will also need
to be changed to one. Running the game scene, we can now run between
all of our levels, both clockwise and
counterclockwise, and the player is positioned
correctly every time. If your levels have
many transitions, I recommend setting
a convention for your game on how you
organize your transitions, such as clockwise from
North, for example. You may also want to change the index number to a string and organize your transitions into a dictionary instead
of an array. I would also like
to make sure that both the character
and the camera are pointed away from the transition that the player
enters a level from. In the level script,
just like we returned the entrance global
position from a function, let's also return
the transitions rotation from another function. Now, the game manager
script can access this function to determine which direction they
should be facing. We can pass this information
to the player script and use it to rotate
both the character and the spring
holding the camera, calling a fiction, passing the returned
rotation as an argument. Pase direction, accepting
the y rotation as afloat. Can easily set the spring
arms rotation to match. But the spring arm
must be rotated 180 degrees to be
behind the character. Since the rotation is
measured in radians, that means we can either
add or subtract Pi from this value to point the camera in the
correct direction. We can then pass
this rotation value down the chain to
the character class. And add another
function to rotate the character rig to immediately face the desired direction. Let's try it out. Even if we position the
character and camera at weird angles before
entering the doorway, they will both be facing away from the doorway
on the other side. This will help the player understand which
way they came from. We now have the player able to transition between levels
and our game scene. In the next lesson, we'll save and load the
players progress. I'll see you in the next lesson.
10. Save: Hello, friends. In
the previous lesson, we completed our level
transition process. In this lesson, we'll save and load the players progress
through the game. Our file script has multiple
variables containing custom resources we
used to keep track of the game settings as well
as the players progress. Let's start with the
settings resource. In the ready function, we are creating a new
settings resource, but we should only
do that if one doesn't already exist
that we can load instead. The I condition will first check if the ceti resource exists, and if it does, load it, else, create a new one
and save it right away. This line will actually be the else block of
an IF statement. Godo allows us to save and load any resource using
Singleton classes. The resource loader
can load our resource, calling the load method, passing a string
argument, a file path. Instead of accessing a
resource within our project, this time, we want to load something from the
user's system. The file path starts
with user Colon slash. The Godo engine will use
the shortcut to find an appropriate
location on any system to save and load files
unique to each user. Let's name the file
settings and give it an extension of RS for resource. This load function
returns a resource, which we can assign to
our settings variable. Before loading the settings, we can perform a
similar function to first check if the file exists. Since the file path
is now written twice, we will also be
using it more too, and this is a value that
should never change. I would be more efficient to
declare it as a constant. If the settings
file doesn't exist, then we can create it. And immediately save it using
the resource saver class, calling the save method. The save method requires
a resource to save, which is our settings resource, and the file path to save it to, which is the settings path. Now when running the game, if the settings
exist for the user, then they will be loaded, and if not, they will be created. But we also need to make some changes to the settings menu. All of our settings need to be set to the values
that were loaded, which they are in
the ready function. We have set the initial
slider position of the volume setting using the numbers stored in
the settings resource. But we also need
to set the volume of the music node
and emit the signal here like we did below in response to the slider
value being changed. When the player opens
the settings menu, adjusts some settings,
then it would be a good idea to save those settings when
the menu is closed. We can override our menu
classes close method, so when the settings
menu is closed, we will tell the file auto
load to save the settings. Back in the file auto load, we can add the function
that saves the settings. The progress resource
on the other hand, will not be handled in
the ready function. We can separate each
of these methods into public functions for accessing the progress
resource in a variety of ways. Checking if it exists, starting a new
game, and loading. Using the same format
as our settings, we can declare a
save path constant. The safe file exists function, will return a Boolean, returning the same
value returned by resource loader do exists. Passing the save
path as an argument. Creating a new progress resource
when new game is called. Saving the resource
when saved game is called and loading the resource when load game is called. In the title scene, we
can now easily create a new progress resource
when new game is pressed. And load the existing safe
file if continue is pressed. It would be good UI design to have the continued
button disabled b and only enable it if there
is a safe file to load. Let's grab a reference to the continued button
using add on ready. In the ready function,
if a safe file exists, we can enable the
continued button by setting its
disabled property to false Then also tell
it to grab focus. That way, the default option
will be new game the first time the game is played and continue every time after that. All we need to do is decide
when to save the game. Since this file script
is an auto load node, you can easily call the save
function to do this from the pause menu or save
point object in your game. I'll just have my game
automatically save every time the game
manager unloads a level. Back in the title
manager script, let's also complete
the exit function. Like we fade in when
the game starts, we can await fade to
black before exiting. Quitting the game is a built in function of the scene tree, which we can access
with get tree, and simply call the Q function. In our custom resource scripts, we will need to add a
export to the front of any variable declarations we want to be written
to the Safe file. Any variables we don't want to save can just omit
the export tag. If, for example, you are using the safe point method
common to JRPGs, the variable saved in
the safe file would be an ID number associated
with the safe point, so that would be exported. But the variables containing
the current level and transition ID would not need to be saved
in the safe file, since they are not
needed to load the game, but are still needed to
transition between levels. So they would not need to
be exported in that case. In my game, I will need to
export these variables, since I am automatically saving my game with
every level transition. One final note in the
file auto load script. Using the dot RES extension, we'll write a safe
file as a binary file, making it difficult for
the user to access. Adding a T, making the extension
TREs for text resource, we'll write the safe file
as a common text file, which can be viewed and edited in any text editing software. Both are acceptable formats. I'll save the settings as
text and the progress as a binary file. Let's try it out. Running the game, the
continue button is disabled, but we can start a new game. In the game scene, I'll run
into a different level, which should save my game. Pausing the game, I'll change
some of the game settings. Then exit the game, returning to the title scene. Now the continued
button is active, since the safe file
exists to load. But let's exit the game first, which fades to black and
closes the game window. Now, I'm running the game again. This time, the continued
button is enabled, since our safe file persisted
between play sessions. Continuing our existing game, we start in the other level, and the game settings are also
the same as we left them. A. Now, let's take a look at the files that were
created on my hard drive. The settings file can
be opened in notepad. We can see the exported
variables and their values clearly and even edit
them and save the file. Next, let's open the save file containing the players progress. Most of this information is. Even if we were to edit
something and save the file, Attempting to load the
saf file will crash the game since it is no
longer formatted correctly. Let's run the game one more time and see that
our settings file, which we manually
edited a notepad, is loaded, and the
values are correct. So saving resources in
binary format is more secure and text resources
are very easily edited. In my opinion, it is often more beneficial for ND
developers to be able to troubleshoot save
file editing with your players than
to hide progress data to prevent cheating. We now have the
players progress, saving and loading
between play sessions. In the next lesson, we'll wrap up with displaying
the game's credits. I'll see you in the next lesson.
11. Credits: Hello, friends. In
the previous lesson, we saved and loaded
the players progress. In this lesson, we'll
display the game's credits. Let's start by creating a
new scene, the credit scene. Since this is typically just a black background
with white text, I think that a user
interface node makes the most sense
as the root node. Let's give the root note a script and call
it credits Manager. Inheriting from Scene Manager and sort it into the
manager scripts folder. We can copy the F note from any other scene and paste
it into the credit scene. Then assign it in the inspector, along with adding a music
track for the credit scene. In the title manager script, we can now set the behavior of the credits button in the main menu to change
to the credit scene. We should also save the credit scene in the scenes folder. Then in the credits
Manager script, let's add a function
that returns back to the title scene. In the input function. We can check if the player has
pressed a specific button. I'll just reuse the jump button, and if so, return
to the title scene. Our scenes are now connected and can transition
back and forth. Most games that are still in development will
have the credits accessible from the
main menu since the end of the game is
yet to be completed. So there will be
no way to access the credit scene by
completing the game. Having your credit
scene included throughout the development
of your game also helps keep track of all who make a contribution to it throughout
its entire life cycle. Speaking of which, credits
are usually just a series of text labels organized
vertically, slowly scrolling upward. So adding a vertical box
container to the scene, let's name it scroll. And have it anchored to
the bottom of the screen. But with the wide setting. Now the left anchors are
both in the bottom left of the screen and the
right anchors are both in the bottom
right of the screen. This is typically where
credits will originate from. Credits are often broken down into categories or job titles, followed by a list of names of contributors
in that category. So inside our vertical
box of all the credits, we can add more vertical boxes
to contain each category. I'll call this one game design. Inside the category,
there should be a label for the title
of the category. And I'll have it
say game design. Credits are typically
aligned to the center. I'll make the font white. Size 64, and use the fancier of my two
fonts for category titles. Let's duplicate this label no to add another
label for our name. Then reduce the font size and change the font to one with more emphasis on legibility. We are now listed as
the game designer in our games credit scene. But the credits are starting
inside the game window. They should start off the
bottom of the screen. Let's switch back to
the scroll vertical box and change its anchor
presets to custom. This will keep the
anchors where they are, but allow us to change
each setting individually. We want the grow direction of this control element to go down, not up, changing the
setting from top to bottom. Let's collapse and duplicate the game design
category to create our next category,
game development. Since all of the
settings are duplicated, we only need to
change the names of the nodes and the text
properties of the label nodes, and also add any
additional names to each category as required. And repeat this process to continue adding as
many categories as you need for your
game to make sure everyone involved is credited. I'm also going to
give credits to the artist who made
the character models, animations, and environment
models, K Laberg. Penzila, who created the
user interface assets, and Eric the Funny Baron, who produced all of the music. And finally, end with a thank
you message to the player. I'll also make the thank
you message larger. Our credits are all here, but they're all cramped
together and difficult to read. Let's add some separation to the vertical box container
to separate the categories. I'll set each category 256 pixels apart from each other
to give them lots of space. And we can see that
the credit scroll will get very long very fast. The last thing I want to
do is make sure that the thank you message is displayed
on its own at the end. So I'll give it a
custom minimum size y value matching the window
size setting of my game, 720 pixels, and center the text vertically within
this space using the labels vertical
alignment property. Back in the credits
Manager script, Let's export a variable for the scrolling speed and give it a default
value of something like 32 pixels per second. And grab a reference to the
scroll using add on ready. Then in the process function, which takes Delta as
a float parameter representing the
seconds that have passed since the last frame, change the y position
of the scroll, subtracting speed times Delta. Since when working
in two dimensions, zero is at the top
of the screen, and moving something up is moving in the
negative direction. We then need to
stop the scrolling once the thank you message
reaches the screen. Looking in our two d view, selecting the
scroll and exposing the transform position
value in the inspector, we can move the scroll up until it reaches the
desired position, where only the thank you
message is displayed. Taking a look in the
inspector at the transform, we can see that the y
position of the scroll is now equal to the y
portion of the size, but is a negative number and plus the height of
the screen size 720. Back in the script,
we can check if the y position of the scroll
has exceeded this value. C hecking if it is less
than the negative of size dot y plus the window
height of 720 pixels. We can be even
more accurate with this calculation by accessing the display server
singleton class and asking it for the
current window size, then access the y value. If this is true, then the end of the scroll
has been reached, and the thank you message is
at the center of the screen. At this point, we would like the scroll to stop scrolling. L et's create a Boolean
variable called, which will default to false. We can then set it to true
when the scroll has met this condition and also set the y position
to match this value exactly so it is perfectly centered in case it
overshoots the mark. Since we are performing
this calculation twice, let's change it
to a variable and calculate it using add on ready. Then at the top of
the process function, we can check if the
scroll is at the end, and if so, return, preventing the scrolling
behavior from happening. This works, but instead of just exiting when the player
presses the jump button, I would rather have
the scrolling speed and only return to the title screen at the
end of the scroll. In the input function, if the player presses
the jump button, then also check if
the end of the scroll has been reached before
switching to the title scene. Exporting two new variables, let's call them slow
speed and fast speed, both as floats with
default values of 32 and 512 pixels per second. The actual scrolling speed no longer needs to be exported, but instead, let's set it to the slow speed
using add on ready. We can then set the scroll
speed to the fast speed if the player presses
the jump button before the scroll
has reached its end. And set it to slow speed if
they release the button. A Let's try it out. Clicking on the credits button, switches to the credit scene. The credits begin scrolling up from the bottom of
the screen slowly. But we can speed it up by
holding down the jump button. Once the thank you
message has been reached, the game will wait
for a new press of the Jump button to switch
back to the title scene. And that's it for the
essentials of game development. You now have a fully
function skeleton project that could be used to
develop a game of any genre.
12. What's Next?: Welcome to my course on Dialogue and event Systems in Gado. This course is a continuation of game development essentials
in Gado but can be followed and applied to any project that involves a character that
the player can control to interact with NPCs or objects by pressing a button and
displaying dialogue. You're welcome to join
our discord server to work on this course
alongside your peers. In this course, we will
cover the creation of dialogue boxes, events,
and interactions. Then tie them together
with progress flags, camera controls, and
character animations. When you're done, you'll have a comprehensive dialogue
and events system for your game that can
be used to produce high quality cut scenes and complex branching
narratives that react to players choices and allow them to progress
through the game. You'll also learn useful skills for working with the
Gada game engine, organizing and designing your projects to
be more scalable. You will be learning
how to code with GD script with everything
explained in detail. We will apply object oriented design principles
in our scripts, inheritance, encapsulation
and abstraction, alongside other software
engineering design patterns like Singleton and
Composite pattern, to keep them organized, customizable, and reusable
for a project of any size. All of the project files are
also available on Github, if you need to review
the project as it was after completing
each lesson. These videos were recorded
using Gadot version 4.2 0.2. The project starts
with assets from K Kits Character and
Dungeon remastered packs made by K L auberg and basic
Guy bundle made by Penzla. All are available to
download for free on dot IO.