How to program in Godot - GDScript Tutorial
Zusammenfassung
TLDRThis video serves as a detailed introduction to GDScript, focusing on the programming language designed for the Godot game engine. It highlights the syntax, fundamental programming concepts such as variables, functions, conditional statements, input handling, and more. Beginners are guided through creating scripts and making interactive elements in Godot, while seasoned developers receive insights into GDScript's specific functionalities, such as signals and inheritance. The video is structured in clearly defined sections, encouraging viewers to learn at their own pace, with practical coding examples to reinforce learning.
Mitbringsel
- 🎮 GDScript is tailored for Godot.
- 💻 Syntax resembles Python, making it beginner-friendly.
- 🔍 Key programming concepts include variables, functions, and conditions.
- 📥 Input handling is essential for user interaction.
- 📦 Functions are reusable code blocks.
- 📊 Dictionaries allow for organized data storage.
- 📏 Signals facilitate communication between nodes.
- 📚 Clear structure aids learning and reference.
- 🌐 Inheritance enables class hierarchy.
- 🛠️ Use CodeCrafters for hands-on coding practice.
Zeitleiste
- 00:00:00 - 00:05:00
Introduction to GDScript, a high-level, object-oriented, imperative programming language with a syntax similar to Python, designed specifically for Godot. The video aims to cover key concepts from basic to abstract for beginners and experienced programmers alike. Emphasis on learning by segments and returning as needed.
- 00:05:00 - 00:10:00
The speaker begins the practical lesson by creating a simple Godot project, adding a Main node, and introducing script functionality that prints 'Hello, world!' in the console. It explains the 'ready' function and the importance of proper indentation, repetition of code errors, and the basics of running scripts in Godot.
- 00:10:00 - 00:15:00
Modifying nodes is explained using a Label. The speaker modifies the Label message displayed in the game and changes its color using the 'modulate' property. This section illustrates live node interaction within the game context and setting properties through code.
- 00:15:00 - 00:20:00
Input handling begins with setting up an action for the spacebar to change the label's color. Project settings allow binding keys to custom actions. GDScript detects input through the built-in '_input' function, showcasing real-time feedback for user actions and tying them to the game logic.
- 00:20:00 - 00:25:00
Introduction to variables, their creation and manipulation, using examples to manage player health. Variables can hold various data types, including integers and calculations. The speaker also discusses the importance of allowing for logical gameplay consequences when managing these data structures.
- 00:25:00 - 00:30:00
Explained the function and significance of if-statements in controlling game logic based on variable conditions. Introduces the concept of chaining if-statements for more complex decision-making scenarios, highlighting this vital control structure's role in establishing game rules and responses.
- 00:30:00 - 00:35:00
Discussion of comments in GDScript to improve code readability and organization. It covers how to add comments and best practices for commenting out sections of code without affecting execution, including an example of avoiding empty functions using 'pass'.
- 00:35:00 - 00:40:00
Explains variable scope in GDScript, emphasizing where variables should be declared for accessibility. It touches on the flexibility of GDScript to create dynamic typed variables and the importance of knowing the expected data type for successful gameplay mechanics.
- 00:40:00 - 00:45:00
Overview of basic data types in GDScript—Boolean, Integer, Float, and String—along with examples of casting between types, storing coordinates in vectors, this knowledge empowering developers to manage data effectively within the game logic.
- 00:45:00 - 00:50:00
The section discusses functions as key components for organization in GDScript, showcasing creating functions with inputs and outputs. The creator emphasizes how functions can manipulate game data and improve code understandability, encouraging structuring code to enhance gameplay efficiency.
- 00:50:00 - 00:58:09
Demonstration of how to retrieve random numbers using GDScript functions for gameplay mechanics, incorporating randomness into player experience with algorithms that vary outputs based on weighted probabilities, enhancing game unpredictability.
Mind Map
Video-Fragen und Antworten
What is GDScript?
GDScript is a high-level, object-oriented, imperative programming language with a syntax similar to Python, specifically designed for use with the Godot game engine.
Can I learn GDScript as a beginner?
Absolutely! The video is tailored for beginners and provides foundational knowledge in GDScript programming.
What topics are covered in this video?
The video covers variables, input handling, conditions, dictionaries, functions, signals, arrays, and more.
Is this video suitable for seasoned programmers?
Yes, seasoned programmers transitioning to GDScript will find it beneficial as it provides an overview of tools available in GDScript.
What is the significance of variables in GDScript?
Variables are essential for holding information such as player stats, health, and other dynamic data.
How does input handling work in GDScript?
Input handling allows you to make the game responsive to user actions, utilizing a system of input actions.
What are signals in GDScript?
Signals are messages that nodes send to notify other nodes when a particular event occurs.
What are functions used for in GDScript?
Functions bundle code into reusable packages and can accept input parameters and return values.
How does inheritance work in GDScript?
Inheritance allows one class to inherit properties and methods from another, facilitating better code organization.
What is the purpose of dictionaries in GDScript?
Dictionaries store key-value pairs, which are useful for quick access to associated data.
Weitere Video-Zusammenfassungen anzeigen
Marcus Aurelius - The Power of INDIFFERENCE
Marcus Aurelius - The Power of INDIFFERENCE
Buddha - Be Aware, Become Free
Stanford Seminar - Driving Exploratory Visualization through Perception & Cognition
LAWRENCE DA ARÁBIA é o maior filme da história (Análise)
BUILD THE BEST HARMONY UNIT! | Tribbie Guide - Best Relics, Lightcones & Teams | Honkai: Star Rail
- 00:00:00GDScript
- 00:00:01GDScript: A high-level,
- 00:00:02GDScript: A high-level, Object-oriented,
- 00:00:02GDScript: A high-level, Object-oriented, Imperative,
- 00:00:03GDScript: A high-level, Object-oriented, Imperative, and Gradually typed programming language
- 00:00:05with syntax a lot like Python,
- 00:00:07Built specifically for Godot.
- 00:00:10I know that sounds like a lot, but today we are going to learn it.
- 00:00:13While it's of course impossible to cover everything an entire programming language has to offer in just one video...
- 00:00:19...We'll certainly try!
- 00:00:21In fact, we will cover everything from Variables and Conditions...
- 00:00:23...to getting Input,
- 00:00:25...to getting Input, Inheritance,
- 00:00:26...to getting Input, Inheritance, Dictionaries,
- 00:00:26...to getting Input, Inheritance, Dictionaries, Signals,
- 00:00:27...to getting Input, Inheritance, Dictionaries, Signals, and the list goes on...
- 00:00:29And who knows? Perhaps we'll even figure out what "GD" stands for.
- 00:00:33Gandalf Script?
- 00:00:35Green Day Script?
- 00:00:37Gadolinium!?
- 00:00:38We might never know!
- 00:00:39We'll start with the things that I consider to be the most important for beginners,
- 00:00:43and then move on to more abstract programming concepts as we progress.
- 00:00:47It's not as much a follow-along as it is an overview.
- 00:00:50So if you're a beginner: lean back, and know that you don't have to understand everything the first time.
- 00:00:55Also, it's totally okay to stop the video and try some of these things out.
- 00:00:58You can always come back to it at a later time.
- 00:01:01We've made sure to split everything into clearly named sections,
- 00:01:04so hopefully you'll be able to use this as a reference manual going forward.
- 00:01:08And if you're a seasoned programmer transitioning to GDScript;
- 00:01:11this video should provide you with a nice overview of the tools at your disposal, and how to approach doing different things.
- 00:01:18Keep in mind that this is a video on GDScript, not an introduction to Godot.
- 00:01:22So if you've never opened up Godot before, I suggest you begin with our video on making your very first game in Godot.
- 00:01:27Alright, without further ado, let's learn GDScript.
- 00:01:30But first!
- 00:01:31This video is sponsored by CodeCrafters.
- 00:01:33If you've been writing software for a while and are looking to take your skills to a whole new level: CodeCrafters is the place to be.
- 00:01:40CodeCrafters allows you to practice writing complex software by challenging yourself with real-world projects.
- 00:01:46You get to build your own git implementation, your own HTTP server, or even your own BitTorrent client.
- 00:01:52Along the way, you will practice advanced system design and gain a greater understanding of lower level programming concepts,
- 00:01:57such as Networking, OS Interaction, and Performance.
- 00:02:01And when you're done, you'll have a really impressive project to add to your portfolio.
- 00:02:05On top of this, you will get to try new languages like Python, C, C++, and many, many more.
- 00:02:11Also new challenges are constantly being added.
- 00:02:13I for one voted for the compiler challenge to be next.
- 00:02:16So become a better software engineer today by signing up for CodeCrafters.
- 00:02:20Use the link below to get one week free and 40% OFF on all membership plans.
- 00:02:25Hello, world!
- 00:02:26So here we are in an empty Godot project.
- 00:02:28I t ' s⠀r e a l l y⠀s p a c i o u s⠀i n⠀h e r e . . .
- 00:02:30As it is custom, let's begin by making a script and adding some code to print a message.
- 00:02:36To do that, we need a test scene so let's go and hit the [+].
- 00:02:39I'll add in a node, rename it to "Main", and let's save this as a scene.
- 00:02:45So I'll hit [Control] + [S], hit "Save".
- 00:02:47Let's add a script by pressing the scroll.
- 00:02:49We'll use the default template, and the name of the script should be "main" as well with a non-capital "m".
- 00:02:55This creates a script with two functions.
- 00:02:57We'll start by focusing on the top one named "ready".
- 00:03:00Think of this as the start of our game.
- 00:03:03As it says here: This function is called when the node enters the scene for the first time, which happens right when we play our game.
- 00:03:10So we can put code in here that we want to happen immediately.
- 00:03:14I'll get rid of the other function for now.
- 00:03:16You can zoom in by holding down [Control] while scrolling.
- 00:03:19Now, right now the only thing in our ready function is the "pass" keyword.
- 00:03:22"pass" basically means do nothing, and it will appear in functions we haven't filled out yet.
- 00:03:27So let's replace it with some code that prints our message.
- 00:03:30We'll write "print()". And inside the parenthesis, we'll put our message in quotation marks: "Hello, world!".
- 00:03:38We can then run the game by pressing this button here or hitting [F5].
- 00:03:42It's gonna ask us which scene we are going to run. We'll hit "Select Current".
- 00:03:46And while our game isn't very exciting yet, we can now see that the console prints our message.
- 00:03:51The Output window here or the Console is where it will display prints and it's also where we check for errors.
- 00:03:57Now to stop our game from running, we'll press the stop button here or press [F8].
- 00:04:01Syntax
- 00:04:03Now, this might have worked for you and it might not.
- 00:04:07That's because there are a few things to be aware of when writing GDScript.
- 00:04:11The first is that statements like print here terminate with a new line.
- 00:04:15There's no need to add a semicolon at the end like in many C-based languages.
- 00:04:20And GDScript, similar to a language like Python, uses indentation to determine the structure of your code.
- 00:04:26This means that you use tabs to tell GDScript where your code belongs.
- 00:04:30In our example here: I'm using a tab to tell Godot that our print line belongs to the ready function.
- 00:04:36If I delete this tab, I get an error.
- 00:04:38Also, GDScript is case-sensitive.
- 00:04:41So if I write "print" with a capital "P", I will also get an error.
- 00:04:45So just a couple of things to look out for there.
- 00:04:48Now printing to the console is cool, or I mean, maybe not as cool as backflips, but well, I can't teach you to do those.
- 00:04:54But I can teach you how to display text in the game window instead.
- 00:04:57To do that, we'll need to modify a node through script.
- 00:05:00Modifying nodes 1.0
- 00:05:03First, we need a node to modify. Let's use a Label.
- 00:05:06So I'll add a new node, choose "Label". I will zoom in. Center it on the screen.
- 00:05:12Make some more room. Let's add a default text: "THIS IS A LABEL". I will center it, and overwrite the font size.
- 00:05:22Now, to edit the text of this label through script, we need two things:
- 00:05:25We need a reference to the label, and we need to access the property in the label that we want to change.
- 00:05:31In this case, the Text.
- 00:05:33If we hover over Text, we can see the name of the property through script, which is "text" with a non-capital "t".
- 00:05:39So in our main script, we'll first get a reference, and we can actually do this by simply taking the label and dragging it into our "ready" function.
- 00:05:46Then we can access the property by going ".", and we'll type in "text" and set it equal to "Hello, world!".
- 00:05:53And now we play, our label changes to "Hello, world!".
- 00:05:57And we can use this exact method to change other things as well. Such as the color of the text.
- 00:06:02In fact, there's a property called "modulate" which allows us to tint sprites and other UI elements.
- 00:06:07In the inspector, you can find it all the way down here under visibility. Let's change it to green.
- 00:06:12So again, we'll click and drag to create a reference to the label, write ".modulate", and set it equal to "Color.".
- 00:06:19Here we have a bunch of different colors. We'll choose "GREEN".
- 00:06:22And voila, our label is now green.
- 00:06:24So this is how we can make changes to nodes when our game is running.
- 00:06:28But right not much is happening during our game.
- 00:06:30So let's see how we can use Input to make our game respond to our actions.
- 00:06:34Input
- 00:06:35Let's say we want to turn this label red when we press the space bar.
- 00:06:39To do this, we first need to set up an input action.
- 00:06:41We'll go "Project", "Project Settings", "Input Map".
- 00:06:45Here we can add actions. Actions allow us to bind keys to something that we want to happen.
- 00:06:50Common actions would be shoot, jump, or so on.
- 00:06:54In our case, we want to change a color.
- 00:06:56I'll just go ahead and call it "my_action" so we can use it later for other things.
- 00:07:00Now let's hit "Add", and here is our action.
- 00:07:02Let's find the key to it by pressing "+", It's now listening for input, I'm gonna press this space bar and hit "OK".
- 00:07:08And now, our spacebar is bound to "my_action".
- 00:07:12Then in our script, we need to create an input function.
- 00:07:15We'll right "func" for function, "_input", and let's hit enter to autocomplete.
- 00:07:21Now just like "ready", this is one of the built-in functions of Godot.
- 00:07:25But instead of being called at the start of the game, it runs every time the game receives any input, such as when we press a button.
- 00:07:32"event" is what we call the information about what triggered the function.
- 00:07:35Was it a movement of the mouse or the press of a key?
- 00:07:38We need to check if the event that triggered the input was our action being pressed.
- 00:07:42So we'll write "if event.is_action_pressed()"... As you can see, our action shows up here: "my_action".
- 00:07:51Then we'll put in a colon, and if we press "my_action", well then we'll then change the color.
- 00:07:56So we'll drag in the label, write ".modulate", and set it equal to "Color.RED".
- 00:08:02And we can just as easily check if the action was released.
- 00:08:05We'll go "if event.is_action_released()" this time, "my_action". Well then we'll take the exact same line, and change it back to green.
- 00:08:14So now when we play and I press the space bar, it turns red. And when I release it, it turns back to green.
- 00:08:20That's right, we made a party...
- 00:08:23That's right, we made a party...A DISCO PARTY!
- 00:08:24That's right, we made a party...A DISCO PARTY!
- 00:08:25That's right, we made a party...A DISCO PARTY!
- 00:08:25That's right, we made a party...A DISCO PARTY!
- 00:08:26That's right, we made a party...A DISCO PARTY!
- 00:08:26That's right, we made a party...A DISCO PARTY!
- 00:08:26That's right, we made a party...A DISCO PARTY!
- 00:08:27That's right, we made a party...A DISCO PARTY!
- 00:08:27That's right, we made a party...A DISCO PARTY!
- 00:08:28Now this is just one out of many ways to handle input in Godot,
- 00:08:31but since we're focusing on the GDScript language, I'm not going to go further into it here.
- 00:08:35I'll make sure to provide a link to where you can learn more about input if you're interested.
- 00:08:38Variables 1.0
- 00:08:41Variables are essentially containers that hold information.
- 00:08:44In the case of a player character, we could use variables for information like the player's name, health, damage, and perhaps if the player is alive or not.
- 00:08:53Let's make a "health" variable.
- 00:08:55At the top here, I'll write "var" for "variable", the name of our variable, and then we'll set it equal to a default value.
- 00:09:02Now we can print this variable inside our "_ready" function, we write "print(health)",
- 00:09:07And if we play, you can see that it now prints "100".
- 00:09:10Now we can easily change our variable.
- 00:09:13So before printing, let's assign a new number: "health = 40".
- 00:09:18We can also assign a calculation so "health = 20 + 30". We'll set our health to 50.
- 00:09:25We can also add "health += " and then "20", we'll add 20 to our health.
- 00:09:32And subtract "health -=".
- 00:09:36To multiply, we do " *= ".
- 00:09:38And to divide, we do " /= ".
- 00:09:41So if we run this now, it will do all of these calculations from top to bottom and then print out the results.
- 00:09:47Which turns out to be a 120.
- 00:09:50Let's look at an example where our player takes some damage and we want to subtract the damage from our health.
- 00:09:55Let's try decreasing our "health" variable whenever we press the action we made earlier.
- 00:10:00So, in the input function, we'll check if the action was pressed...
- 00:10:06and then we'll subtract from our health, and we can print the result.
- 00:10:11So now every time I press the space bar bound to "my_action", we can see that I reduced the health.
- 00:10:17Really cool.
- 00:10:18But, as you can see, we can currently reduce our health below zero, and nothing happens when we do so.
- 00:10:24If we were making a game, we'd probably want our player to die and the game to restart.
- 00:10:27Luckily, we can make our game react to variables by using IF statements.
- 00:10:32If-statements
- 00:10:33If-statements (or conditionals)
- 00:10:34IF-statements are the glue that holds all of our logic together.
- 00:10:38We've actually already used IF-statements to check if our action was pressed.
- 00:10:42But they can do so much more. Yet, they are really simple.
- 00:10:46An IF-statement is just a condition that needs to be met in order for the code inside to be run.
- 00:10:51Let's add an IF-statement here to check if our health reaches zero.
- 00:10:55So I'll make some room, write "if health = 0", we died. else, we print: "YOU ARE HEALTHY!".
- 00:12:00And even cooler, we can chain together IF-statements using "else-if" or "elif" for short.
- 00:12:06So we check if our health reaches 0, and if not: else if/elif, our health goes below 50, well then we'll print that we are injured.
- 00:12:19And if neither of these two are true, we'll go to else and say that we are healthy.
- 00:12:23And if we play, we can see that at 80, we are healthy. At 60, we are still healthy, then we are injured, and at 0, we die.
- 00:12:30Very cool.
- 00:12:31Note that the IF-statement we made here is nested under another IF-statement.
- 00:12:36You can easily layer IF-statements to get more and more specific functionality.
- 00:12:40However, try not to overdo it. You can quickly get lost in the ifs.
- 00:12:45Actually, a lot of people don't know this but that's why programmers and philosophers have so much in common.
- 00:12:50We both tend to spend a lot of time with the ifs and the what ifs.
- 00:12:54So if you get frustrated along the way, remember that
- 00:12:57So if you get frustrated along the way, remember that so was Play-Doh.
- 00:12:58ˢᵐᶦʳᵏˢ
- 00:12:59Anyway, that's it for IF-statements.
- 00:13:01Comments
- 00:13:02We use comments to help explain and remember the what and the why of our code.
- 00:13:06Adding a comment is simple and can be above or after a line.
- 00:13:11So if we add some code, we can make a comment above the line, and this is a comment about the code that follows.
- 00:13:19Or we could make one right after, and this is a comment about this line only.
- 00:13:24We can also use comments to temporarily remove parts of our code from being executed.
- 00:13:28Here we simply put a hashtag (#) in front of the code,
- 00:13:31and it's good practice to emit the space here to indicate that what follows is code.
- 00:13:35We can also select multiple lines of code, right click, and toggle comment.
- 00:13:39Just remember that you can't have a completely empty function.
- 00:13:42We need to add the "pass" keyword to avoid an error.
- 00:13:45Variables 2.0
- 00:13:47When creating or declaring a variable, we need to think about where we do so.
- 00:13:52If we declare a variable inside of an IF-statement for example, we can only use that variable inside of the IF-statement.
- 00:13:59This is called scope, and it's something that beginners often make mistakes with.
- 00:14:03If we want to use a variable in many different places in our script, we make sure to put it at the top, outside of any functions.
- 00:14:10So this is a script wide variable. This can be accessed anywhere in your script.
- 00:14:15If you were to declare it inside of a function, you would only be able to use it in that function.
- 00:14:21So this is a variable we can only use in "_ready()".
- 00:14:24Now, a really cool thing about variables in GDScript is that you can declare them without thinking about what type of data is inside them.
- 00:14:31Here's a variable that is either true or false, and here's a variable that stores a number.
- 00:14:36As you can see, they're created in the exact same way.
- 00:14:39In fact, we can even change the type of data that they hold just by reassigning them.
- 00:14:43So we can actually set our "coolness" to true and this runs completely fine.
- 00:14:48That being said, we still need to be aware of what type of data we're working with.
- 00:14:53Because some parts of our game will expect a specific type of data...
- 00:14:56...And we'll get an error if we try to use another type without converting.
- 00:15:01Now in GDScript, we have the four classic data types:
- 00:15:04Boolean (or bool) for true and false,
- 00:15:07Integer (or int) for whole numbers,
- 00:15:10Float for numbers with decimals,
- 00:15:12and String for text.
- 00:15:14Converting from one type to another is called "Casting".
- 00:15:18For example, we can cast an integer into a string.
- 00:15:21So if we have a number that is equal to 42, and we have some text which is a string,
- 00:15:28well then we can add the number to this text,
- 00:15:31but we convert into a string by writing "str (For String)" and then the variable inside of the parentheses.
- 00:15:38And if we print this, it works fine.
- 00:15:41We can do a similar thing to say, convert a float into an integer.
- 00:15:45So if we have a variable called "pi" that is equal to 3.14, then we can convert this into an integer by going "int(pi)".
- 00:15:53And let's print this, and it prints "3", a whole number.
- 00:15:57Note, however, that this automatically truncates the number, it does not round. It simply gets rid of the decimals.
- 00:16:04So 3.9 also results in 3.
- 00:16:07Some other data structures that are really common in GDScript are Vector 2 and 3.
- 00:16:11Vector2 stores two floats: x and y. And Vector3 stores three floats: x, y, and z.
- 00:16:20These are most commonly used for positions.
- 00:16:22So I can create a variable called "position" with some coordinates. I can then modify one of the components, so "position.x += 2" adds 2 to the x component...
- 00:16:33...And then we can print the whole thing.
- 00:16:35And this gives a Vector3 of 5, -10, 5.
- 00:16:39Now, by default, GDScript is what we call dynamically typed. This means we don't define what type of data a variable can hold when we declare it, which we have been using so far.
- 00:16:49This makes it fast to create variables and flexible because we can reassign data of another type at will.
- 00:16:54However, it is more prone to error, and less performant than static typing.
- 00:16:59Luckily, GDScript allows us to statically type any variables we want.
- 00:17:03We can pick and choose totally based on preference.
- 00:17:07To statically type a variable, we simply add the type when declaring it.
- 00:17:11So here if we have a "damage" variable, we can specify the type, and then set the value.
- 00:17:16So now this variable always stay an integer.
- 00:17:19We can even have Godot automatically determine the data type by simply writing " := " and then the default value.
- 00:17:26This is called "invert typing" and the result is exactly the same. The type is still static.
- 00:17:31Godot simply recognizes that 15 is a whole number and sets the variable to a type of int.
- 00:17:36This also means that the variable cannot change to another type.
- 00:17:39If we try to set it to a string, we get an error.
- 00:17:43Now adding "@export " in front of a variable will allow us to set it using the inspector.
- 00:17:48The value that we set in the script will just be the default value.
- 00:17:52So if we save this, and select our node, we can set a value in the inspector.
- 00:17:56If we print our "damage", we can see that this updates in our game.
- 00:18:00And we can always press the circular arrow to revert back to the default.
- 00:18:04Now, sometimes you want to define a variable that cannot change. For this, we can use constants.
- 00:18:09So I'll write "const" for "constant", we'll call it "GRAVITY".
- 00:18:13As you can see, it's standard practice to name constants with capital letters, and we'll set it equal to -9.81.
- 00:18:19Now we can use this like any other variable, but we cannot change it.
- 00:18:23If we try to do that, it throws an error.
- 00:18:26And that's it for variables!
- 00:18:28Great Job!
- 00:18:29Functions
- 00:18:31Functions are the bread and butter of programming.
- 00:18:33They allow you to bundle up your code in small reusable packages.
- 00:18:38So far, we've been using some of Godot's build-in functions like "_ready()" and "_input()".
- 00:18:42Notice how these are prefixed with an underscore.
- 00:18:45This is to show that they are not activated or called by us, but by the engine itself.
- 00:18:50But we can actually make our own functions and use them to do really cool things.
- 00:18:55Common functions to making games are things like jump, die, shoot, and respawn.
- 00:19:00And making a function is actually really easy.
- 00:19:03Let's make a function for jumping.
- 00:19:05So we'll right "func "...
- 00:19:06...(Or "funk" in that case)...
- 00:19:08...And the name of our function, open and close parentheses, and a colon.
- 00:19:12And then in here, we'll put all of our code for jumping.
- 00:19:15So we could add an upwards force, play a funny sound, and a jump animation.
- 00:19:20For now we'll just print "JUMP!".
- 00:19:23But nothing is currently triggering or calling our function.
- 00:19:27Let's call it whenever we press the action we made earlier.
- 00:19:30So "func _input(event):", and if our function was pressed, then we call our jump function by writing "jump" followed by ().
- 00:19:41And now when I press space, it displays "JUMP!".
- 00:19:43And at this point, I encourage you to add the sound effect yourself.
- 00:19:46OIOING! OIOING! O-
- 00:19:50A lot of fun to be had here.
- 00:19:51Now this already makes our code more readable and reusable.
- 00:19:55But right now, our function is just a simple command. We call it, and it does some things.
- 00:20:00However, we can actually use functions for much more than this. They can have inputs and outputs.
- 00:20:06Let me explain: I like to think of functions as machines.
- 00:20:09Take for example this coffee maker.
- 00:20:11Just like the name implies, it performs a function: To make coffee.
- 00:20:15To do this, however, it needs some input: Some water, a filter, and some ground coffee.
- 00:20:20With this, it performs its function and then spills out an output: Delicious Coffee.
- 00:20:28In code, we call the input we give our function: Parameters. And the output is called: Returns.
- 00:20:34So let's make a function that adds together two numbers.
- 00:20:37"func add()", and inside our parenthesis, we add the parameters.
- 00:20:42So number one we'll call "num1", and the second number we'll call "num2".
- 00:20:47Then inside our function, we can add these together and store them in a "result" variable.
- 00:20:52So we'll set that " = num1 + num2", and for now, let's just print the results.
- 00:20:58Then in "_ready()", let's try calling this function.
- 00:21:00So we'll call add(), we'll give it first number 3, and the second one, let's do 8.
- 00:21:06And let's actually call it again. So let's do a harder one.
- 00:21:09Let's add 245 with 111.
- 00:21:13And indeed, it prints "3 + 8" which is 11, and 356 which is the result of our second calculation.
- 00:21:19So here we've created a function with two parameters, the numbers we want to add together.
- 00:21:24But the function actually doesn't return the results, it just prints it.
- 00:21:28This means we can't actually access the result where we are calling the function, up here in "_ready()".
- 00:21:33To return the result, we replace "print()" with "return".
- 00:21:37So we'll do "return result". And now up here, we can store the result in a variable, set it " = add()", and we'll add together 3 and 5, and we can print that.
- 00:21:49And the result is 8.
- 00:21:50And because we have access to it up here, we can do more things, like add more numbers to it.
- 00:21:54So we can say "result = add()", and pass in the result from before, and "10", and then print it.
- 00:22:02And there you go, we now get 18, because 3 + 5 + 10 is 18.
- 00:22:07Really cool.
- 00:22:08And just like when declaring variables, we can also statically type parameters and return types to make it clear that the function is made to work with numbers.
- 00:22:15So after "num1", we'll specify that this is an integer, we'll do the same thing with num2,
- 00:22:20and here we can set the return type using an arrow (->) and this is also also an integer.
- 00:22:25So now this function will only work with numbers.
- 00:22:283468211.5
- 00:22:31Random numbers
- 00:22:33Getting random numbers is very easy.
- 00:22:36The function "randf()" gives a random number between 0 and 1.
- 00:22:40It's great for assigning probabilities to your code.
- 00:22:43Say, we are deciding what loot to give the player and we want to make some loot more rare than other.
- 00:22:48Well in this case, we could create a "roll" variable and set it " = randf()".
- 00:22:53Then we could check if the roll is >= 0.8, which means there's an 80% chance that we print: "You found a COMMON item.".
- 00:23:03And if not, well then there's a 20% chance that "You found a RARE item!".
- 00:23:09And if we play this, most of the time it's going to display that we found a common item.
- 00:23:14But if we keep playing,
- 00:23:16If we keep playing.
- 00:23:18Keep play- [:: Did you know that 80% of programmers quit playing right before they make it BIG? ::].
- 00:23:20...Eventually...
- 00:23:21...Eventually... eheheh...
- 00:23:22...There's a 20% chance so...
- 00:23:24...At some point...
- 00:23:26...We'll eventually...
- 00:23:27...We'll event- chuhahahaha- this is c-
- 00:23:29We'll eventually find a rare item.
- 00:23:32That was crazy...
- 00:23:33We can also use "Randf_range" and "Randi_range" to get a random float or integer between a minimum and maximum value.
- 00:23:41Say to assign a random height to a character.
- 00:23:44So, "var character_height = randi_range()" for random integer between 140 and let's do 210.
- 00:23:53And we'll print that "Your character is ", then we'll insert the height (remember to convert to a string), "cm tall."
- 00:24:03And, we'll roll a character, and apparently,
- 00:24:06And, we'll roll a character, and apparently, I am a 𝗚𝗜𝗔𝗡𝗧!
- 00:24:07Basketball, here I come!
- 00:24:09Documentation
- 00:24:11GDScript is actually pretty well documented.
- 00:24:13And one of the really cool things about it is that the documentation is linked with the editor.
- 00:24:17If we hold down [ctrl] and click on something we would like to know more about (such as the "randi_range" function), it opens up the documentation right here in the editor.
- 00:24:26This is a really quick and easy way to check if you're using a function correctly.
- 00:24:30It also works for classes which we'll learn about later.
- 00:24:34Arrays
- 00:24:35Sometimes you want a variable that can hold more than one thing.
- 00:24:38In fact, sometimes you might want to store a whole list of elements.
- 00:24:42For this, we use arrays.
- 00:24:44Defining an array is very easy.
- 00:24:46Let's make one to hold items in an inventory.
- 00:24:49So we'll create a normal variable and set it equal to open and close square brackets.
- 00:24:54This creates an empty array.
- 00:24:55We can then add elements inside the square brackets.
- 00:24:58I'll add a "Potion" string, the number 3, and the number 6.
- 00:25:02Notice how unlike many other languages, GDScript has no problem mixing data types within an array.
- 00:25:08But if you want to constrain an array to a specific type, we can of course statically type it.
- 00:25:13So here we simply add the type.
- 00:25:15This is an array and the elements inside it are going to be strings.
- 00:25:19So that's the syntax for that.
- 00:25:21And now we get an error because 3 is not a string, so we'll replace this with "Feather".
- 00:25:26And 6 is also not a string, we'll replace that with "Stolen harp".
- 00:25:31Now the way we access elements in an array is by using an index.
- 00:25:35When you add an element to an array, it is automatically assigned a number based on it's position in the array.
- 00:25:41In this case, the first element "Potion" has an index of 0.
- 00:25:44"Feather" has an index of 1.
- 00:25:46And the "Stolen harp" has an index of 2.
- 00:25:48So to access and print the first element in our array,
- 00:25:51we write "print(items[])" then we access the first element which has an index of 0.
- 00:25:57And this spells out "Potion".
- 00:25:59Pretty cool.
- 00:26:00We can just as easily change elements.
- 00:26:03This changes the second element to "Smelly sock", and the third element to "Staff".
- 00:26:08And arrays also have functions for finding, removing, or adding new elements.
- 00:26:13So to remove at an index, we write ".remove_at()", and then the index 1.
- 00:26:18This removes the "Smelly sock".
- 00:26:20And to add a new element, we write ".append()", and then we'll append an "Overpowered sword" to the end of the array.
- 00:26:29Now, arrays can quickly become long and difficult to manually go over.
- 00:26:33Luckily, we can use something called "Loops" to help out.
- 00:26:36Loops for n in range(1,3)
- 00:26:37Loops for n in range(2,3)
- 00:26:38Loops for n in range(3,3)
- 00:26:39Loops allow us to repeat code a number of times with small variations.
- 00:26:43They are perfect for iterating through all the elements in an array.
- 00:26:46If for example, we wanted to print out all the items in our array here, we can do that easily using a for loop.
- 00:26:52So we write "for", then then the name of the item that we're currently looking at, we'll just name that "item".
- 00:26:59"in" the array, so "in items"...
- 00:27:02We will print out the current item we're looking at.
- 00:27:05So, "print(item)".
- 00:27:06And as you can see, it prints: Potion, Feather, and Stolen harp.
- 00:27:09This is cool because we can have any number of items in our array.
- 00:27:13The code will work just fine.
- 00:27:15We can add more functionality, say to only print items that are more than 6 letters long.
- 00:27:20"if item.length()", so the amount of characters in "item" is greater than let's say 6, then we're going to print that item.
- 00:27:29And it prints only "Feather" and "Stolen harp", because "Potion" is exactly 6 letters.
- 00:27:33We can also create loops that run a certain amount of times.
- 00:27:37If we write "for n in 8:", it creates a for loop where the variable "n" starts at 0 then goes all the way to 7.
- 00:27:46So if we print "n", we can see that it runs 8 times. And "n" starts at 0, and ends at 7.
- 00:27:54We say that "n" is the current iteration of the loop.
- 00:27:58Another type of loop is the "while" loop.
- 00:28:00This repeats as long as a certain condition is met.
- 00:28:03Now I think most people who know me would say I'm a glass-half-full-kind of person.
- 00:28:08And to those who disagree, let me go ahead and prove it by using a while loop to fill up a glass halfway.
- 00:28:14So I'll create a glass variable and default it to 0.0.
- 00:28:18The glass is currently empty, no arguing about that.
- 00:28:21Then we'll create a while loop that continues while glass is less than 0.5,
- 00:28:27so we haven't filled it up halfway yet, in which case, we will add a certain amount to our glass.
- 00:28:34We could do a constant amount like this, or in real life when I try to pour something into a glass, I'm not that precise.
- 00:28:40So let's add a random number instead...
- 00:28:43So we'll add a random float between 0.01 and 0.2, and then we'll print out how much is in the glass at the current time.
- 00:28:53Then when the loop is done, we get to continue down here in our code, and we print that "The glass is now half full!".
- 00:29:02And if we run this, we can see that the glass slowly becomes more full with a random amount each time,
- 00:29:07and then it says that "The glass is now half full!".
- 00:29:10As you can also see, it overshoots a bit because we're adding random amount each time and not climbing the value.
- 00:29:16Just like when trying to hit exactly half a glass in real life.
- 00:29:19My wife says that I add too much milk when I make her coffee. I think I'll refer her to this going forward.
- 00:29:25Now, we actually have to be really careful when using while loops,
- 00:29:28that we don't create an infinite loop.
- 00:29:31These can be tricky to avoid and can easily crash our program.
- 00:29:35Say, if I were to remove this line that adds a little bit to our glass every time,
- 00:29:40well then our glass would never become half full, and we would be stuck in this loop forever.
- 00:29:47Now, I don't think this will actually crash Godot. Indeed it didn't.
- 00:29:50But we get a lot of overflow errors.
- 00:29:53And as you can see, our game is definitely not responding.
- 00:29:56Not good.
- 00:29:57Finally, in both for and while loops, we can use the "break" and "continue" keywords.
- 00:30:01"break", as the name suggests, breaks out of the loop and moves on to the code after.
- 00:30:05While "continue" immediately skips to the next iteration of the loop.
- 00:30:09So if we were to write "if glass > 0.2", and then "break"...
- 00:30:16We can see that our while loop ends way early because we exit out of the loop before we even get to print that the glass reached 0.2.
- 00:30:24Dictionaries
- 00:30:25Dictionaries They sound really boring,
- 00:30:27Dictionaries They sound really boring, but they're really cool.
- 00:30:28While arrays great for storing lists of elements, it's sometimes confusing to access each element with an index.
- 00:30:35Sometimes it makes more sense to use a dictionary.
- 00:30:38Dictionaries hold what we call Key-value pairs.
- 00:30:41Let me explain...
- 00:30:42In real life, we use a dictionary to look up the definition of words in a language.
- 00:30:47The key in this instance is the word we're looking up, and the value is the definition.
- 00:30:52Dictionaries in GDScript work the exact same way.
- 00:30:55Creating an empty dictionary is really easy.
- 00:30:58We write "var", I'll call this one "my_dict", and set it equal to some curly brackets.
- 00:31:04This is an empty dictionary.
- 00:31:06We can then add key-value pairs inside the curly brackets.
- 00:31:09Say for example, that we have multiple players in our game.
- 00:31:12We can use a dictionary to keep track of them.
- 00:31:15Each player has a username, this will be the key. And a level, this will be the value.
- 00:31:19So I'll create a player called "Crook", and his level is going to be 1.
- 00:31:24I'll create another player "Villain", with a level of 35.
- 00:31:29And finally, a player with the username "Boss" with the level of 100.
- 00:31:33If defining on one line is too much, you can split it up like this...
- 00:31:39And it's good practice to add a comma to the last entry so you are ready for a new one.
- 00:31:43And let's name this dictionary "players".
- 00:31:45Then to get the level of a player, we simply type in the username.
- 00:31:48Let's print the level of our "Villain".
- 00:31:50So "print()", and we'll write "(players)", type in "["Villain"]",
- 00:31:55and this should print: 35.
- 00:31:57And you can just as easily assign a new value or add another entry.
- 00:32:01So we can reassign the "Villain" here to say, 50,
- 00:32:05and add a new entry called "Dwayne"...
- 00:32:08...with a whooping level of 🗲999🗲.
- 00:32:12And just like with arrays, we can loop over a dictionary with a for loop.
- 00:32:16When we do this, we are actually looping over all the keys in our dictionary.
- 00:32:21So all the usernames, and "for" each "username in players",
- 00:32:26we will go ahead and print out the username,
- 00:32:28I'll add a string to separate, and then the level.
- 00:32:33So here we access our "players" dictionary and input the username we're currently looking on to get the value.
- 00:32:39And remember to convert this into a string so "str" to cast to a string.
- 00:32:44There you go, it prints our entire dictionary.
- 00:32:48Now, just like with arrays, you can have multiple data types of keys and values in the same dictionary.
- 00:32:54And even cooler, you can actually have arrays or dictionaries inside another dictionary.
- 00:33:00This might sound really abstract, but it's actually pretty cool, and often comes in handy.
- 00:33:04Say, for example, we don't just want to store the level of each player, but also other info like their health.
- 00:33:11Well then we simply replace this value with another dictionary.
- 00:33:14So open and close some curly brackets, here we'll add a key called "Level" with a value of 1...
- 00:33:20...and another key called "Health" with a value of 80.
- 00:33:24And at this point, it's a good idea to use tabs to keep our code clean.
- 00:33:27Let's duplicate this...I'll assign some values...
- 00:33:33And now we can access a value by using two keys.
- 00:33:36Let's print the "Health" of our "Boss", so "print(players)", we go into our "Boss", and we access the health.
- 00:33:44And this prints "500", which is correct.
- 00:33:47So this way, we can start to think of ways we can structure the data about what's going on our game such as player stats, inventory, buffs, and so on.
- 00:33:55Enums
- 00:33:57Enums are convenient way to define tags or states in our game.
- 00:34:01Say we're making a game with a bunch of units and we want a way to mark each one as either hostile, neutral, or ally.
- 00:34:08In this case, we could create an enum that defines these tags.
- 00:34:12So at the top here, I'll write "enum" to create a new enum,
- 00:34:15and then in the curly brackets, I'll write "ALLY", "NEUTRAL", and "ENEMY".
- 00:34:21And now we can use these states in our game.
- 00:34:24For example, we can create a variable called "unit_alignment", and set it equal to any one of these states.
- 00:34:30I'll just set it to "ALLY".
- 00:34:32And this makes it really quick and easy to then check in our code if our unit has a certain alignment.
- 00:34:38Also, let's just give our enum a name here to stay organized.
- 00:34:41So I'm going to call this "Alignment",
- 00:34:44And now, whenever we are accessing a state, we have to go inside of "Alignment." and here you can see our state,
- 00:34:51so I'll go "Alignment.ALLY" by default.
- 00:34:53And then inside of "_ready()", we can check "if unit_alignment == Alignment.", let's check for "ENEMY",
- 00:35:02and if it is, we'll print "You are not welcome here.".
- 00:35:07And if it is not, so it's not an "ENEMY",
- 00:35:09we'll just write "Welcome.".
- 00:35:11And now if we run, we can see that our console displays "Welcome.", because our "unit_alignment" is currently "ALLY".
- 00:35:19This is much safer than using, say, a string or an integer to represent states.
- 00:35:25Because Godot will throw an error if we misspell a state.
- 00:35:28If, for example, I misspell "ENNEMY", we can see that Godot throws an error.
- 00:35:32But I think one of the real powers of enums is that we can use them for exported variables.
- 00:35:38To do this, we mark our variable here with "@export"...and then set the type to "Alignment".
- 00:35:45And now, we can actually set our "unit_alignment" in the inspector.
- 00:35:49So I'm going to set mine to "ENEMY" here,
- 00:35:51and now when we play, it prints "You are not welcome here.".
- 00:35:55Really cool.
- 00:35:56Now what is actually happening behind the scenes here is that Godot is creating a constant for each state in our enum.
- 00:36:02So this code here is actually completely identical to our first line.
- 00:36:07And as you can see, the value here increases from 0 to 1 to 2, kind of like an index.
- 00:36:13So an enum is essentially just a bunch of constants with an increasing value.
- 00:36:18Which means that if we try to print one of these states,
- 00:36:21say, you print "Alignment.ENEMY",
- 00:36:25The console simply shows the value of that constant, which is 2, Because it's the third state.
- 00:36:31This is rarely something you need to take into account, but it's nice to know what our code is actually doing.
- 00:36:36And we can even overwrite the default value if we want,
- 00:36:39so we could set "ALLY = 1, NEUTRAL = 0, " and "ENEMY = -1",
- 00:36:46and now our console displays "-1" because that's now the value of our "ENEMY" constant.
- 00:36:52Now something that pairs really nicely with enums are "match" statements, because they allow us to quickly add functionality for each state.
- 00:36:59Match
- 00:37:00Match is the Godot equivalent of the switch statement from other languages,
- 00:37:04and allows us to execute different code depending on the value of a variable.
- 00:37:08In this case, here we can use a match statement to add some code for the different values of our enum.
- 00:37:13So I'll write "match", then the variable we are matching based on, which is "my _alignment", colon,
- 00:37:18and now, we add code for each value.
- 00:37:20So in case "my_alignment" is "Alignment.ALLY", I'll print "Hello, friend!".
- 00:37:29If it is "NEUTRAL", I'll print "I come in piece peace!".
- 00:37:34If it is "ENEMY", "TASTE MY WRATH!".
- 00:37:39And a cool thing here is that we can actually add a default response as well.
- 00:37:43To do that, we do "_:", and so if it isn't any of the cases above, we'll print "𝓦𝓱𝓸 𝓪𝓻𝓽 𝓽𝓱𝓸𝓾?".
- 00:37:52And if we set my alignment to say, "ENEMY"..."TASTE MY WRATH!".
- 00:37:58Modifying nodes 2.0
- 00:38:00So far when we need to access a node, we've done so by simply dragging it into a script,
- 00:38:05which creates a dollar sign followed by the path to the node.
- 00:38:08I've created a few empty nodes here to demonstrate.
- 00:38:11A "Player" node with a "Graphics" and "Weapon" node, and an "Enemy" node.
- 00:38:15So if I drag in the "Weapon" node, it creates a path that goes into the player and finds the weapon.
- 00:38:21And we can actually store this reference in a variable.
- 00:38:24To do this, we simply drag it to the top, and hold down [ctrl] while releasing.
- 00:38:29This automatically creates a variable with the name of the node and the correct path.
- 00:38:33And as you can see, it uses the "@onready" keyword.
- 00:38:36This is because Godot has a very strict order in which nodes are created.
- 00:38:40And if we open the game and try to find the "Weapon" node before it exists, we will get an error.
- 00:38:45"@onready" simply makes sure Godot waits until all child nodes have been created so we don't get any issues.
- 00:38:50And just a quick note, the dollar sign here is just actually shorthand for using the "get_node" function.
- 00:38:56So this is actually identical.
- 00:38:58Also, you might have noticed that the path is relative.
- 00:39:01Our script is on the main node, so it starts right after that node.
- 00:39:05We can print the absolute path through script.
- 00:39:08We'll "print(weapon.get_path())".
- 00:39:11This prints the absolute path starting from the root of our game all the way to "Weapon".
- 00:39:16Now paths are great for many things.
- 00:39:18But sometimes, they can be a bit inflexible.
- 00:39:21They break if we rename any of the nodes in the path,
- 00:39:23and it's best to only use paths when the node we want to access is the child of the node we're working on.
- 00:39:28We'll talk about parent-child relationships later.
- 00:39:31Luckily, we can also use the "@export" keyword to reference other nodes.
- 00:39:35So if we write "@export", and create a variable called "my_node", we can set the type to "Node".
- 00:39:41And then in the inspector, we can assign whatever node we want, or simply click and drag.
- 00:39:47I'll drag in the "Player".
- 00:39:48We can also check if the node is a certain type by using the "is" keyword.
- 00:39:52So in "_ready", we'll check "if my_node is" of type "Node2D:",
- 00:39:57In which case, we'll print "Is 2D!".
- 00:40:00And it is.
- 00:40:02We can even be specific about what type of node we want to be able to reference.
- 00:40:06If, for example, we only want to reference "Sprite" nodes, we just change the type of "Sprite".
- 00:40:11So, "Sprite2D:"
- 00:40:13I'll reset the variable here, and now, we can now only assign "Sprites".
- 00:40:18Which in this case are the "Graphics" and "Weapon" nodes.
- 00:40:21Now if we play, it still prints that this node is a 2D node,
- 00:40:25even though "Sprite2D" and "Node2D" are two different nodes.
- 00:40:29This is because "Sprite2D" inherits from "Node2D".
- 00:40:33But we'll get into that in the "Inheritance" chapter.
- 00:40:36Signals!
- 00:40:37Signals are messages that nodes can sent to each other.
- 00:40:40We use them to notify that a certain event occurred.
- 00:40:43It's probably easiest to look at an example.
- 00:40:45Godot has many built-in signals, take this UI button.
- 00:40:49If we select it, and go to "Node", we can see a list of all the signals on this node.
- 00:40:55I'm going to connect the "pressed()" signal to our main script.
- 00:40:58So double click it, I'm going to select "Main", and hit connect.
- 00:41:02As you can see, this creates a function called "_on_button_pressed():",
- 00:41:05and over here, there's a green arrow pointing to it.
- 00:41:08This means that a signal is currently connected to this function.
- 00:41:11If I click on it, we can see the source, which is our button.
- 00:41:15Let's just add quick a print line here,
- 00:41:17and now if we play and press the button, it prints "MONEY".
- 00:41:22Get it?
- 00:41:22Now this is because whenever we press the button, it emits the "pressed()" signal, and everything connected to it gets called.
- 00:41:29In this case, our own button "pressed()" function,
- 00:41:31which indeed prints "MONEY".
- 00:41:33We can connect as many functions as we want to a signal,
- 00:41:36they will all be called when it gets emitted.
- 00:41:38This is really cool because it allows us to link together nodes in a way where they don't have to be aware of each other.
- 00:41:44The button has no idea which functions are connected to the signal. It just tells it to emit.
- 00:41:48This makes signals great for separating different parts of our game, or "decoupling".
- 00:41:53Say we're playing a character that can level up by getting enough XP.
- 00:41:57Whenever we level up, there are probably many game systems that need to update.
- 00:42:01The UI, our player stats, perhaps we have spells or achievements that unlock.
- 00:42:06And updating all of these from the player can quickly become a mess.
- 00:42:10Instead, we create a leveled up signal that all these systems can connect to.
- 00:42:14Then, all we have to do is have the player emit the signal whenever we level up.
- 00:42:18Let's try it out.
- 00:42:19Instead of a button here, I'll create a timer.
- 00:42:22So I'll add a "Timer" node, and set it to Autostart.
- 00:42:26This will count down from one, and when it reaches 0, it emits a signal called "timeout()".
- 00:42:33Let's connect that to our script, and make it increase our XP.
- 00:42:36So we'll create an XP variable, default it to 0.
- 00:42:41Then in our function, we increase our XP, and let's also print it.
- 00:42:47If our XP then reaches, say, 20,
- 00:42:50we'll set it back to 0, we leveled up, and we can start over for the new level.
- 00:42:54And if we then play, it's going to increment our XP every second, and when we reach 20, it's going back to zero.
- 00:43:01Now let's create a signal that other nodes can connect to.
- 00:43:04So at the top, we'll right "signal leveled_up",
- 00:43:07and if we save this and select our "Main" node, we can actually see that signal.
- 00:43:12We can connect it to other nodes but just for this example, we'll connect it back to our "Main" node.
- 00:43:17This created an "_on_leveled_up():" function which is called when the signal is emitted.
- 00:43:21Let's print a message that says "DING!".
- 00:43:23Now we simply have to emit the signal.
- 00:43:25Let's do that when reach 20 XP.
- 00:43:28Here we'll write "leveled_up.emit()".
- 00:43:31Now when we reach 20 XP, it emits the signal, and we print: "DING!".
- 00:43:36Awesome!
- 00:43:37We can also connect signals through code.
- 00:43:39I'll disconnect the signal in the editor, and instead, in a "_ready():" function...
- 00:43:45...I'll write "leveled_up.connect()", and the name of the function we're connecting to: "_on_leveled_up".
- 00:43:52Careful here, this will probably autocomplete with parenthesis.
- 00:43:55We don't need those, we just want the name of the function,
- 00:43:58and disconnecting is just as easy: ".disconnect()".
- 00:44:02We can also pass parameters through signals.
- 00:44:04This could be used to pass the level we've reached or other useful information.
- 00:44:08I'll just add a quick message that we can print.
- 00:44:11So on a signal, we'll add a parameter.
- 00:44:13We'll make sure that our function supports this as well.
- 00:44:17And instead of printing "DING!", we'll print whatever message was input,
- 00:44:20and then when we emit the signal, we can put in a message.
- 00:44:23Let's put in: "GZ!".
- 00:44:25And voila, it displays "GZ!".
- 00:44:28And that is signals!
- 00:44:29Get...
- 00:44:31Get... Set...
- 00:44:32Get... Set... GO!
- 00:44:33Or just
- 00:44:34Get/Set
- 00:44:35Getters and Setters allow us to add code for when a variable is changed.
- 00:44:39This means we can do things like clamp a value within a certain range,
- 00:44:43or emit a signal, letting other parts of our code know that the variable changed.
- 00:44:47The example that is always used for this is "health".
- 00:44:50So, we'll add a "health" variable, default it to a 100,
- 00:44:54and then we'll use a colon to go and define a setter.
- 00:44:57In the parenthesis, we name the value that was passed in.
- 00:45:01So this is the value that we are trying to change our variable to.
- 00:45:05And now, we can decide what to do.
- 00:45:06We could, for example, set our "health = ", not the value directly, but clamp it first between 0 and 100.
- 00:45:15So this makes sure that our "health" does never exceed a 100 or go below 0.
- 00:45:20We could also emit a signal here,
- 00:45:22so we can create a "signal health_changed()" and we can actually input the "new_health" as a parameter,
- 00:45:31and then in our setter, we can go "health_changed.emit()" and put in the new "health".
- 00:45:37Of course, we need to make sure to connect this signal.
- 00:45:39We can do that through the editor.
- 00:45:42And let's just print the value.
- 00:45:44So we'll print the "new_health".
- 00:45:46Then in "_ready():", let's try this out by assigning a value to "health".
- 00:45:50Let's try assigning -150, and now if we play, we can see that it prints 0.
- 00:45:55This is because we put in -150, it clamps this to 0, and then it emits the signal,
- 00:46:03the signal then calls the "_on_health_changed():" function, and we print the "new_health", which is 0.
- 00:46:09So that's a good example of adding a Setter.
- 00:46:12Getters are more commonly used to convert values.
- 00:46:15So let's add two variables, we'll have a "chance" that is equal to 0.2,
- 00:46:19and then we can have another one which is our "chance_pct".
- 00:46:23We'll make this of type "int", and we will add a getter.
- 00:46:27Here, whenever we try to get the "chance_pct", we'll simply return the "chance" variable " * 100".
- 00:46:34And now, if we try to print this variable, "print(chance_pct)",
- 00:46:39or change our chance to, let's say, 0.6, and print it again,
- 00:46:45we can see that our "chance_pct" is completely based on whatever our "chance" is currently equal to.
- 00:46:51And so it starts at 20% then goes to 60%.
- 00:46:55And for good measure sake, we'll add a setter also.
- 00:46:58So, if we set to a new value, we will modify our "chance", and set it equal to the new value divided by 100.
- 00:47:07And since our "chance" is a float, we'll make to cast that value.
- 00:47:11So now, instead of changing the "chance", we can directly change the "chance_pct", to say, 40.
- 00:47:19There we go.
- 00:47:19Classes
- 00:47:21Classes This is a good one.
- 00:47:22GDScript is an Objected-oriented programming language.
- 00:47:25This means that we generally try to structure our code inside of contained objects that interact with each other.
- 00:47:32We primarily do this using Classes.
- 00:47:35For now, try to think of a class as a blueprint.
- 00:47:38Say, for example, we are making an RPG and we need to populate it with a bunch of characters that the player can interact with.
- 00:47:44So we create a "Character" class that has some variables and logic that all the characters in the game should have.
- 00:47:50So for variables, we could add things like name, health, and a line of dialogue for when you interact with them.
- 00:47:56For logic, we could add functions like talk() and die().
- 00:47:59Then we take this class and create Instances.
- 00:48:02Instances are specific versions of the class.
- 00:48:05So we could create an instance named "Potion Seller" that has a health of 50 and a line that says:
- 00:48:11"You can't handle my strongest potions!"
- 00:48:13And a bunch more instances, all of which have the same variables but different values.
- 00:48:18And all of which can talk:
- 00:48:19"I used be an adventure like you."
- 00:48:22and die().
- 00:48:23AUHHH!
- 00:48:24So that's the idea of classes.
- 00:48:26In Godot, you've actually already encountered a bunch of classes.
- 00:48:29That's because all the built-in nodes in Godot are classes.
- 00:48:33Which actually makes a lot of sense if you think about it.
- 00:48:35All the nodes are self-contained objects, with a bunch of variables and logic that we can create instances of.
- 00:48:41If we add a Sprite node, we're instancing the Sprite class.
- 00:48:45So when we create a script, we are actually also creating a class.
- 00:48:49I have to put a DISCLAIMER here that we're technically not creating a class.
- 00:48:52But for all intents and purposes, it works just like one.
- 00:48:55So let's forget all about that for now.
- 00:48:58So let's abandon our main script here for now and instead create a new node.
- 00:49:02So search for "Node", and let's name this node "Character", and add a script to it.
- 00:49:09Now to make it more clear that our script is a class that defines a character, let's set the class name to "Character".
- 00:49:15So right at the top here, we'll go "class_name Character" with a capital "C".
- 00:49:21And we can now add some variables.
- 00:49:23We'll mark these with "@export" so that we can set them in the inspector for each character instance.
- 00:49:29So "@export var", let's add a "profession" of type "String",
- 00:49:35and give each character a "health" as well of type "int".
- 00:49:40And so we can now see that our character here has these two properties in the inspector,
- 00:49:45and we can go ahead and fill out the profession.
- 00:49:47This is going to be the "Potion seller" who is going to have a health of, let's say, 40.
- 00:49:52And now we can create more instances by duplicating this node.
- 00:49:56So let's duplicate it a few times.
- 00:49:58The second character we'll name "Ex-adventurer", with a health of 30.
- 00:50:04And the last character will be our "Knight", and we'll give him a health of, say, 150.
- 00:50:11So now, we have these three instances with different properties,
- 00:50:14and we can also give them a function.
- 00:50:16So let's add a "die()" function,
- 00:50:18So I'll write "func die():".
- 00:50:20And this function is going to set our health to 0, as well as print our profession, and the fact that we died.
- 00:50:29And that's it for our "Character" class, but currently nothing is triggering this function.
- 00:50:35So let's go into our main script now, and here we can get a reference to one of our characters and call the "die()" function.
- 00:50:43We could drag simply and drop one of these nodes to get a reference,
- 00:50:46but because we have named our class, we can actually use an "@export" variable for this instead.
- 00:50:52This way, we can set the connection in the editor and we don't have to worry about the path changing.
- 00:50:56So we'll "@export var", and here we'll have a "character_to_kill" of type "Character".
- 00:51:04And now if we save that, we can see that, we can see that our main node now has a slot here for the "Character to Kill".
- 00:51:10And we can either drag in one of these character or hit "Assign" and select one.
- 00:51:15I'm gonna choose "Character3".
- 00:51:17And now in our "_ready():" method, we simply go to our "character_to_kill" and call the "die()" function.
- 00:51:23And if we run this, indeed, the "die()" function is called on our "Character3" which is our Knight.
- 00:51:29So it says: "Knight died.".
- 00:51:32And if we choose another character, say, the first one,
- 00:51:36we are instead killing the Potion seller.
- 00:51:39Really cool.
- 00:51:40Inner Classes
- 00:51:42These are classes that exist inside of another class.
- 00:51:45You mostly use these for bundling together variables, and maybe add a function or two.
- 00:51:49And they can be a good alternative to dictionaries because they are sometimes a bit more safe to use.
- 00:51:54Say we have this character class here, and we would like to add some equipment.
- 00:51:58We could create an inner class called "Equipment".
- 00:52:01So I'll write "class Equipment:", and then we can give it a couple of variables.
- 00:52:06I'll create an "armor" variable, set it to 10 by default, and a "weight" of 5.
- 00:52:12Now we could can use this class around our script, so we could create some variables using it.
- 00:52:17I'll create a "chest" that is equal to "Equipment.new()".
- 00:52:22Calling ".new()" will create an instance of the "Equipment" class.
- 00:52:25So now we have one piece of "equipment", one instance which is a "chest".
- 00:52:30We'll also create "legs", and again, we'll call "Equipment.new()".
- 00:52:34Then in the "_ready():" function, we can access these classes.
- 00:52:38So we can go into our "chest" and change the "armor", and set it equal to 20.
- 00:52:44We can then print "chest.armor", or maybe print "legs.weight".
- 00:52:50And this is also where you can see that this is sometimes safer than using a dictionary.
- 00:52:54Because GDScript will recognize that there is a "weight" variable inside of the "Equipment" class.
- 00:52:59And if we try to access something that isn't there, we'll get an error before playing the game,
- 00:53:03not only when we reach this point in our code.
- 00:53:06This is called being "type-safe".
- 00:53:08And now, if we run this, we can see that each of the instances of our character will print these two pieces of equipment.
- 00:53:15And the armor of the chest is 20, and the weight of the legs is 5.
- 00:53:20Inheritance
- 00:53:21So we know that all nodes in Godot are classes, and we can effectively make our own using scripts.
- 00:53:28But there's one more important thing we need to know to understand how Godot is structured: Inheritance.
- 00:53:33Inheritance refers to the ability to derive one class from another.
- 00:53:37We've actually already been already doing this.
- 00:53:39Notice how our script says: "extends Node"
- 00:53:42So our script derives from the Node class.
- 00:53:45This means that all the functions and variables of the node class are also available in our script.
- 00:53:51Godot actually has a very nice way of visualizing this.
- 00:53:54When adding a new node, we can see al the nodes available to us.
- 00:53:59But some of these are organized under other nodes.
- 00:54:02That's because these nodes inherit from the top ones.
- 00:54:06For example, both "AnimatedSprite2D" and "Camera2D" inherit from "Node2D".
- 00:54:11This is because "Node2D" is a base class for all things that exist in 2D space.
- 00:54:16So since both a camera and a sprite needs a position in our world, they both inherit from it.
- 00:54:21And even cooler, we can actually find our Character class on this list as well.
- 00:54:26That's because when we create a Character class, we're essentially defining a new node type.
- 00:54:31And we can see that it inherits from "Node".
- 00:54:34Really cool.
- 00:54:35Now, all of this probably sounds a bit abstract.
- 00:54:38Because,
- 00:54:39Because, it is.
- 00:54:40But what it means in most cases is simply that we need to make sure we inherit from, or "extend", the right class for what we're working with.
- 00:54:47If, for example, we are making a script that moves around our player,
- 00:54:50we would create a "CharacterBody" node, and add a script to that node.
- 00:54:55We could name it, say, "player".
- 00:54:57And as you can see, the script then automatically "extends CharacterBody2D".
- 00:55:02And now, we have access to all the functionality in it,
- 00:55:05such as the "velocity", and the "move_and_slide()" function which moves the node around.
- 00:55:11Composition
- 00:55:12Now even though Godot uses Inheritance for it's nodes, there are often better ways to structure your code.
- 00:55:17Godot actually leans itself really well to another way called Composition.
- 00:55:22I'm not going to explain that here since there's already a really good video on this subject by a channel called "Bitlytic".
- 00:55:28But I definitely recommend you to check that out.
- 00:55:30Call down, signal up
- 00:55:32When writing GDScript, there are of course many best practices that we can choose to adhere to.
- 00:55:37Going all over them is a video of it's own.
- 00:55:40But one that is particularly important is call down, signal up,
- 00:55:44which we use as a rule of thumb when communicating between nodes.
- 00:55:48Every scene in Godot is a tree of nodes, and the beginning of the tree is called the "Root" node, which is the highest in the hierarchy.
- 00:55:54When looking at two nodes, where one is right above the other, we say that they have a parent-child relationship.
- 00:56:00The node that is above is the "Parent", and the node that is below is the "Child",
- 00:56:04And that child might also have a child and so on.
- 00:56:08Call down, signal up means that nodes are fine to call functions on the nodes below them in the hierarchy, but not nice versa.
- 00:56:14Instead, nodes below should use signals to communicate that something has happened.
- 00:56:19The nodes above can then connect to these signals as they choose, and act accordingly.
- 00:56:24Think of it like real life.
- 00:56:26Parents are allowed to tell their children what to do,
- 00:56:28but children shouldn't directly command their parents to do things.
- 00:56:31Instead, children signal their needs to the parents and the parents decide the next step.
- 00:56:36I'll keep in mind that this is a way to remember a good coding practice, it is definitely not a parenting advice.
- 00:56:41But what if we need to communicate between two nodes that are on the same level?
- 00:56:45You guessed it, these are called siblings.
- 00:56:47Well, here the common parent is in charge of connecting the signal from one sibling to the function on the other.
- 00:56:54This is most often done in the "_ready()" function right at the beginning.
- 00:56:58So, that's the idea of the rule. But of course, always implementing this concept correctly requires some practice.
- 00:57:03I'll have a link to a good article with some nice examples.
- 00:57:07Styyyle
- 00:57:08During this video, I've tried my best to adhere to the official GDScript style guide.
- 00:57:12These are the conventions we use for naming and order to keep our code elegant and readable for others.
- 00:57:18I won't go into further detail here, but I'll have a link to the official guide.
- 00:57:22PHHFFFFFFFUUUUHH.
- 00:57:24That was a lot of code for one video!
- 00:57:27Tehuhuhu- heheHUHAHAHAHAHA
- 00:57:29Of course, I haven't covered everything you are ever going to need in GDScript.
- 00:57:33But I hope this served as a nice overview of the most important aspects.
- 00:57:36Also, don't forget to check out CodeCrafters.
- 00:57:39Become a better software engineer today by using the link below and get a free week and 40% OFF on your CodeCrafters membership.
- 00:57:46So, this is the last video we've prepared for now.
- 00:57:48If you liked our new videos on Godot, and have a topic that you would like to see covered, please let us know.
- 00:57:53While we don't know what the future holds, we've a had BLAST making these videos,
- 00:57:58and I hope you've enjoyed watching them.
- 00:57:59So, until next time.
- 00:58:01Stay Awesome!
- 00:58:02And func-ky!
- GDScript
- Godot Engine
- Programming Language
- Game Development
- Signals
- Functions
- Input Handling
- Variables
- Inheritance
- Dictionaries