How to program in Godot - GDScript Tutorial

00:58:09
https://www.youtube.com/watch?v=e1zJS31tr88

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.

Mehr anzeigen

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

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