Introduction to 3D Game Development in Godot! | Thomas Yanuziello | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Introduction to 3D Game Development in Godot!

teacher avatar Thomas Yanuziello, Indie Game Developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Introduction

      1:21

    • 2.

      0-1 Introduction to Programming

      8:03

    • 3.

      0-2 Constants & Variables

      9:11

    • 4.

      0-3 If Statements & Conditions

      9:46

    • 5.

      0-4 While Loops & Operators

      9:23

    • 6.

      0-5 Arrays & For Loops

      10:32

    • 7.

      0-6 Stacks & Functions

      10:53

    • 8.

      0-7 Match & Return

      9:34

    • 9.

      0-8 Scene Tree & Inheritance

      10:21

    • 10.

      0-9 Abstraction & Encapsulation

      10:41

    • 11.

      0-10 Polymorphism

      8:01

    • 12.

      0-11 Dictionaries

      8:54

    • 13.

      0-12 Debugging

      9:08

    • 14.

      Basics

      13:51

    • 15.

      Scripts

      9:27

    • 16.

      Controls

      15:09

    • 17.

      Import

      8:47

    • 18.

      Walking

      12:53

    • 19.

      Rotation

      14:05

    • 20.

      Jumping

      12:45

    • 21.

      Gravity

      15:38

    • 22.

      Camera

      12:46

    • 23.

      Dungeon

      13:29

    • 24.

      What's Next?

      1:56

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

205

Students

2

Projects

About This Class

In this course, we will cover everything you need to know about using the Godot Engine to build a third person character controller; from the very basics of working in 3D space up to and including controlling a character and camera in a 3D scene.  We will explore how to use math, physics, and vectors to achieve the third person gameplay you expect from most modern 3D games; with the left stick moving and rotating the character, and the right stick rotating the camera around them.  If you are unfamiliar with the Godot engine and scripting in GDScript, I've included beginner lessons in how to write scripts.  If you already know how to use Godot and write scripts, feel free to skip over these lessons and jump straight to the lesson named Basics.

Click on my website link in my profile to join our discord server!

You'll also learn useful skills for working with the Godot game engine, organizing, and designing your projects to be more scalable.  You will be learning how to code with GDscript, with everything explained in detail.  Our scripts will be written to be highly customizable and reusable.  All of the project files will also be available in GitHub if you need to review the completed project.  These videos were recorded using Godot version 4.2 using assets made by Kay Lousburg on itch.io

Meet Your Teacher

Teacher Profile Image

Thomas Yanuziello

Indie Game Developer

