Master Godot UI: Customizing Interfaces with Control Nodes | Thomas Yanuziello | Skillshare
Search

Playback Speed


1.0x


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

Master Godot UI: Customizing Interfaces with Control Nodes

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.

      Promo

      1:31

    • 2.

      Hello World

      8:03

    • 3.

      Constants & Variables

      9:11

    • 4.

      If Statements & Conditions

      9:46

    • 5.

      While Loops & Operators

      9:23

    • 6.

      Arrays & For Loops

      10:32

    • 7.

      Stacks & Functions

      10:53

    • 8.

      Match & Return

      9:34

    • 9.

      Scene Tree & Inheritance

      10:21

    • 10.

      Abstraction & Encapsulation

      10:41

    • 11.

      Polymorphism

      8:01

    • 12.

      Dictionary

      8:54

    • 13.

      Debugging

      9:08

    • 14.

      Setup

      3:52

    • 15.

      Label A

      9:46

    • 16.

      Label B

      6:10

    • 17.

      Texture Rect

      8:54

    • 18.

      Button A

      9:24

    • 19.

      Button B

      7:43

    • 20.

      Link Button

      10:05

    • 21.

      Theme Type Variation

      4:33

    • 22.

      Panel

      7:30

    • 23.

      Color Rect

      7:23

    • 24.

      Menu Bar

      6:20

    • 25.

      Popup Menu

      10:00

    • 26.

      Signals

      10:32

    • 27.

      Save Data

      8:50

    • 28.

      Counter

      8:18

    • 29.

      Line Edit A

      10:50

    • 30.

      Line Edit B

      7:46

    • 31.

      Option Button

      13:06

    • 32.

      Horizontal Split Container

      3:28

    • 33.

      Tab Container

      10:45

    • 34.

      Tree A

      12:45

    • 35.

      Tree B

      9:47

    • 36.

      Scroll Container

      12:30

    • 37.

      Radio Button

      9:52

    • 38.

      Check Button

      4:01

    • 39.

      Vertical Slider

      5:39

    • 40.

      6 4 Progress Bar

      7:32

  • --
  • 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.

8

Students

--

Projects

About This Class

Are you ready to start building your very first game in the Godot game engine?
Do you struggle to use Godot's Graphical User Interface (GUI) Control nodes?
Or do you want to challenge yourself to build an entire game in a single scene using only Control nodes?

In this course we will explore everything the Godot GUI Control nodes have to offer by building a complete game from scratch.

The beginning of the course focuses on how to write scripts and the GDScript language.
If you're already familiar with Godot and GDScript, you can skip over these lessons and jump straight to the Setup lecture.

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

Meet Your Teacher

Teacher Profile Image

Thomas Yanuziello

Indie Game Developer