Teacher
Level: Beginner

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction: Welcome to my introductory course on three D Game Development and Gada. In this course, we will cover everything you need to know about using the Gudo engine to build a third person character controller from the very basics of working in three D space up to and including controlling a character and camera in a three D scene. We will explore how to use math, physics, and vectors to achieve the third person gameplay you expect from most modern three D games, with the left stick moving and rotating the character and the right stick rotating the camera around them. When you're done, you'll have a good understanding of third person character control and a fully functional implementation that is highly customizable for any three D project, whether it's a platform or action or PG, or even a cozy farm sim. You can take the project in any direction you wish. You'll also learn useful skills for working in the Gada game engine, organizing and designing your projects to be more scalable. You'll be learning how to code in GD script with everything explained in detail. Our scripts will be written to be highly customizable and reusable. All of the project files will also be available on GT Hub if you need to review the completed project. These videos were recorded using Gada version 4.2. Assets downloaded from HO made by Kusberg. 2. 0-1 Introduction to Programming: Hello friends. If you haven't done so already, you'll need to download the GdoGameEngine from gdongine.org. The engine is free, but you can donate to support the project if you want to. There is no installation process. You can just extract the zipped file and open it. Be sure to relocate the executable to a better location. The initial launch window is a list of your current projects. But if you don't have any projects yet, you'll be prompted to explore demo projects. We can ignore this and just create our own new project either by clicking on this button in the project list or the Create button at the top of the window. Here we can give our project a name, which would typically be the name of your game. I'll just put intro to programming. The project needs to be stored on our computer's hard drive, and we'll create a new folder with the same name of the project. By default, it wants to store everything in the documents folder, but it would be better to create a new subfolder for Godot projects. The renderer changes how certain two D and three D objects are rendered, but we won't be drawing anything, so we can ignore these options. The renderer can also be changed later. And version control compatibility with Github is included by default if you want to use it to backup your project or share it with others. When you're ready to start, click Create and Edit. The GDOEditor window will open our newly created empty project. The editor is divided into five main docks. A preview of our game is in the center. The lower left dock contains a file browser showing the contents of the project folder we just created. It currently only contains the Gadot icon. The upper left dock contains the scene tree, a list of everything that is in our current scene, which is currently empty, so it wants us to create a root for the scene tree. We won't be drawing anything, so we don't need to specify if this scene is two D or three D or a user interface. So we can just select other node. There are a lot of different nodes we can create, but we are only interested in learning how to write scripts, so we will just use a default node. We can rename our new node by clicking on it after it has been selected or right clicking to open its context menu and selecting rename. Let's name this node Lesson one. Selecting any node in the scene tree will reveal its properties in the inspector, which is docked to the right side of the window. We can see the name of the node that is selected and its properties organized into expandable categories, but none of these properties are applicable to what we're doing. Before we do anything else, we should save our new scene, either by selecting Save Scene from the scene menu or using the shortcut Control S or Command S. This opens a dialogue box where we can specify a name for our scene with the TSCNEtension, and a destination folder. Let's create a new folder for Lesson one, then save the scene in this folder. We can now see in the file system tab that our new folder has been created for Lesson one, and it contains the Lesson one scene, which is marked with the Clapperboard icon. Next, we'll attach a script to the root node of our scene, either by selecting the root node and clicking the attached script button or by right clicking on it and selecting attached script. The language is GD Script, which is the programming language we are going to be learning. Every script will inherit from the node type it is attached to by default. Since the root node is a default node type, our script inherits from node. We are not using a template nor a built in script, and we can specify where this script will be stored within the project. Let's put this in the same folder. Lesson one. When naming scripts, it is important to not be arbitrary, but instead describe the type of object or behavior we are creating through the script. So let's name this Hello. Scripts have the file extension dot gD. Then click on Create to create the script. This will switch our preview to script view, displaying our newly created hello script. We can also see our script in the file system tab indicated with a cog icon, and the Lesson one root node has the script icon to show us that it has a script attached to it, which when we hover over, will tell us the name of the script. Clicking on this icon also opens the script if it isn't open already. Our script only has one line, which describes its inheritance from the basic node type using the keyword extends. This just means that our script can do everything a node can do plus anything we write here. It also allows us to override the behaviors of the node that already exist with new behaviors of our own. For example, nodes have a function named ready, but it doesn't do anything. So if we want our script to override this behavior of doing nothing, we can declare our own function of the same name using the keyword funk. Funk is then followed by the name of the function, typically written in lower snake case, which means all lowercase letters separating words with underscores. Many of the functions defined by nodes are preceded with an underscore, and the name must be an exact match to override the behavior. The name of the function must be followed by parentheses and a colon. We'll go over why later. Pressing Enter after this line, the next line is automatically indented by one level. This is how GD Script knows which lines are contained within the function and where it ends. The line is highlighted in red because functions are not allowed to be empty. So it is a good idea to write the word pass to give new functions a body that does nothing. Now that the error is gone, we can see this blue arrow icon appear in front of the function declaration, which is the override symbol, indicating that this script is now overriding the ready behavior of our node. Instead of doing nothing, we want our node to say hello, which we can do with a print statement, replacing the line that previously said pass. The print statement looks similar to a function declaration with a name followed by parentheses because it is calling a built in function. This function requires an argument, which is what goes inside the parentheses. And this argument is what will be printed. Using quotation marks, we can write anything we want, and it will be printed out by our script when the node is ready. Since the script is named hello, the behavior we want to create is for the node to say hello. With our script ready, we can run our game using the run current scene button in the upper right or using the shortcut F six. This will display an empty window, since our game doesn't draw anything, but it also opens up the fifth dock, displaying the output panel. And in our output, we can see our node has printed the words hello world when it was ready. We can end this simulation by closing the window, clicking on the stop button, or using the shortcut F eight. We can change the text that is printed to say anything we want and run it again to see that our node can print out anything we tell it to. We now know how to attach scripts to nodes and run them. In the next lesson, we'll learn about constants and variables. I'll see you in the next lesson. 3. 0-2 Constants & Variables: Hello, friends. We will be starting each lesson by first creating a new folder for the lesson. Then creating a new scene in that folder of the same name. Double click on a scene in the file system tab to open it. With multiple scenes open, we can switch between them by clicking their tabs in the preview doc. Today, we'll discuss the differences between constants and variables using days as our frame of reference. So let's attach a new script to the root node of the scene and name it days. Let's start with constants and declare one at the top of the script using the keyword const, followed by the name of our new constant, which is conventionally written in upper snake case. A constant is like a container where we can store information, but that information is never allowed to change. Something that we would consider to be a constant might be the number of days in the week. Since it can't be changed, it must be given a value immediately, which is why we have an error. We must follow the name with an equal sign, the assignment operator to give the constant a value. The number of days in the week is seven. It is reasonable to assume that it will always be seven and it is the same for everyone. Therefore, it should never need to change its value. Conversely, we have variables, which are also containers for storing information, but ones that are designed to be changed. We can declare a variable in the same way using the keyword VR, then giving it a name conventionally written in lower snake case. This makes it easy to see the difference between constants and variables in our scripts. Something that changes frequently might be the value of today. Unlike constants, variables are allowed to be empty. Let's override the ready function of our base node and print out the values of both the number of days in the week and today. We can see that the number of days in the week is seven, but the value of today is printed as null, which means empty or no value. Let's fix that by initializing today to be Thursday. And we can make our output more readable by adding some context, starting with something like today is then leaving a space after is a plus sign, then our variable. In this case, the plus sign is being used to append two pieces of sentence together. If we try to do the same thing with our constant, however, it will cause an error because our constant is a number, not a word. When we declared our constant, we assigned it a value of seven, which is a number without any decimal points, which in math is called an integer. So our constant has a type of integer. Today, on the other hand, was assigned a value of some characters wrapped in quotation marks. In programming, we call this a string. So our variable has a type of string. The plus operator can only be used when the types of both operands are compatible. We can get around this by using a built in function to change the type as it is being used here to a string, passing the constant as an argument to the function. This will not change the type of the original constant, only how it is being used here within the context of the append operation. And we can continue adding more context after with another appended string as well, remembering to add an extra space at the front. So now we can print out our constant and variable using them in easy to understand sentences. After these lines, let's pretend a day passes. We can add extra context to our scripts that will never be seen by players, but only exists to help us as developers understand our own code or code written by others. These are called comments and are created using the OctathorpPound sign or hashtag. Any text written after this symbol will be colored in gray and won't affect how the script runs in any way. They can be on their own lines or at the end of a line of code. So let's use a comment to explain that we are pretending that a day has passed between the code above this line and the code below. We'll change the value of today to be Friday and print out another sentence. Today is plus Today, which now holds a different value. We can see that the value of today was first printed as Thursday and then changed and is printed as Friday on the next line. The comment had no effect on the script being run. If we try to change the value of our constant, obviously, this will cause an error because constants are not allowed to be changed. If we no longer want to use some lines of code in our script, we can easily change them to comments by highlighting them and pressing Control K or Command K, adding the comment symbol at the front of every highlighted line. Pressing it again will remove the comments. We can specify the type of variable or constant by adding a colon after the name. Followed by the type name, we want to restrict this constant or variable too. Let's restrict our constant to be of type int and today of type string. Other than strings and integers, there are two other primitive types we can use to store basic information. If a number has decimal points, it is no longer an integer and instead a different type called a floating point number, which uses the type name float. Let's add another variable for today's date and give it a type of floating point number with a value of 12.05, which is today's date December 5. And the last type is called Boolean using the shortened name Boole, which can either be true or false. So we'll add another variable for whether or not today is a holiday. I'll restrict this to be a boolean, but I won't give it an initial value. Let's add two more print statements to print out the values of these variables with context. Today's date is plus date, and today is a holiday. Colon plus is a holiday. When we run this now, we can see our date printed out, but is a holiday says false, despite not giving it a value. This is because all primitive types have a default value, but the default value can only be assigned if we specify the variables type. Since we specified that is a holiday is a boolean, it was assigned to the default value for a Boolean, which is false. The default value for both an integer and a floating point number are both zero, and the default value for a string is just an empty string, which is not the same as null, but is a string with no characters in it. If we don't specify the type of a variable, its default value will be null, but it can be assigned a value of any type, and its type can change at any time. Let's remove the type from date. Then give it a new value of December 13 and print it out a second time. The variable starts out being assigned a value of a floating point number, gets used by the print statement, then assigned a string value before being printed out again. This is allowed because GD Script is a loosely typed language. This offers greater flexibility when writing our scripts, but also greater responsibility to avoid type compatibility errors like we saw with the plus operator. It is recommended that you always give your variables a type, not only to avoid errors, but also to improve the efficiency of running your scripts. If the engine knows the type of your variables, then it also knows exactly how much memory it needs to occupy. This also applies to using constants in place of variables. If the engine knows the value can't change, then it can be used more efficiently. In the next lesson, we'll learn about how to control the flow of our scripts. I'll see you in the next lesson. 4. 0-3 If Statements & Conditions: Hello, friends. I've already gone ahead and created a new scene in a new folder for this lesson. Today we'll learn how to control the flow of our scripts to run different lines of code, making choices about whether or not we can see. So let's attach a new script to the root node of our scene and name it vision. We will start our script by overriding the base nodes definition of the ready function. The most basic method of controlling the flow of our scripts is the I statement, which starts with the keyword I. I is followed by a conditional statement which must either be true or false. For now, let's put true, which is then followed by a colon. Similar to the colon at the end of our function declaration, this marks the end of the statement, and continuing to the next line, it is automatically indented another level. Any lines of code following this if statement, which are indented will only be run if the condition of the if statement is true. So let's print out something like the condition was true and then run the current scene to see our print statement working. If we change true to false, we can see that the print statement is skipped over this time. We can add another line after the if statements indented body on the same indent level as the I statement simply with the keyword se followed by a colon. This creates another conditional body that will be run only if the condition of the original I statement was not met. Let's duplicate the print statement and change it to say something different. Since the condition was false, the se block was executed instead of the I block. And if we change the condition to true, the reverse will happen. This isn't very useful if we always know the outcome of the conditional statement to be true or false, but we can replace it with a variable. Let's declare a Boolean variable, which is whether or not the light is on in the room. Booleans are conventionally named in such a way that they imply their use as a condition. In this case, light is on is implied to be true or false based on how it is named. Now our if statement can set its condition to be whether or not the value of our variable is equal to true. When comparing two values to see if they are equal, we use a double equal sign, known as the equal operator, which is different from a single equal sign like we used before as the assignment operator. We will then print out either I can see or I can't see as a result of whether or not the light is on being equal to true. Since the default value of a boolean is false, we can see the output as I can't see. But if we set the variable to true, it changes to I can see. Since the variable itself is a boolean, either true or false, we can remove the comparison operation altogether and just use the boolean as the condition and get the exact same result. We can reverse the value of any boolean, meaning if it is true, it becomes false or if it is false, it becomes true by using the keyword not. Like the STR function we used in the previous lesson, this only changes how it is being used in the local context and does not actually change the value stored in the variable. So if not, light is on, then we can't see, and else we can see. Naught can also be represented by an exclamation point. Let's change the value of lights on and run it again. I'll then revert these changes back to how they were before. We can make our conditions much more complex by combining multiple conditions together using logic operators. Let's create another variable named Has dark vision. This variable implies that we should be able to see even if the light is not on. So we can add to our if statements condition to be either that the light is on or that we have dark vision. And if either of these conditions are true, then we can see. Only if both conditions are false, will it result in us not being able to see? What about if, instead of having dark vision, we are wearing night vision goggles, since they only allow us to see in the dark but not in the light. Changing the variable name, the condition of our I statement needs to change. Since if both conditions are true, then we shouldn't be able to see. Only if the values of these two conditions are different, should we consider the condition to see to be met? We can use the not equals operator to check if the two values are not equal to each other. If you are familiar with logic operations, this is known in other languages as an exclusive or. Now, only if either the light is on or we are wearing night vision goggles and not both, will we be able to see. Next, let's consider that our hypothetical person might not be human and add a variable for the number of Is that they have, which should be represented as an integer. For now, we'll give it a value of two Is. Ignoring the other variables, what would be the condition that we set to be able to see based on this variable? If the number of eyes we have is anything greater than zero, then we should be able to see. We can also express this using another operator, greater than or equal to and change the right hand side to one. So they must have eyes greater than or equal to one. These are comparison operators that will return a Boolean result by comparing the value of numbers on its left and right sides. We can logically conclude that it should be impossible for any creature human or otherwise to have a negative number of eyes. The value of our variable should only be zero or something larger than zero. And the condition to be able to see really boils down to these two options. Do we have zero eyes or non zero eyes? Just like the boolean variable can be used implicitly as a condition, an integer can also be used in this way with zero being considered a false condition and anything other than zero being considered true. The same logic is also applied to floating point numbers. We can declare a float variable, let's name it range of vision and give it an arbitrary value of 7.8 meters. We can use our comparison operators to determine whether this number is greater than less than or equal to a certain value, or we can use it implicitly in its own condition with zero being considered false and anything else being considered true. What about strings? Let's change things up slightly and declare a string variable for what we heard and give it a value of bumps in the night. Then we might logically use this as an if statement as if what I heard was not an empty string, then we can say, I heard something. Otherwise, I heard nothing. And just like the other variable types, we can drop the comparison and use the variable itself as the condition. With all variables, their default values are considered to be false, and anything else is always considered to be true when used as a condition. If we declare a variable without a type, let's call it what I am looking at, but don't give it a value, then we already know its default value will be null. Even without a type or a value, any variable can be used as a bullying condition. Any variable that is empty, meaning its value is null is considered to be false. If we were to populate this variable with an object, then its value as a condition would be considered true. We don't currently have any other objects to play with, but we can always use ourselves. So let's set the value of what I'm looking at to be the keyword self. And this results in being able to see. In the next lesson, we'll use loops to repeat lines of code multiple times. I'll see you in the next lesson. 5. 0-4 While Loops & Operators: Hello, friends. Today we'll learn how to repeat lines of code multiple times with loops by imagining a lady who has fallen in love for the first time holding a flower. So let's write a new script and name it flower. Then override the base nodes definition of the ready function. Our imagined lady is holding a flower that has a number of petals, which we will store in a variable and restrict its type to an integer. Then give it a value of some arbitrary number, let's say five. We'll also declare another variable named He Loves Me, as a boolean, which we believe with its default value of false. We can start our script by printing out a story about the lovestruck lady holding her flower. She removes each petal from the flower one by one, stating either he loves me or he loves me not. First, we need to reduce the number of petals on the flower by one. So we will start with the number of petals variable, then use the assignment operator to assign the variable a new value. The value we want to assign it is the number it already has minus one. We then want to change the value of the booling variable. He loves me to be its opposite. So he loves me, is assigned to naught He loves me, reversing the value of false true or vice versa. Next, we can print out the results. He loves me. We may want to encapsulate this part in quotation marks, but if we try to put a quotation mark inside our string, it will instead mark the end of the string. Some special characters like quotation marks can be created inside a string using a backslash. If we write backslash quotation mark, this now represents a quotation mark character, not the end of the string. And we can include another one after the Ladies declaration. Let's duplicate this line and change the declaration to say, He loves me not. Another example of a special character we can write using the backslash is the backslash itself. We can also combine the two lines above and separate them with backslash N, which represents a new line. So they are printed out on two output lines from a single print statement. We now have two possibilities that we want to print out based on the value of our Boolean variable. So it would seem that we should wrap these in an if statement. If he loves me, then print this. If he loves me not, then print that. But in cases where if statements are contingent on a single boolean variable to make a minor change, there's a more efficient method. So let's return to the original format. Our declaration will be he loves me, if he loves me, else, he loves me not. This is not the same as an if statement despite using the same keywords, but it is called a ternary operation. Since it does not create a new block of code with an indentation, it is actually executed more efficiently and is recommended to be used in simple cases like this one. We can actually reduce the size of the variation even more by only changing it to either be an exclamation point or a space followed by the word not, then the exclamation point. This works for the case if he loves me, but if we change the value to true by default, then we only see not in the other case. Just like mathematics, there is a specific order of operations in which our scripts will execute multiple operations on the same line. Since both the string append and the ternary I are both operators, one must be executed before the other, and in this case, it will be the string append. And just like in mathematics, brackets are always the first to be executed. So we can wrap any operation in brackets to ensure it will be executed first in the order of operations. Wrapping our ternary statement in brackets, it will now run before the string append and give us our expected result. So now we have our block of code which needs to repeat for as long as the flower still has petals. Using the keyword WW, much like an if statement, we can repeat an indented block of code as long as the condition remains true. The condition in this case being that the number of petals on the flower is not zero. Whatever the number of petals on the flower is, it will be reduced by one each time. Then after the print statement, the logical flow of this script will return to the beginning of the WWloop to check the condition again. When the number of petals reaches zero, the condition is no longer met and the loop breaks, proceeding with the remainder of the script, if there is any. So let's write a short conclusion to our story based on the results. She stands up with tears in her eyes. But based on the final value of He Loves Me, she's either happy or sad, and we will print out the different final phrase. Running this scene, we can see our story printed out in the output panel, and with five petals, the lady is happy. If we change the number of petals to six, then we get a different result since the loop repeated six times instead of five. But what happens if we change the number of petals on the flower to zero? The loop doesn't run at all, and the love was doomed from the beginning. I don't recommend following this example, but just watch what happens if we give the flower a negative number of petals. While this is realistically impossible, in our code, it is very problematic. Since the condition of our wall loop will never be met, it will never break, and the script is trapped inside it, never finishing. This is an infinite loop and should be avoided at all costs. In this case, we can prevent the infinite loop by changing our condition to be if the number of petals is greater than zero, since a negative number will still break the loop. We can use another operator when reducing the number of petals as a shortcut. Removing the need to write the name of our variable twice, we can perform simple mathematical operations on our number of variables using a special assignment operator. In this case, the minus equals operator can change this line of code to number of petals minus equals one. These shortcuts exist for all basic arithmetic operations plus equals multiply equals or divide equals, and can be used with any number or variable as the other operand. There is one more operator that exists for basic arithmetic as well, the modulus operator, represented by the percentage sign. Modulus performs a division operation, but doesn't return the quotient as the result like the division operator does. Instead, it returns the remainder of the division. This entire script, other than the story being told, is really just determining if the number of petals on the flower is an even or odd number, but is doing so in the most inefficient way possible, counting down each individual petal. So let's comment out the entire wall loop and instead use modulus to speed up the process. Knowing that all even numbers are divisible by two, we can use number of petals modulus two. The only possible result as a remainder of this division will either be zero if the number of petals is even or one if the number of petals is odd. Our script could be simplified to just if the number of petals modulus two is equal to one, then the lady is happy. Otherwise, she's sad. Since we understand how integers are interpreted as conditions, we don't even need to perform the comparison operation here and can just shorten it to I number of petals modulus two. The result of the modulus operation could be assigned directly to the Boolean variable to get the same result without the need for an if statement, and the zero or one will automatically be converted to false or true accordingly. We could go further by removing the variable and just using the modulus operation directly inside the turny statement. In the next lesson, we'll learn about collections of related variables. I'll see you in the next lesson. 6. 0-5 Arrays & For Loops: Hello, friends. Today we'll consider how we can organize large amounts of variables, especially when they hold related information. I've already gone ahead and created a script named census and overridden the base nodes definition of the ready function. For our hypothetical scenario in this lesson, we are imagining a census worker who needs to record how many people live in each house on a single road in their town. We can declare a single variable to hold all this information, Let's name it Main Street. And the type of this variable will be an array. An array can hold a list of variables of any of the other types. In this case, the type of the variables we are looking for are integers, representing the number of people who live in each house. We can specify the type of contents of an array using square brackets after the type name array and putting the type of the contents inside the square brackets. Now this array can only hold integers as its contents. Before we can access the contents of an array, it needs to have a size, the number of variables that are contained inside it. Unlike other variables, arrays are more complex and have their own functions. We can access these functions by following the name of the variable with a period, then the name of the function. In this case, the resize function, which sets the number of addresses inside this array, representing the number of houses on Main Street, which we will say is six. Our imagined town is a little unusual in that the house numbers on Main Street start at zero. So our census worker starts at the house at zero Main Street and knocks on the door to ask the resident of the home how many people live in the house. We can store a value in an array starting with its name, then using square brackets to mark the address of the specific integer, in this case, address zero, then using the assignment operator. We can assign an integer to that address. So let's say that the house number zero on Main Street has five people living in it. Moving on to the next house on Main Street address number one, someone answers the knock at the door and tells the census worker that there are two people living in that house. At the next house, address number two, the house is under construction and has a for sale sign, which means this house is clearly unoccupied, and our census worker records the number of residents as zero. At number three Main Street, nobody answers the door, despite the fact that there is a car in the driveway and signs of occupation. What might we consider the census worker should record as the number of residents? We might think that it could be zero. But how would we differentiate between an unknown number of occupants and an unoccupied house? If we consider that the number of occupants of a house should never be considered a negative number, we could use the number negative one to mean that the number of occupants of the house is an unknown value. And at number four Main Street, there is, again, no answer at the door. But the house also appears to be neglected, and likely nobody has lived there for quite some time. We could assume that the number of occupants is zero maybe used the same negative one value for an unknown number of occupants or create more negative value codes for different situations like using negative two to mean probably unoccupied but not confirmed. Address number five on Main Street doesn't even have a house, but it's just a vacant lot. So again, this could be represented with a zero or another negative value to encode some sort of meeting. Let's print out the results of the census and see what they look like. We can see our array represented as a comma separated list of numbers encapsulated in square brackets. This does a decent job of representing the number of people living in each house on Main Street. But what if we wanted to use this data to get a total number of occupants of all the houses on Main Street or on average? If we calculated the sum of all of these numbers, the unknown numbers being represented as negatives would skew the results. One of the best benefits of using arrays to store related information in this way is how easy it is to loop through it. Using the keyword four, we next write the name of a new variable, one which will represent each individual element inside the array, followed by the keyword in, then the name of the array. Like functions, if statements are wa loops, this is followed by a colon and an indented block of code which will be repeated. These lines of code will be repeated once for each of our six houses on Main Street, and we can name the variable house, as it will be used to access each individual house inside the loop. Let's say we first want to find the average number of occupants who live in an occupied house. That would mean adding up all the positive numbers, then dividing by the number of occupied houses. So we'll need three new variables, one for the total number of occupants, another for the number of occupied houses, both as integers, and a floating point number for the average. For each house in Main Street, we can check if the house is occupied, if the number stored in the array at that address now stored in the house variable is anything larger than zero. If this is true, then the total number of occupants in occupied houses can be increased by house, and the number of occupied houses incremented by one. After all six houses on Main Street have been checked, the loop breaks, and we have our two integer numbers. So we can calculate the average by dividing the total number of occupants by the number of occupied houses. Then print out the results. There are plus occupied houses, plus occupied houses on Main Street, with an average of plus average occupants plus occupants. But if we run this code, the average is represented as an integer, three, which we know is not correct. Since there are a total of seven occupants in two occupied houses, the average should be 3.5. This happened because we performed a division operation with two integers, and the result of the operation will also be an integer, even though we are assigning it to a floating point number variable. Godot also provides a warning against using integers in division operations. To resolve this, we can change our integer variables to floating point numbers in their declarations, or we can change their type locally before performing the division. Much the same way as we changed integers into strings before appending them, we can change an integer into a floating point number before performing a division. Changing the result of the division to a floating point number as well. Changing the type of a variable in this way is called casting. We are casting an integer to a float. Now we can see the results we were expecting with an average of 3.5 occupants per occupied house. A benefit of GD Script, being a loosely typed language is that we do not need to specify the types of our variables, including the types contained within an array. If we allow our array to contain any type of variable, we can change how our census worker records information. Let's assume address zero remains the same with five occupants. But address one also provided the names of the two residents who live there. Instead of recording the occupants as a number two, we could record their names in a string. So Main Street address number one is occupied by Jordan and Ashley Smith. House number two was unoccupied, which we could represent as a zero. But we might also consider recording it as a boolean instead, and give it a value of false to mean that the house is under construction and can't possibly have occupants. At number three, nobody was home, but there are definitely people living there. We just don't know how many. We could also store this value as a boolean and give it a value of true. At number four, it really seems like nobody lives there. We may want to mark it as zero occupants. At address number five, since there isn't even a house there, we might actually consider using null as the value since null represents the absence of any value. We can use the keyword null to assign this to the array at address five. Now our printed array looks quite different, containing variables of all different types instead of just integers. And we can discern more information from it. Arrays can even contain other arrays as their contents. So let's change our list of names from a single string to an array of strings. We can create this array at the same time it is being assigned using square brackets, then populating it with a comma separated list of strings as its contents. So if we wanted to know how many people are living at Address one on Main Street, we could print that out as Mainstreet one dot SIE. In the next lesson, we'll learn more uses of arrays to create more dynamic data collections. I'll see you in the next lesson. 7. 0-6 Stacks & Functions: Hello, friends. Today we'll continue working with arrays in different ways by setting up a game of Old made. I've already gone ahead and created a script named Old Made and overridden the base nodes definition of the ready function. In order to play old made, we need a deck of cards, which is a collection of variables which we can hold inside an array. Since the game of Old Made doesn't really care about the suits of the cards, we can ignore that information, and each element in the array only needs to contain the cards rank from ace to king as an integer. To generate our deck, we can use a four loop. But instead of using the four loop to iterate through an array, it can just be used to count through our card ranks. Starting with four, we can declare a variable to represent a card's rank, followed by the keyword in, then specify a number. If we were to consider King to be a number, it would be the 13th rank. For now let's just print out rank so we can see what happens. The four loop starts with a rank value at zero and counts up to 12, repeating this loop and print statement 13 times. For the sake of sanity, it might be a good idea to consider card number two to be represented by rank two, and thus Ace would be one, and king would be 13. While starting a four loop at zero is really helpful for iterating through arrays since their first index number is zero, it isn't that helpful in this case. We can change the way a four loop counts by replacing the 13 with a function call, range. Function calls are followed by parentheses containing arguments. The arguments for this function are the lower and upper limits of the range. The lower limit will be included in the range, but the upper limit will not be included. Range 114 will contain all numbers 1-13, we can see that is printed out in the output. Another way of adding information to an array instead of using the square brackets and index number is to use a function named append. This append function accepts an argument of the value being added to the array. This will position the value being added to the end of the array, wherever that may be. And since there are four of each rank in a deck of cards, one for each suit, we should repeat this line of code four times for every rank. We can do this with another four loop, this time starting at zero and counting up to, but not including four. Printing out the deck, we can see our array has four of each number 1-13 for a total of 52 cards. But to play old made, we want to remove three of the queens, or in this case, 12. So in our four loop that is iterating through the card suits, let's check if the rank is 12. And in this specific case, we only want to allow 112 to be added, using the keyword break to instantly break out of this loop. Break will only end the most indented loop it is currently in. So we will end the suit for loop, but not the rank for loop. This will then increase the rank to 13 and proceed with adding four thirteens to the deck. And testing it out, we can see how there is only 112. The first thing that happens in our game is shuffling the deck of cards, which in GD script is a simple built in function of rayse. We only need to call this function and we can see that the cards get shuffled in our deck. We will also need another variable, an array of hands of cards to represent our players. Each hand inside the hand array needs to hold multiple cards, so they are also arrays, which we will need to initialize before we can use them. So counting from zero up to but not including four, we can append a new hand to the array. Our hand starts off as an empty array represented by square brackets. If we imagine our deck of cards, the front of the cards being the side with the rang and suit, the back of the card being blank, then we might also imagine the deck being face down. The front of the array is the bottom of the deck, and the back of the array is the top of the deck. The process of dealing out the cards can be done by iterating through our deck, but we do not actually need to reference specific cards at all, since it doesn't really matter what they are only that the card is taken from the top of the deck and handed to a player. So to remove a card from the top of the deck, we can call another function. This one is called pop back. It not only returns the last element in the array, but also removes it from the array. We then want to add this card to the player's hand and we'll start with player one, assuming player zero is the dealer. Just like we can use pop back to remove a card, we can also add a card with pushback. The card we are pushing onto the back of the player's hand is the same card we are popping from the back of the deck. We can simply put this function call inside the brackets of the function call and use its return value as the argument to the push function. Using pushback is the exact same as append. The append function is a legacy of strings, since they are usually stored as arrays of characters, while push and pop come from using stacks or cues like we are doing now. To deal our next card, we'll need to increment the hand that is being dealt to. Let's store that in a variable as an integer and give it an initial value of one. Replacing the one in our array index with our variable, we'll need to increment it by one after each card is dealt. But this will obviously just count up to 51, and there are not so many hands. So we'll need this number to return to zero each time it reaches four. This can be accomplished using our trustee modulus operator, assigning hand to be modulus equals four. But if we want to allow our code to work with any number of players, it would be better to replace four with the size of our hands array. To see where we are so far, let's print out our hands by iterating through the hands array and printing out each one. Then print out the deck. We can see our four arrays and the deck is empty. We can even change the number of players and the cards will deal out to the correct number of hands. Our ready function is getting rather large. We can organize our code better by declaring our own functions. Separating our logic into small chunks, we have a section that creates our initial deck of cards, one that creates the player's hands, and one that deals the cards. Using the keyword funk, we can give each of these blocks of code a name followed by parentheses and a colon. Initialized deck, initialize hands, and deal. Then the ready function can call each of these functions in sequence. Our functions can also accept arguments, such as the number of hands we want to have. Let's pass four as the argument. To write our function to accept the argument, we need to add a matching parameter inside the parentheses of the function declaration. Much like declaring a variable, it has a name and optionally also a type. Since we are using this to resize an array, it's a good idea to restrict it to be an integer. We can then use the parameter inside the function much the same way we would use any other variable. The last thing we need to do to start playing would be to remove all of the pairs from each player's hand. So iterating through each hand in the array of hands, we can call another function to remove the pairs from that hand, passing the hand as an argument. Giving this function a definition, we can then accept a hand as an array parameter. We will need to iterate through the hand with a variable. Let's call it card one. But we can stop before we get to the last card. So we'll only go from zero up to the size of the hand minus one. We can then iterate a second time in much the same way, but this time, start at the card after card one and go all the way to the end of the hand size. Inside these 24 loops, card one and card two now contain every unique pair of cards in our hand, so we can easily compare the two cards to see if they are a match. If they are, they can be removed from the hand, starting with card two. If we removed card one first, then the index of card two would actually change as a result, since all the cards would move one position. If we find a match, we don't want to continue this loop or either loop, but we want to restart the entire process again. A simple way of doing this would be with a while loop, a Boolean variable for whether or not we found a pair with a default value of true. While a pair was found, we can immediately set it to false. This allows us to enter the loop but not stay here unless a pair was found. In other languages, you could do this with a do Wile loop instead. If we find a pair, we can set the variable to true, then also break out of both loops resetting the while loop. If both four loops complete without finding a matching pair, found pair will be false and the while loop will break. We can repeatedly run our simulation and see that none of our players' hands will ever contain matching pairs, and the players are ready to start playing their game of Old Maid. In the next lesson, we'll use another form of flow control. I'll see you in the next lesson. 8. 0-7 Match & Return: Hello, friends. Today we'll use another form of flow control to simplify complex conditions. I've already gone ahead and created a script named months and overridden the base nodes definition of the ready function. Let's start by declaring a variable to hold the current month and set it to be January and a second variable to hold the number of days in the month, which will be left as zero by default. We can print out a simple sentence stating that the number of days in January is 31. But first, we want to populate our variable with the number of days based on the value of month. So let's write a function that can do just that. Assign the number of days in the month to be returned from a function we will write, which will accept the month as an argument. Giving this function a definition, accepting the month as a parameter of type string, it also needs one more piece of information, a return type. The type of value returned by a function is defined after the parentheses with a hyphen and greater lansinn an arrow, followed by the type name. In this case, it is an integer, representing the number of days in the month. If we do not specify a return type, the return type for any function we write is null by default. Inside our function, we need to return an integer, which means we should declare an integer at the top of it, and it will need to hold the number of days in the month. Then at the bottom of the function, we will return the value held in this variable. Between these two lines, we need to populate our variable with the correct number of days based on the month. If the month is January, then the number of days is 31. We learned previously that we can provide an alternative with the se keyword, but that only provides us with two options out of the 12 we need. We can use another keyword, if, short for se if to provide another condition which will be checked if the original condition was false. So else if the month is February, then the number of days is 28. And we can repeat this as many times as we need to, providing a case for every possibility. But many of these options have the same outcome, either 30 days or 31 days. We could vastly improve the efficiency of this if statement by combining the most common outcome, 31 days to be the default case and replace all of them with a single else keyword at the end. So if the month is not February or any month with 30 days, then the number of days must be 31. All of the cases resulting in 30 days can also be combined into a single statement using the or operator. If the month is September or if the month is April or if the month is June, or if the month is November, then the number of days is 30. But the statement is quite long with more cases, it might run off the screen. For simple case by case statements like this, there is a cleaner and easier to use keyword than the I statement called match. Match is followed by the name of a variable, a colon then an indented block of code. Inside a match block, we can specify any number of specific cases for the value of the variable. Let's start with February. The case is also followed by a colon and another indented block of code. This code will only be run if the value of month is February. We can group cases together in a comma separated list. So our I statements condition can be shortened into just a list of four months. If the value of month is September, April, June, or November, then the number of days will be set to 30, and our block can be replaced with a wildcard case represented with an underscore, setting the number of days to 31 if the value of month is anything else not specified above. This is the same as using default in other languages. Each of these blocks of code are called branches. And in GD script, a match statement will only ever execute the uppermost branch whose condition was met. In other languages, this is similar to a switch statement, but differs in that switch statements can execute more than one branch if multiple conditions are met. Because of this restriction, there is no need to include breaks in match branches. If we wanted to print out the number of days in every month, we would probably want to use a loop, but we can't simply count or increment a string like January to somehow become February. So it is common in code when representing things that exist in a sequential order to put them in a numbered list called an enumeration. An enumeration in GD script is just a group of related constants. We might have a constant for January with a value of one, another for February with a value of two, et cetera. But using the keyword Enum, we can name the group of constants, let's name it months followed by braces. Inside the braces, we can group the constants together and they will each be assigned a number automatically based on their position in this list. Anything contained within the braces does not need to stay on one line and can be broken up into whatever format you prefer to make it more readable. Indentation doesn't matter here, but usually when writing anything encased in braces, most coders will indent to the contents, so it looks similar to a function. While I prefer the braces to be lined up vertically, many coders prefer to put the opening brace on the declaration line like so. Just like Constance, enumeration entries are conventionally named in upper snake case. Now that we have an enumeration, the month can be restricted to the type of this enumeration. We can also assign it a value from the enumeration, starting with its name, followed by a period, then the entry in the list. The month being appended to the string output is no longer a string itself and must be cast to a string before it can be appended. The argument being passed to the number of days is no longer a string and is now a month, and we can alter our function to accept a month as a parameter too. Since month is now a month, not a string, we'll need to change our conditions to match using the name of the enumeration followed by a period, then the entries in the list. But when we print out the results, we see that instead of January, our month is written as the number zero. While the type of our variable is a month, which is an enumeration, the actual value is treated as an integer, its position within the enumeration. Since January is the first entry, its value as an integer is zero. February would be one, et cetera. This allows us to modify the value of month with simple arithmetic, so January plus one becomes February. If we wrap this code in a loop that repeats 12 times, we can iterate through all 12 months with ease. But what about writing the name of the month as a string? We could do this in a couple of different ways. Our enumeration type has a property named keys, which is all of our entries as an array of strings. If we index this array using our integer variable, it gives us the entry name as a string matching that number. But following the Upper snake case naming convention, we may not want to print out the name this way. We could rename all of our enumeration keys in Pascal case instead, or we could declare a constant containing the array of strings, holding all our months names in a more presentable format. Then use the month as an index into this array. Another thing we can do with enumerations is manually set the values of the entries using the assignment operator. Let's say we want January to be one instead of zero. The rest of the entries will automatically update their values to increment from one, so February is now two. But this no longer matches the constant array indices. So if we wanted to use this to write the name of the months, we would need to either add an empty entry for index zero, Or subtract one from the month as it is being used to index the array. In the next lesson, we'll learn more about how the engine uses and executes our scripts. I'll see you in the next lesson. 9. 0-8 Scene Tree & Inheritance: Hello, friends. Today we'll learn about how the engine uses the scene tree to run our scripts. I haven't yet attached a script to the root node of the scene tree, but we'll instead start this lesson by adding more nodes. Right clicking on the root node, then selecting add child node, still just a normal node, we can rename it to be president and pretend our scene tree is the structure of a company. The president node is indented because it is a child of the scene's root node. With a node selected, we can also use the shortcut Control A or Command A to add another child node. Let's name this one operations manager, and it is indented another level because it is a child of the president. Let's now attach a new script to the president node and name it employee. Overriding the base nodes definition of the ready function, we can simply print the name of this node and append is ready. This script can be attached to more than one node. So let's attach the same script to the operations manager node by dragging it from the file system tab to the scene tree. Then run the scene to see what happens. We can see that the operations manager is ready, then the president is ready. Both nodes that have the script attached run the script, and since they have different names, the output was different. We can also see that the operations manager was ready before the president was ready. To understand why we'll need to add more employees. The operations manager will oversee a number of operators. I'll add another child node to the operations manager and name it operator one. Then attach the employee script. We can right click on any node except the root node and select duplicate to create a copy of it. The duplicates name will automatically include an increasing number, so we can have operator two, three, and four, et cetera. Since the node we duplicated had a script attached, the copies created also have the same script attached. Since the operators have the same indentation level and are all children of the operations manager, they are called siblings, and siblings are not allowed to have the same name. Our company should have more than one department, so let's duplicate the entire operations department by duplicating the operations manager. All of the child nodes are also duplicated. Since the operators are not direct siblings, they're allowed to keep their original names. Let's rename the manager to be marketing manager, and they will have just two marketers working on their team. Parent nodes can also be collapsed to hide their children or expanded to show them. All of the employees of this company have the same employee script attached to them. So let's run the scene and get a better idea of the order in which the scripts will be run. We can see that the first operator is the first to be ready, followed by each operator in sequence, and finally, the operations manager. The same follows for the marketing department. Then finally, the president of the company is the last to be ready. How is Gadot deciding this order of execution? The engine starts at the top of the scene tree and works its way down, but it does not consider any node to be ready until all of its children are ready. So the president is not ready because the operations manager is not ready. The operations manager is not ready because the operators are not ready. The operators are ready in sequence, since they have no children. Once all of the operators are ready, then the operations manager is ready. But the president still isn't ready because the president has another child who is not ready. The marketing manager was not ready until all of the marketers are ready, and finally, the president is the last to be ready. Since the root node doesn't have a script attached to it, its ready function is that of a default node, which is empty. Besides ready, there are other functions of node we can override to be executed later after a node is ready. The most common being process, also preceded with an underscore. The engine will try its best to run the process function of every node in the scene tree 60 times per second using the default project frame rate. So process uses a parameter named Delta, which is a floating point number to represent the number of seconds that have passed since the previous frame. So most of the time it will be one 60th, but not always. We do not need to use this parameter for anything today, so we can proceed the name of this parameter with an underscore, so Gudo knows that we don't need to use it and shouldn't bother with it. But in order to override the process function, it must have the same number and types of parameters. Let's tell our employees to do some work every frame by printing out a statement saying their name appended to does work. Running this scene, we see that all of our employees are working very hard, writing their print statements 60 times per second. This is not the same as an infinite loop as work is actually being done the way it is intended and nothing is broken. Let's make this a little easier to understand by only allowing each employee to do one unit of work using a Bolan variable has done work with a default value of false. In the process function, if the employee hasn't done any work, then they will do work and change the value to true. One 60th of a second later, since the variable is true, they will not do work anymore. But if we declare this variable inside the process function, then it will only exist inside the process function and only for one single iteration of it. Next frame, when the process function is run the second time, the variable is declared again and still defaults to false. So this will have no effect. For our variable to persist, it needs to exist outside the context of the process function. So we'll declare it outside the function without any indentation. Variables declared here can be accessed anywhere in the entire script. This concept is called scope and is one of the main reasons why we use indentation in our scripts. So we can see which variables exist within which scope. Running the scene now, we can see each of our employees declares that they are ready to work. Then only once everyone is ready, they each do one unit of work, but now in a different order. The order in which nodes and the scene tree execute their process functions follows the exact order of the scene tree from top to bottom. Parents do not need to wait for their children like the ready function. We may want all of our company's employees to be similar but not exactly the same. The work of a manager isn't really the same as that of a worker. We can use inheritance to create different behaviors for similar objects. In the same way we are writing scripts that extend node, but changing how they get ready and how they process. First, we need to give this script a name that we can use as a reference, using the keyword class name, followed by a name that describes the behavior of the script, usually the same as the script name, but written in upper Pascal case. Now that the engine knows this is an employee, let's create a new script and name this one manager. But instead of inheriting from node, this time we can inherit from employee. Because our manager script extends employee, everything written in the employee script will still apply to this one, but we can add more or make changes. A manager is still an employee, but the way they do work is different. We should replace the script attached to our manager nodes with the new manager script. Let's override the employee's definition of the ready function. So when a manager is ready, they won't just say they are ready, but they will say that their entire department is ready. In order for both of our managers to be able to output the name of their departments, they will need a variable to hold the name of their department as a string. But how can we populate this variable to hold different values for different departments? If we proceed our variable declaration with the tag at export, this variable will become accessible by other parts of the Gadot engine, namely the inspector panel. In order for a variable to be exported, it must be declared at the script scope level. Selecting the operations manager and the scenetre, there is now a field in the inspector panel where we can give the department variable a string value. This way, the operations manager and the marketing manager can each have their own value for this variable that we can easily control. Running the scene, we can see that the nodes which have the manager's script attached now declare that their department is ready, while the other employees all use the previous definition in the employee script. The process function in the employee script is also inherited by the managers and is run the same as before. So let's change our manager's process function. Managers will say something different. Name plus manages workers to do their work. The has done work variable from the employee script was also inherited and does not need to be declared here to be used. In fact, we can't declare a variable with that name since it would cause a conflict with the inherited variable of the same name. Let's run the scene again and see how our managers now do their work differently from other employees. In the next lesson, we'll learn more about using nodes in the scene tree and object oriented design principles. I'll see you in the next lesson. 10. 0-9 Abstraction & Encapsulation: Hello, friends. Today we'll explore how to write classes that are easier to use and understand. Imagining a car as our frame of reference. Let's create two nodes in our scene, a driver and a car as a child of the driver. Then attach a new script to each of the same name. Cars are very complex pieces of machinery, and most people who drive them have no idea how they actually work. They only understand how to operate their car. Let's start in the car script and give it a class name of car, then add a variable for the year of the car as an integer. Another for the make of the car as a string and another for the model also as a string. Exporting all of these, I'll set my car to be a 2007 Honda Civic. When creating a new class, be sure to save the script before trying to use it. In the driver's script, the driver will need a reference to the car, which we can store in a variable of type car. Nodes can access other nodes in the scene tree, most often their children using another tag similar to export called on ready. Using the tag on ready, we can assign the variable value after the node is ready, which means all of its children are also ready, but before calling the ready function of this node. We can assign it to the return value of a built in function G node, which accepts a node path as a parameter. If we specify the name of the child node in quotation marks, we now have a reference to the child node stored in a variable, and we can access its properties and functions, including those in the attached script. Overriding the definition of the ready function, we can access the car node and print out its year, make and model. The ready function is executed after the on ready tags. A shortcut exists for the get node function to make this easier, the dollar sign, followed by the same node path. Et's switch back to the car script and add another variable. This one specifying whether or not the engine is running as a Boolean. As a driver, we know if the engine is running and we have control over whether or not the engine is running, since we can choose to start or stop the engine using the key. So we would consider this variable to be public. Another property of our car might be the number of cylinders in the engine as an integer, and I'll assign it to have a value of four. This is something that the driver doesn't need to know, can't access easily and should never be allowed to change. So unlike the public variable, this one would be considered private, and we can mark it as private by proceeding its name with an underscore. In the driver script, we can set the value of engine is on to true by putting the key in the ignition and turning it. But first, we should be pressing the brake pedal. Let's use a function to set pressure being applied to the brake pedal, which we will assume is up to a maximum of 20 kilograms of force. So to start our car, we should probably set the brake pressure to somewhere around 10 kilograms. Back in the car script, we will need to define this function which sets the brake pressure. Like the engine is on variable, this would be called a public function, since it is being used by another script. This function will accept a floating point number as the parameter representing the amount of pressure being applied to the pedal. But what does the car actually do when we apply pressure to the brake pedal? From the perspective of the driver, the driver chooses to apply a certain amount of pressure to the pedal without ever knowing the exact position. From the perspective of the car, the position of the pedal is what is being measured and used to stop the car. The car uses the position of the brake pedal to adjust the amount of pressure being applied to calipers which hold brake pads against the wheels of the car, not to mention the complexity of anti lock brakes or other systems. So if we were actually programming a real car, we would need to write several complex functions here that convert the simple action of the driver applying pressure to the brake pedal to the complex mechanics of squeezing the calipers. This is part of the internal functions of the car and not anything that is controlled or accessible to the driver through any means other than applying pressure to the brake pedal. So we would consider these to be private functions, since they are only meant to be used by the car itself. And we can mark our private functions by proceeding their names with an underscore. Notice how this is the same as the process and ready functions. The way GDScript uses private variables and functions is more like the way other languages use protected ones in that they are accessible to inheritors. GD Script does not actually enforce these privacy rules, however, and scripts can still access private members. These conventions exist purely to facilitate better class structure and design. Another way of using comments in GD Script is to create regions of our scripts that are related, using the region keyword followed by the name of the region. We can end a region using the keyword and region. So let's make a public region and a private region in our car script, separating what should be accessible to the driver and what should not. To keep things simple, let's say that the driver only needs to be able to turn the engine on or off, apply pressure to the throttle, apply pressure to the brake pedal, or turn the steering wheel. What actually happens inside any of these public functions is the internal mechanics of the car itself. The driver does not need to know how any of it works to operate the car. For example, when pressing the throttle, what is actually happening is that the car opens an air intake valve on the engine, letting more air and oxygen into the engine. So the car needs to use a sensor to measure the amount of airflow and the amount of oxygen that is now flowing into the engine. Then use that to adjust the amount of fuel being injected into the engine's cylinders. All of these functions are private, since we do not need to know what they actually do in order to drive the car. Way our car moves would be in the process function, and we'll need another private variable named phase to represent which of the four phases each cylinder is currently in. In the car's process function, if the engine is running, we can iterate through each of the cylinders of the engine. Adding together the cylinder and the phase, then modulus four, we get a different phase for each cylinder. Matching this number, we can tell each cylinder to perform a different phase, the intake stroke, compression stroke, combustion stroke, or exhaust stroke, each accepting the cylinder as an argument. Then increase phase and cycle back to zero when it reaches four. Each of these functions can then control the intake valves, exhaust valves, fuel injectors, and spark plugs of each cylinder to run the engine. The amount of force being produced by the combustion strokes of each cylinder would then move the car forward while the brake pads try to stop the car, and the steering wheel position changes its direction. The car's position would then be moved by the direction, speed, and Delta time. Don't worry if none of this makes sense to you because that is precisely the point of this lesson. You do not need to understand it. Since all of this is in the private region of the script, the driver does not need to know how any of this works in order to drive the car. They only need to know how to use the public region of the script. The separation of public and private enforces two important principles of object oriented programming. Encapsulation is the public region, providing access to public variables or functions that are easy to use and simple to understand, and abstraction is the private region, hiding the details of complex systems or mechanics which do not need to be understood to be used effectively. You will know if your classes adhere to these principles if you can hide all of the private contents and still easily understand how it is meant to be used. So our driver can now simulate driving the car by accessing only the public regions of the car script, from pressing the brake and starting the engine to releasing the brake, pressing the throttle, turning the steering wheel, et cetera. These principles are not only important for our own scripts, but also allow us to use scripts written by other developers without fully understanding how they work internally. They also make R scripts more flexible. Imagine if we swapped out our gas car with an electric car. Would anything in the driver's script need to change? Only the car script would need to be changed for an electric car script, since all of the public variables and functions would still exist in the electric cars script. Likewise, we could also change the driver script to an autopilot script, one which only needs to be given a destination and will automatically be able to operate the car. In the next lesson, we'll learn even more about object oriented design principles. I'll see you in the next lesson. 11. 0-10 Polymorphism: Hello, friends. Today we'll cover the last of the object oriented programming principles, polymorphism. Extending the concepts of inheritance and abstraction, we can write classes that represent a more abstract concept, a collection of more specific classes that are related. Let's use animals as an example by creating a script named animal that extends node, but we don't need to attach this script to any nodes in the scene tree. Here we can give the script a class name, then define properties and behaviors that are common to all animals, like a variable to hold the plural of the animal as a string, and another for the name for a grouping of this animal. And let's also define a function for the animal to move, eat, or speak. While we can easily define the plural of animal as animals, there is no word for a group of animals. We also can't define how an animal moves or eats without knowing what kind of animal it is. So the bodies of these functions can remain blank. But we might consider some behaviors to be common enough that they should have a default, such as speaking. While there are many different mammals that make a wide variety of noises, the majority of animals don't, including fish and insects. Most people would not describe them as speaking. So when telling any random animal to speak, we might consider the default behavior to be silence. So let's add some different animals to our scene tree, starting with a cat. We can now use inheritance, like we have before to make any number of different animal scripts that inherit from animal like cat. We'll then override the ready function to give the variables which are inherited from animal a value that is appropriate for a cat. The plural would be cats, and a group of cats is called a Clouder. The behavior functions can also be overridden to change the silent speaking behavior to meow. Cats walk on four legs to move around, and I'll say that they eat fish. The actual contents of these variables and functions are not relevant, but the focus is on how they are inherited from the abstract concept of an animal to a more specific instance of a cat. We can repeat this process to make many more different kinds of animals to add to our scene tree like a bird or a fish. And giving each a new script inheriting from animal, they can each provide their own unique values for the variables and implementations of the functions they inherited. The plural of bird is birds. A group of birds is called a flock. They fly to move around, they eat worms, and they chirp to speak. I'll get a head start on my fish script by copying the contents of the cat script. And the plural of fish is fish. A group of fish is called a school. They swim to move around, they eat algae, but they don't speak. By omitting the speak function, fish will use the inherited definition from the animal script for speaking. So far, all we've done is use the object oriented principles we already know to inherit, abstract and encapsulate the properties and behaviors of animals. But all of this allows us to use polymorphism by treating all animals as if they are equal and interchangeable. First, let's add a script to the root node of our scene and call it Zoo, inheriting from node. Our zoo is full of different animals, but it doesn't matter what those animals actually are. Overriding the definition of the ready function for our Zoo, we can easily iterate through our list of animals and treat all of them as if they are just animals. A quick and easy way to store all of our animals into a single variable. Let's call it animals is to use a function of node called Get children. This returns an array of nodes, all of the child nodes of this node conveniently organized into an array in the same order as the scene tree. Because this relies on the children, we must use the tag at on ready. Since we know that all animals have the same properties and behaviors, we can iterate through each animal in our array of animals and tell each one of them to speak. And despite treating them all as animals, each will speak in their own unique way as defined by their own particular scripts or use the definition inherited from animal if they don't have their own. We can create as many levels of inheritance as we want to. So let's create another abstract script for mammals, inheriting from animal. Mammals are a subcategory of animals, including all animals which have mammary glands and use them to nurse their young. These are unique properties and behaviors of mammals that do not apply to other animals. So we can add a new variable to the mammal script for the number of mammary glands this animal has and write a new function called nurse. Again, the contents of the functions are not relevant to this lesson. This script will also need a class name in order to be inherited. Switching over to the cat script, since cats are mammals, and so we can give the number of mammary glands a value for cats, which should be eight. And we can add new mammals to our zoo, as well, like a monkey. Monkeys are also mammals, but they only have two mammary clans. I'll once again copy the contents of another script to make things faster. The plural of monkey is monkeys. A group of monkeys is called a troop. They move by climbing trees. They eat bananas, and I'll say that they say oh. Our Zoo script can now take advantage of this new inheritance structure to apply unique conditions specifically for animals that are mammals. We can directly use class names in conditional statements to say, if the current animal is a mammal, then we will execute some extra code exclusively for mammals. I'll just print out some extra lines describing our mammals. And we can see our output is both consistent for all animals, but also has something unique specifically for the two mammals. You can imagine how we could change our list of animals in our zoo to be any different kinds of animals, and we could alter our structure of inheritance to categorize them as precisely as we need to. In the next lesson, we'll learn about another data collection we can use to group related information. I'll see you in the next lesson. 12. 0-11 Dictionaries: Hello, friends. Today we'll use an alternative to the array that is sometimes used sort of like a class without any functions called a dictionary. Imagine a row of lockers like you would find in a high school or a gymnasium. If we declare an array of variables, we might consider the array index of each to be the lockers number. Let's say there are ten lockers, and we can access each individual locker to put something inside, starting at locker zero, one, and so on. What we put inside each locker does not matter. It can be an integer, float, string, boolean, null, or an instance of a class. And we can print out the contents of the lockers to see them. Now imagine that when we put something in one of the lockers, we don't just close the door of the locker, but also put a pad lock on it. This padlock requires a three digit number combination to open. Previously, if we wish to retrieve the contents of the locker, we only needed to know the lockers number, walk over to it, and take the contents out. Now we can ignore the lockers number, and instead, we need to know the combination to open the padlock. Let's say that the combination is 149. As an array, assigning something to number 149 would mean that the array would have to have 150 lockers. If we were to change the type of our array to a dictionary, this would still work except we don't need to set a size for the dictionary before we can access its contents. Notice how the output has changed. Dictionaries are represented by braces instead of square brackets, and each entry is a number representing a key followed by a value of what is contained in the locker locked by that key. There are also no entries in the dictionary for any key which was not assigned a value. As a dictionary, this number is not an index or address. It is a key that we use to open a lock. And just like the contents of the locker can be any datatype, the keys we use to lock them can also be any datatype with only one restriction. Every key must be unique. Otherwise, we're unable to find the locker that matches the key. So let's try locking up a locker with a different kind of lock, one that uses letters instead of numbers. So the key of this dictionary entry is a string, but its value is a number. The complete opposite of this locker, whose key is a number and value is a string. Let's lock the next locker with a spin lock, which requires three numbers to unlock it, which we could store in an array. So our dictionary entries key is an array represented by square brackets containing a comma separated list of three integers. Maybe this locks number includes a decimal point, so the key is a float. And somehow this locker is locked by photosensitivity and can only be opened if the lights are off. So its key is a boolean. We can even use custom classes as either our keys or values in dictionaries. Let's write a new class for key, which will not need to inherit from anything since it won't be attached to a node, nor will it be inherited. Our key class needs a class name and will have a variable that describes its teeth. The implementation of this class is not relevant, but we can pretend it is a key. Back in the locker script, we can store an instance of our key in a variable. Let's call it key one of type key by assigning it to the class name of key Nunu creates a new key, which is assigned to our variable. We can now use this key to create an entry in the dictionary. We can also create more keys to create more entries. As every key we create is considered unique. The output now displays the keys as reef counted followed by a long number. The names we have used in other classes were inherited from node. But since key doesn't inherit from node, it doesn't have a name. Ref counted is short for reference counted object, and it's the base class for GD script. If we write any class which does not inherit from anything, it will inherit from reef counted and be assigned a random long number. We can ignore this and just understand that our keys are reference counted objects, meaning Gadot is automatically keeping track of each unique instance of each object we create. But what do we want to store in the lockers? Just like arrays can contain other arrays, dictionaries can also contain other dictionaries. Matching how dictionaries are printed out, we can create our own dictionaries on the fly using braces. Each entry starts with a key followed by a colon and then the value that matches that key. Common use of dictionaries isn't to create anything like this locker scenario, but instead to create something more like a class, albeit one without any functions, only variables. Let's say that this locker contains a plush toy, which we will represent with a dictionary. And this plush toy belongs to a set of collectible plush toys that are similar but varied in their own ways. Each of the plush toys is a different type of animal with a color and a name. Each of these can be keys in the dictionary. Let's say that this locker contains Bruno the brown bear. And the next locker contains Arthur the White Goat. We can see our dictionary plush toys inside the dictionary of lockers. It so happens that color is also an already defined class in Gadot. So instead of storing our colors as strings, they can be stored as colors using the class name color. As you can see, there are lots of predefined colors for us to use, and we can tell by how they are named in upper snake case, that these are constants of the color class. We could even flop this around and pretend that the plush toys contain an NFC or near field communication tag, making them the key to access the locker. And we can put whatever we want inside the locker just as before, including another plushy. Let's make another plush toy to store inside the locker, which is unlocked using Arthur as a key. This time, it's cutie, the gray elephant. So re imagining this scenario, there is an NFC reader near the lockers, and we are holding a plushe with an NFC tag inside, something like what you might find in an escape room. If we hold the plush toy against the reader, we need to check which locker matches the plush toy and unlock it, if any locker matches at all. But if we try to access a key in a dictionary which has not been assigned a value, we will cause an error. Much the same way we would if trying to access an array index out of range. We can check if a dictionary contains an entry matching a specific key using the function has, then passing the key as a parameter. This allows us to check if a dictionary entry exists before trying to access its value, avoiding the error. If the dictionary doesn't have the key, I'll print out a different message. Instead of defining the same dictionary twice, it would be better to store in a variable and use the variable twice. In the next lesson, we'll explore some different techniques that you can use when you get stuck or need more information. I'll see you in the next lesson. 13. 0-12 Debugging: Hello, friends. Today we'll go over a few different strategies that you can use to help you when you inevitably get stuck. I've already attached a script to the scene's root node named debug. You've probably noticed the autocomplete feature that is available if you specify the type of your variables. This not only allows you to see all of the properties and functions of the class, but after selecting a function, you can also see the types of all the functions parameters so you can provide matching arguments. A, when you know the types of all your variables, this makes mismatch errors clearly apparent, and you'll be warned of the error without having to run it. Without strict types, the engine doesn't know what is mismatched until attempting to execute the line. If you want to browse through a built in class like color, we can easily access the information by clicking on either of the buttons in the upper right corner of the panel. Online Docs opens a tab in your default web browser to the GADO API matching the version of the editor that you're using. Here you'll find basic information about Gdo tips on getting started, detailed tutorials on the basics, community links where you can find additional resources, and a complete class reference. As you can see, there are a lot of classes. And selecting any of them, we can see what they inherit from and what other classes they are inherited by. Each has a description of what it does. Links to tutorials on how to use it, a list of properties and methods. When talking about classes, we tend to refer to variables as properties and functions as methods. There are also signals, something Gadot uses for automatic communication between nodes, enumerations and constants like we have used before. All of these have their own subsections and descriptions of how to use them. Back in the GDA Editor, search Help opens a window in GDA where you can see a list of every class. Selecting a class, we can see all of the same information that is available from the GDA website in our editor, so we can access it offline. Returning to our script, we can even access this information easier by holding down the control or command key than clicking on a class name, bringing us straight to the reference information for that class. The same can be done for specific functions and variables. Clicking on them not only opens up the reference page to the class, but also brings us to the function or variable that we clicked on. There are also global functions that we can use mostly to perform mathematical operations like clamp, for example. If we use this function, we can see the parameters it is expecting and we can use it to clamp our variable to be 0-1. Holding the control or command key and clicking on the name of the global function will allow us to access the reference for this function, complete with a description and how to use it. These are all contained within the global scope class. I'll add another variable and another line to my script. If we run our scene, we can view the scene tree of the running simulation through the scene panel by clicking on the remote toggle. This is not the same scene tree in the local tab that we build to define the scene like a blueprint. This scene tree is the one that was constructed by the engine to run the scene. Selecting any node, we can see its properties in the inspector and we can see that the ready function has already run since the colors have different values. We can change the values of any of these properties too. We can also use print to print out whenever we want to see what our variables hold at any moment. And it helps to be as descriptive as possible with your print statements, so there is no confusion about where they are coming from. But if we change things up and use the process function, I'll just change the color gradually over time, then add a print statement here. Since this process function is running 60 times per second, the print statements value as a debugging tool is greatly diminished since the output log is just going to repeat the same message faster than we can possibly read it or react to it. We can pause the simulation while it is running, using the pause button or pressing F seven. This allows us to see the remote scene tree as it is at this moment and review the contents of the output panel with the simulation paused. But this still isn't very useful, as several frames will be completed between the time when the simulation starts and when we can pause it. If we click to the left of any line of code, we can add an automatic breakpoint indicated with a red circle. When we run the current scene, the simulation will pause when it reaches this line before executing it. This is much more helpful if we want to see our simulation run one frame at a time. Now we can see how each frame the color gradually changes one frame to the next. Let's have our process function call another function to perform a simple task. I'll just change the color gradually by reducing its red value. So now we should see the color gradually change every frame becoming less red and more blue. With the breakpoint still at the first line of the process function, I'll run the scene. It stops here, and we can see that the next line of code to be executed is indicated with a yellow triangle. While the simulation is paused like this, we can tell it to execute only a single line of code at a time, rather than an entire frame by selecting step over from the Debug menu or using the shortcut F ten. This will execute only the line of code which is pointed at by the yellow triangle, then immediately pause again. So we can see the changes made only by that single line of code, and we can see the yellow triangle point to the next line waiting to be executed. We can continue running our script line by line if we continue stepping over until the process function ends and the frame is complete, returning to the top of the process function to run a second time. Now we can watch each individual change to the color as its blue value is increased separate from when the red value is decreased, even though they happen during the same frame. In the case of function calls, there is another option to step into the function. If we select this option or use the shortcut F 11, the yellow arrow will instead step into the function being called and execute that one line at a time until its completion. When the function is complete, the yellow arrow returns to the original function to continue executing that one. Using these tools, we have a very focused view of what our classes are doing and when, how they are behaving and we can watch them in as slow motion as we need to to figure out what is going wrong at what specific moment in time. If you can't solve a problem yourself, the GDA community is very helpful and always willing to lend a hand. There are Discord servers for GDA developers, including my server where my students help each other out. The link to join is in my profile. There are also online forums on websites like Redit where you can ask questions too. You now have a basic understanding of most of GDScrip syntax and the tools needed to explore these concepts further. When you're ready, please ask me for a discount code to any of my game project courses, and good luck. 14. Basics: Hello, friends. Our objective for the first section of this course is to create a three D character that the player can control in third person perspective. Our first lesson will be setting up a basic three D scene and a new project. Opening Gada for the first time, there are no projects. Cadeau recommends exploring official sample projects. Press cancel to ignore this recommendation. We will be starting a new project from scratch. Click on the new button to create a new project. We will need to give the new project a name. I'll name mine Gada three D course. And select a folder on your hard drive to store it. I recommend making a folder for Gada projects in documents. Then click the re folder button to create a new folder at this location with the name of the project. You can change the renderer settings for your project. Forward is best for high fidelity, PC or console games. Or the mobile renderer for mobile devices. I like to use the compatibility renderer for most projects because it is most compatible with web builds that can be uploaded to sites like dot IO. Lastly, God supports integration with GT Tub version control by default. Click on create and Edit to create the project and open it for editing. The Gdo editor window is divided into several tabs. In the center is a preview of what the current scene looks like, as well as several tools we can use to edit things. To the upper left is the scene tab, which holds a list of everything in the current scene. Below that is the file system tab, which holds all of the imported assets for our project like models, textures, sounds, and music. As well as all of the files, we will create using the Gada editor, like scenes, scripts, and resources. Currently, the only file here is the Gada logo. To the right is the inspector tab, where if we select anything in the scene, we can see its properties and edit them. All of these tabs can be resized and rearranged as you see fit to best accommodate your workflow. Our scene is a vast empty three D space divided by three colored axes. The red x axis represents right and left. The green y axis represents up and down. The blue Z axis represents forward and backward. Let's get started by clicking on the three D scene button in the scene tab. This has created a root node for our scene in the scene tab, named Node three D, which is also the nodes type. We can rename this node by double clicking on it or right clicking and selecting rename. In many games, this scene will be referred to as the main scene or the game scene. You can also name it after the style of game play that will be used, such as the platforming, exploration, combat, or puzzle scene. I'll name mine game. This root node will form our scene tree as we add branches to it. Either click on the plus button or right click on the root node and select Add child node to add a branch to the scene tree. There are many different node types to choose from that serve many different purposes. Node types follow a hierarchy of inheritance. So all nodes share the common type node. But then there are node three D, Canvas item, node two D, control, each with many different subtypes. If you know what you're looking for, you can type the name of the node into the search field. Let's add a mesh instance three D node. You can see that a mesh instance three D node is inheriting properties of a geometry instance three D node, visual instance three D node, node three D, and node. Click the Create button to add the mesh instance three D node to the scene tree. You can see that the node is added to the scene tree, but it is indented to imply that it is a child of the root node. Any node with children can be collapsed or expanded to hide or show its child nodes. This is supposed to be a three D image, but nothing is displayed in the preview window. That is because this mesh instance doesn't have a mesh resource telling it what to draw. With the mesh instance three D node selected. Look in the inspector tab to view and edit the nodes properties. The properties are divided into the same types we saw when creating the node. So we can see which properties are inherited from different types and which are specific to our mesh instance. We can see that the mesh field is empty. Clicking on the drop down, there are several options, including some basic shapes, box, capsule, cylinder, quad, prism, sphere, and Torus. Let's make this shape a plane. Now we can see a three D shape drawn in our preview. Clicking on the mesh resource expands its properties so we can view and edit them as well. Here we can set the size of the plane in both the x and y dimensions by either typing a number into the field or clicking and dragging on the field label. Units are generally considered to be meters or approximately three feet in imperial measurements. But there is no requirement to use this ratio. In a strategy game, a unit might be a kilometer or mile, or if you're playing as a tiny insect, then a unit could be a centimeter or an inch. Now that we have something in our scene to give us a point of reference, let's explore the scene a little more. There's a grid of lighter gray lines dividing the x z plane into units with every eighth line being lighter. We can zoom in or out using the mouse wheel. Zooming out, we can see that this pattern continues as each eighth, larger subdivision is also marked, 64 units apart, which is eight squared. And again, every eight or 512th unit eight the power of three. Zooming back in, the original grade becomes visible again. Holding down the mouse wheel and moving the mouse allows us to rotate the world about the y axis and also tilt our perspective up or down. Holding down the right mouse button and moving the mouse allows us to rotate our perspective, looking right, left, up or down. Still holding down the right mouse button, we can use the WASD keys to fly through our scene while also using the mouse to orient our point of view. If it seems like you're moving slowly or not at all, you're probably zoomed out. This allows us to quickly and easily view our scenes from any position or angle. The mesh only defines the shape of a three D object, not its color lighting or anything else. Those are all set by material. Expanding the drop down labeled material, select new standard material three D. Then click on the newly created material resource to view and edit its properties. Materials have a lot of properties to control how they interact with light. Expand the albedo section. Here we can adjust the color or also use a two D image file to apply texture to the material. Try dragging the Gada logo into the texture field. If there is both a texture and a color, then each pixels color will be multiplied by the color value to produce the result. In the upper right corner is a series of buttons, one of which is a clapper board with a play icon, run current scene. Clicking on this button will prompt us to save the current scene if we haven't done so yet. Otherwise, it will save the scene automatically. This allows us to run a simulation of our game starting from this current scene that we are editing. Nothing is displayed. We can close the window to end the simulation. This is because there is no camera in the scene. A camera is required to view three D scenes. A camera is just another node type. So we can add it to the scene the same way we added the mesh instance. We need to use a camera three D to view three D scenes. The camera is added at the scene origin, which is the center of the floor, and is looking in the negative Z direction. We can see which direction a camera is looking as well as the width of its field of view based on the magenta gizmo shaped like a pyramid. And the triangle shows us which direction is up as well. With the camera three D nodes selected in the scene tree, clicking on the prev toggle will show us what the camera sees. Where the camera is sitting right now, it can't see anything. Click the pre toggle again to return. We need to move it away from the floor and rotate it to look at the floor. We can move the camera easily along a single axis by clicking and dragging on any of the three arrows, red for the x axis, green for y, and blue for Z. We can also move the camera along a plane by clicking and dragging any of the three squares. This will lock the associated x y or z value while we move the position along the other two axes. Move the camera up along the green axis and forward along the Z axis. Depending on the perspective of your editor, forward may seem like backwards. But for clarity, forward is used to refer to the positive Z direction, which is the direction of the blue arrow points. This can also be done by locking the red axis and moving the camera along the Y Z plane like so. Next, we need to rotate the camera to look down at the floor. We can rotate about a single axis by clicking and dragging any of the three circles. Rotating about the green y axis will be like turning your head to the left or right. Rotating about the blue z axis will be like tilting your head to either side or even upside down. Looking down is a rotation about the x axis, which is red. Click the preview to see if your camera can see the floor clearly. With the camera three D nodes selected, look in the inspector in the properties inherited from node three D. Expanding transform, we can see that the position and rotation values of the camera have been edited. All three D nodes will inherit this property for controlling their position and rotation in three D space. Here, we can manually edit the values, or we can click and drag on the labels to adjust them with the mouse like we did with the plane. Scale is a multiplier of a three D objects size. By default, the X Y and Z values are all linked together, so editing one will edit all three. Camras don't have size, so changing the value doesn't affect the camera. But setting the scale to a negative value will effectively mirror its rotation. We can reset the scale to one by clicking on the reset button and ignore this property. Checking the preview, the camera can now see the floor. But if we run the current scene, the floor is black. This is because there is no light source in our scene. In the editor, the floor is illuminated because there is preview some light and environment, which we can toggle on or off using these buttons. Turning them off the floor turns black, which is what we see when we run the scene. So for the camera to be able to see the floor, we need to add a light source to the scene as well. Add a directional light three D no to the scene. Like the camera, it is added at the scene origin facing the negative said direction. A directional light simulates sunlight, producing parallel rays of light over the entire scene. Is position does not affect its behavior, only its rotation. We can move and position it anywhere in the scene, preferably somewhere where we can see the white arrow gizmo. This arrow points in the direction of the light rays. Rotate the light source, so the light is pointing down towards the ground, illuminating it for the camera. Now if we run the scene, the camera can see the floor thanks to the light reflecting off of it. Every runnable game needs to have these three basic components, something to look at, a camera to see it with, and a light source. If you need to practice with three D positions and rotations, try making an abstract three dimensional art piece using mesh instances. Note that lights do not cast shadows by default. To enable shadows, click on the light source node, expand the shadow section under light three D, and click on the enable toggle. You can also add additional lights to the scene, adjust the energy or color to produce a wide variety of lighting. Feel free to experiment with different properties of materials and how they interact with lighting as well. When you're done, you should have a good understanding of the relationship between meshes, materials, cameras, and light sources. How they're used to create a three D scene, how to move and rotate objects in three dimensional space, and how to navigate your scene. In the next lesson, we'll start receiving input from the player. I'll see you in the next lesson. Okay. 15. Scripts: Hello, friends. In the previous lesson, we set up a basic three D scene with meshes, materials, a camera, and a light source. In this lesson, we'll accept input from the player. Click on project in the menu bar and open the project settings. Switch to the input map tab at the top of the project settings window. In order to achieve our objective of controlling a three D character, we need to accept inputs for the four movement directions, left, right, forward and backward. Type, move left into the add new action field. Then click Add to add the action. The action name is a description of what the input is used for in our game, conventionally written in lower case separated by underscores. Actions are triggered by events, which we can add by clicking on the plus button. Events can be keyboard keys, mouse clicks, or movement or input from a joy pad. We can search for the desired inputs by expanding their categories, filter the search results, or use the listening field to just press the button we want. For move left, most games use the A key or the left stick of a joy pad tilted to the left. Adding multiple events will better accommodate different styles of play. We also need to be able to move right, forward and backward, adding a new action for each. I'll use the D key or left stick right for right. WK or left stick up for forward, and S key or left stick down for backward. The dead zone value is the minimum amount of pressure or tilt required to trigger this action. Now the engine will listen for these input events and trigger these actions. When you're done, click the close button to close the project settings. Let's create a new node that will react to the input actions we created. This node doesn't need to represent any physical object in our scene, so it can just be a basic node, and name player. Anything in our scenes can be given behaviors by attaching scripts to them. Right click on the player node, select Attached script. Or with the player node selected, click on the attached script button in the scene tab. The default language for scripts in the Goda engine is GodaScript, which is very easy to learn and well optimized for game development. Scripts inherit the behaviors of the node type that they were attached to. In this case, just a normal node. Templates are provided for different node types to provide basic functionality. The template check boxes on by default. Let's take a look at what the default template for a node script has in it. We are creating a new script, not using one that is built into the engine. We can give the script a name, which will be recommended by the engine, matching the name of the node it is being attached to. It is conventional to use snake case when naming scripts like the input actions. Lowercase separated with underscores. Click the create button to create the script, and the editor will automatically switch to script view to edit it. You can switch back to three D view anytime by clicking on the three D button at the top of the editor. Likewise, switch to the script view by clicking on script. The top line specifies the node type that this script can be attached to, which is node. The keyword extends, allows this script to inherit the properties and behaviors of a node, so we can use them in the script. Functions are behaviors that the script will run on the node, it is attached to. Our script template starts off with two functions, ready and process. Gray text, starting with an ctathorp or comments. Text that exists only for our benefit as the developer to explain the script in plain language. The comments tell us that the ready function is automatically called when the node enters the scene tree for the first time. This is not when we create the node in the editor, but when we press the run current scene button. The simulation will build the scene tree, and when the player node is added to the scene tree and is ready, then it will call this behavior. The process function is called every frame, which with the default project settings is 60 frames per second. This function has a parameter named Delta, which is the amount of time that has elapsed since the previous frame in seconds. This variable is represented as a floating point number, a number with decimal points. We can specify the type of any variable with a colon, followed by the type name. Since Gudo script is loosely typed, this is optional, but specifying a type will improve the efficiency of the engine and reduce the frequency of bugs. So it's recommended to always specify the type of your variables unless you need to allow multiple types. Okay. We can use the print command in our scripts to output basic text to the output console here at the bottom of the window. The print command takes a string as an argument, which is a sequence of characters contained within quotation marks. Let's print ready during the ready function and process during the process function. We can also use the plus operator to append strings together, allowing us to append the value of Delta to the process string. But Delta is a float, not a string, which is not allowed. We can cast the type of Delta from a float to a string using the command STR, passing Delta as the argument in brackets. Run the current scene and stop it after a brief moment. In the output console, we can see that ready was printed once, followed by process 0.016, et cetera many times, which is the frame rate of one 60th of a second. Sometimes the number might be higher because that frame took slightly longer to process. So we can see that these behaviors are being called automatically by the engine. Let's remove these print statements and the entire ready function. Instead, we will want to check if the player is giving any input during the process function. To store this information, we need to declare a variable at the top of the script using the keyword VR. Let's call it move direction. Preceding the name of the variable with an underscore marks it as private, meaning that only this script needs to be concerned with it. The type of our variable will be a vector two. A Vector two is a structure for holding two floats named x and y. We've already been working with Vector two when resizing the plane mesh and vector three is when editing the position or rotation of any three D node. During the process function, we can set the value of move direction to the current state of the movement input actions we created earlier using the assignment operator or equal sign. Move direction equals input vector. Input is a singleton class, a built in script that we can reference by its type, since there will always only be one instance of it. The period accesses variables or functions, which in this case, are called methods because they belong to a class. The method we are calling is named get vector. Get vector accepts four strings as arguments, which are the names of the input actions that will be used to create a vector two, which will then be stored in the variable. A negative x value, which is left will be subtracted from a positive x value, to calculate the value of x in the vector two. Likewise, a negative y value up will be subtracted from a positive y value down to calculate the value of y. This may seem reversed, but when looking at the input mappings, the up direction is listed as the negative side of the axis and down is the positive. Also, the camera looks in the negative set direction. To move forward from the camera's perspective actually means to move backwards through three D space. This is consistent with two dimensional games since the scene origin is at the top of the screen and positive values move down, not up. For now, we can just print the value of our variable to see what it is. Let's run the current scene and test it out. The output console is constantly printing zero, zero because no input is being given. If we press any of the WASD keys or tilt the left stick on our joypad, the value is being printed will change. Left means the x value is negative, positive. Down sets the y value to positive, which is toward the camera and up negative away from the camera. Closing the window, we can see a warning message. The value of Delta is not being used by our process function. We can add an underscore to the front of Delta to mark it so the engine knows that it can be ignored since we don't need it. We are now able to read player movement input from either keyboard or JoyPad. In the next lesson, we'll use the player's input to move an object through our scene. I'll see you in the next lesson. 16. Controls: Hello, friends. In the previous lesson, we started receiving input from the player. In this lesson, we will move a three D object through our scene using that input. Let's start by removing all the meshes from our scene except the floor. And adding a new node to the game scene, a character body three D node. This node comes with some built in functionality for the physics of moving a character but doesn't actually represent anything the player can see. So we should add a child node to the character body, a mesh instance three D. Which we can populate with a capsule mesh with the intention of replacing this with an actual character later. The character body three D is at the scene origin on the floor, as is the mesh instance. If we consider the characters position to be between their feet, then the capsule should be moved up in relation to the character body. With the mesh instance three D nodes selected, which should be childed to the character body, move the capsule up along the y axis until it is on the floor, which should be one unit. Selecting the character body three D node, if we move this node, the mesh instance will also move. Child nodes follow the position, rotation, and scale of their parents. Okay. There is a warning in the scene tab stating that the character body needs a collision shape. This is another child node. We need to add to the character body three D node, a collision shape three D node. It is not assumed by the Goto engine that collisions will happen when meshes collide. Instead, we can add an extra shape, usually one that is much more simple to detect collisions in order to make the calculations more efficient. Much like the mesh instance three D node needs a mesh resource, the collision shape three D node requires a shape resource. We'll use the same capsule shape and move it up one unit to be in the same position as the mesh. Okay. This is now a branch of nodes that all relate to our character specifically and have nothing to do with the overall game scene. Right clicking on the character body three D node, select safe branch as scene, and give this scene a name. You can name this scene the name of the character or just leave it as character for now. The child nodes in the scene tree are now hidden, and the character node has a clapper board icon. If we click on the clapper board, the character scene is opened up for editing. Okay. A new tab is opened in the editor for our new scene, and we can switch between the game scene or character scene by clicking on the tabs. The character is now a separate scene from the game scene that we can edit in isolation. Any changes we make to this scene will alter any instances of it in other scenes. Just like our player node has a player script, the character node also needs a character script to tell it how to behave. Attaching a new script to the root node of our character, this new script will inherit from character body three D. We can use the basic template for this node type and click Create to open the New Script. There are a few differences to this template. The character has a speed and jump velocity stored as constants. Constants are similar to variables, but more efficient for values that never change. A gravitational force is being read from the project settings. Okay. Instead of the process function, there is a physics process function. By default, both the process and physics process run 60 times per second. But in games with lots of complex physics bodies, the physics process frame rate can be reduced to improve efficiency without sacrificing graphical updates or response time. Anything to do with velocity or force calculations are done in the physics process function to keep them separate. During the physics process function, if the character is not on the floor, then gravity times Delta is being subtracted from their vertical velocity. If the player presses the UI accept action, a built in input action assigned to the space bar, then the character will jump. This template is also using similar code to read the movement input from the arrow keys. The input is then converted into a vector three by adding a zero for the y value and using the vertical y movement for the zt axis, converting up and down inputs on the keyboard of controller into forward or backward movement. This is then multiplied by the nodes basis, which is a structure of three vector threes pointing right up and forward. These are the same three colored arrows that are displayed in the editor when we select any three D node. However, these arrows are in global space. If we rotate the character body, the arrows remain the same. The nodes basis vectors are displayed if we switch to local space by clicking on this togle or pressing the T shortcut key. As the node rotates, its basis vectors or its local interpretations of right up and forward will also rotate. These are the directional vectors that are being used to determine the direction the character will move when input is given. The direction is then normalized. Meaning regardless of any rotation or partial tilt of the left stick, the length of the directional vector will become one, resulting in the character always moving at a constant speed. This is shorthand for if the direction is not zero. Then move the character along the x and z axes by direction multiplied by speed. If the direction is zero, then move their velocity toward zero at a rate of speed. Finally, the move in slide method is called to apply their velocity and external forces for the duration of Delta to figure out where the character should be this frame. If we try to run this scene, nothing is drawn because this is the character scene which has no camera or lighting. So switch to the game scene and run this swan to test the game. The character immediately falls through the floor. This is because the floor doesn't have any collision. Let's add a static body three D node to the scene. A body is a general term for any object which is affected by physics or collisions. Static means that it is unchanging, or in this context, that it will not move either from physics like gravity or from collisions with other bodies like our character. The floor mesh instance should be a child of this static body node as it is the visual component of it. Then we need to add a collision shape three D node to give the floor a shape with which it will collide with other bodies. This shape will be a box. Expanding the box shape resource, we can give it a length and width matching the floor, -5 meters in both length and width and a height of zero. We can rename the nodes to reflect this new arrangement, so the static body three D node is the floor, and the mesh instance three D is a child using its default name. Now when we run the current scene, the character's collision shape will collide with the floor's collision shape, stopping them from falling. The floor will not react to the collision because it is static. Crossing the arrow keys will move the character body three D node and pressing the space bar will jump. Back in the character script, the comments tell us that as good practice, we should replace the built in UI actions with custom gameplay actions, which we already have. So we can comment these two lines, pressing Control or command K. But the direction variable is being declared here inside the physics process function. This is called the scope of the variable. It is declared inside this function and therefore only exists inside this function. Like we did with the player script, we can declare this direction variable at the top of the script outside the physics process to expand its scope to the entire script. We'll set it to be private by proceeding it with an underscore and set its type to be vector three, a structure containing three floats for XYZ. And also edit the name of the variable inside the physics process to match. To move the character, let's write a new function in our script named move, which we'll accept a parameter called direction of type vector three. This direction parameter is different from our direction variable, since this one doesn't have an underscore, and its purpose will be to store the value that was passed into this move function, assigning it with the assignment operator. The name of this function move does not have an underscore, marking it as public, which means that it is meant to be accessible to other scripts. Okay. Our player script will call this method to move the character based on the move direction that was received. We can switch between scripts by clicking on them in the list of scripts to the left of the script view. Switching to the player script, we can add a new variable to hold the character of the players controlling. This is a private variable underscore character of type character body three D. Adding at export to the front of the variable declaration, will allow it to be accessed by other parts of the Gda editor, namely the inspector tab. In the game scene, if we select the player node, the character variable is listed in the inspector and we can assign its value. Since we specify the type of the variable to be character body three D, only nodes of that type or nodes that inherit from that type can be assigned. Assign the character node to be the value of the character variable in the player nodes script, either by clicking on a sign and selecting it from the list or clicking and dragging the node into the field. Instead of printing the value of move direction, we want to call character do move and pass the move direction as an argument. But the type of move direction is a vector two, and the type of the parameter expected by the character do move is a vector three. Even if these were the same type, this would not be the correct direction to move the player. Since the direction that we expect the character to move is dependent on the camera's perspective. Let's add another exported variable for the camera. Then assign its value. We'll rename move direction to input direction and create another variable for move direction of type vector three. How do we convert the two dimensional input direction into a three dimensional move direction based on the camera's perspective? We multiply the camera's basis by the input direction, similar to the way it was done in the template code, but we'll do it a little bit better. First, take the camera's right direction, it's red arrow, which we can access with camera dot basis dot X and multiply it by input direction dot x. If the player presses right, they will move along the camera's right direction, and if they press left, then input direction x is negative and right multiplied by negative number will go left instead. Then add using the plus equals operator, the cameras forward direction, it's blue arrow, camera dot basis do multiplied by input direction dot y, converting the vertical input into movement along the cameras forward and backward directions. But the camera is currently rotated since it's looking down. So if the player moves forward, they will be moving slightly down into the ground, moving slower than they would left or right, and moving backward, they will actually be trying to move up into the air to move closer to the camera. Multiplying the basis vector by vector 3101, will remove any y component, flattening it onto the x said plane. We then need to normalize the flattened vector three, so it will always have a length of one and not affect the player's movement speed. This will remove any effect of x or z rotation on these directions, but the camera's y rotation will still alter the player controls. Now when we run the current scene, the players script is checking for input, every frame. If we give an input, then it will check the camera's right and forward directions, flatten them onto the ed plane, normalize them, and tell the character what direction to move based on the input. Then the character's physics process will move them in that direction. Moving the character using the camera's flattened basis vectors allows the player to maintain control of the character from any camera angle, and even while the camera is moving or also being controlled by the player at the same time since the movement direction is calculated dynamically every frame. Separating the player inputs from the character is also a more flexible structure since the character of the player is controlling can easily be changed. The script telling the character where to move could easily be controlled with game logic instead of the player input. We now have the player able to move a capsule through our scene, and the next lesson we'll import a character model to replace the capsule. I'll see you in the next lesson. 17. Import: Hello, friends. In the previous lesson, we used to play your input to move a capsule through a three D scene. In this lesson, we'll replace the capsule with a character model. Let's start by downloading an asset pack from Soto. For this course, I'm using the free version of Kits character Pack Adventurers. Clicking on download now, you'll be prompted to pay, but you can click on No Thanks. Just take me to the downloads. We optionally choose to support the artist by giving any amount of money you feel is appropriate. There are also paid versions of these asset packs, which include more content. I recommend trying them out for free and paying for the extra versions if you like their work. Then click on download to download the assets. While waiting for the download to complete, let's go back to the asset page and take a look at what is included in this asset pack. There are four characters all with a variety of weapons and accessories, and each character is animated with a wide variety of animations for us to use in a consistent format. The models are efficient for use in mobile games and are compatible with the Cada engine. These assets are free for personal or commercial use under creative common zero license. Be sure to check the licensing for assets you download before using them in your projects. In some cases, attribution may be required, or use in commercial projects may be forbidden entirely. When the download is complete, extract the contents of the zipped folder somewhere on your hard drive. Then open the folder to see what's in it. We want to use the characters and the models are available in two different formats. For this course, I'll be using the GLTFFiles. Importing the assets into Coda is as simple as clicking and dragging them into the editor window. Our resource folder is starting to look a bit messy, so let's organize it. Right click on the root resource folder and create a new folder. Let's name this folder, imported assets. Then create another folder inside that one named characters. Group select and drag the imported character assets into this new folder. Okay. We can also create a new folder in the root resource folder for holding scripts and another for scenes. The scripts have a cog icon and the extension of GD. While scenes have the board icon and their extension is TCN. You can organize your resources, however, makes sense to you and best accommodates your workflow. Before we can work with the imported assets, we need to edit some import settings. Select any of the character GLB files in the file system tab. Then switch the doc that is currently displaying the scene tab to the import tab. Here we can edit how the God editor interprets the files and generates the imported assets. Most of these settings are fine for our purposes. First, let's add a root type of character body three D. By default, the character will have been imported as a node three D. But we want our character's root node to be a character body three D to be compatible with our character script. If we change any of the settings, they will not be updated unless we re import the asset. Next, click on the Advanced button. The advanced import settings window opens, and we can see a preview of what the imported asset will look like. You can zoom in and out with the mouse wheel or click and drag to rotate the model to get a better view. To the left is the scene tree that will be generated by this asset, including a skeleton, bones, meshes, and an animation player with all the animations. Before we can work with this character model, we need to allow certain animations to loop. Let's start with the idle animation. With the idle animation selected, we can preview what it will look like with the play button at the bottom of the window. But it only plays once and then stops, which is exactly the problem. On the right side, we can change the import settings for the animation, changing loop mode to linear. Now when we hit play, when the animation finishes, it will return to the start and play again. Ping Pong will instead play the animation backward to return to the start and forward again after that. Let's set it to linear. Then repeat for any animations in this list that contains the word idle, blocking, or running. While not covered in this course, you may also want to allow looping for shooting, spinning, blocking, and spell casting too. When you're done, click on the re import button to re import the assets with the updated animation settings. You will need to repeat this process for the other characters to before being able to use them. Switch to the character scene. Click and drag the character asset onto the root node of the character scene to add it to the scene tree. Let's turn on preview lighting so we can see our imported character better and delete the mesh instance node drawing the capsule mesh. The barbarian node is just one node, not the big tree of nodes we saw in the import settings, and it has the clapper board icon, implying that this is an instance of another scene. Like we created the character scene from the game scene. We can do that in reverse. Right clicking on the Barbarian and selecting make Local. This will break the link this node has with the imported asset and make it a unique version that only exists in this scene. And now we can see all of its child nodes. The animations are contained within the animation player node. Let's collapse the rig node for now to hide most of it. Next, we want the Barbarian to be the root node of the scene. We can right click on it again and select make seen root to organize the scene tree so that this becomes the new scene root node. Next, let's click and drag the collision shape three D node to re parent it to the Barbarian node. Making this the collision shape for the Barbarian and delete the original character body three D node since it is now redundant. However, we still need a character script attached to the root node. We can also add this by clicking and dragging it onto the root node. Our scene tree still looks the same as it was before. Only the mesh instance three D node is now replaced with the character rig, and there is an animation player node. Our character appears to be holding far too many items in their hands. Expand the rig node to view its children. We can hide any of the items being held in the character's hands by clicking on the icon beside them. We can also hide the character's hat and p for any of the characters other than the rogue. The rogue has an entirely different model for when the hood is up since the hair is also removed. Depending on the version of Coda or the assets you're using, there may be a red X icon showing that there is a problem with this asset. Selecting it and looking in the import settings, we can change the GLTF naming version to do 4.1 or earlier and re import the asset to fix this issue. When you're done, collapse the rig once more. If we save the character scene from the scene menu or pressing control S or command S. Then switch to the game scene. We can see that the character in our game scene has been updated running this scene, we can move the character around just as before, but they are awkwardly moving around in the TPs. We now have the character model imported into our project. In the next lesson, we'll animate our character walking. I'll see you in the next lesson. 18. Walking: Hello, friends. In the previous lesson, we imported character models into Cada. In this lesson, we'll animate the character to walk around our scene. Let's start by moving the camera back a bit since the character is larger than the capsule was. The character movements are very rigid. The character is either moving at full speed or not at all. This doesn't feel very good and isn't how anything actually moves. There should be acceleration up to a maximum speed and deceleration before stopping. Opening up the script, we can see that the character speed is declared as a constant. Let's instead declare some exported private variables to define how our character moves. Starting with their walking speed measured in units per second, which is best represented as a float. We can give variables a default value using the assignment operator in the same line when they're declared. I'll set it to one. We also want acceleration and deceleration values measured in units per second squared. Also float types. Consider how quickly you would switch from standing idle to walking at full speed. About half of a second sounds reasonable, which is an acceleration of double the walking speed. And also how quickly you would come to a complete stop when walking at full speed. This is usually faster than acceleration, so I'll make it four. Selecting the Barbarians root node, we can see the exported variables available to be edited in the inspector tab. Editing these numbers will overwrite the default values provided in the script. But pressing the reset button will return them to their default values. This allows us to easily experiment with different values and also provides the possibility of different characters having different movement speeds. To make things easier, we can separate the characters velocity on the x z plane from the velocity along the y axis, allowing us to isolate adjustments in movement velocity from things like jumping and gravity. Let's declare another private variable X Z velocity of type vector three. At the start of the physics process, we can set the value of x velocity to be a new vector three, copying the character's velocity on the x and z axes, while setting the y value to zero. We can then make our adjustments to the characters X Z velocity here without worrying about the impact of their y velocity. Then at the end of the physics process, before movement slide, we can set the characters velocity to be the adjusted values for X and Z, but the same value as it was for y. This method we'll also have additional benefits later. Okay. Next, we will need to edit this block of code that moves the character. The statement is dividing this into two cases. If the player is providing a movement direction or not. Instead of breaking the velocity down into x and z components, since we removed the y velocity, we can simply edit the x z velocity directly here. If the player is providing a movement direction, we should assign x z velocity to move toward direction multiplied by walking speed at a rate of acceleration times Delta. This will take whatever their current velocity is along the X Z plane and move it toward their walking speed in the direction that the player wants to go at a rate of acceleration. Since Delta is the amount of time that has passed since the last frame, usually one 60th of a second. It will adjust the acceleration to match the frame rate, resulting in the character accelerating and moving at the specified speeds, regardless of what the game's frame rate is set to. Likewise, when no movement input is given, the character should decelerate to a velocity of zero at a rate of deceleration multiplied by Delta. We can delete the original speed constant now since it is no longer used. Let's switch to the game scene and try moving the character now. Now the character accelertes up to a walking speed and deceleratees more naturally. Back in the character scene, we can switch to three D view so we can see the character. Select the animation player node and the animation tab will open at the bottom of the window. The animation currently being viewed is one handed male attack chop, and the character model has assumed the first frame of the animation. We can press the play button at the top of the animation tab to preview the animation. Expanding the animation tab and scrolling through the animation tracks, we can see that each bone is keyframed to produce a resulting animation. Use the Zoom slider to adjust the amount of time displayed in the animation tab and you can see each individual keyframe. Using the drop down, we can select any of the animations that were imported, including idle. And previewing this animation, since we configured the import settings, the animation will loop. So the animation player node can play any of these animations. We just need to control which animation is playing. You can do this through a script, but the code will be very long and have to accommodate too many possibilities. The best way to control which animation is playing is using another node, we can add to our character scene, an animation tree. The animation tree needs to be told which animation player it is controlling. So clicking on the assigned field beside Anim player, we can select the animation player node from the list. Next, we need to provide a resource for this node just like we did with the mesh instances and the collision shapes. This resource is called a state machine. Creating the state machine we'll open the animation tree tab at the bottom of the editor window. We can pan the viewport of this window by dragging with the mouse wheel pressed. The state machine will track what state the character is in and play the correct animation for that state or even blend multiple animations together. We simply need to tell the state machine what those states are and also how and when the character will transition between states. For now, we will only have one state, which will blend the idle and walking animations together. If the character starts walking from idle, they shouldn't immediately transition from idle to walking, nor should they immediately transition back when they stop. Not to mention that the player will be able to tilt the left stick slightly to move slower if they want to. We need the idle and walking animations to blend together depending on the character's movement speed. This can be achieved with a blend space. Right clicking on empty space, add a one dimensional blend space to the state machine, and name it locomotion. Then click on the Connect Nodes tool. Click and drag from start to locomotion. Making this the default state where the character will start when we run the game. Switch back to select and move nodes. Then click on the pencil to edit the locomotion blend space. A one dimensional blend space has one axis, which is displayed horizontally ranging from negative one to positive one named value. If we consider this to be the characters speed, we can rename it to speed and set its minimum value to zero and maximum value to one. One, in this case, should be considered to be 100% of speed, not a speed of one unit per second. We can add animations to the blend space with the create points tool. Clicking over at zero. We can add the idle animation. One, the walking animation. Click set the blending position within the space. Then click anywhere along the axis to see how the animation looks at that speed. You can also click and drag to see how changing the speed over time blends the animations together. Okay. Now, all we need to do is have the character script, tell the animation tree the character speed. Nodes can communicate with each other easily by assigning them to variables, and we can do it all in one line of our script using at on ready at the start of the declaration. Let's call this variable, underscore animation of type animation tree and assign it of value. Using the dollar sign, we can then specify a node path to access any node in the scene tree, starting from this node that the script is attached to. If the node we are trying to access is a child of this one, we simply specify the node's name. Following a sequence of nodes can be done by separating the node names with a forward slash. If the name of the node contains spaces, you can put the node path in quotation marks. If you're ever unsure about the node path, you can click on any node in the scene tree and select Copy Node path. Then paste it as text. Our script now has a reference to the animation tree node. All we have to do is set the blend space position after our X Z velocity calculations. Using the variable, we can access the set method to set any property we want. The property we want to set is defined as a string, representing a path to the property, using the same format as the node path. We can use the suggestions to auto fill this parameter, looking for our locomotion states bled position. Like with the node path, selecting the animation tree in the scene tree, we can expand parameters, locomotion, and copy the property path. Then paste it as text. Then specify the value we want to set this property to, which will be a value 0-1 representing the percentage of the character's maximum speed at which they are moving. Since the D velocity is a vector, we can use the length method to know exactly how fast the character is moving along the D plane. Then divide this by their walking speed to convert it into a number 0-1. Let's see how this looks when we run the game scene. Now the character blends between the idle and walking animation relative to their speed along the x d plane, but they aren't rotating to match the direction that they're moving. Try experimenting with different values for the character's walking speed, acceleration and deceleration to see how they feel. In the next lesson, we'll add rotations and jumping. I'll see you in the next lesson. Okay. 19. Rotation: Hello, friends. In the previous lesson, we animated our character's locomotion. In this lesson, we'll rotate the character model to look in the direction they're walking. Starting in the character script, if the player is providing movement direction input, then we can simply tell this node to look at its current position plus the direction. This will rotate the character body three D node so that it's forward basis vector is pointing directly at these coordinates. Since we have restricted direction to the x said plane, this will result in only ever rotating the character about the y axis to face the direction the player wants to move. But the rotation will be immediate and point the character in the opposite direction. Since Gudo reverses the Z axis, we can correct this by simply changing the plus direction to minus direction. Also, if we have any nodes childhood to the character in the game scene like the camera, for example, then it will also be moved and rotated, which isn't what we want. The solution isn't going to be sufficient. Okay. In the character scene, we can see that all the visual components of the character are children of a node three D named rig. Selecting rig in the scene tree, we can edit its transform, clicking and dragging the y rotation slider handle to rotate the character model about the y axis. This way, the character model can be rotated independently of the character body three D, so it won't affect any other children in the game scene. In the character script, much like we did with the animation tree, we can use at on ready to store a reference to the rig node in a private variable. Then when we add movement velocity to the character, we can also tell the rig to look at where it's going. The character now faces the direction they're moving without affecting other nodes. But the immediate rotation might not be what we're looking for. So let's make the character rotate a little bit each frame. We can define a character's rotation speed as an exported private variable and give it a default value. Game engines typically calculate rotations in radians, not degrees. If you're not familiar with radians, a complete circle of 360 degrees is Pi times 2 radians. There's also another mathematical constant named T, which is Pi times two. But if we consider the use case for this value, we are only concerned with how fast the character will rotate up to 180 degrees. Otherwise, they would simply rotate in the opposite direction. So we can define the exported variables value as a multiple of Pi or how many half rotations the character can make in 1 second. Which inverted will tell us how long the character will take to rotate halfway around. Alternatively, you can define your character's rotation speed in degrees and use the degree to radiance function to convert it when the game starts. I'll use the Pi method and say that my character should be able to turn around in half of a second, making the rotation speed Pi times two. Okay. In the physics process, if the character is giving movement input, we first need to figure out what angle we are rotating two. We'll call this the target angle. We can find our target angle using trigonometry. If target angle is theta, which in this case, represents the y rotation of an object facing in the direction, we can find it using the inverse tangent function. A t two. This takes two float arguments, which are direction dot x and direction dot z values. Now target angle holds the value, we want to gradually move our y rotation to. Next, we need the difference between these two angles, expressed as a number between negative Pi and positive Pi. Telling us how far we need to rotate and in which direction. Subtracting our current y rotation from target angle will provide the difference between the two angles. To ensure that the character will only ever rotate along the shortest path, we then wrap this result using the rap f function, making sure that the result is always between the value of negative Pi and positive Pi. We now have all the magic numbers, we need to rotate the character, but how do we use them. We can determine the direction of the rotation using a function called sine, passing the angle difference as the argument. This returns negative one if the number is negative, zero, if it is zero, and positive one if it is a positive number. If we multiply this by the character is rotation speed, and Delta, then we know how much they can rotate and in which direction in a single frame. Adding this to their y rotation, we'll rotate the character, but we'll also overshoot the target angle. So adding a clamp function, we can restrain the value rotation speed times Delta to only be between zero and the absolute value of the angle difference. The absolute value is with the sine removed, so a negative number will become positive. Then multiplied by the sine to ensure the character rotates in the correct direction. This will stop the character from overshooting the rotation and stop at the target angle. To make this more efficient, since we are only using the target angle once in the calculation of the angle difference, we can copy and paste its calculation into the same line and have one less variable. And since this process is running 60 times per second, it should not be declaring any variables. So we'll move the variable declaration to the top of the script. Lastly, we need to remove the look out line below since it is no longer relevant. Now the character rotates gradually to face the player's input direction. And we can alter how fast they rotate in the inspector. Next, let's add a run button that the player can hold down to move faster. Open the project settings and switch to the input map tab. Add a run action. I'll use the shift key or left face button. In the player script, we can react to the individual button press events more efficiently than we did with the movement inputs using a different function, underscore input. This function is only called during frames when there is an input event happening. Instead of Delta, the parameter for input is an input event, which is a structure containing information about what the event was that happened. Inside this function, we can then check this structure to see if the run button was pressed. If it was, we can tell the character to run. Okay. Likewise, we can also check if the run button was released. And if so, tell the character to walk. In the character script, we can add a running speed to go along with the walking speed. Then add another non exported private variable called movement speed with a default value of walking speed. All we have to do in the walk and run functions is set the value of movement speed to be the walking speed or running speed accordingly. Then use the movement speed in our velocity calculations. However, when determining the blend position for the character's locomotion animations, we will use the running speed. Selecting the character's animation tree, we can then edit the locomotion blend space. Now, if we consider a speed value of one to be the character running at full speed. We can use the select and move points tool to move our walk animation somewhere closer to the middle of the axis and add another animation on the far right for our running animation, either by switching to the create points tool or right clicking. At any value of speed, the blend space will blend together the nearest animation to the left and to the right applying weights to the animation based on their proximity. Back in the game scene, before testing a running, we may want to give our character some more room. So selecting the floor, we can edit the size of the floor and all of its children quickly by increasing its scale. Now when we run the game scene, the character can not only walk around the scene, but also run around by holding down the run button. Depending on the values used for acceleration versus deceleration, it may seem like the character slides around struggling to redirect their momentum. If we take a look at the physics process, the statement breaks this down into whether the player is providing movement input or not. We can break this down further to also account for if the player wants to move in the same direction as the character or not. To check if what direction the player wants to go is similar to the direction the character is already moving. We can use some vector math. A dot product is a way of multiplying two vectors together that will result in a number representing how similar or different those vectors are. The dot product of two vectors, both pointing in the same direction will be one, and opposite directions, negative one. If the two vectors form a right angle, they are perpendicular, their dot product will be zero. So we can check if the results of the dot product is greater than or equal to zero to know if the character should be accelerating in the desired direction. Since the player wants to move in more or less a similar direction to where the character is already moving, Otherwise, the dot product is negative, which means that the player is trying to move more or less in the opposite direction. We should tell the character to decelerate, not accelerate. Now the character's deceleration can be applied when the player suddenly changes direction. We now have the character rotating and running around our scene. In the next lesson, we'll add jumping animations. I'll see you in the next lesson. 20. Jumping: Hello, friends. In the previous lesson, we rotated the character to face the direction they're moving. In this lesson, we'll add jumping animations. Let's start by enabling shadows from our scenes sunlight. Select the directional light three D node, expand the shadow section, and turn on the enable toggle if it isn't on already. This will give us a little bit more perspective of how high the character is jumping. In the character scene, if we select the animation player node, the animation tab should open at the bottom of the window. You can also open it manually by clicking on animation here. Looking through the list of animations, we can see that there are several different jumping animations. If we select any of these animations, they will not play. This is because the animation tree is in full control right now. Select the animation tree and click the active toggle to disable it while we explore the jump animations. Switching back to the animation player, open the jump full short or long animation and hit play. These animations aren't very flexible. We want our jumps to be more dynamic. Opening the jump start animation, we can see that this animation is what should happen when the player presses the jump button. Zoom in so you can see the animation key frames. Since there are 30 frames per second, they are spaced 0.033 3 seconds apart. We can set the snap to this value to allow us to scrub to each individual frame using the timeline. If we switch our preview to orthogonal left or right view and zoom in on the character's feet. Note that the character's feet remain on the ground for a whole 0.3 seconds of the animation. This is important, since if we add velocity to the character at the start of the animation, it will look wrong. This is then followed by the jump idle animation, which should loop until the character touches the ground. Which should then lead to the jump and animation. Breaking these animations down like this allows the jump to be of any duration and still look good to the player. Also note that the final frame of the jump start animation is identical to the first frame of the jump idle animation. This is also true about the final frame of the jump idle and the first frame of jump land, but it's less importance is the moment when the character lands could happen at any time. Selecting the animation tree node, we can turn it back on in the inspector and then take a look at the state machine. We can add the three jump animations to the state machine. I'd like to put them above the locomotion blend space, arranged in the same sequence left to right as they will be used. Add a connection from jump start to jump idle. Then look in the inspector panel. These are the properties of the transition between states. We want these two animations to play in direct sequence since the last and first frame are identical. When the jump start animation is complete, just immediately switch to jump idle. Expanding the switch section, we can set the switch mode to at end. The arrow icon in the state machine will change to indicate that this transition will happen at the end of the animation. Adding the transition from jump idle to jump land. We want this transition to happen immediately when the character touches the ground. Meaning we won't know exactly what frame jump idle is in when this happens. To avoid awkward twitching, we can cross fade the two animations together for a brief window like one tenth of a second, just long enough for the change to not be immediate. As for when the switch happens, we want it to be immediate, but under a specific condition, when the character touches the ground. Expanding the advanced section, we can write any condition or combination of conditions we want in the expression text area to control when this transition will happen. Like in the character script, we can use the character body three D nodes built in method is on floor to check if the character is touching the ground. When this becomes true, the transition will start cross fading from jump Idle to jump land for one tenth of a second, then playing the remainder of jump land. However, to use this feature, we need to reselect the animation tree node and change its advanced expression base node property. This is the node that the advanced expression will be called on, and we want to use the character body three D node for this to check the value of is on floor. Next, at a transition from jump land to locomotion. We will want to allow this animation to play in its entirety before transitioning back to locomotion. So it's switch mode should be at end. While the final frame of jump land is the same as the first frame of idle. If the character lands while in motion, then we don't want to immediately switch from jump land to walking or running. So we can add a cross sad to this transition as well to blend it more smoothly. Adding a transition from locomotion to jump, this will be triggered by the player pressing a button. Expand the advanced section and change the advanced mode from auto to enabled. This turns the green arrow gray in the state machine, since the transition is no longer automatic. This will allow us to manually trigger this transition through our character script later. Since we don't know what the locomotion blend space is doing at any random moment when the player presses the jump button, adding a brief cross fade to this animation will help smooth the transition too. Lastly, the jump idle and jump land animations could also be useful for if the player walks off of a ledge instead of jumping. Adding a transition directly from locomotion to jump idle will accommodate this case. We can set the advanced expression for this transition to be if the character is not on the floor. And cross fading from locomotion to jump idle to smooth the transition. But what if the conditions for transitioning from locomotion to jump start or jump idle are both true at the same time. The character has left the floor and the player pressed the jump button this frame. If you want to control which transition takes priority in this case, you can select each transition and set their priority setting. The tool tip tells us that lower numbers are given priority when multiple transitions are possible. I'll give priority to jump start in this case by setting its transition priority to zero. We can preview how these animations, transitions, and crossphads will look by clicking on the play button on any state to force the state machine into that state. Note that this character is floating in empty space and is therefore not on the floor. Our character script is still using the basic template code to make the character jump. We should update this to match how we handle inputs for walking and running. Open the project settings and switching to the input map tab, we can add an action for jump. I'll use the spacebar and bottom face button on my controller as events for the jump action. Okay. Closing the project settings and opening the player script, we can add another condition to our statement to check if the jump button was pressed. And if so, tell the character to jump. Switching over to the character script, we can add a public jump function. Okay. Before we can write the jump function, we need access to the state machine. Adding another variable declaration below the animation tree. Starting with add on ready, we'll get a reference to the state machine playback. It's type is animation state machine playback and we can access it from the animation tree using square brackets. Inside the square brackets, we can specify a path to what we're trying to access as a string. The thing we want to access is parameters playback. Okay. Like before, if we select the animation tree node, we can see the playback resource listed here. Hovering over its name gives us the property path, or we can right click and copy its path and paste it as text. Now that we can access the state machine when the character is told to jump. If the character is on the floor, then we can request that the state machine travel to the Jump start state. Make sure that the name of the state matches exactly. Then also copy the line from below, which applies the characters jump velocity, and remove these lines from the physics process altogether. If we test this out, the character will jump, but the animation and velocity are not in sync. Adding another separate private function named apply jump velocity, we can set the character's y velocity to the jump velocity, moving the character body upward. And make sure the script is saved before you proceed. Disable the animation tree for a moment. Then in the jump start animation, scrub to the frame when the character's feet leave the ground. Clicking on plus add track. We can add our own tracks to this animation. The track type we want to add is a call method track. Next, select the node we want to call a method on, which is the character body three D node. Scroll down to the bottom to find our newly added track, and right click on that track where the scrub line intersects it. Then select Insert key. We can call any method from our character script or any of the inherited methods from the character body three D node itself. The method we want to call is to apply the jump velocity to our character. Remember to reactivate the animation tree before testing. Now when the player presses the jump button, the character will start the jump animation and wait until their feet actually leave the ground before moving upward. Okay. In some games, you may want the character to jump more responsibly. In that case, it is important to use animations where the character's feet leave the ground at the start of the animation. We now have the characters jumping and falling animated. In the next lesson we'll improve how the jumping feels and how it is controlled by the player. I'll see you in the next lesson. 21. Gravity: Hello, friends. In the previous lesson, we added jumping animations to our character. In this lesson, we'll improve the jumping and falling mechanics. Testing of the jump, there are a few problems. It's slow and floating. The player has just as much control over the character in the air as they do on the ground, and we can't change the jump height in any way. If we look in the character script, the jump velocity is defined as a constant. We can also see that the force of gravity is being retrieved from the project settings. If we open the project settings, we can follow this string to find the gravity settings under Physics three D deft gravity. The direction of gravity is also editable here. Let's increase the gravity so the character feels heavier. I'll multiply by two. Note that this change will affect everything in the entire project that is subject to gravity. If you only want to affect the character's gravity, we can instead do that in the character script. In the game scene, switch to three D V. In my mind, it doesn't really make sense to define a jump velocity. Instead, I like to think of how high I want the character to be able to jump. We can quickly and easily create basic geometry in our scene using a node type called a CS G or constructive solid geometry. Let's add a CSG box. Move it to the side and rest it cleanly on the floor. Add collision to the box by clicking on the collision toggle in the inspector. We can resize the box by entering values into the inspector or clicking and dragging the red dots in the preview. Resize the box until it represents the maximum height you would like your character to be able to jump onto. We can see exactly how tall the box is with its y value in the inspector. But make sure that the bottom of the box is on the floor. It's transform position y value should be half of its height. Duplicate this box either by right clicking on it and selecting duplicate or using the shortcut Control D or command D. Place the duplicate next to the other box. Make this box taller than the previous one and have it represent a height that the character should not be able to jump onto. In the character script, like we have defined a bunch of variables to customize how the character moves, we can do the same thing with how the character jumps. Let's export a private variable named jump height as a float and give it a default value somewhere in between the heights of the two boxes we just made. This will allow the character to jump onto the first box, but not the second. Then add a private variable not exported to hold the actual jump velocity that is required to reach that jump height. We now have two completely separate categories of variables in our script, one for controlling how the carrier moves and another for controlling how the carrier jumps. We can add another line above these to further emphasize their distinction at port category. Then provide a string name for the category. I'll call these locomotion variables and the ones below jumping variables. This not only provides us more information here in the script, but also separates them in the inspector. I'll also move direction into the locomotion category and gravity into the jumping category. I mark it as private with an underscore. If you want your character to experience a different force of gravity from other objects in your game, you can also export another variable. Let's call it mass and give it a default value of one. This will act as a multiplier to the gravity being added to the character. So at 0.5, gravity would be reduced by half, or with a mass of two, gravity would double. In the ready function, we can calculate the jump velocity using physics. The jump velocity required to reach the desired jump height is the square root of jump height multiplied by gravity, also multiplied by mass if you're using it and multiplied by two. Okay. Switch the jump velocity being added to the character's y velocity for this new variable, and we can now delete the constant since it is no longer being used. We also need to multiply graphty by mass in the physics process. Let's run the game scene and try jumping in our boxes. The character can jump high enough to reach the first box, but not the second. Try experimenting with different values for jump height, gravity, or mass, and see how they change the feel of the characters jumping. What if the player doesn't want to jump at full height? Most games will have the character to do a smaller jump tapping the button, and a higher jump, holding the button down. The character animation having a delay before actually jumping can really be beneficial for implementing this feature. Let's start by not defining a jump height, but both a max jump height and a min jump height. Which means we will also need a minimum and maximum jump velocity. And calculate both of these values in the ready function. Okay. We then need to scale the amount of velocity being added to the character based on the amount of time the button was held down. To measure time, there's a convenient node type we can add to our character a timer node. Let's name this jumper. And grab a reference to the timer node during the ready phase, the same way we did with the other nodes. Since the name of the node contains spaces, the path is enclosed in quotation marks. Splitting the jump into two server functions, let's call the first one start jump. When the jump button is pressed, we can start the timer. Then when the jump button is released, we can set its paused property to true. Which means that when we start it, we will also have to set its paused property to false. So we'll need to edit the players script to call start jump when the button is pressed and complete jump when the button is released. In the inspector, we can see that the timer is set to count down from 1 second, which is longer than 0.3 seconds, so that's fine. But we need to check the one shot toggle, so the timer will only count down once and not reset itself every time it completes. When the jump velocity is actually added to the character, we can pause the timer if it hasn't been paused already. In case the player is still holding down the jump button. We can start by adding the minimum jump velocity. Then also add the difference between the maximum and minimum velocity. Multiplied by one minus the time remaining on the timer, divided by the maximum amount of time possible, which we determined from the animation is 0.3 seconds. We never want this to be above 0.3 seconds so we can apply the min function to cap it at 0.3. If the player presses and releases the button within a single frame, this will be near zero, and the result will be minimum jump velocity. If the player holds down the button, the holds 0.3 seconds, this will be zero point 3/0 0.3, which is one, and the result will be the maximum jump velocity. An amount of time in between will scale linearly between the two results. Let's try it out by running the game scene. Tapping the jump button, the character jumps up to a height of 1.5 units, and holding down the button, they jump up to 2.5 units. Holding down the button for different durations produces jumps of different heights. Next, let's add more exported variables to our jumping category. Air control and air brakes. These are multipliers to acceleration and deceleration respectively, which will reduce the amount of control the player has over the character in mid air without eliminating it entirely. 0.5 is a good place to start for their default value. You will need to test to determine how you want the character to feel in your game. In the physics process, the first block of code rotates the character rig to face the desired direction. In most games, this will happen regardless of whether the character is on the ground or in the air. Next is adding the gravity, which only applies when the character is not on the floor. This block is moving the character, but it is doing it in a way that only makes sense when the character is on the floor. But applying it every frame, regardless of whether they are on the floor or in mid air. Then the locomotion animation blend position is being set, which again, only applies when the character is on the floor. Applying D velocity and the move and slide method are universally applicable. So we can separate the parts of the physics process into general physics, ground physics, and air physics, writing a new function for ground and air physics, both private and accepting a flow parameter for Delta. We can then cut and paste the gravity into the air physics function and the movement calculations into the ground physics function. Then in the physics process, if the character is on the floor, call ground physics. Otherwise, call air physics. Passing Delta as an argument to both. Our physics process is now much smaller and easier to understand, while the ground and air physics are separated from each other and easier to manage as well. So how do we apply air control and air brakes? Copy and paste the x velocity calculations from the ground physics into the air physics. Then multiply anytime the player is trying to control the character in the air by air control. And anytime they are not trying to control the character in the air by air brakes. This is not the same as acceleration and deceleration in this case, since when trying to turn around in midair, the player is still trying to control the character. Applying the jump velocity 0.3 seconds after the jump animation starts, we'll produce a large amount of coyote time to the character. If you don't want this, you can require that the character still be on the floor before adding the jump velocity with an if statement. Also, the movement speed initialization should be using add on ready. Currently, this is initializing movement speed to the default value of walking speed. But if we were to overwrite the default value of walking speed, it would not be assigned to movement speed. Use geometry blocks to build a small obstacle course for your character. How difficult is it to control the characters jumping? Experiment with different values until you get the feel you want for your game. We now have the characters jumping and following mechanics, feeling more realistic. And the next lesson, we'll add camera controls. I'll see you in the next lesson. 22. Camera: Okay. Hello, friends. In the previous lesson, we adjusted how forces affect our character in midair. In this lesson, we'll add camera controls to our game scene. Like we've done in previous lessons, it would be easy to simply child the camera to the player. The camera will follow the player around, but then rotating the camera around the player would be complicated. But if we just add another layer of separation, a no child to the player with the camera then a grandchild, this becomes much more simple. The camera tilts and rotations that we expect from third person controlled games can be achieved by only altering the rotation values of this intermediary node, either about the x or y axes. Rotating about the y axis will rotate the camera around the character, like we expect from tilting the right stick horizontally. Rotating about the x axis will tilt the camera up or down. What we expect from tilting the right stick vertically? Because the camera is a child of this node, it will match its rotation and stay focused on the character as well. We can adjust the camera's focal point, moving it from the character's feet up to their head by moving the node up along the y axis. From here, we can also adjust the camera's horizontal offset property to further offset the camera along the x axis to produce a shoulder camera effect. We can even control how far away the camera is from the character and control that dynamically by changing the node type. Right click on the node and select change Type and search for spring arm three D node. We can also edit the name of the node to reflect its new type. With the camera as a child of the spring arm, we need to reset its transform, so it has the same position and rotation of the spring arm. In the inspector, we can see the length of the spring is set to 1 meter. The camera will always try to reposition itself to be 1 meter away from this node along its Z axis. Let's increase that distance to something higher like 5 meters. Okay. This spring arm node also has collision. If there's a collision object between the origin of the spring arm and the camera, then the margin is how far from the collision the camera will be positioned. The camera will be positioned as close to the spring arm's length as it can get before a collision happens, compressing the spring. If the obstruction is removed, the spring will decompress and push the the full distance away. This is done with a ray cast, checking a single point along the spring arms set axis. But if we also specify a shape for the spring arm, we can give the collision volume replacing the raycast with a shape cast. Requiring there to be more empty space before allowing the camera to move back to the full distance. If we make the shape a box, we can define its dimensions, and now the box will be used to detect how far along the spring arm the camera can go. To control the camera, we need to add more input mappings to the project settings. Open the project settings and switch to the input map tab. Add actions for left, right, up and down. I'll use the arrow keys and stick tilt for the events that will trigger each of these actions. In the player script, we already have a reference to the camera, but it isn't the camera we are actually going to control. We need to get a reference to the spring arm node. And set it in the inspector. In the process function, we can call a function we haven't written yet. In a script we haven't even created yet until the spring arm to look in a direction. The direction to look we can get from input like we did here, input vector with left as the negative x, right as the positive x. Look up as the negative y, and down as the positive y. Let's create the spring arm script. It will inherit from Spring arm three D. There is no template for spring arm three D scripts, and the default no template isn't relevant. Let's uncheck the template toggle, allowing us to start from a blank script. I'll just name this spring arm and save it in the scripts folder. We already know that we need to write a public function n, which will accept a direction parameter of type vector two from the player script. Okay. We only need to then rotate this spring arm about the x axis by direction and the y axis by direction x. But like everything that happens during a process function, it's happening 60 frames per second, and we want it to work independently of the frame rate, which means multiplying it by Delta. But we don't have access to Delta here, since this function doesn't have it as a parameter. We can add it to the list of parameters and pass it from the player script. Okay. Or we can simply call a built in method, get processed Delta T to retrieve it. Now the spring arm holding the camera will rotate with the right stick tilt. These rotations are happening at a speed of 1 radian/second. We can edit the speed of the rotations by exporting a private variable. Let's call it rotation speed. Then multiply this by direction and Delta. If you want these rotations to have different speeds, you can export separate variables for them. Okay. While the y rotation is typically allowed to loop endlessly around, the x rotation usually has limits. The controls will break if we allow the camera to tilt all the way to looking directly up or directly down. Since the cameras axis flattened onto the x sad plane will become zero or inversed. So we need to clamp the spring arms rotation to be between a minimum and maximum value. If the camera being flat with the horizon is a rotation of 0 radians, looking straight up or straight down is a rotation of Pi over 2 radians in either the positive or negative direction. We only need to make sure that the absolute value is less than Pi over two. Clapping the x rotation between negative and positive Pi over three is a quick and easy solution that still offers a wide range. We can also export these values and allow these limits to be tweaked. Okay. You could go further and use two fifths of Pi, or even use the degrees to radiance function to convert 89 degrees to radians. So long as the camera can't go directly above or below the character, Okay. In the player script, we need to make some adjustments to our character controls. Since the camera is now child of the spring arm, the camera is no longer rotating, at least not within local space. The spring arm is rotating and the camera remains stationary relative to it. We can get the cameras global basis vectors, which instead of being relative to the parent node, will be relative to the entire scene. Alternatively, we can use the basis vectors of the spring arm itself. Okay. And in this case, we don't even need a reference to the camera at all. We now have the camera easily controlled by the player. In the next lesson, we'll import and implement environmental models into our game. I'll see you in the next lesson. 23. Dungeon: Hello, friends. In the previous lesson, we added camera controls to our game. In this lesson, we'll build a room for our character to run and jump around it. I've gone ahead and downloaded K Hits Dungeon remastered Asset pack from HDO. Opening the assets folder. If we look in the G LTF folder. There are a large number of models that we can easily import into ado for our project. In Go, let's first make a new folder inside the imported assets folder named Dungeon. With this new folder selected, importing the assets will place them in this folder. Just like with the characters, we will need to make some adjustments to the import settings of these assets. Let's start with floor dirt Large. Double clicking on the asset or selecting advanced from the import settings tab to open the advanced import settings window. If we select the mesh instance three D node, we can allow God to generate a collision shape for this asset automatically by checking the physics toggle. A wire mesh of the collision shape is visible in the preview, and a new physics section is added with several options. Since the floor is part of the environment, we expect it to have collisions but not move as a result of those collisions or be affected by external forces like gravity. It's body type is static. There are a few different options to choose from for generating this collision shape, and we can see that the current one was produced using decomposed convex. This is fine for most objects in our game, but if we tilt the preview, we can see that the collision shape is noticeably far above the floor in certain parts. This would result in the character's feet not touching the floor, which wouldn't look very good. Selecting simple convex from the drop down. This collision has fewer polygons than before and the same problem. The character's feet would not touch the floor in some places. Selecting tri mesh, the collision shape will match the mesh instance exactly. Since these assets are low poly, this isn't really as much of a problem in terms of performance as it would be if this mesh were made of thousands of triangles. The character's collision shape will collide with these rocks, however. Depending on how you want your game to work, that might be a good or a bad thing. Just be aware of it. If you want to ignore the rocks entirely and just have a flat box for collision, then select box for the collision shape. The box collision shape will not be automatically configured to match the size or shape of the mesh. But we can edit its dimensions and positioning here at the bottom of the panel. I'll select primes for this floor to demonstrate how the character's collision shape interacts with the rocks. Once your floor tile has the collision shape, you prefer, click import to re import the asset. Next, let's take a look at an object we might want to be more dynamic, like the Boxarg asset. Like we did with the floor tile, we can select the met instance three D node and add physics by clicking on the toggle. Since we want this to not only have collisions, but also move around and be affected by external forces like gravity, its body type should be dynamic. Then select a collision shape for it. I'll switch this to simple convex. Some objects, you might not want to bother with collisions or physics at all, like banners or other such decorative items. In those cases, there's no need to bother editing the import settings. Repeat this process for any assets you want to have physics apply to. There are a lot of assets in this asset pack. There's no need to do them all right now. You can always edit their import settings and add them to your game later if you want. We want to build a room with these assets, which we can do by clicking and dragging any asset into the scene tree to add to the scene. But manually positioning each individual asset is tedious. Not to mention that we may want our game to have different rooms. Like we have the character defined as its own separate scene, we want our games levels to also be their own separate scenes. That way, the game scene can load whichever level the player is currently playing in. Let's delete the floor and CSG boxes. We can instead replace them with a new node three D to represent a level of our game. Let's call it dungeon. Since the game scene is meant to play any level and different levels are probably going to have different lighting, let's make the sun light a child of the level node. Right clicking on the Dungeon node, we can save it as its own scene. Named Dungeon saved in the scenes folder. So all visual components of the game scene are actually contained within other scenes. The only things in our game scene should be the player input handler, a character, spring arm, and camera for the player to control and the level. Then click on the clapper board to open the dungeon scene. Here we can edit our dungeon separate from other stuff in the game scene like the camera or character. In the dungeon scene, we can add a new node type, a grid map node, which will allow us to quickly build out the level using the assets we imported. But the Grid map requires a resource called a mesh library, which we have to generate first from the imported models. From the main menu, select scene new scene to create a new blank scene and select three D scene in the scene tab. This scene won't actually represent anything in the game, but will act as a collection of three D models to use in the grid map. Coking and dragging any three D models you want in your grid map onto the scenes root node, we add them to the collection. The models will all be at the scene origin occupying the same space, which is fine. We can filter our resources to make the selection process easier using this field. Filtering for GLTF will show only the GLTF files. Group selecting the assets, they could be added to the mesh library much faster. Once all of the models you want to use are in the scene, save the scene not in the scenes folder, but in the imported assets Dungeon folder. And name it Dungeon Mesh Library, with the extension T SCN. Next s scene from the main menu, Export as Mesh library. Let's save this in the same folder, call it Dungeon Mesh Library. This time with the extension Mb. Back in the Dungeon scene, selecting the Grid Map node, we can assign our new mesh library in the inspector. We can drag the resource from the file system tap into the field or click on the dropdown and select Quick load. This will present a list of resources within the project of a compatible type. Selecting our newly created mesh library, we can see that the meshes have been populated into the selection grid. Selecting the floor tile asset. We can place a four tile anywhere in the grid map by clicking. Positioning the tiles, they will snap to grid coordinates along the X D plane. Expanding the section of the inspector tab, we can edit the cell sizes that the floor tiles are snapping to. Since we know these tiles are 4 meters wide, let's set the XD cell size to four. Now, we can easily place a three by three grid of floor tiles by clicking. And delete them by right clicking. All of these four tiles are on for zero. This is the cell coordinate on the y axis. If we increase the floor, then the tiles will be placed 2 meters above and likewise at floor negative one, 2 meters below. And we can edit the floor height difference with the cell size y value. If we want to add meshes to the mesh library later, we can return to the mesh library scene. This last wall asset needs to be re imported with an earlier G LTF naming version. We can add more meshes to this scene. Then report it. Make sure the merge with existing checkbox is checked to update the existing mesh library to include the new meshes. Back in the dungeon scene, selecting the grid map, the new meshes are added to the selection grid. Not all of the meshes are 4 meters. Many are 2 meters or even 1 meter or smaller. Changing the grid dimensions to 1 meter, I can erase the current tiles since they are now overlapping. Instead, select coordinates that are a few cells apart to redraw the four tiles. But now a smaller mesh can be placed at coordinates every 1 meter instead of every 4 meters. Okay. While deciding on the location to place the mesh on the grid map, the WASD keys can also be used to rotate the mesh. With a rotating about the x axis, S y axis, and D Z axis. The Wk will reset back to the default rotation. Create a basic dngon scen from the assets and save it. If there are any objects you want to place not on exact grid coordinates, they can still be added to the scene separate from the grid map and positioned manually. Then return to the game scene. In the game scene, the dungeon floor is above the character. So let's move the character up above the floor. When we run the game scene, the character can collide with the floor, walls, and any other objects in the scene, which had physics generated by the import settings. With these tools, you can build a wide variety of levels for your game quickly and easily, adding, removing or changing things as needed. We now have a fully functional third person character controller, allowing the player to control a fully animated character walking and jumping around in a three D world. Okay. 24. What's Next?: 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.