Teacher
Level: All Levels

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. Promo: Are you ready to start building your very first game in the Gudo game engine? Or do you want to learn more about how to use Gdo'sGraphical user interface control nodes? Or do you want to challenge yourself to build an entire game in a single scene using only control nodes? In this course, we will explore everything that Godo Gui Control nodes have to offer by building a complete game from scratch. It is recommended that you design your own game for this project. Your game must have only one scene and only use Gooey control notes. We'll start with building a title screen, containing a title, a background image, a start button, and social media links, exploring the wide variety of options that are available for each node type. Then transitioning to the main game, adding a menu bar with plenty of different styles of menu options, including icons, shortcut keys, and sub menus, writing scripts that can create and manage player data and settings in Godo's native scripting language, GD Script. Moving on with developing game mechanics, specific to my design, we will explore the remainder of Gudo'Guy control nodes in detail as we go so you can figure out how to apply them to your project. If you get stuck, our discord server is full of other students, hobbyists, and professional game developers who can help you out. Click on the website link in my teacher profile to join. Let's get started building our game. 2. Hello World: 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 Gado 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 dotGD. 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. 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. 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 s 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 naught, 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 eyes. 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. 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 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 boolean variable. He loves me to be its opposite. So he loves me, is assigned to not 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 WOW, 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 WoW loop 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. So 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. And 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. And 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. 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 workers 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 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 market 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. 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. So range 114 will contain all numbers 1-13, and 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. So 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. So 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 Wile loop. If both four loops complete without finding a matching pair, found pair will be false and the Wa 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 Mad. In the next lesson, we'll use another form of flow control. I'll see you in the next lesson. 8. 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 sign, 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 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. 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. So 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. 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 scenes 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. 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. So 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 Ban 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 in 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 GADoEngine, 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 scene tree, 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 managers 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. 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, Get 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 GD Script uses private variables and functions is more like the way other languages use protected ones in that they are accessible to inheritors. GDScript 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. 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. So 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. Dictionary: 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 data type, 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 we 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. 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 GADO 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. Okay 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, sort of 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 GoDO 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, good luck. 14. Setup: Hello friends. If you haven't done so already, you'll need to download the GdoGameEngine from Gudo engine.org. Then extract the contents of the zipped folder. Be sure to put this unzipped folder somewhere safe on your computer. Open Gudo when you're ready. Hopefully, you've completed the intro to programming series, so you have a basic understanding of the editor and how to write scripts. Click the plus Create button to create a new project. Our objective in this course is to create an entire game using only user interface nodes in a single scene. You can name your project anything you want, such as UI course, unnamed UI game, or if you have a name for your game, you can use it here. Godot will automatically make a new folder for your project in documents, or you can click Browse to change the location. I like to have all of my GDOPjects in a subfolder of documents. This project won't involve any complex rendering, so the renderer won't matter, but I'll use compatibility so I can export to Web. And GODO provides Version Control support with it by default, which is very helpful, and I highly recommend learning how to use it for your projects. Since our game should contain only a single scene using user interface nodes, let's start by creating our root node as user interface. User interface nodes all have green icons to make them easily identifiable in the scene tree. We can rename our nodes by clicking on them after they're already selected or right click on them and select rename. Let's name this node title screen. Then save this scene in our project either through the main menu scene, save scene or using the shortcut Control S or Command S. Despite the fact that the root node is named title screen, we are intending to make this entire game in one scene. A common name used in this case for the scene would be main, so that's what I'll name Mcene. And with only one scene, we can just leave it in the project's resource folder. If you're not sure what I mean by user interface nodes, select the root node of the project. Then take a look at its properties in the inspector, expand the theme section, click on the down arrow beside theme, then select new theme. This scene now has a theme which will change how all of its control nodes are displayed. Click on the new theme to open it in the bottom panel. Expanding this panel to get a better view of all its contents, we can see quite a wide variety of user interface nodes previewed. Label buttons of various types, editable text, numbers, and sliders, separators, a progress bar, tabs, a tree, and a scroll bar, all of which is contained in a panel and several containers used to organize everything. Some control nodes are not included here, including one which can be used to display two D textures. If you haven't decided on a game for your project yet, try to come up with something you might try to make using only these components. We are now ready to start our new game project. In the next section, we'll create a basic title screen for our game. I'll see you in the next section. 15. Label A: Hello, friends. With our project started, we're going to start our game with a basic title screen. The most basic thing we need for a title screen for our game would be the title. Let's start by adding a new node to the scene tree by clicking the plus button, right clicking on the root node and selecting add child or using the shortcut Control A or Command A. To complete the challenge of using only user interface nodes, we'll only use the nodes that are sorted under control. This control is also the same type as the root node of scene. We can see in the description that control is the base class for all graphical user interface controls. These nodes will adapt their positions and sizes based on their parent control nodes. The control most often used to display text is named label indicated with a tag icon. We can also see the inheritance of the label class as each node type is indented below the type it inherits from. Any node or class that inherits from another will have all of the same inherited properties plus its own extra properties. So the label node is a node, but it is also a canvas item, a control node, and a label. Each of those come with their own properties and behaviors. We can see that the label node is indented below the root node as it is a child of it. The root node is the label's parent, so the label will adjust its position and size based on the parent node. We'll see what that means soon. Let's rename the label node to title and have a look at its properties in the inspector. The first property is the text that the label will display. So we can enter the game's title here. There are also several options for controlling the text alignment, wrapping, justification, paragraph separation, clipping, ellipses and tab stops. None of these are really applicable for our title, except maybe uppercase if you would prefer to change all lowercase letters into uppercase letters. The properties under displayed text are only applicable for situations where there is too much text to display all of it at once. And BD options are available for languages that are displayed right to left instead of left to right. None of this is important for our game's title. Let's press Play and see our title displayed. Since no scene has been selected for the main scene of this project, we are prompted to set it. Press select current to set this scene as the game's main scene, and we can see our game run, displaying the title in the top left corner of the window. Press the stop button to end this simulation. This works, but it isn't very good. We probably want to move the title and increase its size. Looking further at the properties of the label node, these are all of the properties of the label node itself, but there are more sections of properties if we scroll down. The next section holds all of the properties that the label node inherits from control. If we expand the layout section, then the transform section, we can see the size and position properties of the label node. If you can't see this rectangle in your two D view, adjust it until you can using the mouse wheel to pan and zoom. This blue rectangle is the display area of the game as it will be rendered when we run the game. Notice how the red X axis overlaps with the top side of the blue rectangle making it appear magenta. The green Y axis overlaps the left side, making it cyan, and the origin point of the scene is in the top left corner. If we click and drag the title away from the top left corner, its position X and Y values in the inspector are updated to reflect its new position. Notice how moving the label down increases the Y position and moving it up decreases it. Let's put the title somewhere around the center of the screen. Then press the play button to see the change. The title is now displayed in the center of the screen. But what happens if we resize the window? The position of the title is determined by its distance from the top left corner of the window. So if we make it bigger or smaller, the title is no longer in the center of the window. Press the reset button to return the label back to the origin, position 00. You probably noticed the green X at the origin when selecting the label node. These are four anchors, all pointing to the same position. Let's change the layout mode of the label node from position to anchors. This adds another property named anchor presets, which we can change. If we change the anchor presets to full wreck, this is the same as the root node with an anchor in every corner. If we select the root node, the anchors are all pointing to different corners of the screen. R select the label node. The rest of the options will anchor the label to different locations on the screen in corners centered or stretched on one axis along the screen. Let's anchor the title to the center of the screen. Now, if we hit Play, then resize the window, the title remains centered even if the size of the screen changes. Can also customize how these anchors affect the node by changing anchor presets to custom. This allows us to change the anchor points, defining them as percentages of the screen's width or height. Since anchors are displayed pointing at the corners, each of these anchor point settings will affect two anchors. I'm fine with keeping the title centered horizontally, but I would prefer if it were moved up slightly to about one third of the screen. Adjusting the top anchor point to one third, the top left and top right anchors move up. I'll also set the bottom anchor point to one third, so all the anchors point to the same position. And Anchor offsets can be used to offset the node's position from the anchor point by set amount. This can lead to the node size being larger than the text, and so the label's alignment property will affect where it is drawn. I'm fine with having the label centered on the anchor point, so I'll reset all of my anchor offsets to zero. The grow direction will affect how the node is repositioned relative to the anchor point based on its size. If told to grow to the left or right, top or bottom, we can see how the node resizes and repositions to accommodate the text. A game title will often have a unique font and font size from anything else in the project. In cases like these, we can ignore the theme we created in the first lesson and instead expand the theme override section of the control properties. Before we can change the font, we need to import a font into the project. I downloaded this font from Google Fonts. We can import files into the project by dragging them into the Godot Editor window. We can now set this as the font for the title. We can also change the font size. I'll set mine to 49. Styles aren't often used for labels, but to demonstrate, we can add a style box flat to add a background color to the font. Clicking on the style box allows us to change the color among many other options. Like I said before, this isn't really something that's often used for labels, and we'll go over style boxes in more detail soon. I will remove the style box from my title label by clicking on the reset button. I'll instead add theme overrides for the font color, shadow color, and outline color, making the font white and the shadow transparent. Then add constants for the shadow offsets, outline size and shadow outline size. Since the title is only one line, line spacing would have no effect. As mentioned earlier, parent control nodes affect the size and position of their children. As we saw before, the title screen node is anchored to the full rectangle of the display area. But if we resize it, its children will recalculate their sizes and positions to fit the size and position of their parents. In this case, the title remains centered horizontally and one third from the top of the title screen's area. We can reset this back to the way it was by resetting the anchors of the title screen. We now have our game's title displayed on our title screen. In the next lesson, we'll add a background. I'll see you in the next lesson. 16. Label B: Hello, friends. Before moving on to textures, we can explore a little more of what labels have to offer. None of the changes made in this lesson will be safe to the project, and I will just revert back to the end of the previous lesson before moving on. Any video titled B will follow this rule, providing only extra information about the previous topic, but can be skipped over. Selecting the label node, we have a property named label settings. Much like the theme resource from the first lesson, we can create a new label setting resource here, but it will only affect this label. Creating this label settings resource, we'll ignore the theme overrides and use these settings instead. Here we can change all the same settings that are available in the theme, spacing, font, font size, color, outline, and shadow. Et's add a second line of text to the title. If for any reason, the size of a label node is larger than the text, the alignment properties will determine where the text is drawn, whether it is on the left, center, right, or filling the entire width, top, bottom, et cetera. If the text is too long to fit inside the designated width of the label node, we can use Auto rap mode to change how the text is automatically moved to the next line. Arbitrary will just cut off at whichever character happens to be at the end of the label's width. Word will try to wrap at spaces between words, and word Smart will break a word if it doesn't fit into a single line. If the meaning of any property is not obvious and you want more of a detailed explanation, hovering the mouse over the property name will provide a tool tip. I'll use some Lormipsum as placeholder text to explain the next few settings. Justification flags can be used to change how the fill text alignment is implemented. Starting in Gudo version 4.4, you will be able to change the new line character to anything you want. Clip text can be used to prevent any text from being drawn outside the boundaries of the label node. If clip text is activated, then text overrun behavior can be changed to alter how the clipping is implemented. Trimming characters or words even adding ellipses to replace characters or words The ellipsis character can also be changed to anything you want. Normally, tab characters in the text area are ignored when the label renders the text, but you can also add tab stops to your labels. Clicking on the packed float 32 array to expand it. Click the plus add element button to add a tab stop. Then set the value as a decimal number. Multiple tab stops can be used to sort of create charts or columns within the label node. Returning to orm Ipsum to help demonstrate displayed text, if we have a large amount of text in a label node, then we can customize the amount of text that is drawn using these properties. Lines skipped will hide lines of text starting at the beginning, while max lines visible will hide lines of text starting at the end. Negative one is used to mean unlimited in this context. Visible characters can be used to control the exact number of characters that will be drawn. This is directly tied to the visible ratio property, which is the same thing, but expressed as a percentage of the whole text. Changing one value will change the other automatically. If both character visibility and word wrapping are being used, then how word wrapping is implemented can be changed using visible character behavior. For example, this word would not fit on the first line if it were all drawn, but it does fit only because of the number of visible characters. Changing the visible characters behavior to characters after shaping, the word is wrapped to the second line regardless of whether or not it is completely visible. The glyph layout settings are only used if the layout direction is set right to left, and the BD settings are for languages specifically write from right to left. I'll now revert the project back to the way it was before this lesson. That's all of the label options. In the next lesson, we'll move on to texture Rx. I'll see you in the next lesson. 17. Texture Rect: Hello, friends. In the previous lesson, we added a title to our title screen. In this lesson, we'll add a background image. I found this image that I think suits my game idea nicely on free pic.com made by UpClac. Make sure you check the attribution requirements and licensing of assets you find online before you use them. I've downloaded this image and we'll import it into Gadot so I can use it as my title screens background. To prevent my project resource folder from getting too cluttered and disorganized, I'll create a folder for imported assets. Then inside that folder, create another for funs. Then drag each file into the appropriate folder so I know where to find them. To draw this texture, we'll need to add a new child node to our scene. The node we need to use to draw images on screen is the texture wrecked node. Let's rename this node background. Then populate its texture property with the imported image. We can no longer see the title. The image is being drawn over it. By default, each node in the scene tree is drawn from top to bottom. So the title is being drawn first, then the background is being drawn over it. We can rearrange our nodes easily by dragging them. So the background is sorted above the title in the scene tree. This way, the title is drawn over the background. This image is much larger than the display area for our game. We want the background image to cover the entire display, so we'll change its anchor presets to full wrecked. This moves the image to be centered within the four anchors, but it is still too large. If we change the expand mode of the texture wrecked node, we can tell it to ignore the size of the texture and instead use the size that is specified by the anchors. Now the image covers the entire display area, even though the aspect ratio of the image does not match the aspect ratio of the display area exactly. It's hard to tell with this image, but it is now distorted, being stretched vertically. Other options exist to force the image to resize to fit the width, ignoring the image's height, suitable for fitting into a horizontal arrangement or fitting the height, ignoring the image's width, better to fit inside of a vertical arrangement. Either of the proportional options attempt to do the same as the previous options, but also maintain the images aspect ratio. With fit width proportional, the image does cover the entire display area while maintaining its aspect ratio. Next, we have the stretch mode options, defaulting to scale, which will allow the image to stretch any way that is necessary to meet the demands of the expand mode and anchors. Tile mode is for images that are smaller than the size provided. So let's swap the image out for the Gudo icon. Now the area is filled with tiles of the image. Undoing this change, keep will ignore the expand mode and force the image to keep its original dimensions and aligning it with the top left corner. Keep centered will also keep the original dimensions of the image, but center within its area. Keep aspect will allow the image to shrink or grow to fit inside the area. Switching back to Ignore size, since the aspect ratios don't match, the image doesn't cover the entire screen. Keep aspect centered does the same, but also centers the image within the area. Keep aspect covered will resize the image, maintaining its aspect ratio, but ensure that it covers the entire area. So now the left and right edges of this image are being cropped. If you want to make sure the entire image is shown, you would probably use keep aspect or keep aspect centered and change the background color to fill the unused area. If you don't mind cropping the edges, using keep aspect covered is also a good option for background images. The flip horizontal and flip vertical toggles will just flip the image if that's something you wish to do. Let's press play and see how it looks. Remember that if we resize the game's window, the anchors will change and affect how the image is drawn. And while the image will scale itself to fit the window, the title will only reposition itself, since it is being drawn to a specific font size. But we can change some settings to have more control over this behavior. From the main menu, select project settings. Then look under display for window. Here we can edit our viewport dimensions, changing the dimensions of the game window when we run the game, and also the dimensions of the blue rectangle in the editor. It's a good idea to set this to match the dimensions of your target publishing platform. The anchors haven't updated yet, but selecting or resizing the nodes will force them to update, and we can reset their anchor presets back to full rect. I we can change whether or not the game opens in a window or full screen, but don't change this setting until after you have some way of exiting your game. You can also change where the window will be displayed on screen by setting the initial position type to absolute, then changing the screen coordinates for the window. And also which screen it will be displayed on by changing initial position type to center of other screen, then specifying the screens index. These options will only work on an exported project, not when we run them in the editor. We can make the window borderless. And even change whether or not the window can be resized. This is one way we might fix the issue, but not a very flexible solution. The settings under stretch provide better alternatives. We can change the stretch mode of the entire game from disabled to Canvas items. Remember that all control nodes inherit from Canvas item, so they will all be stretched with the window. Another setting exists for stretching the entire viewport, which will include the output of three D cameras. When performing this stretching, we can change whether or not the game's display will keep its aspect ratio. Ignore will allow the game to squish and stretch any which way. Keep width or keep height will allow the game window to scale only as much as the width or height will allow. Expand will let the games display grow and shrink, still maintaining the aspect ratio, but will expand to fill the entire window. Scale can be used to automatically reduce all canvas items to half size or up to eight times their size. Useful for developing for platforms that have drastically different resolutions than your development machine. And scale mode can be set to integer to restrict the scaling to only dividing or multiplying by whole numbers. This is particularly useful for pixel art since it won't cause any pixel distortion, attempting to stretch or shrink pixels by fractional amounts. Handheld orientation settings are available for mobile development to restrict to portrait, landscape, reversed, or use the device's sensor to determine the orientation. And VSync mode can be changed to determine how the machine's GPU is synchronized with the display refresh rate. These settings are not available when using the compatibility renderer and won't be applicable to anything we do in this project. We now have a background image for our title screen. In the next lesson, we'll add a start button. I'll see you in the next lesson. 18. Button A: Hello, friends. In the previous lesson, we added a background image to our title screen, and this lesson we'll add a start button. Adding a new node to the title screen, just like we have done before, this time we are looking for the button node. Similar to the label node, a button can contain text, but also an icon. Let's name the buttons start. Then also put the same text in the text field, so it will be displayed on the button. There is also an icon field, which we can populate with the Gadot icon to see how it works. The button is drawn as a rounded rectangle encompassing both the text and the icon provided. The flat toggle removes any textures or colors from the button, drawing only the text and icon. However, the entire buttons area is still clickable, and there is still a focus style on the button that will be drawn when it has focus. Expanding the text behavior and icon behavior sections, we can change the alignment of the text and icon to shift how they are displayed on the button. These changes are much more apparent if the size of the button is larger than it needs to be to accommodate both the text and the icon. Some of the same behavior settings are available as the label node to control text overrun. Auto wrapping. And clipping if the button's size is too small to accommodate the text. And the icon can be expanded to fill the size of the button. BD options are also available for languages that are written from right to left. Base button is a class that all buttons inherit from, even buttons of other types. We'll go over these settings in button lesson B. For now, let's edit the properties inherited from control, starting with the layout. I would like my start button to be below my title at two thirds of the screen's height. So switching the layout mode from position to anchor, I'll change the anchor presets to custom. Then move the left anchor to 0.5 and the top anchor to 0.667. The right anchor can't be further left than the left anchor, so it is automatically changed to the same value. Likewise, the bottom anchor can't be above the top anchor. The button is currently to the bottom and right of the anchor, so I'll change the grow directions to both. Then reset the anchor offsets to center the button about the anchor point. With the button positioned, the next step would be to edit its theme properties. If you want the button to be unique, then using the theme override section for this button would be appropriate. But if you want multiple buttons to share the same theme properties, it would be better to only have to set them once. Selecting the root node, we created a theme in the first lesson, or you can create one now if you haven't done so yet. Let's change the default font and font size for our theme, and we can see the effects of this change in the theme preview. While this theme is a property of the root node, not the button, making changes to this theme will also affect the button. This is because the theme is passed down through the scene tree from parent to child in another form of inheritance. If a node uses theme override properties like the title, then these settings will override the inherited theme properties. Taking a look at the theme panel, our theme preview contains a button node with the default theme properties, except that the font and font size have been changed to the new settings. By clicking the Plus button, we can add node types to the theme like button. This adds several properties that we can add to the theme and edit. Starting in the color section, we can change the color of the text and icon while the button is in different states. The normal font color of buttons needs to contrast with its background. I'll just use the default light gray. Often a disabled button will have gray text, sometimes even transparent. Hovered buttons might lighten the color. Pressed is sometimes darker, and I'll also add an outline color. All of the same color options are also available for the icon, which will be multiplied by the icon texture's colors to produce the result. In the constant section, we can add the amount of pixels separating the text and icon when they are horizontally aligned. As well as a maximum width for the icon, the icon's height will automatically be adjusted keeping its aspect ratio. And we can specify the outline size for the text, too. We haven't covered style boxes yet, but if using style boxes of different sizes, you can add this property and set its value to one to force the button to always use the size of its largest style box. We can change the font for all buttons if we want to, but I'll just use my default theme font. I will, however, make the font size for my buttons a little bit bigger than my default font size. In the icons section, we can specify a default icon to use for all buttons. The icon set in the buttons properties will override this icon if it is provided. So if you want most every button in your game to have the same icon, you would set it here. But you could also have special buttons with unique icons, too. The next section is for styles, where we can draw a different background for the button, other than the default transparent black curved rectangle. Starting with normal, this is what the button looks like when it isn't doing anything. Adding it to the theme, we have multiple options for how we provide styles. Style boox texture is used to draw imported images to use as the buttons background. I have these button images which I will import into Gadot with the assets folder already selected. That way, they are imported into that subfolder automatically. Click on the style box to open it in the inspector. Then set the normal texture to the normal style box. I don't want to modify the texture in any way, but the text and icon don't really fit well into the background image. So I'll add content margins to force the icon and texture away from the sides into the flat interior of the button. Moving on to the pressed style, we can add another style box texture. But this time populate the texture with the pressed image of the button. Then add the same content margins as normal. And repeat this to add disabled and hovered. I don't have a unique texture for Hover press, but we can quickly duplicate a style box by clicking and dragging the one from pressed into the slot. Selecting the button in the scene tree, we can preview what the button looks like when it is disabled by checking the disabled toggle in the properties inherited from base button. Running the game, we can also see the button style change when hovering over it and pressing it. However, once pressed, the button now has focus, which gives it this white border. If you want to remove this, you can provide another style box for focus. Or alternatively, we can just add a style box empty to remove it. This button doesn't do anything yet, but we'll have to wait for another lesson. We now have a start button for a title screen. In the next lesson, we'll add social media links. I'll see you in the next lesson. 19. Button B: Hello, friends. Before moving on, we'll go over a few more features of buttons. Remember that none of the changes made during this lesson will be saved. We covered the disabled property of the base button class, but there are many more properties listed here. Toggle mode will turn the button into a toggle, meaning once it is pressed, it will stay pressed until pressed again. Button pressed can be used to set the default state of the button while it is being used as a toggle. If it's not a toggle, the button cannot be set to press. We can change whether the button is considered to be pressed when the mouse button is released or when the mouse button is first pressed. And also specify which mouse button is being used to click the button. If keep pressed outside is turned on, then clicking the button and moving the mouse outside the boundaries of the button will maintain its pressed state until the mouse button is released. A button group can be created to turn a toggle button into a radio button, meaning only one button can be pressed at once. We'll see how this works when we use it in a later section. And lastly, the button can be assigned a shortcut. Let's use the Enter key as a shortcut to press this button. Expanding the shortcut by clicking on it, we can see that it contains a list of events, which we also need to click on to expand. The list is empty, so it has a size of zero. But we can click the plus add element button to add an event to this list. Clicking on the drop down, we will use an input event key to react to the player pressing a key on their keyboard. Clicking on the input event key to expand it, there are a lot more properties here, but we only need to press the configure button to open an event configuration window. This window is actively listening for input events, so pressing any key on the keyboard will automatically select it. We can also add the requirement that they hold down other keys like Shift Alt, control, windows or automatically detecting the control or command key based on the user's operating system. Pressing Okay, will fill in the required information for us. Shortcut feedback will change whether the button's style changes when the shortcut is used, and shortcut and tool tip will automatically add a tool tip to the button that shows the shortcut. Leaving both of these on, let's see how it looks. Hovering over the button, the tool tip tells us that Enter is a shortcut key to press this button. Pressing the enter key on the keyboard also presses the button. If we turn these options off, the tool tip is removed, and while the enter key is still a shortcut, it provides no visual feedback that the button is being pressed. Selecting the root node and taking another look at the style box texture options for our button. I'll swap out my texture with the Gadot icon for demonstration. Texture margins can be added to create a nine slice. Nine slice textures can conveniently be resized with the corners of the image retaining their original size, stretching the edges along a single axis and stretching the interior along both axes to fill the extra space. How these axis stretches are implemented can be changed in the axis stretch settings, either stretching the image, tiling it, or a combination of tiling and stretching to attempt to remove seams. Expand margins can be used to make the style box larger than its area should normally allow, expanding it in any of the four directions. The sub region is used to slice sprite sheets into individual textures. Clicking on the Edit region button opens a region editor window. This window allows clicking and dragging to choose the region, adjusting the edges and corners as needed. There are options for pixel snapping for greater precision. Grid snapping if your sprite sheet is evenly sized and spaced, you can specify the grid size and offset to match the sprite sheet. Then select one or more grid cells to form your texture. Or auto slice will automatically find the textures for you, and you can just select one by clicking on it. Modulate can also multiply the entire texture by another color, adjusting it to a different color. This is often used to add tint, or if your texture is entirely grayscale, then modulation can be used as the main source of color, creating a wide variety of colored style boxes from a single texture. Other options are also available for creating style boxes in the theme editor. All of the other style box options also have the same content margin options as the style Box texture. Style boox empty will draw nothing and has no unique properties. While Stylebox line will draw a single horizontal line across the top of the button. Clicking on the Stylebox line, we can change the line color. Thickness. Switch it to vertical and grow the line in either direction. Using negative values here will shrink the line. Style box flat will draw a simple colored rectangle. Clicking on the style box flat, we can change its color and skew it in either direction. If we add a border width, then we can also change the border's color. And blend the border into the button's background. Toggling draw center will remove the background to draw only the border. If we add corner radius, then the rectangle will curve like it did by default. And we can change how many edges are computed to create the curved corners with the corner detail property. We can add a shadow behind the button, change its color, size, and offset it in both directions too. And anti aliasing can be disabled, or we can change its size to make the edges look smoother. That covers all of the button properties and style box options. In the next lesson, we'll add social media links. I'll see you in the next lesson. 20. Link Button: Hello, friends. In the previous lesson, we added a Start button to our title screen. In this lesson, we'll add social media links. Let's start by adding a new note to the scene tree. This time, it is a Link button. We'll name this node after the social media platform, in my case, Patron. Then also populate the text field with the same information. Copying and pasting the desired URI into the field, nothing more is required to make the Link button do exactly what we expect. Running the game, we can click on this Link button to open a browser window to the URI we specified. The underline option allows us to change whether the text is always underlined, only when the mouse is hovering over it or never. The same BD options are available for languages that write from right to left, and we have all of the same properties inherited from the base button class if we want to use them. This works, but isn't what I want for my social media links. I would prefer to have the platform's icon beside the text and have both clickable as one button. To do that, I'll need a texture wrecked node to display the icon. I'll rename this node icon and use the Gadot icon as a placeholder for now. I don't want the icon to be this large, so I'll set it expand mode to ignore size and stretch mode to keep aspect. Then scale it down to a more reasonable size. Next, instead of using the Link button node to display the text, I'll use label node. I'll rename this node handle and put my handle in the text field. Then move this to the right of the icon. I need to bundle these two nodes together, make them act as one single node. We can do this by using another node as their parent, like the Link button node. Group selecting the two nodes, we can drag them over the Link button node to reparent them to the Link button. So they are now children of the Link button node. By moving the parent node, both of the children move along with it, maintaining their relative positions. If we extend the size of the Link button node to cover the two children, this entire area becomes a clickable button that links to the social media platform. We can also anchor the Link button node to any location on screen, and it will automatically adjust the relative positions of the child nodes. An easier way to be more exact with the horizontal alignment of controls and keep them consistently spaced is to use another control node that serves exactly this purpose. This node is called a horizontal box container. It will automatically arrange all of its children horizontally with consistent spacing between them, making this the child of the link button, then the icon and the handle children of the horizontal box container. The icon and the handle now have their layout mode set to container. Since they are inside of a container, the container is responsible for determining their size and position. So these properties are disabled. The children are also forced to collapse to their smallest allowable size, which means the icon has been squished down to nothing. If we change the expand mode to fit with proportional, this expansion mode is ideal for horizontal boxes, making sure that the texture's width matches the height allowed by the horizontal container. Let's select the root node so we can see the theme editor and add the horizontal box container to the theme. This node type has only a single property we can set in the constant section, which is the number of pixels that will be used to separate the nodes children. Feel free to change this value and see what looks good. Selecting the horizontal box container, its size was originally 40 by 40 pixels by default and expanded to cover its children horizontally, but the height remains 40. If we click the reset button, the container will attempt to collapse as much as it can. Since the label node's text size is demanding 31 pixels of vertical space, the container is also 31 pixels high. This height is also given to the icon, whose own settings set its width to match its height. Since we now know the exact size we need for this icon and handle, we can copy these numbers into the custom minimum size property of the link button, ensuring that it will always encompass the entirety of its children. Then reset its size property to force it to collapse to this size. We now have a social media Link button that includes both an icon and a handle, and the entirety of both can be clicked. Let's create some new asset folders, first for icons, and another inside that for social media icons to keep them separate from other game icons. Then import some social media icons with this folder selected. We can then replace the Gdo icon with the appropriate icon. Selecting the link button node, we can duplicate it using Control D or Command D to add more social media links. Each duplicate will add an increasing number to the name since sibling nodes cannot have the same name. All of the child nodes will also be duplicated but retain their original names since they are not siblings with their duplicates. We probably want these links to be arranged vertically. We can do this with a vertical box container that works just like the horizontal box container, but arranges its children vertically. So let's add that as a child of the title screen and rename it to socials. Then group select all of the link button nodes and reparent them to this vertical box container. The container automatically arranges them vertically for us. I'll rename each link button to the appropriate social media platform. Now that we can see all of our links, let's change their URIs. Their icons. And their handles, if necessary. Since my ink buttons are now different widths, the vertical box container will expand them all to the largest width, making several of these link buttons much longer than they should be. Group selecting all of the links, we can change the container sizing property from this pop up menu in the preview window, changing their horizontal container sizing from fill to shrink begin. So each link button will collapse its width to the minimum size we set earlier. These container sizing properties can also be found in the layout section of the properties inherited from control by expanding the container sizing section. Both horizontal and vertical options are available, collapsing towards the beginning, center, or end of the container or filling it entirely in either direction. Let's anchor these social media links to a corner of the screen where they look good. I'll put mine in the bottom left corner. It's not a good idea to have these kinds of icons or text touching the edge of the window like this. We usually prefer to have some margins. We can easily offset the position of the container node by changing its anchor presets to custom, then adjusting the anchor offsets, adding a positive value to the left side to move it away from the left edge of the window, and a negative value to the bottom to move it up. Selecting the root node so we can see the theme editor, just like with the horizontal box container, we can add vertical box containers to the theme to change the number of pixels used to separate their children. I would like to have most vertical box containers in my project have the same uniform pixel separation as my horizontal box containers. But I'll use a theme override to make this particular vertical box container an exception and double the separation to spread these links out a little more. And we can change the order of the links easily by clicking and dragging them around to reorder them in both the scene tree and the container. We now have social media links on our title screen. In the next lesson, we'll add a variation to our theme. I'll see you in the next lesson. 21. Theme Type Variation: Hello, friends. In the previous lesson, we added social media links to our title screen. In this lesson, we'll add a theme type of variation. Let's add a new label to our scene for testing purposes only. One without any theme overrides, unlike the title. We'll be using lots of these to display all kinds of text and numbers on screen later. Selecting the root node so we can see the theme editor. We can add a new type to our theme, the label node. Like we've done before, we can change the colors constants. Font, font size, and style, if we want. I'll just use the default font and default font size I've already set for my theme. This creates a consistent look and feel for all or at least most of the text in our game. The title being unique, uses theme overrides to define its own special theme properties. While I like the stylish font from my game's title, text, and start button, these social media links should be very clear and easy to read, especially because my name is so difficult on its own. So I have imported a second font to use only for these links, Roboto, while the rest of the text for my game should remain in the stylized font. We could select each handle label and use the theme override section to change their font like we did with the title. But what if we want to change it or we want to add more of these link buttons? We would then have to edit each of the nodes theme overrides to make sure they all match. It would be better to use our theme to set them all at once while keeping them separate from other label nodes. But we've already added the label type to our theme. So in cases where we want to set a theme for some nodes, but not all nodes of the same type, we can add a type of variation to the theme by simply typing our own name into this field. I'll name it handle, since that's what I'll be using it for. This new type has no properties yet because the theme editor doesn't know what the base type is. We have to go to the last page and set the base type for this variation. And the base type is the type of the nodes we want to effect, label. We can either type the name into this field or click the plus button to pick the base type from a list. This will add all of the same properties to this type variation as would be available to a label node. So we can add the same colors, font color, outline color, shadow color, constants like outline size and shadow offset, font, which I will change to roboto. And font size, which I would like to be slightly larger than the default at 28. None of these changes have affected any of our labels, the title, text, or the handles. Selecting the handle nodes, we can even group select all of them together. Then expand the theme section. Next, clicking the Edit button beside the variation field and type in the name of our theme type variation. Just like that, all of these label nodes will now use the theme type variation, so we can adjust their unique settings collectively while keeping them separate from other labels in the project. Changing the font and font size might also cause the horizontal boxes to change their size to accommodate the text. So I'll quickly adjust the custom minimum sizes of my ink buttons to match. And I'll now delete this extra label node since it isn't needed for the title screen. We now have social media links that are easier to read. In the next lesson, we'll add a background for the main game. I'll see you in the next lesson. 22. Panel: Hello, friends. In the previous lesson, we added a theme type variation to give some nodes a consistent theme that differs from that of other nodes of the same type. In this lesson, we'll add a basic background for our main game. Normally games would have a different scene for the title screen and the main game, but in this project, we're doing everything in one scene. By default, the background is just gray. If you want to use an image for your background, you would use a texture rec node to draw it. I don't want just a simple image, so let's start by adding a new node to our scene. This time, a panel node and rename it game. Panels don't really do anything more than a standard control node, except that they also draw a style box. Let's set its anchors to full wrecked so it covers the entire window. The default style box for a panel is the same as a button, just a semi transparent black rectangle with rounded corners. Before importing the image for my panel, I'll create a new asset folder for styles and another one inside that for button. Then move all of my button styles into that folder. Then another folder for panels, and I'll have that folder selected when importing the panel style textures. Selecting the root node so we can see the theme editor, let's add panel to the theme. Panels have only one theme property, their style box. The same options are available for the panel as there were for buttons, empty, line, flat or texture. Let's use a style box texture, then click on it to open it in the inspector and assign the texture as one that was imported. With the default settings, the texture is stretched to fit the size of the panel, which doesn't look very good. Switching the axis stretch property to tile, the image will be tiled instead of stretched to fill the allotted space. Using tile fit will do both tiling and stretching in such a way that the entire region is covered with whole tiles instead of fractions. This image is a little too busy, it might be too distracting, so I'll use the rope border image instead. If we add some texture margins, the image will be sliced into nine different textures that will be used to build the style box with the corners retaining their exact pixel dimensions, the edges being stretched or tiled to connect the corners and the center being stretched or tiled to fill the remaining area. Now that we have a basic background for our game window, we would need to have it hidden by default when the game first runs so we can see the title screen, which we can do by clicking on the eye icon beside it in the scene tree. But since we will spend most of our time developing the game editing the contents of this panel, it would be better if this were visible to us, and the title screen was hidden instead. So we want to swap the game node with the title screen node, making the game node the scenes root node. Right clicking on the game node, select the option make scene root. The game node is now the root node with the title screen as a child of it. So the title screen is originally drawn over the game. To switch from the title screen to the main game, all we need to do is hide the title screen by turning off its visibility. However, now the panel theme is no longer being applied to the panel node, since the theme is a property of the title screen, I will only affect the title screen and its children. With the title screen selected, expanding the theme section, we can click on the arrow beside the theme and save it as a project resource. I'll just leave it in the project's resource folder and name it after the font. Trade wins. This theme is now saved in our project's resource folder. So we can select the game node, expand the theme section, and use the drop down to load this theme. Now the theme is being applied to the entire project like before, since it is a property of the root node. We can also remove it from the title screen node since it is being inherited through the scene tree anyway. Next, we'll need to attach a script to the game node, either by selecting it and clicking on the attached script button or right clicking on the node and selecting attached script. We'll name this script the game manager and create a new folder to hold all of our project scripts. Then create the script in that folder. The script will be written in GD script and inherit from panel, the type of the node it is being attached to. This automatically switches our view to script view and opens the script we just created. This script only needs to turn the visibility of the title screen on or off. To do that, we'll need a reference to the title screen node. Right clicking on the title screen node, we can access this as a unique name as long as no other node in the scene tree has the same name, which will be indicated with a percentage sign. Then clicking and dragging this node into the script, holding down the control or command key while releasing the mouse, we will store a reference to this node in a variable. I'll proceed the name of the variable with an underscore to mark it as private, meaning it is only meant to be used by this script. Overriding the ready function. The blue arrow icon beside the function tells us that this function is overriding the definition provided in the node class. This function is called when the node and all of its children are ready, we can tell the title screen to show itself. Selecting the start button, we can switch the inspector panel to the node panel, which gives us a list of all the signals this node can emit. The signal we want to react to is the pressed signal, which will be emitted when the button is pressed. Double clicking on the signal or right clicking and selecting Connect. We can connect this to the game node, which will automatically generate a new function for us on Start Pressed. The green connection icon to the left of this function tells us that a signal connection will call this function. When the start button is pressed, we will tell the title screen to hide. Running the game, the title screen is shown, and when we click the Start button, it hides, revealing the game panel underneath. We now have a background for our game window. In the next lesson, we'll add a fade transition. I'll see you in the next lesson. 23. Color Rect: Hello, friends. In the previous lesson, we added a background for our main game window. In this lesson, we'll add a fade transition to switch between the title screen and the main game. Currently, the title screen is displayed immediately, and pressing the start button cuts to the game panel, which is a bit jarring. It would be better if we transitioned gradually, which we can do easily with another node. The color wrecked node. Let's rename it fade. The color wrecked node simply fills its boundaries with a solid color, which we can change in the inspector, so we will set it to black. Then set its anchors to full wreck so it covers the entire window. This is how most games start end and change scenes with just an empty black screen. We can adjust the transparency known as Alpha of this color to turn it invisible gradually over time and back to solid black again whenever we need. Let's hide it so we can see our game while we're working on it. Attaching a script to this node, saving it in the scripts folder, the purpose of the script will be to fade in and out. Anytime we are adjusting a value over time, we can use a built in class of Gadot, a tween, which we will store in a variable. Adding a function, let's call it too clear. The function can return a signal that will be emitted when it is done. The first thing we need to do is create a new tween. Then assign this created tween to our variable. This is a function of the node class. Any node can create a tween. Then tell the tween to tween a property. This function has four parameters. Which node? We can use the keyword self to say this node that the script is attached to the fade node, which property? As a string encapsulated in quotation marks, we specify the name of the property, which is the color. The final value of the property, which is the color clear. Let's just write that in all caps for now. And lastly, the duration of time. Let's say 1 second, for example. We can then return the finished signal of the tween, which will emit the signal automatically when it finishes. Clear is undefined, so let's give it a definition by declaring it at the top of the script as a constant. A constant is the opposite of a variable. While they both store information, a constant never changes. They are conventionally named in upper snake case, and we can assign it the value of a color. Then specify in brackets zero red, zero green, zero blue and zero Alpha, which is black, but also fully transparent. Switching over to the game manager script. Let's give the fade a unique name and add it to the game manager as a variable. It can be told to show itself first. Then after the title screen is shown, we can fade to clear to gradually reveal the title behind the fade. When the start button is pressed, we can first fade to black before hiding the title screen, then fade back to clear. But hiding the title screen needs to wait for the fade to black to finish. Since we are returning a signal from this function, we can use the await command to pause this function until the signal is received. So we also need to write the two black function back in our fade script. This function will do the exact same thing as the other function, but only changing the final value that the color property is being tweened to. Instead of duplicating all of the code, it would be better to make this a private function, than have both of the public functions call it. We'll name it tween color and change the final value argument of the tween property function to final color. Then add final color as a parameter of the function declaration. Both too clear and too black can now return the return value of tween color, passing clear as an argument or color dot black since black is already defined for us in the color class. If we were to call the two clear function, then the two black function, while the tween is currently busy, it may cause some unwanted behavior. So anytime this is possible, we should first check if the tween has already been created and if it is running. If these are both true, then we can kill the tween before creating a new one, preventing any conflict from arising, having two tweens trying to change the same property. If we aren't quite sure we want to use 1 second as the duration, we might want to turn this into a variable too. Let's name it duration and set its value to 1 second. Then add the export tag to the front of the declaration to allow us to give this variable a different value in the inspector. Selecting the fade node in the scene tree, we can see the duration variable has a value of one, but we can change it to whatever we want. I'll try 2 seconds and see how that looks. Running the game, the window starts completely black but fades to clear, revealing the title screen behind it. We can't currently click on the Start button because we are actually clicking on the fade color rect, even though it's transparent. The button isn't responding to hovering over it either. Selecting the fade node, we need to look in the properties inherited from control for the mouse section and change its filter property to ignore. Now the mouse signals will pass through the fade node to the nodes underneath it. So let's try running the game again. If we press the start button, it fades to black, hides the title screen, and fades to clear once more, revealing the game panel. We now have a fade transition for our game. In the next section, we'll start working on the actual game. I'll see you in the next section. 24. Menu Bar: Hello, friends. In the previous section, we created a title screen for our game that can transition to the main game. In this section, we'll start working on building the actual game. Let's start by adding a new node to the game panel, a menu bar node. And sort it behind the title screen. The menu bar node doesn't draw anything by itself. So if you want a visual representation of the menu bar, you would need to put this node inside another panel or texture wrect. Then use the texture or style box to draw an actual bar. I'm fine without drawing an actual bar, so I'll delete this extra node. How the menu bar actually works can be a bit confusing. If we ever need more information on how any node works, we can always check its documentation by right clicking on it and selecting open documentation. We'll switch descript view and open the documentation for this node. We can read from the description that this node will create menu buttons for each pop up menu node added as a child. Returning to two D view, let's add a child node to the menu bar of type pop up menu. This isn't strictly a control node, but it's close enough. Simply by adding the pop up menu node as a child of the menu bar, the menu bar automatically adds a menu button with the same name as the node. Let's rename this node something more commonly seen on a menu bar like file. I'll also add another for settings and one more named. You may need to make some other changes before the menu bar will update. The menu buttons are overlapping with the game panel's border, which doesn't look very good. It would be better if it were moved further away from the edge of the window. We could move it manually using either its transform position or anchors and anchor offsets. But I wouldn't want any of my game elements touching the border, so it would be better to apply margins all around that will apply to everything. The panel node isn't a container, so it can't control the positions or sizes of its children. Adding content margins to its style box will be ignored. So let's add a container to the game panel, specifically one that will add margins, the margin container. Then make that the direct child of the game panel with the menu bar a child of the margin container. Selecting the root node so we can see the theme editor. Let's add margin container to the theme. The margin container doesn't draw anything. It only has constants in its theme properties. How many pixels of margin space are added to each side? Since the title screen is a sibling of the margin container, not a child, it will be unaffected by the margins. Let's also add menu bar to the theme. The menu bar allows us to change the font color of all the menu buttons it creates in all of the various button states we have seen before. In the constants section, we have the pixel separation between menu buttons and the font outline size. We can change the font and font size, but I'll use my themes, default settings for those. There are no icons, and we can change the style boxes for the menu buttons created by the menu bar in all the same button states. For my menu buttons, I'll use style box empty as the normal and copy it into disabled and a style box flat for pressed and copy it into hover and hover pressed. Opening the style box empty, I'll just add some content margins to make the button size a little larger than the text. And for the style box flat, I'll add the same content margins, some rounded corners, and darken the color. Then make the hover style box unique so it isn't tied to the others, then open it and change it to a lighter color. Let's see how this looks when we run the game. The menus are all there on the menu bar. They have a curved flat style box in a lighter color when hovered and darker when pressed. Their pop up menus are currently empty, though. There are a few options we can use in the inspector to change how the menu buttons behave. The flat toggle will ignore style boxes for the menu buttons and just draw their text. Turning off switch on hover will force the player to click on the menu buttons to switch between them instead of just hovering. And preferred global menu will allow this menu bar to be drawn by Mac operating systems as the global menu outside the game window. So if you don't want that to happen, you can toggle it off here. But if you are using the global menu, then you can specify where in the global menu to start adding your custom pop up menus with the start index negative one, in this case, being used to mean automatic. And, of course, BD options are also available for languages that write from right to left. We now have a menu bar with several empty menus. In the next lesson, we'll populate the pop up menus with several options. I'll see you in the next lesson. 25. Popup Menu: Hello, friends. In the previous lesson, we added a menu bar to our game. In this lesson, we'll add options for the pop up menus. Selecting the pop up menu node and taking a look at the properties in the inspector, the bottom is an expandable section named items. Here we can add each of the options that will be available within this pop up menu, like New Game, save, load, and exit. These items can be rearranged by clicking on the arrows or by dragging the hamburger icon. I'll make a new asset folder for menu icons and import some menu icons. Then assign each menu item its own icon. For settings, I'll add another item and make this one checkable as a checkbox. This would be appropriate for something that can be turned on or off, like mute, for example. Then I'll add another pop up menu as a child of this pop up menu to create a sub menu, which I will name difficulty. The items in this pop up menu will be my difficulty settings, each checkable as a radio button, meaning only one can be selected at a time. Easy will be checked by default. Easy, normal, and hard. In the help menu, I'll add an item for about with another icon. Then another item that says support. By clicking on the separator toggle, this menu option won't be a clickable button, but a separator between related groups of menu items with its name being written in this separator as a label. I'll then add another item under the separator which will be a link to my Patroon with the social media icon. Selecting the root node so we can see the theme editor. Let's add pop up menu to the theme. Font accelerator color is used to display shortcuts, but we don't have the option to add those in the inspector. Our pop up menus also don't have titles, but the rest of the font color options should be familiar, along with extra font color settings for separators. In the constant section, our pop up menus don't have a closes button, but the horizontal separation can be used to add space between the icon and text of each item. The icon max width will be useful for limiting the size of the icons to match the font height of the text. There won't be any indentation in these pop up menus, but padding can add extra pixels to the start and end of each item, giving just a little bit more space. Font Outline size we are very familiar with by now, which can also be applied to text and separators. These pop up menus can't be resized and don't have titles. But the vertical separation can adjust the number of pixels separating each menu option. There are properties for font and font size of the menu options, title and separators. I'll just leave all of these and use the theme defaults. The icons page can be used to change the icons used for checkboxes, closes button, radio buttons, and sub menus. I have imported some new icons to use for these, so I'll just replace all of them. These images are 16 pixels square and won't be resized. So make sure you're using a reasonably small image for these icons. Even though our pop up menus don't have a closed button, I'll add those two. In the styles page, we can add a style box for the pop up menus panel. I'll use a style box texture drawn with the steel plate image and give it texture margins to keep the screws from being stretched. The hover style box is drawn when the menu option is hovered over. I'll use a style box flat and make it look the same as the menu buttons. Since my separator has text, I can use the labeled separator left and labeled separator right style boxes to change how the separator is drawn to the left and right of my separator's text. If the separator doesn't have any text, then the separator style box will be used to draw it. Embedded borders will not be used by our pop up menus. Let's see how these pop up menus look when we run the game. Clicking on any of our menu buttons, the pop up menu is displayed with the panel. Each item can be hovered over and clicked, but none of the options do anything yet, and the difficulty sub menu isn't there. This is not done automatically. We have to add it through a script. So let's add a script to our settings pop up menu. Saved in the scripts folder, inheriting from pop up menu. Grabbing a reference to the difficulty sub menu, all we need to do is when this node is ready, and therefore its children are also ready, we can call a function of the pop up menu class. Add sub menu node item, which requires a label, a pop up menu node, and optionally the index number of where the sub menu will be inserted. We can just use the node's name for the label and the node itself. By ignoring the index, the sub menu will be added to the end. Let's add another script to the file pop up menu so we can add shortcuts. All we have to do is export variables of the type shortcut so we can set them in the inspector. Then when the node is ready, we can check if these shortcuts have a value before setting them as the shortcuts to the index numbers of each item in this pop up menu. We can populate each of these shortcuts in the inspector with conventional shortcuts we would expect. Control or Command N for new S for save, L for load, and E for exit. Then we should go back to the theme editor and add a font color for our new shortcuts. Now when we run the game, our file menu has shortcuts and our settings menu has a sub menu with radio buttons. But the sub menu icon is hidden behind the items text. Unfortunately, the way these items are laid out, the sub menu icon will only be visible if the item isn't the longest. To remedy this, we could write some code to manually edit the width of the panel or we can rename some of our other items to be longer than the sub menu item. I'll need to add some more horizontal separation and padding to accommodate my large font size. Et's take a look at the properties of sub menu. The first three options will determine if the pop up menu hides after clicking on an option. If we uncheck the hide on checkable item selection option, then the pop up menu will not hide when clicking a checkbox or changing the radio buttons. This might be useful for the settings and difficulty pop up menus. A state item is similar to a checkbox but can have more than two states and must be created through a script. The sub menu pop up delay is how long the mouse needs to hover over the sub menu before the pop up will appear. Allow search will make each menu option in the pop up menu searchable with keyboard input while the pop up menu is shown. The other options are for integrating this pop up menu with different operating system menus. Running the game again, everything is more spaced out, and we can use keyboard inputs to search the pop up menu that has focus. We now have pop up menus for each of our menu buttons. In the next lesson, we'll write scripts for our menu options. I'll see you in the next lesson. 26. Signals: Hello, friends. In the previous lesson, we added menu options to our menu bar. In this lesson, we'll write scripts that will react to each of them being pressed. Starting with the file pop up menu, switch the inspector panel to the node panel where we can see all of the signals this node can emit. Can react to the ID pressed signal by connecting it to the file pop up menu script. So our script will react to the player selecting any of the menu items or pressing the shortcut keys. Unlike the button, this signal passes us in argument. So the function generated has a matching parameter of the same type, the ID number of the item which was pressed. Back in the Inspector tab, we can see the ID numbers of each item if we need to know them or edit them. Inside this function, we can use a match statement to run different code based on which item was pressed. And keep things organized by simply calling a different function for each. Let's simply print out statements that say which function was called for now. After starting a new game, we will need to reset a lot of our control nodes back to their default states, which would be kind of jarring if it happens suddenly. So let's grab a reference to the fade node. Then we can await fade to black before creating the new save data and fade back to clear after. It's impossible to predict just how many things might need to react to the new save data being created. Instead of resetting them all here, it would be better to emit a signal. We can declare our own signals just like variables. Let's name it reset. Then emit it after we've created the new save data. Any node in the scene tree can connect to this signal and reset back to its default state when the player wants to start a new game. Since this process isn't happening instantly, we might want to restrict when it can be called. If, for example, the player starts pressing new game and load game in rapid succession, this would cause unwanted behavior. Let's declare another variable for whether or not this script is busy as a Boolean. Then when any ID is pressed, if this script is busy, it will ignore the input. Setting busy to true before fading to black, then false after fading to clear, none of these functions will be able to interfere with each other after one is called. Loading the game will be essentially the same as starting a new game for now. Exiting the game only needs to set busy to true, fade to black, and then tell the scene tree to quit. This will close the application on any platform except web. Web games tend not to have the option to exit at all. Next, let's look at the settings menu and connect its ID pressed signal to the script. For now, I'll just declare a variable to hold the current value of mute as a boolean, which will naturally default to false and another for the difficulty as an integer defaulting to zero. This pop up menu has only one item, the mute toggle. So I'll just confirm that the idea is zero, then call a function to set the value of mute to be the opposite of what it currently is. Giving this function a definition, accepting the value of mute as a boolean, we need to set the checkbox to actually be checked. Hovering the mouse over any property in the inspector we wish to access through a script, we can see the property path, item zero slash CHEC. Right clicking, we can also copy this path onto the clipboard. Then calling the set function, which accepts the property path as the first argument, we can paste this into quotation marks. Followed by the value, we want to set it to mute. This will mark the checkbox, but won't actually mute anything. Switching the bottom panel to audio, we can see Godo's audio bus editor, which has only one bus by default, the Master Audio bus, whose bus index is zero. Most game projects will have a bus for music, sound effects, and voiceovers, so each can be managed as separate groups. Back in our script, we can access this master bus from a built in Singleton audio server. Then access the set bus mute function passing the bus index, which is zero and the value of mute as arguments. We'll go over Singleton classes in more detail in the next lesson. Connecting the ID pressed signal of the sub menu to the same script, We can set the value of difficulty to match the ID that was pressed. Since they're in ascending order, this makes sense. We then need to set the checked property of all the items, since radio buttons should never allow more than one to be checked at a time. Using a four loop, we can iterate through each item in the difficulty item count. So this will count zero, one, two. We can then use the same set function as before, this time on the difficulty pop up menu, setting the item zero checked property to be whether or not the value of item is equal to the ID that was pressed. But we don't want to only set item zero. So we're replacing zero with percent S. This marks a position inside the string which will be replaced with a value. Following the string with another percentage sign, then the value to replace it with this will set item zero checked, item one checked, and item two checked with zero is equal to ID. One is equal to ID, and two is equal to ID. Before we create the help script, let's quickly throw together a simple about window by adding a panel container to the game and rename it about and sort it to be drawn over the other game elements. Then add a vertical box container, a label, and a button. The label might describe the project, its current version, and who is developing it. The button will be used to close this window and can be told to shrink to the center instead of filling the width of the container. We'll then anchor this to the center of the screen. Connecting the button's pressed signal to the about node, we don't need a script attached to the node. We can just pick the hide function that is defined in the control class by toggling off script methods only. Have it hidden by default. And give it a unique name. Finally, let's create a new script for the help pop up menu. And also grab a reference to the About window and connect its ID pressed signal to the script. Matching the ID, remembering that the separator also occupies an ID, we can either show information about our game or open a URI in a web browser. Using the Singleton OS, short for operating system, using the function Shell Open, then specifying the website. This will not work for web Build, however. So if the operating system's name is HTML five, we should instead use the JavaScript Bridge Sileton to evaluate the following. Window dot location dot href equals then the website. This will now work on any platform. Let's run the game and try pressing our buttons. From the main game, pressing Control end fades to black. We see the new game message output, and it fades back in. Loading does the same thing. And saving just outputs the message. We can check and uncheck the mute toggle, change the difficulty of radio buttons, open the about window, close it, and open my Patrion page in the default web browser. We now have all of our menu options reacting to player interactions. In the next lesson, we'll add the players save data and manage it. I'll see you in the next lesson. 27. Save Data: Hello, friends. In the previous lesson, we connected all of our menu options to scripts. In this lesson, we'll create the player's save data and manage it with the menu options. In order to manage a Saffle, we'll need to create a new script that describes what's in it. Let's name it something like save data and inherit from resource instead of node. This resource isn't anything that will be drawn or even added to the scene tree, but will just be a collection of data that describes the player's information as it is needed to play the game. Starting at the top of the script before extends, we need to give this resource a class name using the keyword class name with an underscore. These are conventionally written in Pascal case. We can then add any variables we want to this script. Let's add whether or not the volume is muted, which would be a Boolean and the difficulty setting as an integer. And also any other variables that might be needed to play the game, like the player's current gold, for example. All of the variable types must be primitives like Boolean, integer, float, or string or built in classes that contain only these primitive types, like vectors or colors. Any information you want to be saved must have the export tag. You may want to include some variables without the export tag, which will not persist between game sessions. While all of these variables have natural default values, we can assign them our own default values, too. Or if we want to include complex algorithms or random number generation, it would be better to override the initialized function inherited from object. Godot will automatically call this function for us when creating the resource, so we can initialize any of the variables we need to. This script must be saved before we can use it in other scripts. Next, let's switch to the file script. Since the resource has a class name, we can declare a variable using it as its type. Then the new game menu option can create a new resource of that type using the class name followed by dot Nu. In order to save our game, we'll need to know where to save it too on the player's operating system, which is best stored in a constant. Let's call it path as a string. Fortunately, Godot solves this problem for us by providing the shortcut user colon slash slash, which will automatically point to a suitable location on any platform. We only need to name the file. Following the name with the file extension dot TRS will generate a text resource file that can be read and edited by anyone in Notepad. Definitely a good idea for debugging and Indi projects. In order to save our game, all we need to do is access a built in singleton class, Resource saver, which has a function called save. This accepts a resource and a path as arguments. Loading the game is much like saving, except we assign the value of the save data to another built in singleton class, resource loader, which has a function called load, accepting the same path argument. These singletons we keep using are just scripts which contain classes, not unlike many we have written, but they are part of the Gadot game engine itself. They are called singletons because they are written in such a way that there can only ever be one instance of them at any time. This makes them very accessible because we can directly access the unique instance directly by the class name, the way we have done here without the need to store it in a variable. We don't need to know where it is or find it because the class name itself is a direct reference to the instance of it, precisely because there is only one. Most of the scripts we write as developers do not need to be unique, and it is actually preferable that they aren't we take the fade script, for example, there is no reason why we couldn't put this exact script on another node in the scene tree and reuse it for a different purpose. But then the need to reference the node the script is attached to by storing it in a variable is the only way of knowing which fade script we are trying to access, since there would be multiple instances. It is possible to create our own Singleton classes in Gadot, but we won't need to for this project. From the game manager script, let's give the file node a unique name and store a reference to it in a variable. Then when the start button is pressed, we can tell the file node to start a new game. The file script will handle the fading back end for us so we can remove it from here. Setting the default value of busy to true will prevent the player from accessing any of these functions from the title screen, which is currently possible using the shortcuts. And we should also disable the start button to prevent it from being pressed more than once. Giving it a unique name. We don't need to store it in a variable since we're only going to use it once and can just set it's disabled property to true when pressed. In the setting script, we'll need to store a reference to the file pop up menu in a variable in order to access the save data resource, and we can delete these temporary variables. When selecting the Mute toggle, we will need to set the value of mute to the opposite of what is stored in the save data. We will set the value of the file save data resource to match mute, making it the opposite of what it was previously. Likewise, changing the difficulty will also need to set the value in the player's save data. When a new save data is created or one is loaded, we will need to set these values to match the save data. So selecting the file pop up menu, we can connect the custom signal we made last lesson to the settings menu script to automatically call a new function. Allowing us to set mute and difficulty to match what is in the newly created or loaded save data. Let's try it out by running the game. Using the shortcuts from the title screen, they are ignored. Pressing the start button now creates our initial save data. If we select Save from the menu, then a save file is created on our computer, which we can open in any text editor like Notepad. We can see the values of our variables. Back in the game, we can change the values of mute and difficulty, save the game, and see the changes immediately in our save file. If we exit the game and start over, We can load our save file and see the changes have been made to the values we saved. We now have all of our menu options functioning. In the next lesson, we'll keep track of the player's gold. I'll see you in the next lesson. 28. Counter: Hello, friends. In the previous lesson, we managed the players save data. In this lesson, we'll add a counter to the screen to keep track of their gold. Let's first think about the overall screen layout and decide where to put such information. With the menu bar occupying the top left corner, I think that having players resource counters in the same horizontal line as the menu bar makes good use of the extra space in the top right, leaving the rest of the window available for the main game. So with the margin container selected, let's first anchor it to the full size of the game panel. Then add a vertical box container to organize the menu bar above the other components we haven't added yet. To the vertical box container, we'll add a horizontal box container, which will organize the menu bar to the left and the resource counters to the right. The resource counters themselves, having both an icon and a number will also be horizontal box containers. Let's name this one gold. Then give it a texture wrecked node and a label node as its children. I have this gold coin icon I will use for my game's currency, which I will populate as the texture for my icon. When building a user interface like this, it's a good idea to have your counters default to the highest possible number you want to allow. That way you know how much space needs to be allotted to accommodate it. I think 10 million should be a sufficiently large number here. The counter is currently sorted by the horizontal box container just to the right of the menu bar, but I would prefer to be in the top right corner and any additional resource counters I might add to fill into the left. Selecting the menu bar, we can tell it to expand horizontally to fill as much space as it can, pushing the remainder of the horizontal box's children to move as far right as they possibly can. Now, if I duplicate my gold counter, each counter will be added to the right, pushing the previous ones left. Next, let's add a script to the gold node, which will be responsible for making sure that the number displayed always matches the number of gold in the player's save data. So let's call it counter and save it in the scripts folder. In order to access the player's save data, we'll need to store a reference to the file node in a variable, and to change the text property of the label node, we'll need that, too. Then we can write a function that updates the counter. All we need to do is set the text property of the label node to be the value of file data dot gold as a string. Whenever the player starts a new game or loads their game, the reset signal will be emitted, which we can connect to this function to update the counter. Next, we'll need to write one or two public functions that will change the value. I'll write two separate functions, one that receives the resource, and one for spending the resource, both accepting an integer parameter for the amount. When receiving, we'll simply set the new quantity of the resource to be plus the amount received, then update the counter. But we may have decided that there should be a maximum quantity that can be displayed. So let's enforce that restriction here. We'll export another variable for the maximum quantity and give it a value. We can then enforce this maximum using the minimum function. The minimum function will return the smallest of all values provided. So either the quantity of the resource plus the amount added or the maximum quantity allowed, whichever is smaller. When spending the resource, we first need to check if the player has enough of the resource to spend the amount. If the quantity of the resource is less than the amount being spent, we probably wouldn't want to continue with the transaction and return. If the player does have enough, then we can reduce the quantity by the amount and update the counter. We can make this function more useful by returning a boolean, returning false if the player doesn't have enough or true if successful. So any game logic that calls this function can react to the outcome of trying to spend this resource as being either true or false. For testing purposes, let's override the input function, which accepts an input event as a parameter. Then check if either the up or down keys were pressed. If up was pressed, I'll have the player receive a random number of gold 1-100. Likewise, when pressing down, they will spend a random amount of gold, and I'll print out the result. Running the game, we can see that the counter was set to match the initial value of the player's gold. Pressing down, the amount reduces until they can't afford to spend anymore. In which case, the counter is not reduced and the output changes to false. Pressing up increases the counter and eventually caps out at the maximum quantity. This will work pretty well for gold, but what if we have different resources that we want to treat with all these same functions? We would need a different script for each resource, which isn't very good design. So let's make this script more flexible and export the name of the resource that this counter is keeping track of as a string. Remember to set this exported value in the inspector with the exact same spelling as the variable in the players save data that you want to count. I'll also change my maximum gold value back to what it should be. We'll also need to declare another variable to hold the quantity to. Now, both transaction functions can start with first getting the quantity of the resource from the players save data using the G function, which accepts the property name as a string. So we'll give it the resource name. We will then perform our transactions on the local variable instead of directly referencing the players save data. And after performing our functions, we can set the variable in the player's save data in much the same way using the set function, passing the name of the property as a string and its new value. We also need to get the current quantity from the player's save data when updating the counter. Now we can duplicate the gold node and have it track a different resource with no changes to the script, only changing the values of its exported variables. Et's add another resource to the players save data, something like robots with an initial value of one. Then also connect to the reset signal to automatically update this new resource counter. Now when we run the game, both counters are displaying the amounts of different resources contained in the players save data. Remember to remove or comment out the input function before moving on. Also, the counters display should have been updated after setting its quantity value. We now have resource counters for our game. In the next section, we'll start implementing some game mechanics. I'll see you in the next section. 29. Line Edit A: Hello, friends. In the previous section, we created a menu bar for our game. In this section, we'll start implementing some game mechanics. Let's start by collapsing the horizontal box container and maybe renaming it something more descriptive like top. We can then add another child to the vertical box container to fill the remainder of the game panel. I'll use another margin container. I'll name this margin container ship, since I'll be using it to display information about the player's ship and expand it to fill the remainder of the game panel vertically. Inside the margin container, I'll put a vertical box container to organize a few components vertically, starting with a label node that simply says my ship, so the player knows that this is their ship. Under that, I'll put a texture rect to draw an image of the ship, use the Gadot icon as a placeholder image, tell it to ignore the size of the image and give it custom dimensions of 150 pixels squared. And finally, a stat block of the ship's various statistics. Similar to a horizontal or vertical box container, we can easily organize large numbers of control nodes into a grid formation using a grid container. Let's name this stats and give it two columns, one for labels and another for values. For the stats, I'll add two label nodes for each, the ship's name, type, the number of crew members, and the amount of cargo it can hold. The labels on the left side of the grid indicating what type of value it is. The labels on the right side can be populated with example information for now, but we want this to be tied to information in the player's save data. The grid container will automatically sort its children into the number of columns specified, keeping all of the rows and columns aligned. Selecting all three child nodes of the vertical box container, I'll have them shrink to the center. I would like the player to be able to change the name of their ship, so let's change the name label node to a line edit node. This node allows the player to edit the text inside it while playing the game. I'll put all of this inside a panel container so it has a nice border around it. Then tell the vertical box container to shrink vertically to the center of the panel container. From this display, we can clearly see the information we will need to store for each ship a picture, name, the type of the ship, both minimum and maximum numbers of crew and cargo capacity. Much like we did with the player save data, we can create a resource script to hold this information. Let's first create a new script folder named Resources, and we can put the save data script in here. Then make a new folder for ships and create a new script in this folder named ship inheriting from resource. Here we can export variables just like we did with the players save data to describe a ship, including a texture two D for the image, a string for the type, and integers for the minimum crew, maximum crew, and cargo capacity. I'll exclude the ship's name since it won't be associated with the type of ship. Be sure to save this script. Right clicking on the ship resource folder, we can now create a new resource and make as many ships as we want. I'll make three ships, a cutter, catch and a schooner. Double clicking on the resource, we can open it in the inspector and edit the exported variables, populating the image with a picture of a cutter, filling in the type, crew limits and cargo capacity variables. Each of our resources can populate the exported variables with their own unique values, creating a category of similar objects for our game that all have the same properties but with different values. To better organize my scripts, I'll create a subfolder for UI yes. Where I will place all of my sub manuscripts, fade and counter. Then another folder for managers, which will hold the game manager and another new script that I will attach to the ship node and name it ship manager. The ship manager's responsibility will be to manage everything that happens with its children, including updating the displayed pictures and text. In order to do that, this script needs to know what all of the ships are that exist in the game. So we'll export an array of ships as a variable. Taking a look in the inspector, we can see our array of ships and add three elements to it and populate each with a different ship. I've designed my ships to function as upgrades and would prefer them to be sorted from worst to best. So I'll put the cutter at index zero, followed by the catch, then the schooner. Now, all we need to know from the player's save data is which ship the player currently has. This can easily be stored very efficiently as just the array index of the ship. Switching over to the save data script, I'll add an exported integer for the player's ship index number and have it default to zero. While I'm here, I'll also add a string for the ship's name. Back in the ship manager script, in order to display the player's ship information, we'll need a reference to the file pop up menu to access the player's save data. I'll rename this node my ship Info. Then grab references to the texture wrec, line edit and label nodes. I'll rename each of these variables so I know they are describing the player's current chip and also at a local variable to hold the player's ship index number. Writing a private function to update the player's ship information, each node will set the appropriate property to that of a ship in the array, using the player's ship index number to index the array. I'll display the minimum and maximum crew together by casting them to strings and separating them with a hyphen. Then also display the cargo capacity with its units in tons. When starting a new game or loading, the reset signal will be emitted and we can connect to this signal to automatically update the player's ship information. A after retrieving their ship index number from the save data. But our ship still doesn't have a name yet. I will randomly generate ship names from a list by first declaring a constant at the top of my script called ship names, which will be an array of strings. I'll then populate this array with a bunch of common ship names. In the on file reset function, if the ship doesn't have a name, I'll assign it a randomly selected name from this list. Indexing the array with a random integer between zero and the size of the array minus one. The last thing we need to do is allow the line edit node to change the player's ship name, which can be done by connecting its text submitted signal to the ship manager script. This will be omitted after pressing the enter key. There are also signals for every time the text changes or when the text size limit has been reached. In our newly generated function, there are two possible situations we should account for if the line edit text is empty or not. Since the new text was passed to us as a parameter, we can easily put it in an if statement with true being not empty and false being empty. If the new text is not empty, we can assign it to the player's ship name. But if it is empty, we can do the opposite, reverting the text in the line edit back to the previous value of the ship's name. This will prevent the player from having a ship with no name. It's also a good idea to tell the line edit node to release focus, regardless of the outcome. The ship name can be read directly from the player's save data when updated. Returning to two D view, we can see that our players ship information is populated with placeholder information. When we run the game, everything is automatically replaced with the information associated with the cutter ship, and the ship was assigned a random name from the list. We can click on the line edit node to change the name of the ship and press Enter to submit the change. Attempting to name the ship nothing is rejected. Saving the game, then starting a new game. The ship's name is replaced with a randomly generated one and loading the game, our saved ship name is restored. We now have our players ship information displayed and can edit the ship's name. In the next lesson, we'll go over the options available for line edit properties and theming. I'll see you in the next lesson. 30. Line Edit B: Hello, friends. In the previous lesson, we allowed the player to change the name of their ship. In this lesson, we'll go over the options available for line edit properties and theming. Selecting the line edit node, let's take a look at its properties in the inspector. Using the text property, we can give it a default value. If the text property is empty, then the placeholder text is displayed instead with a different font color. We can align the text to the center, right? Or filling the entire line will space words out evenly. We can restrict the length of the entered text. It's a good idea to test how much text can be comfortably displayed using a wide letter like a capital M or W. Then use that to judge the maximum text length. If the line edit is not editable, the player won't be able to click on it and edit its contents. If expand to text length is enabled, the box will expand to accommodate the text inside it. Enabling the context menu will allow the player to right click on the line at at node to display a context menu. We can see this in the theme editor without running the game. The context menu is automatically themed to match our game's pop up menus and include all of the typical stuff we would expect to see like cut, copy, paste, select all, clear, undo and redo, a sub menu for changing the direction of the text and options for displaying and inserting control characters. If you do not want to include this context menu, you can turn it off here. For console and mobile platforms, virtual keyboard integration is available, and there are multiple options of virtual keyboard, specifically for inputting numbers, decimals, phone numbers, emails, passwords or URLs. A clear button can be included, which when clicked, will remove all the text from the line edit. The shortcut keys that are displayed in the context menu will be used even if the context menu is not. These can be disabled here. There is an option to disable pasting using the Middle Mouse button on Linux platforms. You can also change whether or not the player can select text using either the mouse or keyboard. Deselect on focus loss will deselect whatever text was highlighted when the line edit node loses focus. Drag and Drop selection can be used to control whether or not the player can click and drag text within this line edit. An icon can be assigned to the right side of the node to indicate better to the player that this text is editable. A pencil is common, but I'll use a feather icon to fit the aesthetic of my game. If there is both a right icon and a clear button, the clear button will be drawn instead of the right icon anytime there is text to be cleared. Flat works just like buttons, removing all styles from the node and only drawing the text and icon. Draw Control characters is the same toggle option that was available in the context menu, allowing control characters to be drawn. Lastly, select all on focus will automatically select all the text when the node first grabs focus. Under the carat category, we can tell the carat to blink and specify the interval of time between blinks. The carat column is the character index of the carat's current location. We can see it if we force the line edit to display the carat even without focus. If using the line edit for secret information like a password, we can turn secret on, and every character will be drawn as the secret character specified here. Displaying the carat made grapheme and BD options are also available for more complex writing systems outside of the Roman alphabet. For my game, I will have the placeholder text as name your ship with a maximum length of 16 characters and allow the box to expand to accommodate the text. I'll keep the right icon as the quill, select all of the text on focus, and have the carat blank turned on. Other than that, the rest of the options are fine as their defaults. Selecting the root node so we can see the theme editor, let's add line edit to the theme. In the color section, we can change the color of the carat and the clear button as it is normally and pressed. We can change the font color of the normal text, placeholder text, selected text, and uneditable text, as well as the outline color of all of them, and the color of the box that is drawn behind selected text. In the constant section, we can change the width of the carat, set the minimum character width for the line **** box measured in Ms, and the thickness of the font outline. Like everything with text, we can change the font and font size, but I'll leave them as the default for the theme, and we can override the icon used for the clear button. The clear button colors will be used as modulation on this icon texture. Lastly, in the style section, we can provide style boxes for how the line edit looks normally when it has focus and when it is read only. I'll use style Box flat for normal. Then click on it to edit it, add my usual seven pixels of corners, borders, and content margins. I'll have the same color as the background with a darker border color. Then I'll duplicate the style box flat to the focus option, make it unique, then open it up for editing. When it has focus, I'll change the border to a lighter color and add a shadow. We can see how our changes look in the theme preview. For read only, I'll just use a style box empty. Let's also add the grid container to our theme. Just like the horizontal and vertical box containers, the grid container only has constants for its theme properties, the number of pixels separating its rows and columns. Don't forget to test how your changes look by running your game. We now have our line edit node configured to match the aesthetic of our game. And the next lesson will allow the player to purchase a new ship. I'll see you in the next lesson. 31. Option Button: Hello, friends. In the previous lesson, we allowed the player to change the name of their ship. In this lesson, we'll let the player purchase a new ship. I'll just be recreating a very similar set of information on the right of this panel container, displaying the stats of a ship upgrade that is available for purchase, starting with a vertical box container, which I will name ships for sale, then put both of these into a horizontal box to organize them side by side. With both of the child notes selected, telling them to expand horizontally, they will share the available space evenly. Inside ships for sale, I'll copy over the label texture wreck grid container and all of its label nodes. I'll have ships for sale shrink to the center vertically to match the layout of the left side. Changing the label to say ships for sale. But I'll change name to cost, change the left label to a texture rect, and use my coin icon instead of text to indicate that this is the purchase price. I'll set it to fit proportional to width and keep its aspect ratio to match the height of the node to the right. Then change that back to normal label node and populate it with a sample number. Then move these to the bottom of the grid. I'll then also add a button to the bottom to allow the player to buy this ship. And shrink it to the center horizontally to match the layout. It might not make much sense to only have one chip for sale, so I'll change the ship's type to an option button. This is a button which will present the player with several options to choose from. This option button is inheriting its theming properties from button. I'm fine using my button theme for the buy button, but I would like the option button to have a different style. Selecting the root node to see the theme editor, let's add option button to the theme. This will allow us to override the theme properties which the option button inherits from button. Most of the theme properties for Option button are identical to that of the normal button node, allowing us to change the font and icon colors of all the button states. Font outline color, font and font size, and style boxes for each of the button states, too. I'll use style Box flat for my option button and make it look just like the line edit node. Then duplicate this style box to pressed, make it unique, change the border color to a lighter shade. Use it for hover, hover pressed and focus. Then I'll just use a style box empty for disabled. We can see how our changes look in the theme preview window without running the game. In the icons section, we can change how the down arrow looks by providing a different icon. Just like with the pop up menus, this icon is 16 pixels squared and will not be resized, it is important that the icon you replace it with is properly sized. There is also a theme property for an icon that will be drawn to the left of the text on the button. I'll just drop in another icon to show you what that looks like. In the constant section, there is a property for setting the size of the button to match its largest style box in case your style boxes are different sizes. This will work when my option button is disabled, since the empty style box is smaller than the rest. The arrow margin is how many pixels are between the down arrow and the right side of the button. Horizontal separation is how much space is between the left side icon and the button's text. Icon max width can be used to limit the size of the left icon. It has no effect on the down arrow. Modulate arrow, if given any value other than zero, we modulate the arrow icon with the font color of the buttons text in all of its various button states. And the outline size draws an outline around the text as we know. I don't want to use the left icon for my game, so I'll delete it. Selecting the option button node, let's take a look at its properties in the inspector. The expandable items section works just like the one we use to build our pop up menus. So I'll add an item for each ship in my game. Each item can be assigned an icon if you wish, disabled or used as a separator. The ID number is used to identify which item in the list is currently selected. With selected as negative one, no item is selected. Fit to longest item will size the entire button to best accommodate the item with the longest text. Turned off, the button will resize to fit the text of only the selected item. And and the Allow reselect toggle will allow the player to select the same item which is already selected, emitting the selected signal again. Many of the same options are also available, which were inherited from button and Base button that we've already explored. It's clear each of my ships are going to need another property, their purchase price. So switching to ScriptVew, I'll add a variable to the ship script for the price of the ship. Remember to save the script. Then give each ship resource a different value for their price. I'll rename my button By. Let's connect the By buttons pressed signal to the ship manager script. And also the item selected signal from the option button. There is also a signal which will be emitted when an item in the pop up menu is focused if you need it. Just like we did with the players ship information, we'll populate the information on the right side of the screen with the information of the ship which is selected from the option button based on its index. So we'll need to store references to the texture wrecked option button and label nodes plus the By button. Since I sorted all of these nodes into a horizontal box container, all of these paths are no longer valid. I could rename all of my nodes to have unique names and use unique name references, but I'll just add the horizontal box container to the path instead. We can copy almost all of the lines of code from updating the player's ship information into the on type value selected function to update the information about the ship for sale, simply changing the array index to the index of the ship which was selected. This will only work if the array indices of the ship array are the same as the ID numbers of the items in the option buttons pop up menu. We can guarantee this by generating the option buttons items ourselves from this script. Overriding the ready function, we can iterate through each of the ships in the ship array, then add an item to the option button, the type of the ship. Since the ship array is indexed from zero counting up, adding each item to the option button, the ID numbers will match exactly. But we must remove all items from the option button and have it start the game empty for this to work. When a new game is started or a game is loaded, we retrieve the index of the ship the player already has. I do not want to allow the player to purchase the same ship they already have or one that is of lower quality. So I'll count from zero up to the size of the ship array and disable each item whose index number is less than or equal to the player's current ship index. I'll then select the next upgrade available to the player if there is one. If the player's current ship is the last index number of the ship array, there are no more upgrades, so I'll tell the option button to select negative one, and all of the options will be disabled. Otherwise, the option button can select the item whose index is one more than the player's current ship index. Since I'm allowing negative one to be selected, this is obviously not a valid array index, so I'll need to add a condition to the on type value selected function to accommodate it. And instead remove all of the information. The last thing I want to do is disable the buy button if the player doesn't have enough money to buy the currently selected ship. Grabbing a reference to the player's gold counter. I'll simply set the value of buy dot disabled to be the opposite of gold dot has enough, passing the value of the currently selected chip. This function doesn't exist, so let's switch over to the counter script and add it quickly. Copying the definition of spend, we simply need to return whether the quantity of the resource is greater than or equal to the amount in question. While I'm here, I'll uncomment the input function so I can cheat to get more money. Finally, when pressing the buy button, we'll call gold dot spend, passing the price of the currently selected ship for sale, and putting this inside of an I statement allows us to react to whether or not the player has enough gold to do it. This should never happen since we disabled the button preemptively if they don't have enough gold, but it's fine to have redundancy. If the transaction was successful, we'll set the player's current ship index to the currently selected ship. Then update the player's ship information and the ships for sale, too. Otherwise, I'll print out an error message so I know this happened. When setting the selected index of the option button through our script, the item selected signal will not be omitted. But we can easily call our function directly instead. Passing the selected index, we just set it to. Running the game, the player starts with a cutter and the catch is displayed for sale. Expanding the option button, the player is not allowed to purchase another cutter, but they can also see the schooner. Buying the catch, the player's ship information is updated and the schooner is displayed for sale. Expanding the option button, the schooner is now the only option. Giving myself more money, I'll re select the schooner to activate the buy button. If we purchase it, now there are no more ships for sale. I should also disable the buy button if there is nothing to buy. We now have ships for sale that the player can buy as an upgrade. In the next lesson, we'll create multiple different pages of information that the player can switch between. I'll see you in the next lesson. 32. Horizontal Split Container: Hello, friends. In the previous lesson, we allowed the player to purchase bigger and better ships. In this lesson, we'll allow the player to resize regions of the game panel. This ship management page from my game is clearly divided into two regions of information, the player's current ship and ships that the player can buy. Organized into a horizontal box container, neither of these occupy the entirety of the space provided for them, so they are expanding to split the space evenly. In cases like this, there is a control node we can use to give the player more control over the layout, allowing them to resize the division of a space between two control nodes, a split container. Split containers can be horizontal or vertical, but can only have two child nodes and will split the available space between them and also allow the player to adjust it. If I rename this node, all of my node paths will break. So I'll just leave the name as is. It doesn't look like anything has changed, but if we run the game and hover the mouse over the center of the game panel, there is now a bar that we can click on to grab and drag to the left or right to resize each side. Each side will only be allowed to shrink to the point where its own child control nodes can't collapse any further. Selecting the horizontal split container, let's take a look at its properties in the inspector. The split offset can be used to adjust the position of the split with zero being where it would naturally sit after the first child control node, negative moving left and positive moving right. Collapsed will ignore the split offset, collapse the first child control node, and position the split immediately after it. Dragger visibility can be used to hide the dragger, making it unusable or both hide and collapse it removing the pixel separation between the child nodes. Selecting the root node so we can see the theme editor, we can add the horizontal split container to our theme. In the constant section, we can remove the auto hided behavior of the grabber by changing the value to zero, which will make the grabber always visible. Minimum grab thickness is the pixel width or height of the clickable area generated to surround the grabber, since the grabber icons are typically very thin, and separation is the number of pixels that separate the two child control nodes. In the icon section, there are three different grabber icons, but we only need to change the top one. The horizontal and vertical grabbers are properties of the parent class split container and will not be used by this horizontal split container node. Running the game, we can see that the grabber is no longer auto hiding when the mouse isn't touching it. The area separating the two child nodes is wider than it was before, as is the clickable area. We now have our margin container split into two resizable regions. In the next lesson, we'll give the player multiple pages of information that they can switch between. I'll see you in the next lesson. 33. Tab Container: Hello, friends. In the previous lesson, we allowed the player to resize part of the game. In this lesson, we'll allow the game to display multiple different pages of information. My game window is now looking more full, and I would not be able to squeeze in more mechanics without overwhelming the player with too much information being on display all at once. To solve this, let's make the node which is below the menu bar a tab container. The description tells us that the tab container will create a tab for every child control, displaying only one at a time. Making the ship node a child of the tab container. It becomes a tab within the tab container. I'll add several more tabs by adding more children to the tab container. All of them will be margin containers. I'll have one to let the player manage the crew of their ship, another for buying and selling cargo, and one for navigating to another port. The tabs generated by the tab container can be rearranged by changing the order of its children. With the rearranging of the scene tree, the ship manager script will have disconnected its path to the gold resource counter node. I can fix this by giving the counter a unique name and using the percent sign annotation instead of the node path. Returning to two D view and selecting the tab container, let's take a look at the properties of the tab container in the inspector. The tabs are aligned to the left by default, but they can be moved to the right or center. They can also be moved to the bottom of the tab container. Current tab is the child index number of the tab which is currently being displayed. If there are so many tabs that they do not all fit across the length of the tab container, then the extra tabs will be clipped and navigation buttons provided to let the player scroll through them. If clip tabs is disabled, then the tab container will expand to accommodate all of its tabs. Turning off tabs visible will hide the entire tab bar. The tabs still exist and can be used through script, but the player will not be able to click on them. All tabs in front can be used to draw all of your tabs over the contents instead of underneath. I'll demonstrate what this looks like later. If drag to rearrange is enabled, the player can click and drag tabs to rearrange their positions in the tab bar. If this setting is turned on and you have multiple tab containers visible and you want the player to be able to click and drag tabs from one tab container to the other, then you can set all of the tab containers to have the same tabs rearrange group ID to allow this sharing of tabs. With the default value of negative one, tabs will not be allowed to relocate to any other tab container. With used tabs for minimum size turned off, the tab container will resize to accommodate the contents of each tab as it is selected. Turning it on, every tab will have the size of the largest tab. Tab focus mode can be used to control how the tabs in the tab bar can grab focus, defaulting to both mouse and keyboard controls. You can restrict to only allowing mouse clicks to give the tab bar focus, so pressing the tab key on the keyboard or arrow keys will not allow the player to switch focus to the tab bar. But if the tab bar has focus from clicking, then the arrow keys can still be used to navigate between tabs, or none can be used to never allow the tab bar to grab focus. The player will still be able to click on tabs to switch between them, but they will not grab focus, so the arrow keys will not be used to switch tabs. If deselect enabled is turned on, then the value of the current tab can be set to negative one to have every tab become unselected. After configuring the properties for my tab container, I'll have it vertically expand to fill the space of my game window. Selecting the root node so we can see the theme editor, let's add tab container to the theme. Starting in the icons section, we can override the icons used for the increment and decrement buttons the player uses to scroll through the clipped tabs and their highlighted counterparts which are displayed when the player hovers the mouse over them. We can also change the drop marker which will be displayed between tabs while dragging and dropping a tab, and a menu icon, if this tab container has been given a pop up menu, as well as its highlight. In the colors section, we can provide a modulation color for the drop marker. A font outline color and font colors for every state of the tabs in the tab bar. Before we continue with the rest of the theme options, let's run the game and see how these settings work. Since my tabs are being clipped, we can click on the increment and decrement buttons to navigate through them. I can click and drag tabs to rearrange them, and the drop marker is drawn and modulated to indicate where the tab will be positioned if I release the mouse button. No menu icon is displayed because this tab container has not been assigned a pop up menu yet. I'll change the drop marker modulation back to white. I'll now delete the excess tabs in my tab container. If we add a pop up menu as a child of the tab container, it will not generate a new tab, since the pop up menu node is not a control node. I'll put it as the top child of the tab container to make things easier. And just put one item in the pop up menu that says, Hello, If we attach a script to the tab container, I won't be keeping this script in my game, so I'll just leave it in the main resource folder with the default name. Overriding the ready function, we can call the tab container function set pop up, which accepts a pop up as an argument. So we can pass that get child with the child index of zero, the top child, the pop up menu node. Next, I'll switch to the ship manager script. Tabs can not only have text but also icons. But just like the pop up menu, they must be added through a script. So let's export a variable that will be the ship tabs icon as a texture two D, which we can populate in the inspector. Then in the ready function, knowing that this is a direct child of the tab container, we can call Get parent to reference the parent node of this node. Then call the function of the tab container, set tab icon, accepting the index number of the tab and the icon texture as arguments. So I'll pass it the index number three and the exported texture. Returning to the theme editor in the constant section, we can give this icon a maximum pixel width. The number of pixels separating the icon from the text of each tab, the font outline size and how many pixels of margin space are added to either end of the tab bar. As always, I will ignore the font and font size settings and use my default theme settings. In the style section, we can change how the tab container, tab bar, and tabs are all drawn. I will set all of them with different style box textures. Panel is the background fill of the tab container where the active child will be drawn. This is usually drawn seamless with the selected tab style. This image has rounded corners, so I'll add texture margins of 16 pixels. I'll copy this over to the style for the selected tab, but make it unique since I do not want the tabs to have the rounded corners on the bottom. Expanding the subregion section, then clicking on the Edit region button, we can use only a portion of the provided image to draw the style box. With grid snapping, I can slice off the bottom 16 pixels to remove the rounded corners. Now the selected tab looks like it is connected directly to the panel. I'll also remove the bottom texture margins. This will move the text and icon down on the selected tab, making it appear more as though it is above the others. Copying this two unselected and making it unique, I'll substitute the texture with a darker one, then put the bottom texture margin back in, moving the text and icon up. Then repeat the process for adding different textures for Hard focus, and disabled. The tab bar background can also be given a style box, so I'll use a shadow. Running the game, we now have a stylized tab bar, and the ship tab even has a ship icon. The menu icon is displayed over on the right, and we can click on it to bring up a pop up menu. I have no plans to use this menu for anything in my game, so I'll delete it. Remove the script from the tab container and delete the script from my project resources. If your tabs are very ornate in their design and you give them expand margins large enough to overlap with the contents of the selected tab, This is when all tabs drawn in front can be used to draw all of the tabs in front of the content of the selected tab. I'll revert these changes back to the way they were. We now have multiple tabs that the player can switch between. In the next section, we'll use more new control nodes to add more mechanics. I'll see you in the next section. 34. Tree A: Hello, friends. In the previous section, we allowed the player to manage their ship. In this section, we'll allow the player to manage their crew. Switching to the crew tab, I'll add a similar node structure to this tab as the ship tab, starting with a horizontal split container containing two vertical box containers, one for the crew the player currently has, and the other for people they can hire, both expanding horizontally and filling the space vertically. Then on the left side, I'll add a new node called a tree and have the tree expand to fill the space vertically. If we select the root node and take a look at the theme editor, there is a tree here we can see populated with different types of tree items, including plain text, editable text, a checkbox, a roller box, and a list of options. The last three are also sorted as children of another tree item. There is a scroll bar that can be used to scroll through the tree items if the tree is taller than the allotted area. Parent tree items can be collapsed or expanded to hide or show their children. I thought this would be a neat way to represent the chain of command for my ship's crew members. Selecting the tree again, there is no way to populate it with tree items from here. It must be done through a script. Recognizing that this is the same way the scene tree works in the scene tab of the Gudo Editor, I can recreate exactly what I want in my tree using basic nodes. With the top of the hierarchy being the captain, followed by the first mate, the second mate, and the third mate. I olden times, they may have also had a fourth mate. I can rearrange these nodes so the first mate has authority over the second, third and fourth mates and add more children to each of them. The second mate is in charge of navigation, so they will have a navigator, helmsman, Brelman, topman and seamen under their command. The third mate is in charge of safety and combat, so they'll have the boatswain or boatswain, a boat builder in modern English, carpenter, surgeon, and gunner under their command. And finally, the fourth mate, if there was one, would be in charge of logistics and resources, so they would command the cook, butcher, and purser. I may also want to allow paying passengers on board the ship, too. Attaching a script to the crew tab, I'll export an icon for it, populate it with the crew icon for my game, and set it as the tab containers tab icon for this tab in the ready function the same way as the ship tab. Then I'll grab a reference to the tree node. Let's write a function which adds an item to the tree. We can add an item to the tree by calling its function create item, which accepts optional arguments for the parent of the item being created and the child index number. Ignoring these arguments, this item will be created as the root of the tree. If we want to change anything about this item, we need to store a reference to it in a variable of type tree item. I'll name it new item. Then we can call the set text function on this newly created tree item, which requires a column number and text arguments. The tree currently only has one column, so the column index is zero, and I'll put the text I want to set it to as a parameter of the function. Knowing that I'm going to have a lot of tree items, I'll store them in a dictionary, then assign the tree item at index of text to be the new item. So now to add the captain to the tree, all I have to do is call my function passing the name of the tree's top child node. If I want to add the rest of them to the tree, I also need to specify the parent tree item when calling create item, so that can be another parameter of the function. When calling this function for the captain, the parent tree item will be null. Now I need to traverse the hierarchy I've built in my scene tree, adding each item to the tree with the correct parent, which seems complicated, but can be simplified using recursion, meaning a function which calls itself. To keep it even more simple, I'll keep the recursive logic separate from adding a tree item and do it in a separate function, which I will name populate tree. The first parameter of this function will be the current node in the scene tree, and I'll also need to know the parent tree item if there is one. I can then call the AD tree item function, specifying the name of the node as the text and passing along the parent tree item. I'll then store this in a local variable returning it from the ADTree item function. Using a four loop to iterate through all of this node's children, we can simply call this populate tree function again on the child node and give it the parent tree item generated from the current node. If the current node has no children, this four loop will not run. The ready function only needs to initiate this process, starting with the tree's top child node and no parent tree item. Running the game, the tree is populated with the exact node structure from mycin tree. Each item in the tree can be selected and parent items can be collapsed or expanded. Let's select the tree node and take a look at its properties in the inspector. Columns can add extra columns to the tree, giving each tree item multiple text entries, one for each column. I'll have three columns for my tree to display roles, names, and pay. Column titles are hidden by default, but can be made visible with this checkbox. Allow reselect will emit the selected signal again if the player clicks the selected item, and we can change whether or not the ight Mouse button can be used to select items. Allow search will let the player navigate the tree items by typing, matching the text of each item. Hide folding will hide the collapse or expand arrows, and enabling recursive folding will allow the player to hold down the shift key to expand or collapse children recursively. Hide root will hide the root tree item. In my case, that would be the captain. The drop mode flags can change how the player drags and drops data into the tree, but that is beyond the scope of this course. Select mode can change whether the player selects a single item, multiple items, or a row of the tree. Horizontal and vertical scrolling can also be enabled or disabled. Now with multiple columns and titles visible, I'll add titles to each of my columns by calling the tree's set column title function. Then also set the last column to not expand. So the role and name columns will occupy the majority of the tree space. After grabbing a reference to the file pop up menu, we'll need to connect the file reset signal to the crew manager script to react to the player either starting a new game or loading their game and populate the remaining two columns of the tree with their crews name and pay rates. Since the tree items are stored in a dictionary, it would make sense to also store the player's crew data in a matching dictionary. For now, I'll just initialize this dictionary to already contain a skeleton crew for the ship, starting with the captain. Each member of the crew needs multiple pieces of information, so that can be stored in another dictionary, giving the captain a default name and a pay rate of zero gold. Then I'll add in a navigator, helmsman, topman and seaman, each with a name and pay rate. Back in the crew manager script, when the file is reset, we can populate the tree with the player's crew members, but we may also need to erase ones that are already there. So iterating through every role in the tree, we'll check if the player's crew has that role. Then call another function, specifying the tree item we are changing, along with the name and pay rate we are changing them to with blank values in the case that the player doesn't have that role. Writing this function, accepting the same parameters, we can just set the text of the tree item at the other columns to be the values passed in. Now the tree is automatically populated with the names and pay rates of the player's crew, but I would like the captain's pay to be blank and their name editable. Setting the tree roots item to be editable at column one is pretty simple with a function call. Then when populating a crew member, I'll only set the text of the second column if the item is not the root item. Connecting the item edited signal to the crew manager script, we can react to the player editing their captain's name. This doesn't provide us with any arguments, but they are stored in the tree class and can be retrieved with get functions. In the case of my tree, only one tree item is editable, so I don't really need to know anything other than what the player entered for the captain's name. If it's not empty, I'll change the captain's name in the player's save data. Or if it is empty, revert the tree item back to the value. This works as long as every role is unique, but I may want to have some roles with multiple crew members. In these cases, I'll change the value stored in my save data from a dictionary describing one crew member to an array of dictionaries describing a list of crew members. I'll give the player multiple seamen and top men to run basic sailing operations of the ship, each with unique names, but I'll leave their pay the same because my ship is unionized. Back in the crew manager script, when creating the tree, I'll need to add as many tree items as the maximum number of duplicate roles as there could possibly be, which I will indicate in the scene tree by adding X followed by a number to the name of the role. Then when populating the tree, I'll search the name of the node for the letter X and store that in a variable. If the letter X is not found, it will return negative one as the result. Declaring new variables for the role and quantity of that role, the role defaulting to the node name and the quantity defaulting to one, if X is found, then I can parse these values out of the node name using the substring function with the role being before X and the quantity after it. Then call Ad tree item with the variables. Add tree item will need a new quantity parameter, and if the quantity is anything larger than one, then the dictionary entry for that tree item will need to be an array of tree items, much like the crew dictionary. Creating the tree item can then be wrapped in a four loop counting up to quantity. And based on whether or not the quantity is one, either set the dictionary entry to the item or append it to the array of items. I don't want a whole bunch of blank repeated roles in the tree, though, so I'll set the tree items visibility to only be true for the first one, then false for the rest. When the file reset signal is received, different code will need to be run based on whether or not the role is an array. If it is an array and the player has that role in their crew dictionary, then the quantity of that role will be the size of the array. Iterating through all of the roles that are available in the tree. If I is less than quantity, then the tree item at index role I can be populated with the player's crew data at the same indices. Otherwise, they'll be blank. And the item's visibility can also be set to true if either I is zero or I is less than quantity. So at least one of each role will always be displayed even when the player doesn't have them yet. Running the game, we can see that the player now has multiple crew members assigned to the top Men and Seamen roles. We now have a tree node displaying the player's crew. In the next lesson, we'll go over the theme options available for the tree node. I'll see you in the next lesson. 35. Tree B: Hello, friends. In the previous lesson, we used a tree to display the player's crew. In this lesson, we'll explore the theme options available for the tree node. Before that, though, I'm going to change this name parameter to crew name to get rid of the warning. With the root node selected, let's add the tree node to the game's theme. Starting in the constant section, we can set whether or not the horizontal lines separating each tree item will be drawn by changing the draw guide's property to zero or not zero. The same can be done for the relationship lines which show the parent child relationships of tree items more clearly. The relationship line width can be changed here. And we can provide different relationship line widths for child relationship lines as well as parent relationship lines. We can also separate the parent relationship line from unselected sibling relationship lines by adding a pixel margin. The horizontal separation is the pixels of space between columns of the tree, as well as the left side of the tree if folding is disabled. Tree item cells can be given pixels of margin space in all four directions. Item margin will also provide extra indentation to the left of tree items. Outline size is the font outline we are very familiar with by now. Scroll border and scroll speed are for altering the behavior of drag and drop scrolling only, not how the scroll bar naturally behaves, so we will not be using this. We can add horizontal pixel separation between the tree items and the vertical scroll bar, as well as vertical pixel separation from the horizontal scroll bar if there is one. And we can give the scroll bars margins as well. Vertical separation adds pixel separation between each tree item. Tree items can contain multiple buttons, and we can use this property to change the horizontal separation between the buttons. Icon max width can be used to limit the size of icons provided for tree items, and their height will automatically be adjusted to maintain their aspect ratio. I'll export another icon from my crew manager script and add it to the root of the tree at column one, so the player can clearly see that they're allowed to edit their captain's name. Running the game, the captain's name now has an icon to indicate to the player that it can be edited. Back in the theme editor in the colors section, there are font colors for default tree item text, disabled font text for when a tree item is editable or a checkbox, but is also disabled, the font outline color, the font color of selected tree items, and also the column title font colors. We can change the color of the guidelines here, and we can also change the color of the relationship lines and have different colors for the child relationship and parent relationship lines. Drop position color is the color used to indicate where dragged items will be dropped, and custom button font highlight is only used for custom buttons when the mouse is hovered over them. We won't be using either of these options. There are different font and font size options for the tree items and the column titles, but I'll continue to use my theme defaults for those. In the icon section, we can change how the expand and collapse arrows look, as well as checkboxes when they are checked or unchecked, enabled or disabled and indeterminate states, too. Useful for parent treat items containing lists of checkboxes as children. So the parent can display the indeterminate checkbox to indicate that some but not all of the children are checked. This behavior is not automatic though and must be scripted. The select arrow icon is drawn to indicate option buttons that display a pop up menu, and the updwn icon is used for spin boxes which edit numbers within a range. The player will be able to click the up arrow to increase or the down arrow to decrease the value. To create these different types of interactable tree items, I'll go to my populate crew member function. Then I'll call the function set cell mode on column two, which is the pay column and change its mode to check. If the value of pay is not empty, then I'll set its editable property to true. Otherwise, I'll set it to false. Now we can see that the tree items in the pay column are checkboxes and only enabled if the player has a crew member for that role. Cell mode icon will display only an icon with no text, and custom can be used to create custom buttons, but we won't be doing that. If we set it to range mode, then we can use it to either create an option button by providing a comma separated list of options as the text property or a numerical range which can be edited as a spinbox. We can configure the spin box range properties by calling set range config, providing the column, minimum value, maximum value, and step value, and set the current value by calling set range, providing the column and the value as a float. If the value of pay is empty, though, I'll change the cell mode to string and just display it as the empty uneditable text. Running the game, the player can change the pay rate of their crew members within ranges of one to nine gold per day with steps of one gold per click. This interaction will also emit the tree item edited signal. So I'll need to check if the item that was edited is the tree's root item before editing the captain's name. I'll also have to write some additional code to change the crew member's pay rate in the player's save data here. Without knowing the role or array index of the crew member whose pay was edited, there's not much I can do other than to iterate through all of them and check which one it was. So iterating through all of the tree items, I'll check if it's an array first. If it's not an array and is the item which was edited, then I'll set the pay of that role to the new range value, cast to an integer. If the role is an array, then I'll have to search through that array until I find the one which was edited, now giving me the array index, which I can use to set the pay of the crew member in that role and array index number. Back in the theme editor, in the style section, we can change the style box panel drawn behind the tree. I'll just use an empty style box to ignore it. As well as the focus style, which will be drawn over the entire tree when it has focus, which I will also remove by setting it to empty. We can change the style boxes for the title buttons normally when hovered and when pressed. I don't want my titles to act as buttons, so I'll also use empty style boxes for these. The only style box I want to use for my tree will be when a tree item is selected. So I'll set it to a style box flat, give it a shadowy color with rounded corners, and copy it into the style box for selected focus. The tree node can automatically include horizontal and vertical scrollbars, so we might also want to add those to the theme too. In the icons section, we can provide icons for increment and decrement in the normal highlight and pressed states. Keep in mind that with the vertical scrollbar, increment is the one which points down at the bottom. I do not want to include these buttons in my scroll bars, but I want to use them to define the clickable mouse area. So I'll add them as placeholder textures and give them a width of eight pixels, but no height. Therefore, nothing will be drawn by them. Then in the style section, I can set the scroll style box to a flat style box. Make it a very narrow black line by adding an invisible border on the left and right sides. I'll then copy this into the focus style as well. For the grabber, I'll make it the same color as my buttons with rounded corners and a shadow. Copying this into the other two styles, I'll then make them unique, set the color of the highlight lighter, and the pressed darker. All of these same options are also available for the horizontal scroll bar as well. We now have our tree node themed to match our game's aesthetic. In the next lesson, we'll allow the player to hire and fire crew members. I'll see you in the next lesson. 36. Scroll Container: Hello, friends. In the previous lesson, we explored the theming options available for the tree node. In this lesson, we'll allow the player to hire and fire crew members. I'll start by adding a button below the tree node which says fire and shrink it towards the center horizontally. Then connect its pressed signal to the crew manager script. We can request which item is selected from the tree node and store it in a variable. If nothing was selected, or if the selected item is the captain, which in my game represents the player themselves, then pressing the fire button shouldn't do anything. So if something was selected other than the captain, I'll get the role which was selected as the text in column zero of the selected tree item. Then split my logic based on whether the role allows multiple people, either erasing the role from the player's crew or checking if the size of the array of crew assigned to that role is not zero before removing the person at the same index as the array index of the selected tree item. This is only necessary because I still have the role represented as a blank entry, even if the player doesn't have anyone assigned to that role. After firing the crew member, I can just call on file reset to update the crew tree with the new information. Let's try it out. We can fire any of the crew members except the captain. Attempting to fire blank entries does nothing. In the save data script, I'll remove the initial crew except for the captain and initialize the arrays as empty. Next, I'll need to generate a list of people looking for work that the player can hire. So I'll add a grid container to the right side of this panel and a button below it that says hire shrink to the center horizontally. I'll have the grid container expand to fill the space both horizontally and vertically. Giving the grid container three columns, I'll add two label nodes and a texture rect as the column headers. Name them and populate them with role, name, and my coin icon. I'll have the roll and name labels expand to fill as much of the space as they can, collapsing the pay column to the end. I'll need to write a script which populates this grid with potential candidates. But first, I don't want to be restricted by the amount of space here. I'll need scroll bars like the tree has. The tree node includes scrolling automatically. But if I want my grid container to have scrolling, I'll need to add another node, a scroll container. The scroll container can only have one child node, which it will clip to fit inside its control rectangle and provide scroll bars if the child does not fit, which I will expand to fill the space provided. We can see the effects of the scroll container by duplicating the child nodes a bunch of times, so it no longer fits vertically and keep adding text until it no longer fits horizontally. Selecting the scroll container node, let's take a look at its properties in the inspector. Follow focus will tell the scroll container to automatically adjust the scroll position to keep the focus control node visible at all times. Both the horizontal and vertical scroll mode have multiple options. The default option auto only shows the scroll bar when it is needed, but we can change it to either always show or never show or disabled, we'll expand the scroll container to fit its child node in that dimension, removing the need to scroll at all. A dead zone can be added to touch scrolling to adjust its sensitivity. Expanding the scroll section, we can change the default scroll position either horizontally or vertically by pixel, as well as change the step increment or decrement, which will move the scroll position when the player clicks those buttons. I have removed the increment and decrement buttons from my scrollbar theme. Now I'm ready to populate this grid with prospective crew members waiting to be hired by the player. I'll center the text in my column headers to prevent them from being clipped by the scroll container. So I'll connect the hire buttons pressed signal to the crew manager script. First, I'll grab a reference to the grid container node. I'll also need to know all the possible roles that can be hired, which I'll store in an array, and a list of all the random people I generate, which I will also store in another array. Then after generating the crew tree, set the possible roles to be the array of keys from the tree items dictionary, removing captain and passenger from the potential roles which can be hired. I'll then call a different function to populate the higher grid. The first thing I'll do when populating the higher grid is clear its contents with another function. After that's done, I'll repeat a number of times, let's say 15 and append a new person to the array of people looking for work. This entry in the array will be a dictionary, containing a role, name, and a pay rate with the name and pay rate being label nodes, initializing them with their class name dot neu. The role will be a checkbox, which is another node that inherits from button. This way, the player can check the checkboxes of everyone they want to hire before clicking on the hire button to hire them all at once. All three of these nodes have a text property which needs to be populated. I'll just give them all random values, starting with the role being a random selection from possible roles. I'll pick a random name from a constant array of strings the same way I did the ship names, providing a large list of potential names in my script in a region tag that I can collapse to keep it from getting in the way. For their pay, I'll just use a random integer 1-9 cast to a string. All three nodes can be added to the higher grid as children. Clearing the grid is as simple as iterating through the array of people who are looking for work and calling Q free on all three of their dictionary entries, then clearing the array. If we run this now, the checkbox has not been themed, and it's inheriting its theme properties from button. So let's add checkbox to the game theme and make it look more appropriate. Starting in the style section, check boxes have all the same styles as buttons, which I will remove by setting them all to style box empty. Then in the icons section, along with the same icon inherited from button, there are checked and unchecked icons enabled and disabled, and also a set for radio buttons. As always, I'll leave the font and font size to use the default theme setting. The same font and icon color options are also available for the checkbox as they were for the button. Note that the icon modulation colors only apply to the icon property, not the checked or unchecked checkboxes. In the constant section, aligned to the largest style box we are familiar with by now, same as outline size and icon max width. Check vertical offset will offset the vertical position of the checkbox icons by a few pixels either up or down. Horizontal separation adds pixels of space between the check Box's icon and its text. If the checkbox has both a checkbox and an icon, the horizontal separation will be applied to both. I'll remove the icon from my checkbox. This looks much better. Back in the script, when the player clicks on the hire button, since my game has rules about not having multiple people in the same role, I'll create an array of everyone who was successfully hired and a string for the role. After iterating through the people who are looking for work, I'll then iterate through everyone who was hired and remove them from the array of people looking for work, then update the crew tree. Inside this for loop, I'll check if the person's role checkbox has been pressed, and if so, set role to be the text from that checkbox. In order to know if this person can be hired, the logic will differ if the role allows multiple people or not, which I know based on whether the tree item dictionary entry at the index of that role is an array. Starting with unique roles, if the player's save data crew dictionary doesn't have that role, then I can set the value of that role to be this person, creating a dictionary of their name and pay rate as an integer. If it is an array, though, I'll have to compare the number of people assigned to that role in the player's existing crew with the maximum number allowed. And if they still have room, then append this new person to the array. If either the player doesn't have room for more of this role or the unique role is already filled, then I'll continue to the next person who is looking for work. If this person was successfully hired, I'll free all of the nodes associated with them from the scenery and append them to the array of people who were hired. This will suffice for allowing the player to hire these crew members. Attempts to hire crew positions which are already filled or at capacity will be ignored. But I do think it's important to enforce the maximum crew capacity of the player's current ship. So I'll write a function which counts the player's current crew, returning an integer, iterating through every entry in the player's crew dictionary, if the entry is an array, adding the size of the array, otherwise adding one and returning the result. When clicking on the hire button, I'll add a variable for the maximum crew. And with each iteration through the people looking for work, I'll check if the player has reached their crew capacity and break out of the loop if they have. I'll need to retrieve this information from the player's current ship. So giving these tabs unique names, I'll grab a reference to the ship manager using add on Ready. And request to the player's crew limits, setting the maximum to be the first element of an array. The ship manager script then needs a function which returns a two integer array of the player's current ship crew limits, simply returning an array of two numbers filled with the minimum crew and maximum crew of their current ship. Now the player is prevented from hiring more crew than their current ship can hold. The player can now manage their crew from this crew tab with mechanics for hiring, adjusting pay rates, and firing crew members. In the next section we'll allow the player to set sail to another port. I'll see you in the next section. 37. Radio Button: Hello, friends. In the previous section, we allowed the player to manage their crew. In this section, we'll provide them with a map of different ports that they can sail between. Like the crew tab, I'll start by switching to the map tab, adding a script to it, call it the Map manager and save it in the manager scripts folder. Then export an icon as a texture two D and set it as this tab's icon by calling the set tab icon on this node's parent, the tab container, passing the icon as an argument, and populate the exported icon with a map icon. Switching back to two D view, I'll start by adding a vertical box container to this tab, containing a texture wrecked node, populated with the texture of a map of the Caribbean Sea, set to fit proportional to its width, keep its aspect ratio, and expand to fill the space vertically, and shrink to the center horizontally. On this map, I'll add a control node to use as a folder to hold all my ports and have it anchored to the full wreck of the map. Then I'll represent each port with check boxes, starting with Havana, Cuba, then Kingston Jamaica, Porto Prince Haiti, and Santo Domingo in the Dominican Republic. These checkboxes are naturally set to toggle mode, so the player can check or uncheck them, but I only want them to be able to have one checked at a time, meaning these should be radio buttons. We can create radio buttons by clicking on the drop down labeled button group and creating a new button group. We don't need to do anything with this button group other than save it as a project resource. Then assign all of the buttons to also use the same button group. Buttons in a button group will use the radio button icons instead of the checkbox icons, and only one button in a button group will be allowed to be toggled on at any time. I'll have the Havana Cuba button disabled as it will be the player's starting location. And thus, they will have three choices to select from for their first destination. I would like to customize how these buttons look without affecting other checkboxes in my project. So I'll add a new theme type variation to my theme, call it port, and set its base type to checkbox. If any port is disabled, whether it is checked or not, I'll set its icon to an anchor, since that will indicate that is where the player currently is. An unchecked radio button will be a sale icon, and a checked radio button will be a red X, indicating that is the player's selected destination. I'll also reduce the font size of these buttons so they don't take up as much space on the map. Selecting all of my port buttons in the scene tree, I'll switch them all to use the port theme type variation. Then reset their size properties to be as small as possible while containing their text. To keep my button text from overlapping, I'll have Kingston and Portal Prince reverse their layout to be from right to left, so the text appears on the left of the radio button icon. To ensure that the buttons remain in the correct positions, even if the map is resized for any reason, I'll set the buttons to anchor mode using custom anchors and position their anchor points to the center of their button icons. I'll then repeat this for each of my ports. Selecting one of the radio buttons, taking a look at its signals, it would not be a good design to connect each of these toggled signals to a different function in the script. That would make adding more ports very cumbersome. But note that the argument being passed by the signal is a Boolean named toggled on. In the map manager script, we can react to any button being toggled with a single function. Let's first declare a variable to hold the player's current port as an integer, which will naturally default to zero, and another for their destination. I'll give it a default value of negative one, which will mean that the player has not selected a destination. We can then write a function to set the destination, first accepting the toggled on parameter to match the argument passed by the toggled signal. Then adding another parameter, the port ID of the button which was pressed. If the button is being toggled on, then the destination will be the port ID. If the button is being toggled off and the port ID is the destination, then the destination has been removed by the player and should be set back to negative one. To connect the buttons toggled signal to this function, we need to expand the advanced section of the signal connection dialogue. Here we can add extra arguments to the signal. The type of argument we wish to add is an integer, and for Havana, I'll leave its value at zero. We can then go back to the list of nodes in the scene, select the map node, and pick a function to call as a reaction to this signal. The function being called must have parameters whose types match those emitted by the signal. Since this signal has toggled on as a boolean and we are binding an extra integer, set destination must have a Boolean parameter followed by an integer parameter. We can then repeat this process for every radio button, changing the value of the bound integer argument to a different value. For simplicity, I'll use each button's child index number in the scene tree. Back in two D view, I'll also add a margin container to the bottom of the map anchored to the bottom wide and put inside this margin container a label to indicate the distance from the player's current port to their destination measured in days of sailing, and a button to embark on their journey shrink to the end of the margin container. I'll then note the position and size of the margin container and add a color wrecked node as it's sibling with the same anchors, size, and position, colored a semi transparent black. The map image is very busy, and adding this tint will just make the label and button more noticeable. I'll have the label invisible and the button disabled by default. Back in the script, I'll grab references to the ship manager and these two nodes using add on ready. Then also define a constant array to hold the distances between each of my ports measured in nautical miles. Creating a matrix of integers, the reflexive positions will be if the current port and destination are the same, so the distance would be zero. And I'll just populate the matrix with some realistic values for the sailing distance in nautical miles between each of these cities. The symmetric values should be the same, assuming you can sail along the same path in both directions. Then after setting the destination, if the destination is either negative one or it is the same value as the current port, then I'll hide the distance label and disable the embark button. Otherwise, I'll set the text of the distance label to the correct numbers of days sailing, then enable the embark button. To access the player's save data, I'll need a reference to the file node two. To calculate the number of days of sailing, I'll use the sealing function, which rounds a float up to the nearest integer and divide the sailing distance in nautical miles by the player's current ship's speed. For this to work, I'll have to add this get speed function to the ship manager script. So returning afloat, I'll return the ships indexed by the player's current ship ID number. And I didn't include a speed value for my ship resource, so I'll open the ship resource script and export another variable for the ship's speed. Then give it a default value of 100 nautical miles per day. I can then give each ship a different speed if I want to, and the player will be able to make these journeys in fewer days of sailing. I would also like the player to be able to toggle off their destination by clicking on it again, which is not allowed by default. If we double click on the button group in the project resources, we can toggle on the Allow press property to accommodate this behavior. Let's try it out. The player can click on any of these radio buttons on the map, selecting it as their destination, and the number of days will update, and the embark button enables. If we press the button which was already toggled on, we can toggle it off too. And now there is no destination, so the number of days is hidden and the embark button disables. We now have a map with several ports the player can choose between as their destination. And the next lesson will allow the player to control the ship's anchor. I'll see you in the next lesson. 38. Check Button: Hello, friends. In the previous lesson, we allowed the player to select a destination from a map. In this lesson, we'll let them control the ship's anchor. Below my map, the vertical box container will hold a horizontal box container, and that will hold a check button which will be used to control the ship's anchor. Like the check box, the check button also inherits most of its properties from button, including its theme. Selecting the check button, let's take a look at its properties in the inspector. I'll start by removing the style theme properties inherited from button by checking the flat checkbox. We can add text to the check button and also an icon. I'll use the text to indicate to the player that this is used to control the ship's anchor. Like other buttons, we can adjust how the text and icon will be drawn relative to the check button's position and how much space is available. Next, let's select the root node so we can see the theme editor and add Check button to the theme. The check button inherits many of the properties we are familiar with from button like font and icon colors. In the constant section, we can align everything to the largest style box by setting it to anything other than zero. Add some vertical offset to the check button, horizontal separation between the icon, check button, and text. Set a cap for the maximum width of the icon. And an outline size for the text, all of which we've done before with other control nodes. As always, I'll leave the font and font size as the theme defaults. In the icons section, we can change how the check button looks when it's checked, unchecked, or disabled, as well as provide an icon which will be used for all check buttons across the project unless overridden by the individual check buttons icon property. And finally, in the style section, we have all the same styles which were inherited from button, which I will remove by replacing them all with style box empty. Selecting the check button node, I'll have my button pressed by default. I'll rename my anchor node and the ports folder before I use them in my script. We'll need to respond to the player clicking on the check button by connecting its signal to a script. Instead of the pressed signal we would normally use for button, we'll need to know whether the check button is being toggled on or off, so we'll connect the toggled signal instead, which passes the toggled state of the check button as an argument. Connecting this to my MAP manager script, I can easily react to the signal based on whether or not the check button is being toggled on or off. Switching to the save data script, I'll add the is anchored variable as an exported boolean and default it to true. Let's also connect the file reset signal to the script so we can initialize this tab with the appropriate information from the player's save data. I'll need references to my anchor node and the array of child nodes under my ports folder. All I need to do for the anchor is set its button pressed property to match the new save data. I'll also need to iterate through all of my ports and set their disabled property to match whether or not this is the player's current port and set none of them to be pressed. The check button reacts to being hovered and clicking on it weighs the anchor. Clicking on it, again, drops the anchor. We now have a check button controlling the ship's anchor. In the next lesson, we'll allow the player to control the sails. I'll see you in the next lesson. 39. Vertical Slider: Hello, friends. In the previous lesson, we added a check button that controls the ship's anchor. In this lesson, we'll use a slider to control the ship's sales. I'll start by adding a vertical slider to the horizontal box container below my map. Unlike the check button, the vertical slider doesn't have text or icon properties, so I'll also add a label node, which I will populate with the word sales, so it matches the style of my anchor control. Let's select the vertical slider node and take a look at its properties in the inspector. The editable checkbox will determine whether or not the player can change the value of this slider, and scrollable will change whether they can do so using the mouse wheel while hovering the mouse over the slider. Tick count will add horizontal lines to the slider to indicate segments like a thermometer. I'll have three ticks on my slider, and the ticks on borders checkbox will change whether the first and last tick are drawn for the maximum and minimum values. The range properties are also important for this node, allowing us to specify what the minimum and maximum values of the slider are, as well as how much the slider can change in its value with each step. Since my slider is being used to control the ship sales, it will only have three possible values, zero, half or one for full sale. These range properties are also used by other nodes, like the scroll bar, which uses the page property, but sliders don't use it, so we can ignore it. Value is the current value of the slider, so we can change what it is by default, and it will automatically snap to the step values between our minimum and maximum. Exponential edit will skew the value to be exponential instead of linear, but only if the minimum value is greater than zero. Rounded will always round the value to the nearest integer. Allow greater or lesser will allow the value to go past the minimum and maximum values. But the slider will not indicate that it has. We'll learn more about these properties in the next lesson. Let's select the root node so we can see the theme editor and add the vertical slider to our theme. All of the theme properties for the vertical slider are also available for horizontal sliders. Since the slider has no text or icons, it has no colors, font or font size. So we'll start in the constant section. If center grabber is anything other than zero, then the grabber will be positioned within the vertical slider space based only on its center point rather than the entire image dimensions. And grabber offset can add some horizontal displacement of the grabber to the left or right. On a horizontal slider, this same property will be a vertical offset instead. In the icon section, we can provide different images for the grabber in its normal, disabled and highlighted states, as well as the image used to draw tick markers along the slider. I'll use a rope naught as my grabber and a cross beam for my tick. In the style section, we can change how the slider is drawn, as well as the filled area below the grabber normally and when it is highlighted. For a horizontal slider, this would be the area to the left of the grabber. I'll have my slider be a thin brown line representing a mast, and the fill will be a style box texture which I will populate with a picture of a sail and expand it both left and right, so it looks like an actual ship sail. I'll then copy this, make it unique, and change the texture for when it is highlighted. To give this sale more room, I'll override my theme setting for the horizontal box container to have 21 pixels of space between child nodes. Selecting the slider node, we need to react to the player using the slider by connecting one of its signals to a script. If you want to react to the value being changed only after the player has released the mouse button and stopped dragging, then connecting the drag ended signal would be appropriate. But I want to react to the value being changed even while the player is still holding down the mouse button. So I'll connect the value change signal instead and connect it to my map manager script. I'll store the value of the sales slider in the player's save data. Grabbing a reference to the sale node, I can also set its value when the save data is reset. I'll also need to add this variable to the save data script too. Let's try it out. The vertical slider reacts to being hovered and we can change its value using the mouse wheel. We can also click and drag the grabber to change its value. We now have a vertical slider controlling the ship's sails. In the next lesson, we'll let the player embark towards their destination. I'll see you in the next lesson. 40. 6 4 Progress Bar: Hello, friends. In the previous lesson, we added a vertical slider to control the ship's sails. In this lesson, we'll use a progress bar to indicate how far along the ship is in its voyage. I'll add a progress bar node to the horizontal box container under my map and have it expand horizontally to fill the space, then position it between the anchor and sail controls. Let's take a look at its properties in the inspector. We can change the direction of the fill and change the value to see the effect of this. We can also toggle the percentage display on or off, and the indeterminate toggle will show us that something is progressing without any indication of how far it has progressed, which we can see if we also check the preview toggle under Editor. All of the same range properties are here and work the same way as the slider. But if we allow the value to be lesser than the minimum or greater than the maximum, the value will be indicated by the percentage, but not by the fill of the progress bar. Let's select the root node so we can see the theme editor and add the progress bar to our theme. Like any node that has text, we can change the font and font outline colors and set the outline size in the constant section. As always, I'll leave the font and font size as the theme defaults, and the progress bar has no icons to change. In the style section, we can change both the background and fill of the progress bar, which I will populate with flat style boxes. I'll use the same blue background color as my panel, round the corners and add some content margins to make it a bit bigger. Then copy this for the fill, make it unique, and change its color to green. To embark on our journey, we'll need to connect the embark button's pressed signal to the script. At the top of the script, I'll need a reference to my progress bar. I'll also declare a boolean variable for whether or not the player is currently sailing, the amount of time that has elapsed as a float and the number of days at sea as an integer. To start sailing, I'll set the boolean variable to true. Time elapsed and days at sea to zero. I'll also iterate through the rest of the tabs in my tab container and disable them, since these should only be accessible at a port, not while sailing. I'll then write another function to arrive at a port, accepting a port ID as an integer. After setting is sailing to false, I'll enable the port that the player sailed from, change the current port to the destination, and disable the new current port, then set destination to negative one, meaning there is no destination. I can then reactivate the other tabs since the player is now in a port. Then set the maximum value of the progress bar to the distance between the current port and the destination and disable the embark button. To sail from the current port to the destination, I'll use the process function to gradually fill the progress bar over time. If the player is not sailing or is anchored or the sails are set to zero, then no progress will be made and I'll just return. Otherwise, I'll increase time elapsed by Delta, and if time elapsed is greater than the number of days at sea, then I'll increment days at sea and pay the crew their daily earnings. I'll next add to the progress bar's value the player's current ship's speed multiplied by their sale value multiplied by Delta. This will result in the ship traveling its speed in nautical miles every 1 second and with 1 second representing one day. If the value of the progress bar reaches its maximum value, then they have arrived at their destination. I'll need to write this payout function in my crew manager script. Grabbing a reference to the crew manager using add on Ready, then switching to the crew manager script. First, declaring a variable for the total payout as an integer, I'll iterate through the player's crew. If the crew role is not an array, I'll add the pay for that role to the total pay. If it is an array, I'll iterate again through each person in that role and add all of their pay to the total. I'll need a reference to the gold resource node so this script can request the player spend this total amount. And finally, spend the amount of gold necessary to pay all crew members. If the player does not have enough money to pay out all of their crew, this would be a good time to end the game with the game over. I added an S to my variables name here. I'll need to fix that. When clicking on a port check box, I'll first check if the player is currently sailing, and if so, press the button that belongs to the destination port and return. This will prevent the player from changing their destination while sailing. If they aren't sailing and are setting their destination, I'll set the value of the progress bar to zero. Let's try it out. The player can select a destination and click the embark button to start sailing, but they won't make any progress toward their destination if they are anchored or if their sails are not unfurled. The ship's speed can be adjusted with the sail at half or full. Upon arriving at their destination, the tabs and checkboxes can be interacted with again. Once at a port, the player can recruit crew members, and sailing to another port, the crews pay will be deducted for every day at sea. We now have a progress bar showing how far the ship is from its destination. And that covers almost all of Godo's control nodes. The few mechanics left to complete this game would only use more of the same nodes we have already used elsewhere in the project. Now you should be ready to complete your project. If you've been following along with this project, see if you can complete this game's market tap and add a game over screen. Good luck.