Master Godot 4: Craft a Survivors Roguelike with GDScript | Chris Tutorials | Skillshare

Playback Speed


1.0x


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

Master Godot 4: Craft a Survivors Roguelike with GDScript

teacher avatar Chris Tutorials, Game Developer

Watch this class and thousands more

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

Watch this class and thousands more

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

Lessons in This Class

    • 1.

      Course Introduction

      1:43

    • 2.

      Godot Project Setup

      6:38

    • 3.

      Art, Sound, and Plugins ~ Import & Install

      8:46

    • 4.

      Top-Down Player Movement

      34:39

    • 5.

      Play Animations from Spritesheets

      9:51

    • 6.

      Player State Machine

      18:49

    • 7.

      Player Input as Component

      13:08

    • 8.

      Switch Animations with Signals

      9:17

    • 9.

      Flipping a 2D Character

      13:41

    • 10.

      Grass and Dirt Tile Map Layers

      16:38

    • 11.

      Placing Trees as Map Props

      15:16

    • 12.

      Adding Stumps & Fixing Y Sorting

      9:41

    • 13.

      Spawn Projectiles with a Timer

      14:54

    • 14.

      Move Projectiles in Launch Direction

      6:25

    • 15.

      Setting Up Enemies and Hitboxes

      16:55

    • 16.

      Dealing Damage to Enemies with Hitboxes and Stats

      9:14

    • 17.

      Death State When Enemy Reaches 0 HP

      13:59

    • 18.

      Rotating and Aiming Projectiles with Weapon Loadout

      5:43

    • 19.

      Adding Sound Effects and Music

      7:21

    • 20.

      Combat System Singleton

      5:48

    • 21.

      Animating Floating Combat Text

      18:26

    • 22.

      Enemy Movement with Player Tracking in Godot 4

      16:53

    • 23.

      Spawning Enemies on a Time Curve

      15:36

    • 24.

      Spawn Off Screen with Calculated Offset

      19:25

    • 25.

      Random Enemy Spawning with Weight & Time Conditions

      15:11

    • 26.

      Player Hurtbox and Enemy Hitbox Setup

      7:09

    • 27.

      Player Invincibility Timer and Custom Stat Definitions

      10:45

    • 28.

      Implementing Periodic Damage for Enemy Hitboxes

      4:35

    • 29.

      Player Defeat

      7:40

    • 30.

      Testing and Enhancing Behavior on Player Defeat

      5:06

    • 31.

      Creating Pickup2D Scene

      6:00

    • 32.

      Implementing Experience Pickup System with Inheritance

      4:42

    • 33.

      Setting Up Collector2D and Fixing Player Animations

      5:24

    • 34.

      Creating Enemy Drop Mechanics and Resizing Pickups

      11:32

    • 35.

      Generating Random Item Drop Positions

      4:45

    • 36.

      Implementing a Pickup Gravity Area

      10:31

    • 37.

      Building a Player UI with HP and EXP Progress Bars

      11:15

    • 38.

      Implementing a Stat Based UI System with Signals

      10:38

    • 39.

      Scripting Dynamic HP and EXP Displays for Player UI

      16:06

    • 40.

      Debugging and Optimizing UI and Pickup Scripts

      5:11

    • 41.

      Leveling System with Experience and Stat Progression

      18:39

    • 42.

      Setting Up XP Thresholds and Signal Connections for Level Up Rewards

      9:47

    • 43.

      Building the Level Up UI Layout and Animated Text

      8:39

    • 44.

      Designing Reward Selection UI and Final Adjustments

      8:35

    • 45.

      Creating a Weapon Leveling System for Game Upgrades

      9:55

    • 46.

      Weapon Items for Leveling Up Weapon Power

      13:24

    • 47.

      Creating a Weighted Item Selection System for Level Up Rewards

      14:30

    • 48.

      Coding Reward Selection with Signal Propagation

      18:35

    • 49.

      Applying Weapon Level Stats to Projectile Spawning and Instances

      10:03

    • 50.

      Building a Rotating Scythe Weapon

      10:21

    • 51.

      Creating a Weapons Display UI for Game Inventory

      19:40

    • 52.

      Connecting Signals and Configuring Weapons UI for Dynamic Display

      19:31

    • 53.

      Creating Stat Boosting Items for Player Upgrades

      11:14

    • 54.

      Implementing HP Boost Pickups and Level Up UI Fixes

      10:33

    • 55.

      Creating a Health Potion Pickup for Enemy Drops

      5:23

    • 56.

      Implementing Camera Shake for Player Hit and Death Effects

      10:14

    • 57.

      Creating a Game Over Screen with Main Menu Navigation

      8:29

    • 58.

      High Score Tracking and UI Display for Enemy Defeats and Survival Time

      17:15

    • 59.

      Saving and Loading High Scores with JSON

      13:54

    • 60.

      Designing a Main Menu with High Score Display and Game Start Functionality

      11:22

    • 61.

      Polishing Script Survivors ~ Gameplay Enhancements & Debugging

      16:34

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

Community Generated

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

128

Students

1

Project

About This Class

Want to build your own action-packed Roguelite auto-battler, even if you’re new to coding?

This beginner-friendly course walks you step-by-step through creating a complete 2D game — Script Survivors — using Godot 4 and GDScript, Godot’s powerful and beginner-friendly scripting language. With no prior coding or game development experience required, you’ll learn by doing, and build a polished prototype from scratch!

What You’ll Learn:

  • Master GDScript Basics and Best Practices: Write clean, reusable code with proper naming conventions (e.g., snake_case), export variables for easy tweaking, and self-documenting scripts using comments for maintainability.

  • Build Core Gameplay Mechanics: Implement auto-attacking weapons (like spears and scythes), enemy spawning systems with adjustable difficulty curves, experience (EXP) collection, and a dynamic player leveling system with impactful upgrades.

  • Leverage Godot’s Scene and Node System: Organize your game using scenes for modularity (e.g., player, enemies, and world scenes), nodes for game objects (CharacterBody2D, Sprite2D), and timers for precise event scheduling.

  • Create Engaging Combat Systems: Design health and damage mechanics, including collision shapes for precise interactions, invincibility frames, and multi-target weapon upgrades for satisfying gameplay.

  • Add Polish with UI and Save Systems: Build intuitive user interfaces (e.g., high score displays, level-up screens) using control nodes and implement a JSON-based save/load system to track high scores for enemies defeated and survival time.

  • Use State Machines for Smarter Logic: Utilize the Limbo AI plugin to manage character and enemy behaviors (e.g., idle, run, attack states), simplifying complex logic and preparing you for advanced AI patterns.

  • Apply Real Game Development Patterns: Learn transferable skills like encapsulation (e.g., GameManager and SaveLoad scripts), resource management (e.g., PlayerContext), and project organization for scalability to larger projects.

By the End of This Course, You’ll:

  • Have a fully playable 2D action game with enemies, upgrades, and a main menu, ready to showcase in your portfolio.

  • Confidently navigate Godot’s editor, from the project manager to the inspector, and use tools like the AnimationPlayer and atlas textures for pixel-perfect visuals.

  • Be equipped to create your own games with a strong foundation in Godot 4, GDScript, and game development workflows, ready to tackle new projects with ease.

Why This Course?

  • Beginner-Friendly: Starts from the basics, with clear explanations of Godot’s interface, GDScript syntax, and game dev concepts, ensuring a smooth learning curve.

  • Hands-On Learning: Follow along to build "Script Survivors," a complete prototype with pixel art, chiptune music, and sound effects, guided by practical examples.

Real-World Skills: Learn professional techniques like state machines, JSON serialization, and modular design, preparing you for bigger game dev projects.

Jump in now and start crafting addictive action games that keep players coming back for more!

Meet Your Teacher

Teacher Profile Image

Chris Tutorials

Game Developer

Teacher

I believe the best way to learn technology--and programming in particular--is through video-based learning. My role is to provide clear, hands-on video tutorials that guide you step-by-step, but ultimately, it's up to you to engage actively, absorb the concepts, and practice consistently. This approach helps you become a more confident and skilled developer.

I specialize in Godot game development using GDScript and C#, breaking down complex topics into manageable lessons that empower you to build your own projects from the ground up.

If you want to experience my teaching style before enrolling, check out my free tutorials on my YouTube channel, Chris' Tutorials. I look forward to helping you grow as a programmer and creator!

See full profile

Level: All Levels

Class Ratings

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

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

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

Transcripts

1. Course Introduction: Hey, everybody, Chris, here. And in this video, I've got something really cool. I'm excited to share with you all. It's my new GIDoGame development course for creating a survivor like game. So we've got a lot of content in there. I mean, it's over 60 lessons and almost 12 hours of video. By far, it is the longest, most complete game development course I've ever put out. So if you've ever thought about making your own fun survivors Light game, that's exactly what we're going to be doing. We'll build it up step by step, right from scratch. We'll be using the GidOEngine version 4.4, which you probably already know is free and open source. So we'll code everything in GD script. Which is the language specifically made for the Gadot editor. It's beginner friendly, it's easy to read, and it's tightly integrated into the ado engine. So it works fantastically for doing game prototypes like this. So whether you're new to game development or you've already done a bit of coding and you just really want to solidify your skills with a new interesting project, I think this course is going to be for you. We'll start simple, getting comfortable with essentials like top down character movement. Then we'll really start to ramp it up, building out to some genuinely complex concepts. You'll be putting in character stats, leveling systems, item pickup we'll even build out a spawning system that's going to position all of your enemies off screen so they can swarm the player from all sides. So in a nutshell, we'll be getting really hands on with the Godos two D scene system. That's going to cover everything else from projectiles to creating polished user interfaces. By the end of the course, you'll have a fully playable survivors like game prototype seen in the background and all of the knowledge you need to make it into a bigger project. So what do you say? Are you ready to turn those game ideas into something real? Come join me in the course, and let's build something pretty incredible together. 2. Godot Project Setup: Hello, everybody, Chris here, in this video, we'll get started with the first step of our Survivors Like and GD Script project by downloading the GdoEngine. If you don't already know, Gadot is a free open source game engine that has its own primary language, GD Script written specifically for Gdo, which is what we're going to be using to code the course. It's a scripting language. You can, of course, use other languages in Gdo like C Sharp, which has strong built in support with a Mnoversion of Gudo. Most of the tutorials you find are going to be in GDScript because GD Script is quick to get started with. It has really great integration with the Editor EUI. It's easy to use, and you don't have to compile your scripts every time you want to make changes to it. So as far as simplicity, especially for small projects, GD Script has a lot going for it. So looking at the Gado website, I'm going to be using the current release version of Gado 4.4 0.1. Specifically going to be downloading the non mono version of it, which means it does not have C Sharp support because the GD Script only version does allow you to export to web. But if you use the Gado Mono, that feature is not supported. So Gadot 4.5 is already coming down the pipeline, and there are a couple features that I think are pretty cool coming out. But because we are trying to keep this to beginners intermediate level course, I don't want to overwhelm anybody with all those concepts. So let's go ahead and click Download and grab 4.4 0.1, and we want the blue version here. It's non Mono. This is going to be for GD Script only. If you did want C sharp, you can grab.net. We won't need that for this version of the project. I might create a C Sharp version of the course later if it makes sense. We'll see how things go. So you're going to want to save this install it and then open the GADoEditor. So once you open it, you should be hit with the project manager, which will give you a list of your current projects. You probably don't have much here. There might be a few demos. But what we're going to want to do is create our new project. So let's create a new project, and you can give this any name you want. I'm going to call it script Survivors subtle name, I know, because it's GD script, and it's a survivor style. So I'll put this in my tutorials directory, which I have specifically set up for these kind of long form tutorials. So let's set that as the current folder, and we should see something like project name here and the path to the full project. Because I made Script survivors A one word, it didn't actually put a separator down here. So I'll just use a dash as the default separator for spaces. And we're going to want the renderer in mobile mode. This is a pixel art course, so we really don't need the Ford renderer for any reason, and we'll just go ahead and create with that. So once your project's booted up, you should see the three D view port here. One of the cool things about Gadot is that it has two completely separate workflows for two D and three D, but it does support both of them. Unlike Unity, they're completely separate entities. So it's not faking two D in a three D system. It's actually completely two D. And if you'll look over on the left in this scene window, you'll see that there's well, really four types of nodes here, two D scenes, three D scenes, user interface. And then if we click other node, there's the base class here node, which has no 2d3d components to it. So it's no position on anywhere, you would use that for stuff like systems, background information processing, not objects actually in the game world. For everything that is in the game world, generally, we're going to be using a two D scene. So that's going to be a blue node icon over here, just as the difference. And then user interface are the control nodes. So that is marked by green, anything that would be an on screen button, floating combat text, UI pop up windows, like when we're doing rewards selection for what you can get for leveling up. Those are all user interface nodes. So, of course, as we go, I'll explain more about what a node actually is. For right now, let's kind of cover the interface a little more. So we have the file system in the bottom left hand corner. That's where you're going to have all of your files in Gdo including any imported assets, your SEN files, which is where you take one or more nodes, and you save that to a scene, which you can create copies of anywhere in your game. So it's basically a template you can reuse. And then other Gdo and non Gudo files like resources as well. So in the Viewport here, you have your X, Y access here. One distinguishing point is down is actually the positive direction for the Y axis. So if you're talking about moving upwards, you want to go negative Y, and if you want to move down on the screen, you'd be talking about a positive Y value. Right is positive X, and then left is negative X. So that will be really relevant when we're moving our characters around the screen. You want to make sure they move in the right direction and that you're getting the input for the right direction as well. If you look really closely around the 1,200 pixel point, at least for me, there'll be the bounds of our user interface. So the user interface is usually on its own separate canvas layer. So it's a completely separate thing from the two D game world itself. You could think of the UI user interface as kind of layered on top of the camera as it's viewing the two D world. So whenever the camera moves, all the UI elements are going to move as well. But the game world objects don't move with the camera generally. So if you move the camera, the UI will stay in place and everything under it, the characters, the enemies, those don't move with the camera. And that's one of the biggest distinguishing factors between the two D nodes and the user interface control nodes. Anything that's a UI element is a control node in Gadot. Over on the right, we have the inspector which is used for defining any properties about your nodes. When you're writing scripts, you can give it a at export tag, which marks it as editable inside of the inspector. That's really important. We'll be doing a lot of that. So you'll edit that over here. Of course, there's other Windows that have varying importance. I'll cover those in the later videos as well. So in the next video, we'll get set up with our project. We'll import all of the art assets that we need, including audio, music, art, fonts. And then there's also a couple plugins I think would be extremely useful for the project, the free to download and use. So we'll cover all that in the second video. 3. Art, Sound, and Plugins ~ Import & Install: So in this video, we're going to continue with our project setup by getting all the assets and audio and plugins all set up into the projects that we're good to go to start working. So over on PaySPN, I have all of the links to the assets we need for the course, everything I used in the pre recording project for script survivors. So if you follow the link up here at the top, I'll zoom in on that. You can basically just go to each of these links, and you only need the free versions of each asset. I have to share it this way to respect the no redistribution rule a lot of these assets have, but you basically only need to follow the links to each store page and grab the free version. So, first off, Pixel Crawler has our character based model we'll use to set things up some weapons and enemies. It's really core to this tutorial, so that's what we actually base a lot of the Alpha. So next up Raven fantasy icons. For UI elements, Crimson Fantasy user interface. The font, I'm going to be using m5x7. But honestly, you can just pick whichever pixel art font you think looks good. There's a lot on itch dot IO as well. For some basic game audio sound effects, Leo passes RPG Essentials SFX free. And then for the music, The Red Hearts prepared to Dev by Tall Beard is absolutely perfect chip tune music for the style of Survivors Light game. It actually works really, really well. Okay, so you download all the zip files, and then what you can do is right click in the file system, create a new folder for art. I'm going to put all the visuals in here. And then right click, create a new folder for audio, anything that's at Sound effects or music, I'm going to be putting in there. And then to quickly open it in Windows File Explore, or the equivalent if you're on Max or Linux, is to right click and do Open and File Manager. Okay. And then you just want to extract all the files into that art folder. So here we have the Crimson fantasy GUI, we have the fantasy icons, and then the Pixel crawler free pack. The only exception is that for the fantasy icons, there's like three or four different versions of it. I was just using the full sprite sheets. These are easiest to work with in Gadot because you can create a atlas texture resource, and then just pick the icon you want to select for that. For each of the items that you're going to be assigning an icon to, I just find it much easier to work that way. And also For Gadot, if it's importing every icon separately, which is an option with that pack, it does take a long time to actually load into the engine. So when you have your images packed into one file like this, it's just much more efficient. I think it's the way to go. Aside from that, I think I was just bringing in the entire directory. I don't change the file names or anything like that. Typically, in Gadot, you would go with snake casing for your directory folder names, but I think it's just simplest to just use what the artists provided here and just go with that. So it shouldn't cause any problems or anything. Just maybe a little on optimized, but that's perfectly fine. And then for the audio of the sound effects, if you open the zip, we'll just come like this. And the music, I was only taking the three Red Heearts box jump, which is the first music track. You can actually just select whichever music track you want to use for your game level, but I think this one works really, really nicely. You know, let's just go ahead and preview for a minute. Yeah, so absolutely killer track for survivor style games, I think. So once you import everything, you put it in your files. If you reopen the Gado window, you should see everything import into these folders properly, and then it should display over here on the left. So you might get some error messages when you're importing things in the first time. You can just confirm that the files are there, and that's the main important thing here. I think these messages can kind of be disregarded. Drop Stable doesn't even have anything to do with that project. It might be related to the caching on my computer. But what's important is we have the files in here. You can double click on them. You can see that it's being recognized, like the music there and sound effects over here. So it looks like we're good. So go ahead and clear that logo. So we need two plugins for the course. One, I think it's just a huge quality of life when you're navigating the file system over here. It's called collapse folders because you might notice that when you open a bunch of folders like this, there's really not easy way to collapse or fold all of them back down so that you can see your full file system here in one window. So what you want to do is go to the Asset Lib up here at the top and then search for collapse. So click on the search bar type collapse. And then the one we want over here is collapse folders. So download that. Install it Okay, I'll say installed successfully. Now we want to go to Project in the top left, project settings and go over to the plugins tab here. Check enabled, hit Close. And what that does is it creates this little icon over here where you can collapse all, and you can have all of the folders, go back to the root. Nothing's expanded, and it's much easier to navigate the file system with that. I think that's just a really handy button. It should be there in Gadot basic, honestly. So the other one is a much more involved plug in, and we're going to be using it for state machines in Gadot It is also used for behavior trees, but because the AI is very simple and a survivor game, we're only going to be sticking with state machines here. There's no need to make it more complex with a behavior tree. So we are looking for Limbo AI, and because I'm in 4.4, I'm going to be using Limbo AI 4.4, of course. You might notice that currently here, at least, there's no 4.5 out yet because, well, I mean, 4.5 still in Dev, so it may work if you decide to use the Dv version or you may run into some issues. It probably will work, but, you know, there might be a bug here or there. So, you know, another reason to stick to the stable version of Gado. So let's download this, and this is going to be our state machine plug in. We can install it. It'll take a little longer because this is a pretty big plug in. I'm going to disregard these messages once again. That might actually be happening because the project name is the same, even though the directory is different, so I'll just change the project name so that that stuff can stop showing up. Hopefully, this will fix. So I'll just change the name and give it the name GD script there. Is it an extra tag? Let's close that I'll clear the messages, and I'll just reload the project while I'm ahead and I'll just see if there's any issues when I reload the project. No, okay, it seems good. Okay. So if we check project project settings and you go to Plugins, you'll notice Limbo AI doesn't show up here. Limbo AI is actually written in C plus plus, so that might be why it doesn't show up as a cado plug in specifically. I imagine he did that to maximize the code efficiency since CplusPlus is faster than C Sharp and GD script. But yeah, that's just how it is. So to confirm that Limbo AI works, we can just quickly create a new TD scene here in the top left. And then right click here on the root node, the node two D, add a child node and look for just type in State. You should see Limbo state pop up and BT state. As long as you see those classes from Limbo AI appearing, then you know that the plugin is installed correctly. So to recap, we should have an audio our sound effects over five folders here, at least one audio track. And the art, we should have Crimson fantasy GUI, Raven fantasy icons, and I'm only bringing in the full sprite sheet folder, and then Pixel Crawler for our main character, our background props and enemies, kind of the main art asset for the course series, really. And then a font. So I brought m5x7 as a dot TTF font. I think other types may work as well, but probably best to just stick to the same one for consistency. And then we have add ons, collapse folders, and Limbo AIA. So that should be all the external files we need to bring in for this course. Everything else is going to be GD script that we write, or scenes that we set up or other Gadot files we create, as well, like resources which can save to a file. So that's going to be it for this video. Next, we're getting started on creating our two D top down game character and adding some basic movement controls to the character, so it can move around on the screen. 4. Top-Down Player Movement: So in this tutorial, we're going to implement top down character movement for our main player character scene. But right before we get into that, I do want to point out that we can delete the demo folder from Limbo AI that automatically imports with the plug in itself. If you want to go through the demo or keep it there, you're welcome to do so. This includes examples on how to use Limbo AI, but I'll be showing you on video for those steps for the state machine specifically. So we don't actually need that for our project. I'll remove that. Okay, and now we're ready to get started. So we want to start off with a two D scene in the top left hand corner, so I'm going to click on create root node two D scene. So that's going to give us a base node two D. Now, the root of our character, generally speaking, we want to be a character body two D, which is a type of node two D. So we can right click on our node two D and go to change Type, and then we'll type in character. We'll find character body two D. Note that this inherits from physics body two D, which is a collision object. Which is a type of node two D. So there's a whole series of classes or object types that the character body is inheriting from, and it acquires all of the traits of all of the classes that come before it. So that means that anything a physics body two D can do, a character body two D can do as well. So let's hit change here to character body two D. And as soon as you do that, there'll be this little yellow triangle warning sign saying that the collision object has no shape. So we need to right click and add a child node, which is going to be a collision shape two D, and I'll just zoom in on the center 0.00 here. You won't see anything yet because we haven't actually added a sprite. But let's start by clicking on shape and the inspector on the right hand side and doing new circle shape. So we might come back to edit that and reduce its size, but first we need the sprite in order to really see what we're working with. So let's take the root node and also rename it by pressing F two. And I'm going to just call it player. You could also call it player character or player character body two D, whatever makes sense to you. And then we're going to save this scene, which is basically the root scene and any of the child nodes into one file, a dot TACN scene file in our project. So if we hit Control S to save, so then we click in the top right hand corner, this create a new folder button. And let's give it a name. I'll do characters, I'll lower case, which is typically the Gadol standard naming, snake case. So if you need spaces, you would put underscore and then other stuff, kind of like that. So let's do characters. And then in characters, I'll create a folder for the player just because I know in advance, there'll be a bunch of scripts, and then we can combine those all on the same subdirectory. So I'll create a new folder. Player. And let's say Okay. So we have player dot TSC and we're going to save that into this directory or folder. So save the scene there. So in Godot, your project is essentially composed of scenes within scenes. So you might have a main gameplay scene. We have all the system setup, and under that, you might load in the game world scene, which is the two D map and the objects that should populate and inside of that two D world, you would, of course, have your player scene, the scenes for your enemies, your orcs, your skeletons, et cetera. And so when your scene is saved to a file, it's really easy to instantiate this because it's just going to be basically taking the saved data and loading a copy of it and putting it in your scene. So really quickly, I could actually demonstrate that. If we click on this new tab, create a new scene button. We can add a new two D scene here. Let's call this the world. So I'll hit F two to rename it, and this will just be world. We can keep it as a standard node two D. We don't really need it to have anything other than a position on the screen. So let's hit Control S to save this scene. I'll go out to the root and we can just save world dot TSN inside of here. Okay, now we want to add a copy of our player into the world. So what we can just do is drag the player scene into the world hierarchy in the scene tab at the top left. So you just drag player dot TSN up here under World. And now there's a copy of the player inside of the game world. So you know it's there because you can hover over it. I'll say player here. You can see the icon for character body two D, and you can see the collision shape that we defined. Now, there's no sprite yet. We'll add that right now. So I'll hit Control us to save the world. We'll go over to player. I'll right click on player, add a child node we're looking for sprite. So Sprite two D. There's also an animated Sprite two D node, and both can pretty much be used for the same purposes. They set things up a little differently. I would say for player characters, it's actually easier to work with Sprite two D and then define the properties you need to animate on the animation player. We'll get more into the animation of a sprite in the next video. So let's add a Sprite two D node. And for right now, we're going to want to load in a texture. When we're loading in the texture here, we're not going to set a single sprite image. We're actually going to set a sprite sheet. So we get our sprite sheet from the art directory. In the Pixel crawler pack, we have, I believe it was entities, characters, body A, and animations. So we'll just start with the idol. So for this character, you get a down side and up sprite sheet. When you're doing top down two D games, you can actually just flip the side sprite sheet. From left to right, which means you don't need to create a fourth sprite sheet for left and right because you can just reuse the same sprite sheet, and it works just as well. Saves you a bunch of time to animate it that way. So we want Idle down to start just so we can actually see the character we're looking at. So we'll load that in here. So this is a base character sprite. So the idea here is that you would be able to add clothes as an additional sprite on top of it, make any customizations to it, but the animations at the core stay the same as you would develop the final character for basically different styles and look. Okay, so a few things probably stand out. First off, the character obviously has no clothes. So this is a base character sprite. The idea is that what you would do is you would take additional clothes sprites and add those on top of your base animation for the final character, maybe as like a wizard's hat or something like that. And the idea is you can swap sprites out to finish up your final character. But for the base animations, they're provided here in the free pack, and you would just build up from there. So we'll just be working with this for the tutorial. The other two obvious issues is, well, one, we have four copies of the character showing on screen at once. So we want to make it so only one shows. And the way we do that is we go to the animation tab here, and we change the H frames to the number of sprites that are showing in this horizontal sprite sheet. So I just change H frames to four. And now it's only showing the frame zero position, which is the first sprite in this idle animation. The third issue is that it's very blurry for the pixel art. So we can fix this across our entire project by going up to project project settings, and we want to scroll down to, I believe it's renderer. Nope, textures. So rendering and then texture default texture filter linear here is more appropriate if you're doing, I guess, things like vector art. But for pixel art, which has to be Pixel Perfect, you want to change this to nearest. And as soon as you do that, it's going to basically show every pixel rendered crisply, as you would expect from a pixel art game. And pretty much for the project, that's all you're going to need to do to set it up for specifically a pixel art format. Now, the character and the collision shape are a little weird here right now. Generally, for top down games, you want the collision shape to go somewhere around the character's feet. So I like to keep my scenes still centered at 00 so that I would know basically the exact position at which the character is represented in the game world and the offset wouldn't be weird or anything like that. So what we actually want to do here is offset not the base scene. We want to keep that at zero, zero, but the sprite node. So if you select the Sprite two D and you hit W to go to move mode, you can left click and hold and drag it up. If you want to kind of snap it to the Y axis, you can hold Shift down, which kind of helps you keep it from going to the left unless you go really far. And we just want to drag this until the character's feet is basically right around there. And then we want the collision shape to be a representation of the character's feet area because this is where the character would be allowed to collide with other objects. So it makes sense that the position would be where the characters actually standing in the two D game world. Um, getting used to the perspective of two D does take a little bit of getting used to because it's a little bit less obvious, I guess, than three D, where would just be more like real life. So here we just have to give it the illusion that our characters actually standing right there, even though the characterprte goes from here to here, this is the area where the character is actually standing. So we can save that there. That should be good. Just for reference that was 4.12 pixels. I'm going to actually just round that to four, just a bit of a preference to kind of try to keep everything to pixel perfect sizes. For a Pix art game, it probably won't be a big deal and cause any issues, but, you know, just in case. So now if we click back to our world scene, we'll see our character is actually standing in the game world. And if we want to play the world, we would hit this play button over here, which plays the current scene of the world. And we'll be able to see the character very, very tiny in the top left hand corner. It's actually almost impossible to see that. So the next thing we probably want is to add a camera. So let's right click on the world, add a child node, do a camera two D. And although the world automatically creates a default camera, even when you haven't added a camera node, when you actually properly define a camera, you can set the Zoom, which is really handy for Pixart because we're talking about working with, like, 48 pixel by 32 pixel characters, something like that. So we want to take the Zoom and probably bump that up to about three or four we can see the camera two D now is also centered on the zero point rather than kind of being more of this default canvas box here. We can actually see the camera is this pink line. And if we run the scene now, our character should be centered and a lot bigger and more visible. So if you want, you can bump that up to a four. I think three is pretty good for now. I'll change it later if we need to. But as long as you can see the character, I think you're good. One other thing when we're playing the game, I think I want this to be much more visible. So let's increase the window size now so that we can design for a bigger screen size more properly so that, you know, when we're doing our previews, you guys can actually see what's going on better. So let's go to project settings, and I'm going to take the window display and change this to something much bigger. So I think something like 1980 by 180 would be good. I mean, that depends on what your resolution for your screen is. For me, that's going to be kind of like this. Okay, I'll try this from my screen. I'm working on a pretty big extra white monitor here, so a little bit of trial on error. I think that'll work good because then I can still see the uh Of button over here. So play main scene pause and stop current running project buttons, by the way. I would say, as long as you can see that, then that'll be fine. So with our game Canvas, this big, I also probably want to boost the Zoom. You can actually edit the properties while the games running. So I can just take this Zoom and change it to four. Let's tap back in, and you can see that the size immediately updated. So if that's big enough for you, you can keep that or we can go up to five. You know, maybe I'll actually do a 5.0 Zoom there. Now for the moment, you guys have actually been waiting for it. Let's get into the actual code. So we need to create a script that extends character body two D and have that attached to player. We can do all of that in one go by clicking on player left click and then just click on the attach a new or existing script to the selected node. And we're just going to call this the player dot gd script. And we are actually not going to use the template character body two D basic movement script. That is, um if I recall more for platformers because it adds jumping and honestly, we can write it a lot better. So let's hit Create, and that's going to jump us into the script editor here. So for right now, I'll just keep it as the embedded script editor. But you can also click this button over here and make it floating if you want to go full screen with the script editor that can be handy if you're getting really into GD Script coding. But here, this script is going to be pretty quick and simple, so no need, really. So let's go up to the top. Okay, so we can see that our script extends character body two D. This means that our script has the base class of character body two D. Okay, so if we hover over the extends character body two D, you can see the class hierarchy here going down from character body two D, all the way to a base Gadot object. This just means that everything along the way, it'll inherit the abilities of each of those different classes. So a character body TD actually can do a lot. We're only going to need a little bit of it, really. So just keep in mind that everything listed there, each of these classes, which you can jump into by clicking on their names if you want to read up about them is something that character body two D can do as well. So to read through all of that would be a lot of documentation right now, good to know about. Good to read up as you go forward, but a bit overwhelming for right now. So let's work on our class. We want to give it a class name so that this script can be easily referenced from other scripts by name by going to the start of the line. And then pressing Enter. Let's go up to the first line and type in class name player. And now, we've defined this script as the players script. So this is basically a global symbol now in our project we can look up and use just by referencing player typed exactly like this with capitalization. You'll see as we go forward in the course that that is extremely useful, especially for something like a players script, which a lot of other classes are going to need to touch on. So let's work on the physics process function for the character. So let's type in FU NC for function and then underscore. And you'll see this menu auto populate with the methods that are defined inside of the parent classes. These are basically functions we can override and add in code so that it actually does something or changes what the base class originally did. So we want physics process here. If you find physics process and hit Enter, it'll give us the full function definition here for the name here, underscore physics process. Okay, so here we have the name of our function. The underscore here indicates that it is meant to be private as and not accessed from other scripts and only to be contained or encapsulated from within this script. The Delta here is the name of our parameter, and float is the type of our parameter. In GD script, you do not have to actually type your functions. So if you want it, you could remove this colon float and just call it Delta. Now what is Delta? Well, that would end up being whatever the collar decided to pass into this function, which in this case, basically has nothing to stop it from being anything other than a float. You would pretty much expect it to be a float. So in this case, it's much better to actually declare its type with colon and then float here. So I do highly recommend this. And then here also an optional component of the function declaration. We have this arrow, which is a dash next to the zero key. Okay, and then over here on the right, we have the return arrow pointing to the return type of void. Void means it has no return. You don't expect it to actually return anything that you would use inside of other functions. So to type that in manually, you would do hyphen and then a graded and sign. And that's your return arrow pointing to the return type. Okay, so, of course, when we type this out, our function doesn't actually have anything in it. You can have a function do nothing by typing in pass lowercase, so that's a reserved keyword there. But without that, if I undo that, then you'll see this error pop up at the bottom over here saying expected endantblock after function declaration, meaning there should be something in here after the function is declared. And you can expand this list here to see all the errors inside of your current script, a pretty handy little window there. So good if you need to copy and paste, you can just select all of it and copy. And you can paste that into any other application you're using. Maybe you're doing a bug report or something like that. Okay, so finally, we can actually start writing some function code. I know I'm going a bit slow, trying to keep it accessible here, especially in the earlier videos will definitely get a lot more intense as the course goes on. So, first off, for our physics process, we want the character to move, which means we need a speed and we need a direction that the character is going to move in. So somewhere we need to declare how fast our character moves. So we could declare it right in the function. But actually, what I want to do for right now is make an export variable where we can actually change the speed in the editor really easily by just going over here in the right and then type in whatever speed we want. And we can change that during gameplay, as well. So let's type in at Export var speed and give it the type of float. So let's walk through this slowly first. At Export here is a keyword that declares it as being able to be edited in the inspector. Okay, and then var here means variable. Speed is the name of the variable. Colon here defines the type, and then float is the type. So we can take this speed. Let's actually write pass here for physics process and then hit Control as to save it. And you'll see speed pops up here and the top right for the player script. Make sure you have the player node selected, and you'll be able to see this. Now, you can also see the default is 0.0. So that's the default value for float. You probably for speed, actually want to assign a real number to that. So I'm going to take this and say equals, let's go with 100.0 hit Control S. Now the default value loads up here automatically because we haven't set a custom value. If you set a custom value by clicking here, let's say 150 and hit Enter now the speed is 150. If we change the default to 200 here and hit Save inside of the script, then you'll see that the custom value still overrides the default of 200. I can hit the reset button here to reset it to the default, so now it's 200. Now I want the script speed to be 100. I believe that's what I had for the default project. We can always change that later and feel free to actually set your players speed to whatever you're comfortable with. That's completely. And that's how we can have our editor defined property be used inside of the script. So export variables are really useful to make it more accessible for editing the details of how our script would edit without needing to know anything about how the code actually works. Okay, so we have our speed. We can write physics process now. Okay, so let's see here. I'm going to first get the input direction. Okay, so for right now, we want to declare a direction variable, so var direction. And you'll see that I'm typing this inside of the function. So this makes it a local variable to the function, meaning as soon as the functions done, this is going to be cleared. It's only used inside of the function unless we return it as our return type. So it's temporary, basically. And we're going to set this equal to input. Now know I'm capitaling the I because this is a class name that we're referencing. So the input class is you could think of it as a global class. You can access them anywhere if you need to have it do something for you. And we are going to be using the Import class to get a vector. So a vector is going to have X and a Y component, a vector two, rather. There's also vector three for three D games, which adds in the Z axis. But for two D games, you only have X and a Y, which just by removing that third axis makes two D games a bit simpler to understand and work with. So we want to get the negative X here. You can see the parameters that this function is defining, and there's actually some extra ones over here like dead zone, which we don't need. But the four we need are negative X, positive X, negative Y, and positive Y. So in one of the two previous videos, I believe I did touch on this, but negative X and Gadot is left on the screen. So we want input for left. Now, you can see it populating with some predefined action string names. You know, it's a string name, by the way, because it has a ampersand here in front of the string, which is quotations, and then the actual name here, ending in quotations. But these are all default UI actions, and it's not a good idea to actually use those. In the sense of controlling your character because they already control the user interface by default. So let's go up to project settings. So project project settings in the top left, Input map, and we type in a new action. I'm going to type in left and then click Add. So our left action, I'm going to hit Add event here, and I like using WASD keys. It's pretty standard for a lot of games. So I'm going to just type in A here where it says listening for input and a rule. Automatically assign it to the A, physical, or a Unicode key. So basically, A on your keyboard. You can click Okay now. And now moving left is assigned to A. And we want to do that for up down and right as well. So type in right, add, and then we click over here to assign the key, add event, hit D for right. Hit Okay. Now let's put up. Click add event, hit W for up, add an action, down, click Add event, S for down, and there is our movement input. So Kidob will automatically handle converting those keypresses into the actual action that we can use inside of the input class G vector function column. So what we want to do here is put the names of those actions we just typed. So we can do quotation left here. And you'll see it actually automatically populates, and you can just hit Enter here to confirm it for negative X for positive X, we want right. You can also see here it just kind of showed up, so we can just double click it. Then the third one negative Y, we can type in down or find it on the list. So down. And then for positive Y, oh, I actually mixed that up. Negative Y is up, positive Y is down. So I'm going to add positive Y down here, and then over here, I'm going to just erase the down and type in up. So I'm pretty sure that's right there. We'll definitely find out in a minute once we play test. We want to take the input direction, and if it's not zero, then we want to move. So if it is zero, we just want to stop moving our character altogether. So you can do that by saying if direction. So this basically already checks if the direction is equal to vector zero or not. So if direction means it's not zero, then we can take the velocity of our character. Now, where did we get the velocity property? Well, our classic stands character body two D, and velocity is a property of the character body two D. So if I was to right click on character body two D and go to Lookup symbol, can scroll down here and see velocity pops up right here on the list of properties. So any properties or methods, which is a function within a class that belong to a parent class are going to be inherited and usable by that inheriting class. So inside a player, we can take the velocity and we can set that to we want the direction times the speed. Now, you'll see this Delta component here. This is the time between frames. We do not need to assign it here for the particular movement style we are using, which is move and slide. There is another one move and collide, where you would pass and the Delta when you call that function, if I remember correctly. So it's a little odd that, you know, the Delta is automatically accounted for here, but that's just how it works. So any key press for movement is pressed. We're going to assign the velocity to the direction times the speed. Then down here after that, we can do move and slide. Now, what would happen right now? Actually, it would be easier just to show. Let's hit play, and we haven't select a main scene yet. So let's select the world scene as our main scene. I'm just going to go up the directories here and choose world. Double click. We have a character. And if I press a movement key and I let go, you'll see that it doesn't stop our character. It'll just kind of keep sliding, which could be used for some kind of lighting mechanic. But for regular movement, we actually want to add a se statement here as the follow up to our I. So se and then make sure you put the colon at the end. So that basically declares what comes following as a block for the function to continue executing code. And we want to take the velocity. We want to reduce that towards Serrel. Velocity equals vector two. So we're actually creating a new vector two here, and we put parentheses in here. You can see that the third type of constructing a vector takes a X and a Y value. So inside the parentheses, hit Enter. So this is going to separate the first parenthesis from the second and makes it very clear to type the X logic here and then the Y logic right beneath it. So we want to move toward the velocity dot x to zero. And we're going to just do that the current speed. So this basically means as soon as we let go, we're dropping the speed to zero immediately. There are a lot of different ways you can do this. You can have it gradually decrease over time if you use a larp function. But this is probably the most straightforward. So we'll just go with that. Move toward. And then on the second line here, we want to get the Y value for the new velocity. So move toward the current velocity dot Y, we're making it approach zero at the change or Delta of speed. So we save that there, and this means this is going to take our current speed, which might be, let's say, up into the right at full speed, and we're going to drop it by the full speed amount, meaning it's immediately going to drop down to zero. We're approaching zero. So as soon as we let go of our input keys, our character stops moving, is the end result of. Actually count that. So to declare a comment, if you want to do that, you put a hash tag and then the description. So you can keep your comments inline if you like, generally. These days, I actually pull them out to the top of the function like this, so Control X and then Control V to paste it. And if you want this text to show up in the automatically generated documentation for your class, then you can do two ampersands. And save it, and then that's going to add it to the player class documentation. And we can access that documentation by hitting Search help here. We search for player, our custom class, hit open, and you can see the name of our properties, which in this case is just referring to a variable that exists as part of the class local. In this case, we could just refer to as a variable that is a local state existing inside of the class rather than a local variable of a function. So you can edit and read from these as you need. In other programming languages, things like fields or properties are a lot more clearly defined. But I think generally for most basic GD Script functions, your property and variable end up being the same thing. You wouldn't declare something like a backing private field like you would in C Sharp. I would say in most cases, though, because, you know, there would still be some reasons for doing that to have a private backing to your public property. Here you can see the method here and if we scroll down the method descriptions. So physics process. As soon as we let go of our movement keys, our character stops moving. So this is actually one of the coolest thing about GD script is that it is essentially self documentation generating. As long as you put the comments where they're supposed to go with the double ampersands, you can comment a field or a property. You can comment a method. And this is really helpful. It makes it easy for other people to basically understand your code without reading the entire script from top to bottom. So you could actually do the same thing with speed here. So two ampersands and say the base movement, the base, the base movement speed, of the player character. Control S save, and then you can see our documentation is still opened here in the script window. So if I click on Player, you can see, oh, now, our speed property has a description which can be easily read. So that's really handy. Keep that in mind. And now we can go ahead and hit Play. So we're going to move our character WASD. Now we will see as soon as we stop Import, the character stops moving. Which is more what you would expect for a character moving on anything standard like grass or stone or whatever, things that should have friction, not ice. And that's the basic movement we'll be using for the rest of the game, basically. Now, obviously, the characters not animated. We don't have the run animations. It's not facing the direction as it should yet. Those are all things we're going to cover in future videos going forward. One last thing I want to add into one last thing I want to add in for this character, well, we're ahead of things, is to add in just one more display name property. So I'm going to actually add a export for a var, a display name. And this is going to be a type string name. And we're going to set that equal to ampersan whatever default you want, you can just make it player and hit controls. So for our player, we can see now the display name string name property is editable inside of the inspector. So I'm going to change this and set it to Jaco, because we're going to make our character by default, have a spear weapon. So I think that works. In survivor light games, generally, you would have multiple characters you can select from. So having them all have a distinguished display name makes sense. Now, real quick, there's also a name property all nodes have. That refers to the node name up here. But you can see things like collision shape two D. You don't port spaces and node names. So to use that as your display name might be a little weird. Like, let's say you add a subclass like fire dragoon, right? But you might make the player node name here, Freragoon without this space, so you can have a distinguished separate display name by having a custom property like this. So what's that that to dragoon? Hit Control S. And that looks acceptable for right now. Eventually, we'll take this input and we'll move it out to a separate script. We'll take stats like character speed, and we'll also make that a separate script. But this is a good start. So that's going to be it for this video. I tried to slow down here intentionally to cover all of the script terminology that we're going to be using for GD script as, you know, the foundation for the rest of the course, these are all things that are going to keep coming up again and again. I hope that wasn't too slow for anybody, but we are going to be speeding up from here. So until the next video, I'll see them. 5. Play Animations from Spritesheets: So as things are right now, our character can technically move, but there's no animation to the sprite whatsoever. So we want our character to animate when it runs idols and, of course, the death animation as well when we lose the game. Okay, so the first thing we're going to do is actually change out the sprite sheet here. If you look in the Pixel Crawler pack, we can grab the Idol sprite sheet for the night, or you can use the rogue or the wizard if you prefer. So I'll grab this and bring it over here. And these are basically side facing sprites. You'll notice that this character doesn't have a up or a down. A few advantages to this, though. First off, if you're only animating one direction and then flipping it, which is what the standard survivor, actually does. It's a lot easier to build characters because you only need to animate one direction. And secondly, you can completely skip over animation trees and just focus on an animation player and a state machine which controls the logic for your character switching between states like Idle and given those reasons, we're actually just going to switch over to the night Idol sprite sheet here. You could use the idol pretty much interchangeably. You could later use, like, the rogue sprite sheet, like right there or the wizard one over here as well. So either of those are going to work perfectly fine, and there's already the setup for having three character classes right there. But it was going for more of a spear wielding knight to start off with. So like a character's named agroun, this spright kind of fits it, and he will be able to throw spears. Okay, so we have our sprite sheet set here. We want to turn this into an idle animation. The He frames have already been set up here, but we need an actual animation player in order to do that. So right click on the player node, add a child node. Look for animation player. Add that in. So when you have the animation player, the animation window will be here at the bottom. You can click right here if you still need it. And we want to go to animation and then Nu and type in the name of the animation. So we're starting with the idle animation, so we'll just type in Idle. And we can add in a property track. So we're going to grab from the Sprite two D some of these properties, namely the texture. So double click that. We're going to right click Property Track, add in H frames, and then property Track, Sprite two D. V frames. Then lastly, for right now, at least we need the frame number itself. So add and frame as a property. So now we can take this Zoom and zoom in a bit on our sprite sheet. First, I want to take the snapping time down here where it says 0.033 3 seconds and change that to 0.1, meaning that we're going to be working in 0.1 second intervals or a tenth of a second. Then enable this snapping to timeline cursor. Now you'll be able to just kind of click here to the 0.1 second intervals, makes it a lot easier to work with. You might have seen on this bright sheet that there's four frames of animation. So we're going to cycle through those every 0.1 seconds, which means four times 0.1, we want a 0.4 second long animation. So over here on the right side, type in 0.4, and then this thing that looks like arrows feeding into each other. Animation looping, you want to click that so that'll make our animation loop. Now, in our animation timeline, there are no keyframes we definitely want to set a keyframe at the start of the animation for all of these properties. So right click insert key. If you left click here, you'll see that the value for the texture is already preset to this night idle animation. If it's not, then you're going to want to change it to the Idle sprite sheet. From here, you can drag and drop like that and put that there. So what this keyframe means is that at the start of our animation at 0.0 seconds when this animation starts playing, it's going to set the texture on our sprite to this value, which means that as soon as the animation starts, we change out the sprite sheet. Now, H frames, we also want to right click and Serta key. Now we already predefined our value here as four in this bright two D. So you can see that when you Serta key, it'll add whatever value it's currently set to as the key frame point on the animation timeline. And whenever these keyframes hit, that means that the value at that time is that value. Now, for our Pix art game, almost always we're going to be using discrete mode over here on the far right. You can see these three dots. What that means is that the value does not change until it hits the key frame point. In something more like a three D game, you might do continuous animation, meaning that it'll gradually go between the values of keyframes. So if you had something like a position keyframe, then it will animate the position between the starting position of zero and the end position of 100 over time as this animation timeline progresses. So a lot of animation in games is actually just changing the values of properties on your object as time goes on. Okay, so continuing to V frames, we can right click insert a key. You'll see that the value here, if we left click on the keyframe is already set to one. That's what we want. Once again, if we look at the Idol sprite sheet, it has four H frames, horizontal frames, and one vertical frame in terms of rows and columns. So that's why we have four for H frames and one for V frame. Not a lot of customization we have to do here. We just have to make sure that at the start, it's set to those values so that our animation plays correctly. Now, what we actually need to do is put in our four frames of animation here. So right click on the first frame and do Insert key. You'll see that for frame, it actually shows you a preview of the sprite. It's going to show very helpful, but you can also left click on it and see that's set to value zero. Now we want to snap to 0.1. If you didn't already choose apply snapping to timeline cursor, you can do that here. That'll make it much easier to go to 0.1. I guess you can also manually type in the value if you want to jump in the timeline. I usually just snap with the mouse cursor, though. Then right click on the frame section, the frame row and do insert key. Now you'll get a line that goes between these. That line means that there's no change in value between here and here. It's just a continuation of the same value. So we want to click here and change that to one, and that'll mean that now it's going to be playing the second frame of animation. Since the Bright sheet is like an array of values, it starts at zero, not at one. Generally, in programming, that would be how it works. So one is actually the second frame of animation and zero is actually the first frame of animation. Let's right click on 0.2, insert a key, click on the key frame point, and then change the value to two, and then go here to 0.3. Right, click insert key. Click here, change it to three. Now, you shouldn't see any lines going between any of these frames anymore. We can go to the start here, hit play, and there is our animation for the character. Now, once again, because we're only doing left and right animation, we're only going to need to do one idol, one death, and one run animation here. And then we'll control flipping of the character a different way. I'll show you that later on. So a much simpler setup and a lot easier to add new characters in if you want to do that. So let's actually duplicate this animation because a lot of this we're actually going to reuse. We're just going to change maybe the He frame value and the texture. And depending on how many run frames there are, we may change the frames here as well. But let's duplicate it by clicking here on animation and do duplicate, and let's do Run. Okay, now we want to click on texture, and we want to drag and drop the run sprite sheet and to here. We can see this has six frames of animation. So what that means is we need to change the H frames value here to six. We want to change the animation duration to 0.6 because we're working on six frames now, not four. And then we're going to want to go to 0.4 seconds and add in the fifth frame of animation. So insert key, click here, change the value to four for the fifth frame and then go to 0.5 seconds. Right click here, insert key, left, click on the keyframe, change the value 4-5 and hit play. And there's our round animation. So duplicating saves a lot of time. Okay, now let's add in the death animation while we're on a roll. So let's duplicate and add in death. And then we want to change the texture here, of course, to the night's death sheet. So drag and drop the death sheet into the keyframe inspector. I can count here. There's six frames. So let's click. Actually, we don't need to change anything else because we just duplicate it from around. We can just hit Play. But you can see it's looping. So characters don't usually do that. So we want to actually disable loop by clicking twice here. It'll make it so that the animation loops back and forth. Like, it goes to the end and then back to the start. That would be more useful for something like a moving platform. We want to click a second time to turn off looping altogether. So go to 0.0, hit play, and the animation should stop here at the end. And then once the death animation is done later, we just need to do something like key a Game Over for our character, and then that'll be that. Okay, so which animation is actually going to be playing when we run the game? Well, let's hit play and find out. Okay, so you can see the idle animation is there, but it's actually not playing any of the animation. Uh, we didn't actually set an animation to Autoplay on Start. Our animations are eventually going to be controlled with something called state machine. That'll be what we get into more in the next video. Let's click on Animation player and change the animation here that we're looking at from death to run. And we'll click over here this little Autoplay on Load button. So click that hit play, and now you'll see that regardless of if our character is moving or not, it's going to be playing the run animation. Note that it also doesn't flip directions automatically. That'll be a simple script we add in later as well. But we have three animations on our character, and we have the ability to switch between them by calling them by name. In the next video, we'll create the state machine with a move state that will be able to switch between idle and Run. 6. Player State Machine: Okay, so our character has animation player and the ability to play the run animation, but we want to switch between idol and run animations depending on if there is key input for our character or not. So let's control this with a state machine. So if you recall at the start of the course, we added in Limbo AI, which is a state machine and behavior tree system that you can use in Gado out of the box, saves you a lot of work, not having to do it manually. Going to be sticking to state machines for the course. I think they are just a little bit easier to work with going forward and much better for simple setups like this. And this is, to be honest, a very simple setup. So let's right click on the player at the base at a child node. And we're going to be looking for the keyword limbo. So you'll see here Limbo HSM standing for hierarchical state machine. As in the root node here, the state machine itself is going to control the states, the limbo states, and it's going to be able to switch between them as we add transitions between those states. So let's create the Limbo HSM. And then I'm going to right click here and we're going to add our first state add child node. So we want a limbo state and just create that. So the hierarchy is the root state machine, and all of the states are what we can switch between. And each of these states are going to execute different code depending on what state the character is in. For this course, we're really only going to have a move state and a death state. So very simple. But still important because we want to know when our character should switch to the death state, and the move state is going to be controlling the idle and move animations. Technically, you could separate Idle and move into their own states, but they're essentially going to be executing the same code because whether the character is idling or moving, it's still free movement for the character. It's just a matter of which animation it's going to be playing. And I don't think that necessarily justifies a completely separate state. So let's right click on Limbo HSM, and I'm going to attach a script to it. This will be called, let's say, player underscore HSM. So it's the player hierarchical state machine. Let's create that. And then up here at the top, we're going to, of course, want to give it the class name, player HSM. Generally speaking, as I'll show in the file system here, if we search HSM, whatever you put as the snake case for the filename, you would also want to have the class name up here, but this, of course, has capitalization and you skip the spaces because this is a type symbol up here. It's a very good idea to keep this name and the script name synchronized. So the player HSM is going to help us to control the player character body through the states that exist on the Limbo HSM. So we want to grab the player and set it as the agent that the state machine controls. Okay, so first for our player HSM, we want a reference to the player that our state machine is going to be managing. So let's do at export var player of type player. And if we've defined the class name for our character player body script as player, this should show up fine. And you can see in the inspector over here on the right that it did show up. So if you want, you can click Assign and assign it to the player right now. So that means our player hierarchical state machine now has a reference to the player. So what we want to do is make sure that on Rady, that this script is going to set up the state machine by initializing it with the player as the agent. So let's create function, underscore Rady, and the return type is going to be void. Pretty much always is for ready functions because this is a default node function meant for setting up your node, as in one of these nodes over here on the left, something that exists inside of your scene after all of its properties have been defined. So the ready function executes the code that when the object is ready, it's going to do some final setup steps. So very common for using before the object really starts running in the scene, but doing the essential final setup steps. While we're going ahead with this, I'm also going to introduce some new concepts as well. So first off, the assert statement. So let's type in assert. So this means you pass in some condition and expect it to evaluate to true. So in this case, what we want to check is that the player has actually been set in the inspector. Whenever you set a export variable here, you're basically depending on the designer or whoever setting up the game project to actually fill in these fields, and it's very easy to actually for to define one of these, especially if you have a lot of scripts running around with these export variables. They're very convenient for setting things up in the inspector, but it's user error prone. So we can just assert that the player is not null. Now you see I do exclamation mark and then a equal sign. The exclamation mark means you invert whatever you're trying to test. So if player equals null, which would actually be two equal signs like that, then you would check that the player is null. But if you do an exclamation mark and an equal sign, then you're saying the player is not and whenever you do an statement, you have the option of also passing in an optional failure message. So what do you want it to say to whoever's running the script when that condition failed to be true? So in this case, I would want something like player agent must be set on the HSM. So hierarchical state machine. So I'm just putting up here at the top, mentioning very clearly that this is a hierarchical state machine. So if you ever have any question about why HSM is, well, it's right up there so let's show this in action. If I run the scene, it's not going to error because we have set the player node as the reference on the export variable for a player. But if I unset player right here and we run it, you're going to get an immediate error message. It's going to say assertion failed. Player agent must be set on the HSM. So the assert is just a check to make sure that what you expect to be true is actually true. So writing statements against a condition is the general idea of testing inside of programming in general. To go more advanced than this, you would write unit tests or integration tests using a testing framework like GD Unit or GUT Godot unit testing. That definitely goes beyond the scope of this course series, but it's something to look up if you have the time later on because writing tests, make sure that your code is actually written good, and it can be helpful in the long. Catching bugs early. So let's move on with that. We're only going to use these little assert statements like this. You can see how it works. If the condition fails, then we get a message, and that lets us know that, oh, we forgot to set something that we should have set something. So it's pretty handy, actually. So let's make sure that in Limbo HSM, once again, we assign the player. And then let's do the setup of the HSM itself. So I'm going to actually make this a separate function because this is going to do a few different things. So let's create another function down here, function. And when I say function, I really mean FU and C, just say. Underscore set up HSM, and then the parentheses, the turn arrow and void had an extra space there. So what we want to call on the state machine is initialize, and we need to pass in an agent. As I mentioned, the agent in this case, is the player to pass in the player, and then we need to call set up HSM. Inside of the ready function. So underscore setup HSM. Okay, so to walk through this, I'm making it a private function with the underscore here, meaning that we should only be executing this inside of this player HSM script. The initialize does all of the background setup for the state machine, and it's going to make it operate with the player as the agent. The other thing we need to do for right here is set the initial state. So which state are we going to be executing first? Let's also rename the Limbo HSM node to player HSM. I like to also keep this kind of synchronized with the script attached to it. So player HSM here. Equals player HSM inside of the scene window. And then the Limbo state here, we'll call this the move state and then attach a new script to it. Also an advantage of if you rename it before you attach the script, if you right, click on the state and then attach the script now, you'll see that the name of the script also defaults to the name of the node. So keeping those three things synchronized up generally a good idea keeps things organized. And we'll just save this into the player folder by default for now, and let's inherit from Limbo state. Create. Okay, so I'm going to call this the move state, class name move state. We might actually re use this for the enemy as well. Since they're all going to be essentially using the same animation setup. So the move state, we're going to need the names of two animations, which are our idle animation and our move animation, which is run. So I'm going to say export for Idl anim, and we're going to set this up type to a string name. And we're going to default the value to Idle. Now, putting the ampersand here to make it a string name is actually optional. When it's declared as a string name here, the variable, it's going to automatically convert. So it's optional if you want to do this as Ah, so let's do export var. I'll just call it run animation because that's what it's going to be called by default in this project, and we'll do equals ampersand. Putting a couple comments here two hashtags makes the comments show up in the inspector. So if I go over to the inspector and I hover over idle animation, you can see the comments actually show up there automatically, which is very handy. You can also see how the property is defined as a string name with a default value of idle or run for the and for this, we want to make sure by checking the animation player that our names match up. So you can see I was doing a lowercase idol, lowercase run. It is casing sensitive when you're using these strings. So make sure that it matches here and here, whatever it shows up in the animation player for idle and run. Okay, so I'm going to right click on Limbo State name here and look up the symbol. And you're going to see that there's a bunch of special methods that this node has enter, exit, and setup and also update. So these are state specific methods. They do sound kind of similar. To what you would have on a standard Gadot node. But the difference is that these only execute when the state machine is actually using this state. The state machine only executes the code of one of the states at a time, and the other states basically just sit there passively until the state machine has determined that it's time to transition into a different state. So that means you can write basically four sets of code and only one is going to run at a time, and that's very handy for keeping unnecessary stuff from happening while your character should not be doing that stuff. Like a character should not be attacking when the character is only running and there's no attack input. Therefore, you don't allow that code to execute by managing it with a state machine. Okay, so we want to start off with this enter function. The virtual keyword for these functions here means that although there might be a default implementation for the function, we can write our own custom code, which completely changes what it does when these functions actually execute. Let's look into the move state script, and we're going to be doing function underscore Enter. So what do we want to occur when the move state enters inside of the state machine? So we want to take the animation player, and we want to set its animation to either idle or run depending on if the character is moving or not moving. Okay, now for our move state, how are we going to get a reference to the animation player? So the idea is that for our agent, it's only going to have one animation player, the animation player of the player. There are several ways you can actually get that reference inside of your Move State script. I'll just try to show the one that off the top of my head, I think would be the most practical here. So if we go to the player HSM, then we can export a reference to the animation player. So let's do at export var animation player, and then Colon animation player. Okay, a comment to make it clear that this is the player's animation player, and then we assign it in the inspector. Now, the reason I'm doing it in this script rather than the children's scripts, like the states, is I just want only one exported reference here, and then we'll use that in order to get the animation player from our state scripts. So if we jump into the move state script, I could just very quickly get the reference that the player HSM here at the top has by doing something like get parent and then dot animation player. So that's really all you'd have to do there. And now we can assign that to a local variable if we like. Other option would be we could have a method which always gets this or we could cache it here locally. So we just need to take this value here and then assign it to a local variable. So var, I'll say underscore animation player, which is of type animation player. Then it could be on Enter, but I'll change it to setup later. We'll do Underscore Animation player. Equals, Get parent. Yeah. You can see it autocomplete, got rid of the function name there. This should be your full line here. On Enter right now, it's going to assign the animation player to whatever the parents animation player is. So that makes it a little easier than having to have export variable reference on each of these state scripts. The state scripts instead just depend on the parent having a reference to the animation player, which I think is fair because a player HSM is really going to be controlling the animations on the player anyway. So I think that the setup makes sense in this case. Now, rather than on Enter, which will be called every time the character enters the state, I will implement the setup function. Function, underscore setup, and then autocomplete that returns void. We'll take this line and put it up here like that. So the setup only runs once when the state machine initializes. So basically, we don't need to get the animation player every time we enter the state, just one set setup. Now, what we do want to occur at Enter is that we play the right animation. So right now, we don't have the right way of determining the animation because our code for that is actually trapped in the player script with this direction being set to a local variable. So we're going to have to pull that out later for this to fully work. But what we can do right now is do animation player dot play, and let's just say let's start with the idle animation. Now, no, I'm putting in the variable here, a reference to idle animation, which will resolve to the string name. So this expects a string name, but we're not hard typing this down here. We want to make sure it's the variable so that we still have the option of changing it in the inspector, if that makes sense. Generally, as a rule, if you have something you want to change, that's a string, you want to bring it out to some kind of variable rather than hard typing it every single time you reference it in the same script. Sometimes, if the message is only going to ever be used once in that one specific spot, it's perfectly fine to type the string your code into your function code rather. That can be fine, too. Okay, so it's going to play the idle animation as soon as the move state starts. Now, to test that, we want to turn off the auto run inside of the animation player. Or maybe we don't actually. Let's stop the game and run it as it is, and it should actually go into the idle animation or so I thought. So let's try churning off the auto play on load here. Now, without Autoplay on load, I think it should play nothing. And so we still have the problem of it looks like the state's not entering because the animation isn't playing. So what we can do to prove that is to go back into the script editor. And I'm going to set a break point here by left clicking to the left of the line number in the script. And this is going to mean that when it hits this line of code, it's going to break and allow us to see what's happening in the debuker. So I'm going to hit play, and it's not going to hit there, which means that the state never actually entered. It's not active. So that means we need to go back to the HSM script and make sure that the move state is actually active at the start of the game. So let's go into player HSM. And I think what I forgot here was to actually call set active on the player HSM itself. So we want to set active true. We initialized, which runs the setup, but we haven't set it active, which actually makes the state machine start running. So now if we hit play, we will hit that Enter line on the move state. So this proves that the move state is actually being recognized by the state machine. To go a little more detailed, you can look at the stack trace in the bottom, and you can see, if you read carefully, that the move state enter function ran, and it ran after setup HSM ran, and you can actually click here and jump to the line of code, which caused Enter to run on the move state. See set Active true here, which calls the Enter on the first state that gets ran. And this all occurred, if you click at the bottom line on ready setup HSM. So you can see, basically what led to enter being called. So it was the ready function on the HSM called the setup HSM, which called set active and then set active made the move state run. So now if you haven't already, you can uncheck this breakpoint and you can click down here to continue or press F 12 on the keyboard, and you can see the idle state is going to run. If you want to change that to the run state, so we take run here and replace play Idle animation with play run animation, hit play. And now you can see that it's playing the run animation when the state enters. So in the next video, we'll focus on actually controlling the move state with player input to make sure we're playing the right animation at the right time. 7. Player Input as Component: Our move state is playing the animation on the animation player for our character, but right now it doesn't have the code to know when to switch to the run animation, when there's actual player input. So to handle that, if we look at the player script, we can see that this is where the direction is being acquired for the player, and we're doing it with a local variable for the direction. So this works for this physics process function, but then we can't use the input direction anywhere else but here. So we would want to bring that out to some kind of property variable that can be accessed by other scripts. Now, we could do something like VR direction here of vector two, and then change right here to set the local variable direction. And that would probably be the simplest option. Together. However, I think if we kept going down this route and we put all of the input inside of the player character body script, then we'd really be breaking a separation of concerns. So the player character body script is mostly about just moving the player. If we add in all of the input functionality, acquiring all the players input into it, then now the script is going to serve two or three or even more purposes as we keep cluttering up the player script. So I'm going to try to bring this out to a separate node script that our player script can still use, but we can also reference on, let's say, our states in order to handle all the input for our player character. So let's right click on the player node, and I'm going to be adding a child node. Let's type in node here. So this will be just a basic node class, create. And I want to rename this to be player input. Let's attach a script to it. Right click attach script, player input. And we can give it the class name, player input so that it can easily be referenced in a export variable. So now from our player script, we need to basically grab this, and I'm just going to control C, paste it over to player input, so I can put it there. But where we want to actually get the direction is inside of the input method. So right now in the players script, physics process is executing every physics processing frame of our game, which by default is something like 60 times a second. We probably are not pressing the keys 60 times a second, so that's a lot of unnecessary get vector call and it probably wouldn't matter so much for one player character. But in general, I think it may be slightly better on the player script to handle the direction input inside of the function underscore input method. So function underscore input, basically, whenever an input event is sent to the game, then it will go through this method and see if there's any code for it to respond to. And the way that we check if the event is actually relevant to us is we would basically check the action name on the event itself. So we'll type in if, and I'm going to make a new method if underscore is movement action. And we're going to pass in the event to this function. Then we're going to do some stuff. Otherwise, we're going to just pass and not do anything yet. What is the I movement action function? Typically, we're just guarding against other types of actions, and we only want to process the input if the action is a movement action. So we'll say function is movement action. Of type event, input event. And then this is going to return a Boolean. So a boolean is a true false value. Is it a movement action? True or false? So this is going to look something like this. Return event that is action, and we're going to actually just copy and paste these names for right now. Okay, so we can see here I'm repeating the same string here that's up here. So generally, I would try to bring those out to its own separate variable. But we could Yeah, why don't we kind of do that right now? And then I'll show you one step we can go beyond that, even. So let's put a variable here, Var left action or actually, let's just say var left is of type string name, and it's going to be equal to this value right here. So let's Control C, copy this, paste it down here, and then I'm going to select this again. I'm going to hit Control R to find and replace, and I'm going to replace it with left. Now, when you're using this tool, it's going to replace what you have currently selected. So I want to replace this with the left name, so I'm going to hit Replace. Now, the second one, which it's automatically going to jump to or maybe it doesn't Uh, right here, we do want to keep as Ampersand Quotation left. So let's click Next match, and we'll find down here we want to replace this with left, so hit Replace. And that'll be basically it there. We should probably just take care of this error message right now, too. So I'm going to declare this far direction, and we'll worry about assigning this properly in a minute. Okay, so that got rid of the error message, you can save the script for right now. So right now, I movement action is returning whether the action is the left action. So does the name of the action on the event match the name left? Now, we have three other actions, so we need to check three other conditions here. So let's quickly bring this out. I'm going to select this Control X to cut it, type in right. Go down here, create a new variable var right, string name equals, and then Control V to paste it in. So we have the variable assigned to the string name right. And do the same thing for up. So select this. Control X to cut, type in up. Go down here. Var up is of type string name, and equals Control V to paste it N. And just one more time. Control X. Type in down, go down here, down, string name equals, Control V, paste it in. Okay, so now we have our four variables which correspond to the actual string names that we're really passing into methods like this. You want another fun trick, you could tab these over like this, select right behind the equal sign hit tab, behind the equal sine hit tab, behind the equal sign hit tab, behind the equal sine hit tab, like that. And you can actually line all of these up and it looks pretty and all that. Might be a little more readable. Up to you if you like it to have the tab or not. Okay, but now the important thing is to finish this method, we need to check the other actions. So we're going to do space. And then the straight line character, which is shift and right under the backspace, the vertical line. So the vertical line, vertical line, and you have to hold Shift down to do that, at least on my keyboard. So those two vertical lines means or. I guess you could actually type in or like that, and it would be the same. And we could just try that if that actually is a little bit easier to read and do event dot is action, and we want Right. Hold on. Write the variable name. Good. So, it looks like that works. So let's just do that again or event I action up or event is action down. Okay, so hopefully that makes sense. We're checking if the event is left action, right action, up action or down action. Now, this line is kind of going a little long. You can see this like cutoff point vertically. So these are kind of just general guidelines here. You can see that we have 107 characters on this line. Not a problem. I mean, I think it's pretty readable, but if you want to make your code go down more vertically, then you can actually break it up here by pressing Enter. Now, if I hit Control S here, it's going to give us an issue. It's going to be expected expression after or. So it currently doesn't know we're trying to jump down to the next lines. So what you actually have to do is put a backslash right after the O, and now it knows that this continues on the next line. So you can keep doing that like this, Backslash Enter to put a new line, and then backslash Enter and put a new line. Now, I would argue this looks pretty clean here. So we have our four conditions, and it's going to check all of them, and as soon as one of them evaluates true, it's going to return true. And if none of them are true, then it's false. So if the action is a movement action, then we're going to be able to process our movement code. Okay, so this is where we actually assign the vector. There's two places we're going to use the input. One is for the facing direction and one is for the movement direction. So let's get the variable for the input. Var input is of type vector two, we're going to set that equal to Import dot G vector. And just like before, we want left, right, up and down. Okay. And there's our input. So whenever we press any key, we want to set the directional input. So direction is just going to be equal to whatever Imports value is. But with the facing direction, it's a little bit special. A character in a four directional two D game cannot face vector 00 because that would be basically looking at itself, not looking to the right, up left or down, which are the only directions it can look. When we're assigning the facing vector, we only want to do that if the direction is not zero. So if input does not equal vector 20, so for a vector two direction of zero, that means X is zero and Y is zero, remember, vectors have X and Y component. So given that that condition is set, then we can take facing and set that to the input. And we'll get rid of this little pass here because we don't need that. And then at the top here, var facing as of type vector two. We can just default this to, let's say, vector two dot right, which works perfectly. If our character is always expected to face right by default on load, you could, of course, make that left instead if you want. And then the direction here, we want to replace this with vector two or if you want to be a little more fancy, although this is the default value as well, we could say colon equals vector two dot zero. So this right here actually means that we're inferring the type of the variable from the assignment, and the assignment is a vector two, which means that this is automatically going to be assigned to a vector two, which means you don't have to type this vector two is equal to vector two. That just looks kind of funny. So, likewise, you can actually just do that down here as well. And make it a little bit less wordy, although both will work perfectly fine here. The only downside is if you change this vector two dot right into something like a string, that this is also going to actually technically be correct because now it's assigned to the string of vector two dot right, and that means that this is now a string. So, you do have to be a little careful about that. I haven't actually seen that be a problem. So I think in these cases, inferring the type of the variable rather than declaring the variable type outright is perfectly reasonable and fine and often a little bit easier to read. So up to you, you can do both. If I'm looking at things correctly, I think that is basically our player script for now. We'll come back and add signals to it a bit later. Okay, so in our player script, let's change things here. So instead of having a local variable for direction and assigning the direction instead of physics process, what we are going to be doing is hitting at Export var input is of type player input. And down here for physics process, what we want to be doing is getting the current direction from the input. So var direction, and I'll infer the type from inport dot direction, going to be assigned to the local direction, and then we use that for the rest of our script by checking, Is the direction actually a value that's not vector 20 is what this line means here. And if so, we take the direction and multiply it by the speed. The difference now is that the input is managed by a completely separate script. Our player script is kept more clean, and we have a reusable player input component that could be used for completely different character designs that need the same kind of player input, even if the character actually handles the mechanics of its movement completely differently. So let's click on the player script, and we want to make sure that Import is assigned. So like before, what we could do is put function, underscore ready, and assert that input is not null and give it the message. Input must be assigned for the character body to move properly, run the game, see that our assert fails, which is good. Click on the player and assign the input to player input, run it one more time, and see that it actually works. And if you do WASD, you can see that it's moving based on the input from our player Import script. 8. Switch Animations with Signals: Right now our up down left right directional input is controlled by the player input script, but we don't have the animations being controlled by the direction on that script yet. So if we took a look at the move state script, we could imagine that we would be able to just grab a reference to the player input and get parent on it just like before. This method, although it works fine generally, it depends on the player HSM script, having a reference to all of these properties. If we change the player hierarchical state machine script, at any point, it may break how this script works, and the script also has kind of a direct dependency on the parent having all of these properties. So we wouldn't be able to reuse this on a completely different state machine that doesn't have, let's say, animation player exposed. So what you can do as another option is to use blackboards. So we look over here on the right when you have the HSM selected, you can see Blackboard plan is an option. So when you create a Blackboard plan, it tells any child nodes in the state machine system what it is going to have access to. And in this case, we want the facing direction of the player input. So I'm going to create a new Blackboard plan here on the player HSM, and then hit Manage. We can go over here and add a variable, and we want to add let's go with facing. So when we're talking about the animations, what's more relevant to us is the direction the character is trying to face rather than the direction that the character is moving in. Those would usually be the same thing, but not always. So we'll have facing here, and then on the dropdown, vector two, so we can click Manage over here and then add a variable. And we're going to look for the direction vector two. So change type from float to vector two. And then we can hit Okay, so in order to get this direction too and have it be used across our different state machine states, we want to go to binding, and then direction, we want to bind this to the player Import script. But if we do that right now, nothing's going to show up. So one of the limitations in sight of the Lambo AI system is that to bind a property onto the Blackboard plan, that property needs to be exported variable. So if we go to player Input, we can take direction here and just give it at export and then save. So you click on player Import, you'll see that the direction shows up here. Technically, this means that you could change the direction before the game starts. It's going to immediately be overwritten by this Iport function as soon as you press a key. So there's not really much of a downside. It's just that the direction has to be exposed for this to work. So on the HSM, we can now bind to the player inport script, and we're going to be looking for the direction property. So choose direction and hit open. Okay. So if you read this now, the direction is now binded to player Import direction, which means that whatever we set on player Import direction is going to be mirrored onto this Blackboard, and we'll be able to use that inside of our scripts like the move state. So while we're at it, we can also set the animation player onto the Blackboard plan, and that will remove the dependence between the state and the HSM. Let's manage and add a variable, I'll say animation player. And let's go to type and choose Node path, I believe is what we want here. Okay. Click Okay. And we assign this to the animation player. So now if we read from the Blackboard for the property animation underscore player, we should be able to get a reference to animation player. Okay, so now if we go to Move State, how this is going to look is more like this. So underscore animation player is going to be equal to Blackboard. So the Blackboard is created at runtime, based on the Blackboard plan. So this is basically the runtime object where we get the variables we need. So we need to do Blackboard dot, Get VR, get underscore var, and we're looking for the property name. So, in this case, that's going to be animation underscore player. The default will be null. We do want it to complain if it can't find it because that means we messed up somewhere around our setup. Now, we can basically remove this line now, and now our script doesn't depend on the HSM script at all. It just depends on the Blackboard being set up to have an animation player, which will get an error message if it does not have animation player in the Blackboard. So now for the direction, we just need to get that on update, I suppose. So function, underscore update, and we're going to be getting the current Blackboard variable. So that's going to be VR direction. It's going to be equal to Blackboard dot Get VR, and we're looking for direction. And we can't infer this variable type because we don't know what Git VR is actually going to return here. So let's declare it as a vector two. Okay, so now we have the direction property, and our script doesn't even need to know anything about the player input script. All needs to know is that the Blackboard has a direction property, and we can use that for our animations. So very simply, if direction is not zero, that's short form for that, then we're going to play the run animation on the animation player. So underscore animation player, dot play, run animation, Ale, animation player, dot play, idle animation. So let's go ahead and hit Play now and see if this works. So if I WAS and D, then you can see that we get the animation movement for our character. Now, there is a big flaw here, which is that the offset on our sprites do not match. Now, that is just the nature of how the specific sprite sheets were set up. I think the size of each frame does not match up. So it creates an offset. Now, that would be something you can either fix in a editor like A Sprite, if you are the artist, or if you just want to make it work, for this case, we can do that in the animation player. So in animation player, we can go to let's say let's check the idle animation. And here we have it centered here as we would expect. So when we go to the run animation of Run, we just need to make sure that this matches the idle animation. So we're going to want to key frame the position on each of our animations. So let's add a track, property track. Let's see, the Sprite two D because we're talking about the sprite offset, not the player character offset. And then we want position here. So let's add a key. Right now it's at negative 14. I'm going to go to Idle. And then we're going to add the same property in advance. So position two D and then add the keyframe here. The reason I'm doing this is because the idle position is already correct, so I don't want to have that change when I'm setting up the run. While we're at it, let's check Death. I think the death animation is actually at the right spot. So all we need to do is go here, add the track, sprite two D, position. Okay, insert the key. Okay, and then that should be good. So now we need to change the value for the round to animation specifically. So hit Q to go to Selection Mode. Left click on the sprite. You should have this big box. You can see probably where the offset came from here because there's so much empty space up here. But anyway, so we just need to hit W and then move this up to right about there. Let me check the transform. Yeah, negative 27 pixels. I think that's what I had and the in the draft project. So let's keyframe that. Let's hit Play and see if that's better. So WASD, it's almost there, but I think it needs to be a few pixels higher. So let's W and then move this up. Let's try 30, hit W. N, it's still not there. Okay, let's move this up more. I'll look at the idle animation. Okay. And that's where his feet are on the ground. So let's go back to run. Okay, and his feet should be up here, two pixels under the bottom. Okay, and then idle, and then run. Okay, I forgot to key frame it. So let me move that once again. I don't think we need the X offset. Let's key frame that at negative 30 pixels here. Okay, let's hit play and test one more time. WASD. That's looking correct to me. So we'll go with that. Let's hit Sop on the player here. And just to be clear, as a more long term fix, it would be better to probably go in and fix the animation spike sheets and just making sure that they all have equal size, regardless of how much movement there is on the animations so that you don't need to mess with the position. Long term, this is a little bit harder to manage, so it's better if it's just fixed on the animation sheet directly. Okay, so for our characters idling and movement, so the last thing we really need to do for our characters basic movement is to make it flip when we're moving to the left and then flip back when we're moving to the right. That'll be the next video, so I'll see you in the next. 9. Flipping a 2D Character: So we have our up down left right movement for the character, and it can switch back to the idle state. That's great. The thing that we need to change here, though, is that our character needs to be able to flip from right to left, depending on if we have a left or right input. So we want to basically flip the sprite node from left to right. Now, Sprite does directly have a flip H here. We could certainly just tell it in a custom script to flip the H based on whatever the player input does. To go one step a little bit removed from that. Instead, what we could do is actually put a parent node which flips itself when the player input facing direction changes, and then that will automatically flip the sprite node because the sprite node is a child of that parent node two D. So if I click on player node and I add a child, let's add a node two D, and then I'll call it flip two facing two D. Let's right click it, attach a script. We'll put this in the default location, create, and we'll make it class name flip two facing two D. And this is going to flip the X scale on the transform, depending on if the character is facing left or right. Okay, so what does this basically mean? If I grab this bright two D and I pull it under flip to facing two D, and then let's put this back up where it was before, towards the top of the hierarchy, I'll take flip to facing two D, and then over here on the right, uncheck the link between X and Y because we're only changing the X scale. Okay? And then type in negative one for the X, and you'll see that the character flips to the left. If we type in one to the X, the character faces the original direction, which is to the right. Of course, you need to be a little bit careful with the setup. If you put your weapons under the flip to facing two Dno, then that means your weapons are also going to be facing left or right. Now, sometimes you want that, and in other cases, you don't your projectile launching system later on, you don't want it to flip based on what this node scale is. But if you were to, let's say, put a spear in the character's hand, you probably would want that sprite to face the right direction when you're facing right or the left direction when you're facing. So you just have to be careful about what nodes you put under here, because anything that is under flip to facing two D in the hierarchy is now going to flip whenever that parent node flips. So just keep in mind what's parented to what, and you should be fine. So inside of our flip to facing two D script, we want to add a export var. And we're going to say input, which is of type player input. Now, for right now, being dependent on player input means that this script is specifically a player's script and not necessarily used by the enemy scripts. For a script this small, in this case, it might just be worth having the direct reference to the player input because that just gives you the simplest, most practical setup. And then the only thing you need to make sure is that the input is actually assigned on the inspector. So we can do that with the assert. Let's do a function underscore already and then assert that input is not null and say, in order to flip reference to the player input is required, once again, you can always just go to the top hit play, and it's going to hit your assertion, so that that test is good to go. Otherwise, at this point, we can just get the signal access from player input and use that to flip this node whenever the facing direction changes. If I recall, I haven't put the signals in yet. So if we right click on player input, look up symbol, we want to go to the top and declare some signals. So a signal is like an event. If you're coming from other languages like C Sharp, it is an event. And in this case, what that means is something triggered inside of your code and you want to notify subscribing objects that something occurred and optionally pass some contextual information or data to the receiving object. Okay, so let me go ahead and declare one, and then I'll explain it a little simpler. So signal is the keyword, and then we would say facing changed, and we'd want to pass in the direction it changed too, so we could say value or facing, and that is going to be a type factor two. So we're declaring the parameter. What we're sending to any receiving objects goes in these parentheses here. So the signal is facing changed. So the event whenever the facing property in this case, down here changes, we want to send out a signal that lets any subscribing objects know that the facing value has changed. So at that time, they can respond to it in their own code. And what this means is that you don't have to be constantly checking the facing property of player input on every single frame update. You just wait for the script to notify you that the value has actually changed to something different. So you have that meaningful information, and you only need to update like once every few seconds because your player only changed its movement direction once every 3 seconds or facing direction in this case. In other words, you become dependent on a signal triggering your other code rather than constantly checking a value for changes manually. So with our facing property down here, we want to take it and add a setter so that when the facing value changes, we actually emit the signal whenever it changes. So if you add a colon at the end, this allows you to declare your setters and getters. You can just do one of them. You don't have to do both. So in this case, we want to say set value. And note this is on a new line. So in this case, we really are declaring a function, though, actually. And this just runs whenever we set the value of this property to something new. So we're changing the default of just setting the value and doing nothing else to setting it and doing some extra code. So when we set the value, we want to take facing and set it to the value. So that would be what it does by default. If you don't declare any setter Getter functions, the Getter would just be more like this, return facing, right? That's what you would expect. You access the property by name, facing, and it would return the value that it's set to. So if you don't need custom code for the Getter, you don't need to declare that because that's what it does by default, and this is what facing would do by default. Now, if you want to do something like emit a signal, then you do need the extra code. So now we want to say facing changed the name of the signal dot emit, so we emit the signal, which means any object subscribing to it is now going to be notified, but we want to pass in the information of the facing. So we pass in facing. So we're taking this value, sending it through the signal, and then in our other scripts, we're going to receive that information, and we can decide what to do with it. Now, there's one other trick I want to show here, though, which is that we don't want facing change to get emitted whenever we set the facing value. This might be getting set to the same value on every frame. I think that the is movement action check actually guards against that because this would only actually trigger when there's a real movement action, which would be, in this case, like pressing the right key or letting go of the right key or the same for the other directions. But it's always good to be more defensive in the programming. Just assume that your other assumptions aren't necessarily correct and then to guard against it. So we're going to put a guard here. If facing is already equal to the value, then that would mean that there's no change. So we just want to return here. We don't need to set facing because the value is already assigned to facing. It's already correct, and we don't need to emit the signal, and we shouldn't emit the signal because the value doesn't actually change. So we have this guard against bad sets to the facing property. But if that guard is passed, then we set the facing and we emit the signal. So with all that, the signal is now going to emit. And we can just go to our flip to facing two D. And after we assert that Import is not null, then we know it's set. So we can go to input dot facing changed. You'll see the signal is actually referenced by name here, and it has that little Wi Fi symbol or signal. So we put input dot facings changed. So now we're looking at the signal name, and we want to connect to it. So dot connect. And what do we want to connect to it? Well, that would be a callable function that we set up here. To keep things, I think, as simple as possible, we should create a custom function at the bottom. So I'll say underscore facing changed. It's going to still error because there's no function with that name inside of our script. So if I hit Enter a couple of times, we'll say function, underscore on facing changed. Now, what parameters is this function going to take? Well, it's going to take whatever was passed into the signal. So in this case, you'd be looking at facing direction. So I'm going to say P, underscore facing, P as in parameter, making it very clear that this value is coming from the parameters that were passed into the function. And then this is a vector two. We're not going to return anything, so we're returning void. And now we can write the code to respond to the signal emitting facing changed. Okay, so when facing is changed, we want to take the X direction of the facing and use that to determine if we should face right or face left. So if p dot facing dot X is a positive value, then what we want to do is we want to take the scale X of this node two D, the flip to facing node, and we want to set it equal to the absolute value of the scale dot X. When you take the absolute value, you're making an always positive number. So to face the right, we're making right our positive direction. So we always want to face the right whenever the input is actually facing to the right. Now, we're not going to say else. We're going to say else if P facing X is less than zero, so we don't want that zero case because our characters should not be able to face the 00 point on the vector two that doesn't really exist as a facing direction for this type of two D game. So we don't want to do anything when that's the case. So that's why I'm checking if the value is less than zero, because less than zero would actually be facing the left. So we do scaled dot X, and we're going to set that equal to negative one times absolute value scaled dot X. Okay, so this is going to always give a positive number, and then we're always going to make it negative. So there's probably a few different ways to math this out here, but I think that should work just fine. Okay, so we go to two D view. We click on flip to facing two D, and we assign the player input. Okay, so that's where we're going to get the signal. We're going to connect to it on Rudy back in our facing script. Okay, so if I hit play, let's do right movement. Our character faces the right, and if I hit left, our character faces the left. If we do top left, we're facing left. If we do down right, we're facing down right. Just test all the eight directions our character can move in with keyboard input, and that looks good to me. So just to be a little bit forward thinking, one thing we could do is grab this code and pull it out to a different function, refactoring it into a new function. So say function update, flip, and this will require the facing direction. So P facing, vector two, and then I'm going to take this, cut it down here and on facing changed actually going to call update flip P facing. Now, the reason for doing this is so that I can also reuse it on ready. So on ready, we'll just say update flip Import dot facing. So when our script first loads, we want to set the facing direction up immediately before any Import actually comes from our player because it might be the case where we decide, let's say, in player input, we export this facing direction, and then we change the default of vector dot right to vector two dot left because for whatever reason we want our characters to face left by default. So now with this, we can be prepared for that by automatically initializing with the right flipping direction based on whatever the input is saying. So now, this is a public function. We could just call this from another script if there was a non Import related case where we wanted to change the flip direction of the character. For instance, maybe during a cut scene, you want to cut out all player input, and you want to call this manually. So your character was facing the right, and in the cut scene, you want it to face the left. So you'd have the option of just calling Update flip on the flip to facing two D node. Okay, so definitely opens up some possibilities there. And that is pretty much all we're going to need to do for the WASD movement of our player. It's looking good. I think all the concerns are separated pretty 10. Grass and Dirt Tile Map Layers: Okay, so since our characters movement and animation so basically playing for both Idol and run, including the flipping direction, I think now is a good time to start putting in some background so the game looks a little more pretty rather than just having that default Gadot gray background. So let's go ahead and switch to our world scene. If you don't have that open, you can go to the bottom left here. Just search for world, and you should be able to find world dottSCn if that's the same name you were using like I was. And then in the world hierarchy, we want to right click and add a new node, and we're looking for a tile map layer. You can see, there was another node called Tilemap, but that's been deprecated, I believe, since 4.3, and since 14.41 and about to go to 4.5, it's definitely recommended that you switch to tilemap layers, which breaks up the tilemap into individual layer nodes rather than having every single layer. Contained inside of one tile map. So let's add a tile map layer. And we're going to call this something like ground, so I'll rename it to ground. And if you have it selected out the bottom, you should have the tilemap window. So open that up if it's not open already. And then we need to find the art assets from our project. So I'm going to go into art and then Pixel crawler environments tile sets, and we're going to be wanting to work with the floor tiles here. So for us to bring this on, we don't want to be on the tilemap, but rather the tile set tab. Look in the top right hand corner while you have the tile map layer selected and you'll see tile set as a resource you can assign here. You want to left click and do new tile set, once you do that at the bottom, there'll be a new Tilset tab. So once you've renamed it to ground, look at the top right with it selected, and you should see a field for tile set. So we want to click here and declare a new tilesetRsource. You do that at the bottom, there should be two tabs here related to the tile map, which is tile map for actually selecting and drawing this tile set, which is for setting up your tiles, and then tile map for drawing your tiles. So we want to go to Tile set. You should see a box over here that we can drag the floor tiles into. So find floor tiles inside of the project. So it's floors underscore tiles dot PNG, and we'll just drag and drop that in here. There'll be this little pop up, which asks if you want to automatically create the tiles, we do. So we'll go ahead and hit us here. And then you'll see in the preview window that all of the tiles have been automatically sliced into actual tile resources that we can use and paint from in the game. So the simplest way to draw would just be to go to Tilemap and then select those tiles individually that we wanted to place. Hit Q to make sure you are in selection mode. And then down here in the tilemap window, we want to be in paint tools, so that's D on the keyboard. And then you can left click to draw the tile. You can go over one and left click and put another tile down. You'll see that right now, the Tap is actually showing in front of the player. That's not what we want. What you can do is just move the ground to the top here, and that'll automatically sort it, so the ground is under the player. So that's the simplest way to do that. So while selecting the tiles one at a time like this will work, it's very, very tedious. So what we probably want to do instead is create a terrain set based on these grass tiles and then use that to automatically draw which tile we need depending on what is adjacent to that tile, if anything at all. So if you expand tile set in the top right, you can go down to terrain sets and then add a new element. So we're going to leave it on match corners and sides and you expand terrains and add an element. And we can just rename this, let's say, grass terrain change the color to something a little bit more grassy, like a green. Now at the bottom, go to tile set, and then we're going to do paint. We're going to select from the property editor terrains. We're going to select the terrain set as terrain set zero, and then the terrain we're drawing is grass. So it should be pretty obvious which ones are the grass tiles here. But in order for the tiles to build correctly and automatically, we need to select the tiles that are supposed to be part of this grass terrain. So you can actually just left click and hold and drag a box around all of these grass tiles, kind of like that. If it's highlighted, then that's going to mean it's basically marked as part of this terrain. And this is dark grass here. We could set that up later. Okay. And then we may need to get these ones down here as well. We'll see how that goes and these light tiles there. Now, these bottom ones look like they're more a dark grass. We can try to include them and see how it goes, but they may not make it in the final cut for the terrain that might be more of a dark shade of grass. So we'll have to see how that actually tiles. Okay, so we've selected all of these tiles as grass. Now we want to go to the top left one, and we want to mark the corners and sides that are grass or are adjacent to grass. So the simplest way to think about it with this tile set is just if the area is transparent, generally, you aren't going to mark it, and if it is solid grass or near solid grass, then you are going to mark as being grass or grass adjacent. If I just left click here and drag around, you're going to see it's filling in the cursor, and we want these eight squares that aren't in the bottom right hand corner to be marked as grass because this right here in the bottom right hand corner is saying that there's something else down here. The tile to the bottom right is not grass and this tile is reflecting that. So it'll basically use this tile when everything down to the right up left and these three corners, are grass, grass adjacent, but the bottom right one is not. So the idea is you have one for all of these scenarios. So in this case, you would do these top six tiles, and this means that if there's no grass underneath it or to the bottom left or the bottom right, then this tile would get automatically assigned. So we just want to keep doing this kind of thing all the way around. So this one looks like all the corners and sides except the bottom left. And then this one is the top four. This one is, I think, just like this one up here again, the top eight to the right. And yes, it is possible to have multiple tiles that have the same I believe they call it a bit mask here. So if we go down to the bottom, you'll see that these are all nine square tiles, for sure, because they're all grass and grass adjacent. So you would actually fill all of these in all the way. And then that will fill in everything inside of these edge tiles. Okay, so we just kind of need to keep doing this. So this one up here at the top is going to be the middle top to bottom, and the right top to bottom. Here we have the eight sides and corners except the top left. This one's the bottom right four. This one's everything except top left. The bottom six tiles, everything except top right, the bottom left four, everything except top right, the left six, everything except bottom and the top left four. We have our corner pieces up here, and we have the grass solid pieces down here. So to draw with it, we just go to Tilemap and then select terrains, grass. And what we can just do is grab this rectangle tool. Let me zoom out the camera here a little bit. And we're going to zoom out with Middle mouse wheel on the game map, and we can just left click and drag a giant box into the background. And there is our background grass. Now, you'll see that these tiles were, in fact, darker. It was a little hard to tell on my screen. So maybe in the tile set, these are also dark grass that it really looked the same to me. So the solution to this is just that we remove the dark tiles from the light tiles when we're assigning the grass. So if I right click on any of these bit mask pieces, then that's going to remove it as part of the tile if we fully remove all the nine sides, corners and center. Now if we redraw this again, those won't be an option when it's filling in the tiles. So I'll just left click and drag another box over it, and there we have the light grass and only the light grass for this entire scene. Now, in a survivor like game, your character may move around the map a lot. So I'm actually going to make this a very large area. We'll just go from here to here just so that there's plenty of space for the character to move around on. Now, you can see it actually takes a while to fill that in because that's a lot of tiles. Okay, I also want to show if I hit E, to go to Race mode and then D for the paint tool. Then we can left click here, and you'll see that it automatically assigns the adjacent pieces based on the tile in the center. So this tile has no tile, so it automatically recognized, Oh, I need the corner and side pieces that we assigned in the terrain. And I can just keep using the eraser brush like this. And you'll see that it's automatically generating the sides and corners. It may have some limitations like needing to go, like, a two by two and a line in order to look right. It depends on the tile set, really, because there's different ways of building the tiles. But at the very least, this is much, much, much faster than if you had to manually assign all of these side and corner tiles. Just letting the computer figure it out for you based on the terrain rules that you define, like we just did, is a huge time saver. So we're going to fill this in. There's also the paint bucket tool down here, so we can just click on this area where all the tiles are the same. And oh, well, we also have to E to remove the erasuod. And now we left click and it all gets filled in. As you can see, it will select whatever region you're looking at. So that could be this entire thing. If you want to fill it in with a totally different tile terrain, you could do that by defining a dirt and switching to dirt down here and declaring it down so this is a little more advanced than I actually intended to go, but let's actually define another terrain with this dirt over here, the kind of orangy dirt so that dirt can transition into grass. So let's in the top right, declare a new terrain type, and I'm going to call this dirt. That brown is fine, I think. You might actually not want to use, like, a brown on brown tiles so that it's easier to see where you've defined and where you haven't. But if it works for you, that's fine. Whatever color you want to assign is perfectly okay. Okay, so we're going to be working with paint dirt in the tile set window, and we're going to do exactly the same thing with these dirt tiles. So here, we're painting the top left eight, not the bottom right, and then the top six, like that. And I'll just keep going around like this. This one's a little unclear, but I'm going to go with the top right four, and we'll see if it works correctly. If when you actually draw with your terrain, it doesn't look, you might have missed one of the bits here or there for, you know, whether it's on or off or center or sides. So sometimes it does take a little bit of trial and error to get right. But definitely worth it in the long run. So just keep going around, you know, same shape. Get the corners and sides where it is dirt and mark it as dirt, and then just keep going until you finish all the way around in the circle. Okay, and then finish up with the bottom three tiles down here. We'll get the light dirt and just skip over the dark dirt for now. Okay, and that looks good. Okay, you know what? I mentioned that I was going to handle the transition from the dirt to the grass. But actually, with this tile set, I don't think that's needed because these tiles individually, they have kind of like transparency rather than grass here next to the dirt. So you don't have to define the individual grass terrain as part of the dirt like this, where you would take these other tiles and say, Oh, that's grass over there, and then dirt in the bottom left. Because it's transparency over here, so that's actually not needed. But in other tile sets, you might need to do that. Where if you know that like one corner is supposed to be grass, then you mark it as grass there. So having two types of terrain for one corner or side. But here, I think we only need one. Let me give this a shot and see how that's going to look. So I'll go to Tilemap. We'll do dirt, and we'll try to draw over it. I'm thinking what we actually need. Okay, so yeah, here we can see that it's marking the sides as transparent. I'm wrong. So I'll try marking these sides as grass, but I don't think that's actually going to give us the expected result. I think what we need to do is just have two separate layers for this particular tile set. Okay. So I'm going to try drawing with that. So tile map mode and then draw with dirt and let's try that. It's still kind of messed up. I'll just try a box. Yeah, still doesn't work. So Control Z undo a few times. And what we're actually going to want to do is just paint with dirt over the grass, or grass over the dirt depends on which layer you want to be on top. So I'll right click on the root, add a child node. Let's say tile map layer. I'll rename it to be let's say dirt. I'll move this below ground so that by default, it shows above the ground. So whatever's on bottom is actually on top. Not sure why it's set up that way. I just is. And then we'll go to the dirt. Oh, and we want to reuse the same tile set, by the way. So in ground, I'm going to right click on the tile set and save it. This is on the top, right, by the way. So save it. Locally into the project. So I can create a new tile sets folder, and then I'll just say ground tileset dot TRs save. Okay, now, if we click on dirt and then go to the inspector, we can click the dropdown, Quickload, ground tile set. Okay, now with this tilemap layer, we can use the same tile sets that we've already previously defined. That's kind of how resources work in general. It's very easy to share them across multiple nodes or multiple scenes, et cetera by sharing the resources like that. Okay, so we're going to draw with dirt here, and then I'm going to draw with dirt over here, and you're just going to see that the dirt just sits on top perfectly because we're painting on different layers, so this just shows on top of the background and because there's transparency over here on these side edges, the grass basically fills in the gaps. The other method I was showing where all of the texture information like the grass and the dirt is all in one tile sprite would be more where you set up the multiple terrains in one tile, which is more complicated, honestly. So I'll rename the ground to grass. Because we're going to be more specific there. So we have two tile map layers, and it's much easier to manipulate them than tile map would have been. Okay, let's see where we're at. We probably want to more, like, draw a dirt path. Okay, so in order for the dirt to assign properly, we want to draw a two by two line. So you want to be using the rectangle tool or the keyboard. If you do just a one tile line, it's going to end up kind of like this. Based on the tile site, it's not sure how to fall on the information here. There's no tile where it has transparency on top and transparency on bottom, so that won't work. You actually need to do two pixel tall path like this and then just drag your box, and that will work for that. So we could just kind of make some paths on the level, kind of like this, you know, just drawing two tile wide paths wherever you want them to go. And that's going to kind of make it a little more interesting than just having plain grass tile. Though it's completely up to you how you want to, like, you know, organize this and make it more interesting. So, like, if you've ever played, let's say, Sim City, that's exactly kind of what I'm doing right here right now, like, creating the road system and basically putting it wherever I want. Not going to fill any residential zones in anywhere or anything like that. But just having some more tiles on the map is a bit more interesting than literally just grass. Okay. So we'll see if that's good. I'll hit Play. And yeah, our character kind of has something to walk on now, so that's a little bit more interesting. 11. Placing Trees as Map Props: And now I think it would be a good idea to add in some props to the game level. So we have some props right up here in the environment folder as well. So there's some animated ones and static ones. We could just start with trees as the simplest prop we can put into the game level. So let's grab uh, I don't know. We'll double click here and you can see the tree models up there. So we might want to grab these big trees, and we could just do that with a sample Sprite two D node. And we can put these in super simple. All we need to do really is go to the PNG file and drag it into the game scene. It's going to automatically create a Sprite two D up here. So just rename it to Tree then we're going to take this texture, and we're not going to draw the whole texture. We're going to draw a region of it. So what we want to do is use a atlas texture so we can select the region we're drawing. So right click here, and I'm going to copy this image real quick because we're going to click on the drop down, go to Atlas texture, which is going to unset that texture temporarily. Expand this and then right click and paste the texture we just copied into the atlas. And there we have it show up again. Now we want to click Edit region. And then inside of here, we'll go to Snap mode, Cid snap, Middle mouse wheel in to zoom in, and we'll just grab this tree like so, hit close, and we have a tree in the scene. Okay, now, depending on your game style, you may decide to add collision shapes to your tree. You would do that the same way you would with, like, a player character. So if we look at the player character, you can see it as a collision shape here and you can make it so that objects will collide with the trees. However, for this demo, we actually want to minimize the amount of collisions that our enemies are going to be doing with any kind of game character. If we have a lot of collidable objects inside of the game and we don't put in something like a navigation agent or use character body two D so they can move and slide along the collision shape, then you're going to get enemies getting stuck. So much simpler than that would just be to skip over the collision and just let any character walk through the tree, have it there. Strictly for visuals. We have the tree here. I'm going to right click on the scene, and we'll just create a new node for all of the trees. So I'll say node two D, create, and we'll move this out of the tree, so it should be underworld. And I could just rename it to be props. I'll move the tree inside of here. And then we can just duplicate this tree as many times as we need. So I'll first right click and save the branch as a scene. We'll put the tree in a new folder, I think. I'll call it. Let's go with Objects. Yeah, and I'll just put it inside of here. So tree dot TSN. We can hit Save N if we want to edit the tree, change it to a different type of tree, then we can just jump into the scene. So to do that, you just find this little icon here and you open an editor, so you can edit this tree and change it to whatever you need if you want to change all of the copies of it in the world. Let me show an example of that. So we have the tree. I'm going to duplicate it a bunch of times, and then I'll hit W to move the copies around the scene. So select tree two, W, move it around the scene. Move mode is right here. Select mode is here. So you need to select it and then go to move mode. And now I want to change the tree, all copies of the tree across the game. So I just go to the tree scene, and then over here on the right, we'll expand the atlas edit the region, and then let's select this red one instead. So red tree, hit close, hit save, and now back in the world, all the trees are that red autumn leaf kind of tree. Now I'm going to go back to the tree, and I'm going to undo that. So let's hit Control Z a couple of times. We'll just go with the standard green tree. And if you want two types of trees, then just find the tree scene in your hierarchy here. So you can just right click here and duplicate it. Let's call it tree autumn and hit Okay. Open up tree autumn so that's double click. Let's zoom in on the tree, and I will take the texture and edit the region, and we can just grab these red trees from over here. And now if we go to world, we can put the autumn trees in, or we can put the tree in just dragging the scene straight into the parent scene, the world. If you get used to that, you can pretty much populate the scene pretty quickly. Now, one thing I do want to change here, and maybe I'll need to demonstrate to make that obvious is that when it comes to Y sorting, the position and the tree scene of the sprite is going to matter because we're going to Y sort from the center position. So wherever the character touches the ground is generally where we're going to want to position the sprite. So up here in this style game because I did that with the player, right? So we want to make sure characters are sorting from where they stand. That also applies to objects. To demonstrate why that's a problem, let's go back to the main scene props. I'm going to go to ordering here on the right. D Y Sort enabled in the world, do the same thing. Go to ordering and choose WYSort enabled. Okay. And now if we play and we move our character in front of a tree, well, the cameras actually not letting us do that. So let's add in a follow cam so we can actually move around the map a little bit more. I'm going to take the player, right click, add a child node, remote transform two D, and then in the right, we want to assign it to the camera. We want to update the position and not the rotation and not the scale, so uncheck those. Okay, now if we hit play, so stop and play. The camera's going to follow our player, and that's a basic follow camera in Gudo. Okay, so we get to this object. If we're up here, you can see that we're sorting behind the tree, which we would expect. But if I go to the halfway point, now the characters standing basically in the tree. Visually speaking, we still want that character to be behind the tree until the character gets to about here. Otherwise, it just looks weird. So to fix that jump into the tree scene, you can click here to open an editor. And we do want this sprite to be up here. But I think if you just move the sprite like this, because it's the root node, it's not going to fix anything. So we'll go back to the world, and yeah, let's just hit play. You'll see what I mean. We'll go over here, and it's going to change the position where it instances these trees, not the position where it sorts from, right? You see the problem still there? So we need to go back to the tree scene, and I'm going to right click. I'm going to add a child node, a node to I'm going to right click on this and make it the scene root. So this is the real tree at the root, and then this is just the sprite two D now. So when you instance an object inside of another scene, it instances the root basically at its center position. So this is the center of the root parent two D node, and now we can offset the sprite. So if I take this sprite, well, it's already actually offset, but you can just W and move it to where you need it to be. Just remember that your transform position is an offset from the root, and the root is where it's actually going to be instanced at the 00 point right here. So that's why this is needed to have a base no two D and then you sprite nested beneath it. So we also want to do the same thing with tree autumn. But you might notice that because these objects are very similar, they're actually repeating a bunch of steps. So what we can actually do is have an inherited scene based on the summer green tree where we only replace the srt image, but everything else is the same for that tree. So I'm going to delete the autumn scene down here. Autumn tree. Let's delete that. Oh, I got to delete it and close the scene. So delete that and then up here, make sure you close this as well. So don't save. And then in the world, it may still have a copy of the scene because it doesn't completely delete it. It's just that now there's no parent scene for this to go to. I'll cut that out as well and save that. So we've moved all the instances. Okay. And now on the bottom left, we want to right click on Tree, do new inherited scene. So now we have unsaved scene based on the parent scene. We can save this as tree autumn, and we want to put this into the objects folder. So save that. And the only thing we're changing here is the sprite. So hit Edit region and grab the sprite for the summer tree, hit close. And if we go back to the main scene, you'll see we'll be able to instance the tree autumn again. It appears I actually messed up. So we need to make the atlas texture unique. Otherwise, you end up with this where it's replacing the trees. So now on the tree autumn scene, we want to take this bright two D, and the first instinct that I had was actually to just edit the region and to change it to this down here. But that's actually incorrect. And let me explain why. So, right here, this is a texture resource, and resources get shared between any scene that references it. And both our tree and our tree autumn are referencing the same resource. So if we change its properties like the atlas region, just like the edit region here on the autumn scene, it's going to actually update those same properties on the original tree scene as well, because they're sharing the same resource. So it's not a new copy of the texture resource. It's literally the same resource. So we want to right click here and then make it unique in the autumn scene. And now we can change this to this texture down here. So select the summer tree and hit close. Hit Save. If we go over to the green tree, you should see it still has the green tree sprite. We can go over to world. Looks like a couple of these trees kind of got unreferenced, so we might want to delete those real quick. So I'm actually going to take props and make it 00. And then I want to take the props node and I want to lock it. So if you click up here, you can lock it, and yeah, that should keep it from moving. So if I hit W and I try to move the props node now, it's not going to move. So one important thing to note is you have a parent node two D here, props, all its children's position is actually based relative to the parent's position. So if you have a grouping node like props, but you still need it to be a node two, probably a good idea to lock it. So I would apply the same thing if you have a enemies or projectiles grouping node, which we'll probably have later on. Now we can just add our trees to props, so I'm just going to pop a few in there. Okay, so if I take this tree that we just created and I hit W and try to move it up past the cening point, then you'll notice when it gets here, it's just going to disappear behind the grass and the dirt. So that is because of why sorting. So what we're going to need to do is in the top left for grass and dirt, we're going to need to select them. I think you can even select them both at the same time. And then go to the right hand side. We're looking for ordering and take their Z index and set it to something like let's say negative ten, any value that's negative. So this will make it so that these are always behind anything that has a Z index of zero. So if we select the tree now and go to W and move it, now it's fixed. It's always going to show on top of these other objects. So this is just about how we're layering our scene. Some objects, you want to sort with other objects on the same Z index, like the character in the tree. But then other objects like the tile map should just always be in the background for these ground tiles because the characters are standing on the ground, so you should see the characters before you see the ground. Okay. And now we just kind of want to fill on the scene with some more trees. You can just select them and duplicate them and move them up. Make sure that you put them in the props node as the parent just for sorting reasons. And then we'll just kind of select these and duplicate them. You can duplicate more than one at a time. So if you just kind of want a certain pattern, then we can just kind of make a group of trees and then just copy all of these around. You can even move these into its own subgroup, like having like a tree block parent, and then in that we'll have a bunch of different trees and you can hide it so you don't have so many tree nodes populating props. If that helps you out. Okay, so let's move some trees around a little bit more, and I could just kind of select all these ten trees. And I'm going to yeah, let's try the blocking thing I mentioned. I'll just right click and add a child node. Let's say node two D, tree block. I'll grab all the trees, move it under the tree block. So just select them all. And now I could save this tree block scene to the project, and we're going to want to save that Objects tree block. Now we can just duplicate the tree block, control D W, move it around like this and kind of just fill in the scene as we need with trees wherever we want them to be. At some point, you probably want to customize this a little bit more, but I guess this is a quick way to kind of prototype the scene, and hopefully it doesn't look too bad when we hit Play and we zoomed in. But yeah, if I was doing this tree by tree, this would take, you know, probably, like, 20 minutes or something. So if we do need to make some customizations, like right here, if we zoom in, I see that this tree you know, kind of close to the road, maybe I want to edit that so I can hit Q and then left click on this. We can see it's part of Tree block three. We can right click in the hierarchy and do Editable children. And then for this one, specifically, we can now select the trees and move them. So like this, we have kind of more of a customized tree block. Okay, and we can hide that. So now we're just looking at 15 tree blocks rather than 150 trees. We can play, go into game mode, and just walk around our game world. And from this Zoomed in, it's pretty hard to tell that, it's just copy pasting the same tree setup like ten times, but it's a lot less boring than just having grass, right? Like, we have the path, we have grass. If we get some flowers or other stuff in here, it might actually almost look like a legit game level. Oh, and here's a tree we can fix. So, you know, a little bit of play testing. You can find stuff like that. Let's zoom in and fix that one real quick. So this is part of Tree block two. If you look on the screen, you can actually see the name of the node that we're hovering over. So this is the parent node, the one that the scene is Enstus the tree block, not its child nodes which are contained inside of that scene. To make this editable, right click on Tree Block two, editable Children. Hit Q to go to Selection Mode, select this tree, move it around, and we can grab this one as well, move it out of the way, and just make those little edits. And yeah, from being zoomed out, it looks like none of the trees are in the road anymore. So that's probably the bare minimum of what we need going on. 12. Adding Stumps & Fixing Y Sorting: So I was looking through the pixel crawler pack for some flowers that would be able to be put around the scene to spruce it up a little bit. Didn't really see that in the free version, unfortunately. But I guess we have these stumps over here. We can try to use that as a prop and just kind of fill in the gaps a little bit and make it a little less dull. So we'll take this and create a new stump scene from it. So I'm going to basically repeat the same steps with the tree. I'll drag this to the scene. Let's go to texture. Right, click Copy. So copy that texture. And then on the drop down, click a new atlas texture, and then we're going to paste in the atlas here. Like so. Okay, so you should see the whole image. Now we want to edit region and select the stump that we want to use in our scene. So Uh, I think this one has a slightly less reddish stump, which looks closer to our actual tree. So let's do that one. Hit save. Now I'm going to right click this and save it to a new scene. So we'll say stump dot TACN. Now, if you recall, we want to offset this stump, as well. We may need to anyway. I think in this case, it does need to be a few pixels down. So that means we need another node two D in the root. So right click Add a child node, node two D, and then right click and make this the root. Okay, so now this is just a Sprite two D. Okay, so the stump texture is a Sprite two D, and then the root is stump. So now we'll save the stump scene. Take the sprite, hit W, move it up probably to right around there for Y sorting purposes. Go back to the world. Now we have the stump. Let's create some more stumps and create some more stumps all the way around the scene. If you happen to accidentally select the grass or the dirt, what you can just do is go up here to the top left and lock the nodes like that. And now you shouldn't be able to select them, I believe. So if we hit Q and select the stump or around the stump, Okay. So now these nodes shouldn't be able to be selectable by clicking around on the map. Let's go down to the stumps down here. They're not using this right base scene. I'm actually going to just select these and delete them. And let's get rid of the node two D, as well. Okay, search the project for stump. Move that into props like that. Okay, now we zoom out. We find this sump, hit W to move it, position it where you want it. Controlled you to duplicate it. Okay, now it has the name stump to when it's parented as props, so that's more like what we're looking for. And just put some stumps around the scene, you know, just things that you can encounter that are more interesting to look at than just grass. Of course, the pack has other stuff like buildings for, like, a crafting game. I didn't think those are the most appropriate. And if you're really going to put buildings in, I think you would start to argue that you really need the collision shapes and possibly navigation agents to make it more believable. But we're keeping it as simplistic as we can, 'cause we're just doing, like, you know, a really simple survivor like game. I think there's enough in the course already that, you know, I don't want to overwhelm anybody, but if at the end, you really want to understand the navigation agents and stumps, let me know. I do have some videos on YouTube for that sort of thing. If you look up Chris tutorial's navigation agent, you should be able to find those, if you want to get started with that. But yeah, this looks like a decent scene set up. Alright, play. Let's run around. Our scene. We got the stumps. Now, we can just walk through it. That's okay. Now, here, this is a little less okay. We can see that the sorting on this tree is not correct. In fact, the tree is not sorting at all. I think so I guess here we need to go into the autumn tree scene and try to fix that. Okay, so let's go to Local scene view over here on the top left. We're looking at the tree autumn scene. Click on the root. And maybe what we need here is to check why sorting enabled for this scene, and you might need to do that in the parent scene, as well. So ordering Y sort enabled. Note that my games still running, so I can tab back and see immediately if it worked or not. And currently, it is not. So let me see that tree in the world. Is it actually part of the parent? Oh, you know what it must be? It's the Tree blocks. Okay, so we have to go into the tree blocks, click on the Tree block ordering YSort Enabled. Oh, yeah, when you create a new level of the hierarchy, if you still want Y sorting to be there, you have to assign YSordEnabled at the parent level. So props has YSorting enabled, but Tree Block didn't. Now Tree Block does. So if I tab back to the game, is why sorting. Okay, so we want to be able to walk over the stumps and see that we can sort in front of the stump and behind the stump. And for the most part, that works. Now, I did also notice that there's a bit of an issue with the trees. They do do the sorting, or at least it was doing the sorting, hold on. Let me check with the tree here. Yeah, this one's sorting. But the position where it's sorting from is pretty off. So what I actually want to do is go into the base tree scene so that it applies to both trees, and Make sure you're in local view here if you have the game running in the top left. Select the Sprite two D node because we're controlling the offset of the Sprite. Hit W and then move this down probably somewhere like there. I'll tab back to the game now. And then you try to walk like this, you should see that the Y sorting is much better. So here we're standing in front of the tree as we would expect. And then here we're standing behind the tree. So that's a lot better. You do have to get those offsets right or things just won't look right. And that's looking like most of what we need here for the level props. I might create a stump group, as well, so I could just take these 16 stumps, and let's give it a parent. So let's right click and reparent to a new node. And then we'll do a node two D. So now all of these stumps have been grouped under one node two D. We're going to call that the stump block. We'll right click here, save the stump block as its own scene into the objects folder. And now we can just duplicate our stump block, hit W, move it around our screen, and just kind of, you know, put stumps other places. So it's not exactly like procedural generation here, but kind of a similar idea where we're just trying to fill the mapping with the pattern. Of the parameters, which are basically just duplicating the positions of the stumps and then offsetting them. So it looks a little different. And we can run around a game one more time. We have the Weiss sorting working pretty good here. Maybe for the stump, we want to change it a couple of pixels so we can go into the stump scene. And then let's go to Local view and move the sprite two D offset down a couple of pixels. So let's see how that is right now. Uh, maybe I got the direction mixed up. Actually, maybe we need to do it more like this and then hit save. So we're moving this up. Okay, that's behind the stump. This is in front of the stump. Okay. Yeah, that looks a little bit right. So just the very bottom of the stump should be below the 00 point of the parent. And we're going to walk around. Yeah, we'll just see stumps and trees. It's not really needing to be hugely varied here because the focus is just on the combat and it's going to be leveling up and picking up grades and that kind of thing. But having something in the background does make it more interesting. So I just wanted to go through all of that. You can, of course, manually move some of these stumps out of the way if they ended up on the road same way as before. So let's go to the world let's find that stump that was on the road, okay? I'll hit Q, we'll select it. So we see Stump block three over here on the left. Right click and do Editable children. Then click on the stump, move it out of the way and do the same thing with any other stumps you find. And here's one. Okay, so I'm going to select this node, Stump block two. We'll editable children. Then click on the individual stump, W to move it, move it out of the way, and that's done. Okay, we can also do that with this tree up here. So that's part of Tree block five. Let's make it editable. Select the tree, move it. Oh, there's another one here. Interesting. I must have actually duplicated a tree block in the same spot. Yeah, right. Let's move this way over here. All right. Okay, I don't really see any other trees on the road. There's one stump over here, so I'll try to fix that one, too. W to move it, move it out of the way. Make the parent editable if you need to. And that's probably going to be almost okay. One last one. So editable children. So select it. Make the parent editable, select the individual node, move it out of the way. And that's basically it. So collapse everything, so it's not cluttering up your hierarchy. That's one of the reasons I made the prop node to begin with. Um, if you had, like, flower textures or other doodads, maybe rocks, you'd probably put those on a separate rocks or doodads layer up here and just position it on a Z index that's higher than the grass and dirt. That would be a good way to get started with that. Alternatively, they can be their individual seen objects. I'm not sure about the performance, though. I would assume if it's part of the tile map, it's probably a lot better than if they are, you know, their own nodes in the game scene. But as long as you don't have too crazily many, it probably isn't an Um, so that's going to be basically our tile mapping portion of the course. And from here, we can kind of just move on to setting up weapons and enemies, gaining exp, leveling up, HP, all of those RPGsh rogue like kind of features. 13. Spawn Projectiles with a Timer: Character can move around. We have something of a game world to play on. Next, what we need to do is create an attack that's on a timer that will create a projectile where we can use that to attack enemies without needing any other key input from the player. In survivor games, you typically end up accumulating a bunch of different weapons, and they just automatically trigger for you. The only thing you need to worry about is walking around on the screen and letting the weapons do the work while avoiding enemies. And that's basically the gameplay loop. So get started with that. Let's create our weapon script and player. So in this two D scene, I'm going to right click on the root. We're going to add a new child node D, node two D here. Going to rename this to be weapon, and let's right click it and attach a script to be weapon dotgD. And this can go into the player folder for right now. So we're going to have this weapon script. We might even make it weapon two D, honestly. So if we want to go into the file system, we can rename that to be weapon two D, if that makes more sense. Okay, so what this weapon two D script is going to need to create the projectile game objects that actually are the two D representation of the weapon in the game world that can damage an enemy is we're going to need a timer. So I'll say var timer of type timer. Now, you can add these directly in the scene inspector over here on the right, like you would add other nodes. But I don't want to have to do that for every single weapon script I create. So to make that a little bit quicker and easier, I'll say function underscore ready. So when our script is ready, we'll just take the timer variable and set that to a new timer. Well, add the timer as a child, so add child timer. That means that this new timer is going to exist under the weapon. If we remove the weapon, we remove the timer as well. And then we want to connect to the timer's signal. So the timer has a time out signal, and we want to connect that to a function, a callable function, so we can say on time time out. And down here, we can go function on timer time out. Now, this signal timeout doesn't actually send any data, so we don't need any parentheses. And then this function is going to have a return type of void. So when the timer times out, we want to basically cast our spell. We want to summon the projectiles. So I think cast is okay name for that function. So we'll just say cast, and we want to pass the direction that our projectile is going to be cast into. So if we're facing up, we're going to cast upwards. So we can get a reference to the facing direction for that, but we'll come back to that in a second. So we'll have function cast, and this is going to take a P direction of vector two, the direction we're casting into, and we can return void for that. So in order for the timeout signal to even be hit, we have to start the timer first. So we'll say timer dot start. So the time that we want to set up for how long until we create a projectile instance is going to depend on the definition of each projectile weapon. And later we'll have different levels of the weapons, so we may actually change the cool down as the weapon levels up so we can shoot more often would be one way we can power up our character. For the weapon two D, we're going to want to have that definition as a resource that we can use so our weapon knows how to operate. So let's do at export VR, and let's say definition of type weapon definition, and we're going to need to create that. So in the file system, let's expand outwards, and then I'm going to create a new folder for our weapons. So I'll right click on resources and do Create New folder weapons. And inside of this folder, I'll just create a new resource. So right click New resource. And let's create it as a base resource for right now. I'll say spear definition. Since our character is a ragont we'll give him a spear as his default weapon. So I'll save it here, Script, we need to create our new weapon definition. So let's right click on weapons, create a new script. Let's say weapon definition for right now, and it's going to inherit from resource and create that. So now we go into weapon definition. Let's give it the class name, weapon definition. And here's where we're going to define the stats in the UI specifics like name for how our weapon is represented in a data format, but not in the game world. That'll be more of the projectile class, which extends from node to D. So we do need quite a few here to kind of pull everything together. I would say the weapon system is probably the most complicated part, so I'm going to try to go thoroughly through it. So first off, we may want to set a name. So at export var name. So nodes by default have a name property, but resources don't. So we can take advantage of that by defining a custom string name. So that's going to be the display name, how we represented in the UI. And then we could say at export var icon of type texture two D. Later we'll add in custom stat blocks for each weapon level, but that's too much for this one video. So for right now, what we do need from here may be something more like a get cool down function, and we may pass in a level here because we don't actually know what level the weapon is from the weapon definition. That is going to be set on the runtime weapon two D object over here. Because this levels up in game. And then the definition is more of an editor definition. It sets up how the weapon operates at each level. So those are separate things. The level is on the weapon to D. So we want to pass on the level that it currently is at. So we could say, P level, and that's going to be an integer. We will return a float the cool down for the weapon. So for right now, I'm going to make this simple and just do let's say if you turn one point. Later, we can make it more costum by actually resolving the cool down based on the level we pass on. But just keeping it simple here so that we can test that everything weapon two D actually works. So in weapon two D, we want to start the timer at, let's say, definition dot Get Cool Down. And we'll just pass in let's say negative one here because we mean to change it. So I'm putting a little to do note here to remind myself to get the actual level, just a note so that, you know, once we test this works, that we can keep going from there and fully implement the system. Okay, we're also going to want to be able to get the projectile instance that we're trying to create. So let's just do a function get scene. We can also pass in the level. This is going to return a packed scene. Let's go ahead and create that weapon scene, the projectile, more specifically. So in two D view, let's go ahead and add a new scene. We're going to create a two D scene here. I'm going to rename this to be Spear. Let's right click on it, attach a script, and this is going to be projectile dot GD. I probably want to save this and oh, let's say the weapons folder. I think that makes sense. And then open Create, that's cancel. Create is over here, and then we want to give it the class name projectile, and we'll save spear dot TSCN inside of weapons so that we can actually see the projectile. We want to attach a Sprite to it. Click on Spear, attach a Sprite two D node, and then we're going to grab the weapon Sprite. Which is going to be part of an atlas texture over here on the top right. So do an atlas texture. And then let's quick load from our project. I think it was 16 by 16. And then we need to get our texture. So go to art in the bottom left. Go to Pixel crawler, go to weapons, wood. And then word dot PNG, we drop that into the Atlas slot. Okay. And now we can edit region, zoom in, and we want the spear, which is right here. So let's drag around that and hit Close. Okay, now we have the spear sprite. So we can at least see our projectile uppear in the game once it is basically loaded in. We want to load in this scene. Let's go back to player, and I'm going to open up the weapon script. Then I'm going to right click on weapon Definition and Loup symbol. And for getting the scene, we're simply going to do something like return load. And now we want to load that spear scene in. So spit dot TSN. You can see that you can just find it in the resources weapons spit dot TSEN and put that in there. So once again, this is a temporary measure just to keep it simple so that we don't have to fully implement it. So in our weapon two D, when we cast, we're going to get that weapon seen from the definition. So we want to say var, projectile is going to be of type projectile, and we're going to set that equal to definition Get SN and we'll say negative one here for the level. You know what? Actually, we could just start declaring our actual level of the weapon. So let's make that a level one. I'll say at export var level. This will be a type of type integer, and it starts at one. Okay, so now we can just pass the level into these functions, get cool down and get seen. It doesn't do anything with it yet, but at least we have it there. And, of course, when we level up our weapon, we're just going to increment the level here by one, and that'll change what these functions give us back if we try to get the cool down or get the scene. Okay, so we get the scene, and then we want to do instantiate on it. Then we want to add it as a child to our projectiles group and the main scene. Where are we getting the node to actually spawn the projectiles in? Let's go to the world scene two D view, and I'm going to minimize this. Let's right click on World and the top left and add a new node two D. I'm just going to rename this projectiles. Now let's go to the top right, and I'm going to use groups here. We're going to assign a group to the projectiles node. So add a new group. I'll just do projectiles, lowercase, make it global, and I'll say the node where all projectiles are contained within, Enter. Okay, now we can see in the projectiles node has this group, and we can reference the node by doing first in group. So let's go back to the script. It's the projectiles underscore. We could also declare that as a constant if we want. So I would say constant projectiles group, making constant all caps the standard convention. It makes it clear that this is a constant value that isn't going to change and differentiates itself from a regular variable like level or definition, which can change during gameplay. So let's set this equal to projectiles lowercase, just like before. So we're going to want to get this node and add it to that node. So let's cache it. As a local variable, let's say var projectiles parent, and this is going to be a node. So to get that on ready, let's do it before the timer. Underscore projectiles parent equals G node. But we have to get the tree first. So get tree dot get first node and group, and we want the projectiles group, which is a string name. In most cases, strings and string names can be used interchangeably. It converts between the types automatically in a lot of cases in the back. We can declare it as a string name here because that's what it expects for this. So we get the first node in the group. If that doesn't exist, we're probably going to get an error in the console, which is a good thing. So we just need to make sure that our game levels actually have a projectiles node like this. If you like, you could save this node into your project and reuse it, right click Save branches scene and then maybe we'll create a levels folder. For anything related to levels and put the projectiles dot TSC in here. Then if you keep reusing it from here, they'll always be in this projectiles group, and you shouldn't basically have it desync between multiple levels. So now you can reuse the projectiles scene without worrying about the values changing between the levels you use it on, so that could be a little helpful. And now, once we've created the projectile, we want to make sure that that gets added as a child to the projectiles parent. So underscore projectiles parent Ad hild projectile. Like that. We can get rid of this now. And then we just want to make sure that the global position is equal to the weapon two D's global position. So let's say projectile global position is equal to global position here. So we're taking the position of the weapon two D node. And then we need to pass it direction into cast. So let's just do vector two dot, right? That's capitals. A constant value. And now the script should be able to run without error. Okay, so it has the scene. I knows what cooldown to set. And it knows to create a copy of the projectile on top of the weapon two D node whenever the timer elapses. So let's run this and give it a shot. We can see get cooldown and null. So we haven't actually set the weapon definition. So we could say, assert that definition does not equal null. So each weapon requires a weapon definition set. So then we need to go to the player. We need to find this weapon two D node. I'll rename it weapon two D. Let's go to the inspector top right, and then define our weapon definition here. So new weapon definition, expand this. We're going to possibly want the icon and the name. So I'll call this spear. And let's get the icon from the spear scene. So we can just take the same sprite and copy it to the icon. So I will right click on the texture. And copy. Now let's go to the player scene where we have the weapon definition set and just right click on icon and paste it in. Okay, so there's our UI representation of the spear. So we hit Play, and we get the spear to instance after every 1 second. Now our projectile doesn't move yet. It has no code to make it move, but we have the time working for putting the projectiles into the world at the right time based on our weapon definition class. Of course, there's a lot more to it. We haven't messed with stats or anything yet, but this is a good start. So I'll get more into this in the next 14. Move Projectiles in Launch Direction: So at this point, we're ready to start putting enemies into the game and then making our projectiles able to hit them moving along the screen. Okay, so let's make it so that our projectiles can move now in the direction that they're launched in. I'm going to go over to the spear scene, and this is the spear projectile, the one that actually instances onto the game world. Let's open up that script. And we're going to want to put a launch function inside of here so that when our projectile is instanced, another script can tell it what direction it's going to move in. So I'm going to put in function launch, and we're going to pass in a direction. So P underscore direction vector two, and that's going to be the direction that the projectiles going to move in. So we want to take that launch direction and make it local so that we can use it in our process functions, process going to update on every second and translate our projectile node across the screen. So var direction is going to be vector two, and we're going to set that up right here in launch. So direction equals P direction. Now, once we launch a projectile, we also want to queue it to free after a certain period of time. Otherwise, projectiles that never hit a target would exist in your game world forever and would eventually cause performance issues. So we do want to make sure that objects free eventually. So we can do that by waiting a certain amount of time. I'll say wait. So this basically means that it's going to come back to the rest of the code that follows this line after the condition is met. So we're going to say get tree. So we're getting the current scene tree that this projectile node exists inside of, and then we're going to do create timer. And we give it a time. So let's just say 1.0 seconds. Everything else you can leave as the default. So this whole thing means we're waiting for the timeout method on the timer that we create using the scene tree, and we wait one seconds for that timeout to occur. And then once that is done, we can just it quarter on the projectile node. That's going to free the projectile on anything under it. Now, a projectile that hits targets, if it hits a target, it's probably going to remove itself before this, but this is just a fallback so that if it just completely misses all the enemies, it'll free itself eventually. Okay, and then we need the projectile to actually move. So let's say function underscore process or physics process, actually, is probably a little bit more appropriate. Let's do that. So usually you use physics process for anything that involves moving physics objects across the game. Now, this is a node two D. We're not adding anything like rigid body physics to the projectile, but just so that it's movement synchronizes with everything else in the game. If the physics process is set to 60 times per second or 60 movement periods per second, then this will move 60 times per second, just to keep it consistent with everything else in the. Export var speed up here at the top of type float. We'll add it to 100. So that'll just be the default speed for the projectile. So things like the speed and the duration that this projectile lasts in the game world are stats that I want to pull out to a separate stat script so that we can define it per weapon and per level of each weapon in a much more organized way. But for right now, it's good enough, so we'll work on that later. So we want to say translate here. Any node TD can translate across the screen. It's movement without any collision. And we're going to say that we're moving in the direction times the speed, times the Delta. Now, if you want to know what this calculates when you're debugging, hitting a breakpoint, then you might actually want to do something more like this. Var move, which is a vector two is going to be equal to this bit right here, Control X, paste that end there, and then you just translate the move. So now if you were to hit a breakpoint, right here, and we may actually be able to do that just by hitting play because physics process is going to trigger as soon as that spirit gets created, then we can see at the bottom, we have the move value of 00 and Y is zero, zero, because we didn't launch the projectile with this function, so it doesn't actually have the direction it defaults to vector two dot zero for right now. Okay, so I'm going to undo the breakpoint. You. Okay, so close the game. It actually brings up a good point I want to touch on, which is that if the direction is vector 20, then that probably represents an issue because we always launch in a direction the direction the characters facing, which should never be vector two dot zero. So we might just expect that the launch function has already been called before we put it into the scene fully with Adhild. So let's just make that function underscore ready. And we're going to assert that direction does not equal vector 20. Call launch to set direction or projectile before adding it child. So we want to so we want to require that we've already set the direction with the launch function and give a note that we should call launch before we call Add hild. So the ready occurs after okay. Now if I run, we should immediately hit the assert as we would expect. So let's jump into the weapon two D script where we're adding the child of the projectiles parent. So now we want to call projectile dot launch in the P direction that we pass into the cast function. So that it'll actually set it to something proper. Now, we can see that down here on vector timeout, we're using vector two dot right. So I guess it would be a good way to test. It should always move to the right in this case. So the problem here is really that our launch is trying to do two separate things. I think it actually makes more sense to pull this bit out to the ready function since we're using that now. So I'll paste that in there, Control X, this, control V, paste it in. This makes sense. As soon as the projectile is ready, we get it ready to free after a set period of time. So this keeps the launch method only handling one thing, which is basically to initialize the object once it's been created. So this is almost certaintly the better way to do it. Okay, now let's hit play. And we have our projectile shooting to the right. You can also see they only last 1 second, probably not long enough, but it's a good test to make sure that they are actually freeing. So at this point, there'll only ever be one or two of the spears in the scene at any time. It shouldn't cause any performance issues whatsoever. 15. Setting Up Enemies and Hitboxes: Our projectiles move to the right. Now, let's create an enemy so that our projectiles can actually connect with it and hit it. So that's going to mean we're setting up a hit box and a hurt box in this video, and we'll worry about stats later on, like HP. So in my project right now, I see at the bottom, there's a few warnings here. These are just mentioning that if you're not using a parameter, you should underscore it. So I'm going to click on each of these and just give it a little underscore there. So I'm not using Delta in this player physics process. So I underscore the Delta. It implies that it's not being used. And then here in another script, I'm going to do the same thing. And just for any of those warnings you're getting, just repeat. You're not using the parameter, then underscore it to make it indicate that you expect to not use the parameter and that that's not an accident or a mistake. And we'll go ahead and create our first enemy. So let's start with an Oc. I'm going to create a new scene up here at the top, and we'll go to two D scene on the left. I'll rename this to be Oc. We'll right, click it, change the type to a character body two D. And select that. Okay, then I want to right click on the orc, add a child node. We'll do a sprite twoD here. Also, right click here and add a child collision shape. And on the right, we'll just make that a little circle shape, kind of like what you did with the player. And let's controls, save the scene inside of characters. I'll make a new folder for enemies. So enemies, put that in there, and then save the orca TAC. Okay, now our orc needs Bright, of course. So let's select this Brit node, and we'll go down to the bottom left. We'll look for I believe it was entities mobs orc crew, and we want to grab one of these. So I was just using the default orc so we can grab the idle sheet. I'll select this Bright two D node, and then over on the right, we're going to drag and drop that idle sheet into here. Okay? Now, obviously, we have a similar setup to the player where we have a sprite sheet. We need to define how many frames H frames are from left to right, which you can see is four. And we want to do that with an animation player. So I'm going to click on Ok, add a child node. Animation player. So select the animation player, go down to the bottom, do a new Idol animation. Okay, and we need to keyframe some of the sprite properties. So let's add a track, property track, sprite two D, and we're going to look for H frames. So double click that. Okay, and then repeat the steps for property track, sprite two D, V frames, property track, sprite two D texture. And the bottom right, enable snapping right here and then change the snap time to 0.1 seconds. So one frame per tenth of a second. And lastly, on the right, we want to change this to 0.4 seconds total duration and enable looping. Okay. On the timeline here, go back to the start of the animation, right click insert a key. So we have our H frames. We want to take that, and in the top right, we change that to four. And then VFrames we want to insert a key. We leave that as one up here. Right click insert a frame. So that's going to be our first frame. Don't worry about how it shows all four frames at once that'll automatically fix in a second. And then we want to right click and insert a key for our texture, which you can see in the top right is already defaulting to our animation here. So we haven't played the animation yet, so it's still showing four frames, but as soon as I play, it's going to show the first frame of the idle animation, and the frame section down here updates automatically to what we would expect. So now we want to go to 0.1 seconds. It should snap 2.1 second intervals, and then right click and sort of key for frame and then change that in the top right to one up here. So that's the second frame of animation. Then add in the frame value of two, which is the third frame up here and the last frame of value three, which represents the fourth frame of animation. Okay, now, if we at play, we should have our idle animation. And for the simple survivor like character, we're only going to make it face left and right. So we just need a flipping node in these individual animations for whether it's facing left and right. So because this is for a simple survivor like game, we're only having it face left and right. So we'll just need to add a flipping node later on. And aside from that, we just need our idle run and death animations. And that's pretty much it unless a character needs a special attack animation. So let's duplicate the idle animation, and we'll put in our run animation. For run animation, we want to take the texture down here and change the value and the top right to the run sheet. So take run and drag that to the top right. Okay. And that is six frames of animation. So we want to take H frames here and change it to six. And then we want to add in frame five and frame six here. And so the key change this value and the top right to four, insert a key, change this value to five, and then take the duration of the run animation and make it 0.6 seconds, hit play, and we should have our run animation. Now, I think it's offset incorrectly, kind of like the idol to the run of the player was. So we're going to need to do that offset setting, too. So let's go to the idol. Let's move this character up. Make sure you're selecting the sprite node specifically. Then hit W to go to move mode and move the sprite up so that the 00 point is centered on its feet down here. Wherever it's center of gravity where it touches the ground should be. And then go to transform on the right and insert a keyframe. So I have negative 14 pixels as the Y position. Key frame that, hit Create and just have the reset track. That's fine. And now we need to keyframe the run as well. So for run, we're going to go to the top right, and I'll make it Let's try negative 27 pixels. Let's try negative 30 pixels. I figured that these are set up kind of the same way that the player was, same artist and all that. So let's keyframe this. And create. And we have our run animation. Now, let's quickly switch to Idol. Okay, so now we have our run animation playing. Let's quickly switch to Idle and it play. And it seems like the pixels line up. We'll have to test that end game, of course, if we're going to actually show the Idle animation. In this case, though, we may not actually ever show the Idle animation unless the enemy is ever going to stop running towards the player. But idea, at least with this setup is going to be that the enemy is always running towards the player as long as the player exists. So we might not ever actually see the idle animation. It would be up to you. Maybe when the player is defeated, the characters just start idling instead of moving around on the screen more because there's no more player to fight. But that would just be like an extra not really required. What we do need, though, is the death animation. So let's take run because I think the death animation is six frames, and we're going to duplicate this to make a death animation so type in Death Enter. We're going to turn off looping for this. Characters only die once. Then we want to take the texture and change it to the death sprite sheet in the bottom left. Drag that into the inspector and let's hit play there. And then if we switch to idle, it seems like it lines up and run seems like it lines up. Okay, so that's probably good. I guess the position on the death is negative 30. Anyway, we have our Oc. We can just pop an orc into the game world. So let's take orc here. And actually, we probably want like enemies hierarchy, so I'll right click insert a node. So node two D, we'll do enemies or could even just be NPCs if we want. Maybe enemies is better. And I'm going to drop an orc from the bottom left. Under enemies. So we can see it's not actually showing the animation yet. So I'm going to hit Open and Editor, and we're going to see what's up with that. We probably need to, like, set a default animation. So I might take death and change that to idle. Let's default it as idle. Okay? Hit play. Let's switch back to the game world. Does that fix this? I guess not. Okay, so since that's not working, we should go to the reset. Animation. So this is whenever you're not playing an animation, it resets to these values. And we can see that the He frames here, if we look in the top right is actually set to one. So if we set that to four, and I guess now we hit play on the reset, then this is what we should see if we go back to the game world. So save your orc scene, go to game world, and okay, now it's looking correct. So I'm going to hit W and move the c. Okay, and then let's just Control D, duplicate a few more cs. And then I think the last thing we want to do here is to take enemies and enable on ordering y sort enabled. Since this is apparent for all the orcs, we do want the orcs to sort with each other, as well. That might be necessary. So that might be necessary to set there. Okay, so our spears can move, but they can't actually hit anything. We haven't set up a hit box, and our orcs can exist, but they have no HRT box to actually take damage. So HRT boxes and hit boxes and Gadot D are going to be Area two Ds. If you're coming from unity, it would be more like a trigger zone. Let's go to Spear and we'll create a Area two D that we'll make as our hit box. So I'll right click on the spear add child node, Area two D, and I'll rename this quickly to be hit box two D. Let's right click it, attach a script. Now, this is going to go probably in more like the root. So let me see. I guess putting it in the weapons folder makes sense here and we'll hit open. So we'll create the script at weaponlash Hit Box two D. Create give it the class name Hit box two D. Okay, so in our HIT box script, we want to immediately connect to the signal that the area already has, which is area entered. Because what we're trying to check is if we enter the area of a RT box. So if we do function underscore ready, then on ready, we can do area entered dot connect, and we're going to connect it to on area entered, which is a function we're about to set up. So create function underscore on, area Entered. This is going to receive and area two D is the parameter. So P area area two D and return type of v Okay, so an area entered this area, which type of areas do we care about? We care about specifically hurt boxes. So if P area is a Hurt box two D and the type doesn't exist yet, so it should be an era here. Then for right now, we could say something like prints then for right now, we could say something like Prance, which is going to print a message to the console. Why don't we try it like this? Percent s and quotation marks means we're going to replace this with a string. Hit percents. We could even say at position, percents, and then we're going to replace those strings using another percent sign. And we have three strings we need to replace inside of that string. So we're going to pass in an array of values we want to do for the replacement. So the first one is going to be, let's just say self. So we're going to get the Hit box, which is going to hit the P area Hurt box. And then we're going to say that the position is the P area dot global position. So an Area two D is still on node two D, so it still has a global position. That'll work for just picking up that our hit box is actually hitting the HRT box. Okay, now we need to make a RT box on the Oc. So let's go to the Oc and I'm going to right click here and create an Agile node Area two D. Let's rename this to be Hurt Box, and I'm going to capitalize the B for box. Two D. Let's right click it, attach a script. We're going to want hurt Box two D. That can just go in maybe characters for now. We're not going to really have any destructible objects, at least in this demo. So you could argue, Okay, this belongs more in objects folder, or maybe you're organizing your scripts altogether in a scripts folder which holds specifically scripts. Organization is kind of up to you. This is just kind of how I'm doing things. So the characters slash Hurt Box two D, we'll create it there. Okay, now this script, we need to call it class name Hurt Box two D. For right now, we won't change anything else about it. We just want that identifier tag, Hurt Box two D. Now, the Hurt box, you can see it needs a collision shape as well. So let's right click and add a child node, collision shape. And then in the right, I'm going to create a new circle shape. So we can define where can this character be hit. So I might hit W and move it up. We can just make it kind of big here. So if I make it bigger, kind of like that, that would be fine. You might also prefer a capsule shape. So if we select capsule shape from you might actually prefer a capsule shape here as well. So if we do a capsule shape, which is more like a humanoid, then we can select capsule shape. And then over here, we can just hit W, move it up here. This would be good if you want, all of the pixels that show on screen for the k to actually be damagable areas. So you could make that argument. And then we just want this to be a child of the Hurt box tot. And then our orc also needs a collision shape. So where can the feet collide for the c? So that is going to make more sense as a circle shape here. And then we look at the orcs feet. Let's just shrink it down here, something like that. So if we chose to have collisions between our orcs and other orcs or orcs and the player, other props and the object, then this is the shape that we would actually be using for that. Now, if you want your enemies to collide with other enemies and players and stuff like that when they're moving by using move and slide, that's something a character body two D can do out of the box. But it is going to have a performance cost associated with that, since it's actually calculating movement based on physics. If you want to get the maximum number of characters on screen, you might consider using translate instead, just like we did with the spear, which means it's just going to move regardless of what's on screen in the direction that it's trying to move. So keeping it as dumb, simple as possible, it's kind of up to you what you want at the end of the day. And how many thousands of enemies you need on screen at once. Okay, so before we wrap this up, I think the spear also needs a collision shape for the hit box. So I'm going to right click here, add a child node, collision shape two D. We go over to the top right. Let's make this a circle shape, and then we'll take the circle shape and move it up to the spear's tip and shrink it, something like that. You might prefer it to be a capsule shape. It kind of depends on how picky you want to be. Capsule could obviously be more of an oval or less of a pure circle. Or maybe even just a rectangle. So if you want to go with a rectangle, you could do, let's say, rectangle shape up here, shrink the sides like this, bring this down, and maybe like that. So using kind of the simplest primitive shape here as possible for the hit box while still trying to maintain the actual shape of the sprite where it makes sense. So I think this would be quite good. I don't think anyone would really notice much that, you know, maybe this half of the spear hasn't hit here. If you really wanted to be picky, you could do it like this. And yeah, maybe that would be a little better. Just a little bit less chance for it to hit it might slide along the edge over here to the left. Once again, one of those more, try it and get a feel of how it's working kind of things and see if it's right for your game or not. Let's go ahead and run the game now, and we'll try to get the orcs to hit with those projectiles. So let's see cannot instantiate a null. Level. Okay. So right now, the projectile is not set on the weapon two D anymore. That's interesting. Let's go to the player, and I'm going to take the weapon two D. We can see that the weapon definition is set here. If I expand weapon two D, let's jump into weapon definition. So we have the look up symbol. Maybe I moved the weapon. Let's see. Where's spear? I accidentally moved spear from weapons spear to this different folder. So let's move that back to weapons. Okay, it will work fine now. We'll hit Load. Okay, and then we have the spears. So let's go to output. I'm going to move this down a bit these orcs are actually being hit by the spear, which is good. We can see the position at which that occurred. The hit box information isn't too useful, but at least we know what type is hitting what type. If it was a completely different type of area, it would say the class name for it here. And that's pretty much working. 16. Dealing Damage to Enemies with Hitboxes and Stats: Hit boxes and hurt boxes are working enough that we can pick up the hit on the HRT box, which is going to be the start of dealing damage to our orc enemies. So in this video, we need to set up the stats for the orc and make sure that we're passing a damage on hit over to the orc that's going to take damage. A big part of this tutorial is going to be actually defining the stats of the spear, the stats of the orc, and setting up the interaction between the two of them. So if we jump into the projectile script, we want the projectile to respond when the hit box hits the Hurt box by passing in the damage of the projectile over to the hurt box and hitting it. So I'm going to put up an at export var for hit boxes. This is going to be an array of hit box two D. Okay. Now, those I want to assign in the inspect So if I click on Spear array two D, the array of hit boxes is over here. We need to increase its size to one, click a sign on the first element, and then choose Hit box. So now we have a reference to the hit box for the projectile inside of the script. It's made as an array because some weapons may actually have two or more hit boxes. So we just make that an option from the start here. And what we're going to do is connect to each hit box signal on hit so that our projectile can deal the damage. So let's right click on the Hit box two D. Symbol here, do Lou symbol, and this will jump over to the hit box script. So rather than print that we hit, we actually want to emit that we hit. So let's create a signal hit up here. Whenever the hit box recognizes a hit, then we'll pass in the target that we hit or the hurt box that we hit in this case. So we'll say Hurt box here is a type of rt box two D. Okay. And so rather than print here, going to cut that out, and I'm going to say hit dot EMIT, and we're going to pass that with the P area as a Hurt Box two D. So now if we go over to the projectile script, we can just connect to that signal on ready. So let's do that before we do the await portion. First, we'll say assert hit booxes dot empty. So we don't want that to be true. So we could say equals, equals false, and say needs at least one hit box to hit a target. Now, this is a little bit dependent on what your intent is. You could theoretically have a projectile that doesn't need to hit. So this assert might not be necessary, but for our game, we do want projectiles to be able to deal damage to enemy targets. So we're going to leave that assert in there. And so after our asserts, we can say four box and hit boxes. We're going to say box dot hit dot Connect, and we could say on hit box hit. So we're going to connect that signal to a new function we're writing down here at the bottom. So function on Hit Box Hit is going to take a P Hurt box could do HRT underscore box if we want to be more consistent. And that's going to be of type Hurt Box two D and return void. Okay, so with the HRT box, we're going to call, let's say, P Hurt box. Let's say dot DL damage, and the amount is going to be based on our calculations right here. So where do we get the damage to calculate? Well, let's just declare final damage here. And we'll set that to ten for right now. And this will be an integer. So all the damage and HP in the game will be integer based. Just to keep it simple, technically, you could do a fraction of a unit of damage, but that's pretty confusing. Most games will just use integers. Okay, so we'll say we deal with final damage here, and we'll worry about damage calculation later. So we could say, to do Damage calculation. Okay, so now our Hurt box, we want to add that deal damage method. Let's go over to Hurt Box. So I'll say function deal damage, and we will pass on the damage of an integer. Oh, we could put a return type here right now, I'll just return it as void because we are expecting the damage we deal to be the damage it receives. We're calculating in the projectile script rather than the Hurt box script, in this case. Let's actually change it to a Boolean return type. So I'll say try deal damage here. The reason for that is that we'll make a condition that it's possible that the stats are impossible to be hit, so we'll have that managed here. So the final fail point of if it's actually going to take the damage will be the invincibility on the stats. So I guess that makes more sense from a box point of view. So we'll say, if not, a stat controller dot can be hit, then we'll return false. Now, the stats are where we're applying the damage to. It's going to manage the runtime stats for the character. So we're going to need to set that all up in a minute. So now would probably be the right time to set that up. So up here at the top, we're going to want to at Export VR, a stack controller reference. So this will be of type stack controller. So we'll create a new script with. Let's go over to the org scene in two D view, and we're going to right click it and add a child node. So this is going to be a regular node. It's not a position space. This is a stats manager, so it's all database. Therefore, it's a node, not a node TD. And we'll call it Stat Controller. All right click here. We're going to attach a script. So the name of it will just be Sat Controller, and we'll put it in the character's folder because our main player character will have a stack controller as well. Let's create the script. I will put in the class name, and I'm going to control VPastedn Stat controller because I copied that from the other script. This manages the runtime stats for an object. So we're going to have stuff like VaraHP which is an integer, and we can set that to a default amount maybe 100. We also have Var Max HP, which can be the same. The idea will be that the HP can never be set above the MX HP. So if you want to just take care of that right now, we'll put another cool in here at the end, and I'll say set value. So HP is going to be equal to the men of the value and the maxHP. Okay, so this right here just limits it so that you can ever set the HP above the maxHP. We might also want a variable for is the object alive. So we'll say far live, this will be a boolean. It's going to default to true. And then we need that function that we were getting an error on. So if I check Hitbox, there's the can be hit function. We have to implement that. So let's go to Stat Controller. Function can be hit, which is going to return a Boolean. So we can put whatever conditions we want in here. For right now, we're going to say, we'll return live. So if the character is alive, it can be hit. Otherwise, it cannot be hit. So back over here, if it's alive, then it will be able to be hit, but this is inversing that with the k. So if it's not alive, then we return false. Basically. Now, down here, we'll say return true because we assume that at this point, it did do damage because the conditions were met. We already passed the Guard clause. So we want to take the SAP controller, and we want to take its HP and we want to set it to the new value. We might want to calculate that right above it. And as a local variable. So new HP is going to be equal to stat controller dot HP minus equals the damage or just minus, actually. So the assignment is actually over here to the left of the new HP. So we set the stat controller's HP to the new HP, and we'll return true. We may also want to put in here like a signal that the Rt box emits, which would also be a hit signal. Okay, so I want to test everything so far. I'm going to set a break point over here on the left so that we can hit that on Debug if the weapon manages to damage the character. So let's hit Play and see if it works at this point. I'm sure I'm missing a couple of things. So we can see at the bottom here that deal damage doesn't exist because it's actually tri deal damage now. So we changed the name and then we'll run this again. Do we hit the breakpoint? We get to here. Stack controller can be hit, but we didn't actually assign the stack controller to the RT box. So this might be another case where we want to assert at the top that the SAT controller actually has been set. So assert stat controller does not equal null. HRT Box needs at controller reference to effect. So the HRT Box needs a reference to the SAT controller so it can change its stats. So let's see. If we go to TD View, Local mode up here, and then we click on the RT Box, we'll just assign on the right the stack controller over here. In case we forget at any point with any hurt box, we have the assert to have a fallback to catch us. So let's run this one more time. We're going to hit that and you can see that we've returned true. You can also see at the bottom that the new HP has been set to 90. Our base HP for the stat controller is 100, so 100 minus ten, we get 90, so we know that that's actually updating correctly. And the next thing we'll do at the start of Next video is going to be to set up the orc state machine so that we can transition from alive to dead once the HP drops to zero and then remove the orc from the scene. 17. Death State When Enemy Reaches 0 HP: Now we want to set up a state machine for our enemies. It's going to be incredibly simple. There'll only be two states off the bat, which are going to be the move state or the death state, which clears it out of the game. So let's right click on Oc add a child node. I'm going to say HSM here, and we're going to grab the Limbo HSM. We could rename this to be Enemy HSM, and then I'm going to right click on it, attach a script. So this will go right into the enemies folder slash EnemysHSM. We'll create that. Set the top here. Enemy HSM. This is the state machine for enemies inside of the Okay, so looking at the player HSM, it's going to be really similar here. I think because we actually switched to the Blackboard, that script no longer needs a reference to the animation player directly, which would be correct if I look over here in the Blackboard plan. There's no need for the second reference. So I can actually just cut that out right there. So we're going to copy this code from player HSM over to the enemy HSM script. So let's say that instead of player, this is going to be a NPC script, which could have its stats set on it. So NPC of type NPC assert that the NPC isn't null. So NPC agent must be set on the HSM, and we'll initialize it with the NPC script. So just kind of changing the names here mostly. So the NPC script, let's go over to the orc scene to D View. And we're going to want to we click on OC, attach a script. We'll say this is NPC, and we'll put it in characters not character slash Enemy, or it could be in enemies up to you. We'll create this script for the character body. And let's call this className NPC. So the NPC will just have easy reference to the stack controller. So that we don't need to necessarily reference those two things separately. Inside of the state machine, it's pretty convenient to access properties from the agent because you always know the agent is going to be there on any state machine script once you've initialized it on the state machine. So if we do export VAR stats here, and then we'll say stack controller, then we can reference that super easy inside of the state machine, too. So over here on the right, we assign the stack controller, and now inside of the enemy HSM, the errors should go away as long as we click on Enemy HSM. Well, this isn't right. It's supposed to say NPC. So we just need to reboot the Cadle project, go to Project, reload project. Okay, and that should fix that up. So now we assign the orc, which is the NPC, and we have a reference to the agent as well as the stats, and that should be most of what we need for our state machine. Okay, so let's right click on the enemy HSM. I'll add a child node. And let's do a Limbo state. I could rename this and say something like NPC move state. Well, right click attach a script. Create. Okay, so next, let's create a Chase state for the enemy. This will be where the core logic for our character moving towards the player is going to be, as well as playing the run animation as soon as we have it into the state. So I'll right click on the enemy HSM. Let's add a child node, a Limbo state. I'll rename it Chase State. And I'm going to right click here, attach a script. So Chase state that probably belongs to an enemy. I'll hit Create. Okay. Let's give it a class name at the top, Chase State, and we'll give it a comment. So this is going to end up running towards the player wherever it is in the game. For right now, we're just going to have it play the animation. We'll set up a transition from the Chase state to the death state and just make sure that our character dropping to zero HP makes the enemy alive inside of the game. So we'll say function underscore, Enter. When we enter, we want to play the run animation. So we can even do function underscore setup, and then we're going to get that animation player. So far, underscore animation player of type animation player, and we're going to get that from the Blackboard, just like we did with the player. So animation player equals Blackboard dot get var. And we're looking for the end quotations animation player, and we want to complain if we don't find it. So we have the animation player. When we enter the state, we play the animation. So we're going to say underscore animation player dot play, and we want to play the animation we define up here at the top. Let's do at Export var. Let's say Chase animation. Sure. And that will be a string name. I'll default it to run. And then we copy this down to here. So we play the right animation when we enter the state. Okay, and our Enemy HSM, we already initialized it. We already set it active. So it probably is going to work. So we can see if it runs now. Let's go into play Mode. Oh, we haven't put the animation player in the Blackboard, of course. So it tells us right away that there was a problem. That's good. Click on Enemy HSM, go over to the Blackboard plan, and let's do New Blackboard Plan, manage it, add a variable animation player. Choose the type of node path. Hit Okay. And now an animation player we want to assign the animation player of the or and now we can use it inside of our states. Okay, hit Play again. And let's see if our orcs are running. They all are. That's great. So we need our death state, and we need to set up our transition. So I'm going to right click on Enemy HSM, add a child node, which is going to be Limbo State. Rename this to Death State. Right click, attach a script, hit Create. We'll call it Death State at the top. So class name Death State. Okay, now in our death state, we also need to play an animation very similar to what we did in the Chase state. This is code that we're going to kind of repeat here, but because we're only doing a few states for our game, I think that doing things like making a new base class is just a little bit too extra at the moment. So I'm just going to copy this over. To the death state. Well, paste that in. And I could just say the animation here that we want for the death state is defaulted to death, and we get the animation played the same way. But then we want to play the animation over here. And because we're not using a base state, we don't have to worry about things like calling Super to make sure that the animation state code actually runs because they're both going to use setup, and they're both going to use Enter. If you declare a new setup or Enter function, it actually overrides whatever's in the base class if you forget to call Super and by the way, you would call Super like this. Super just means that whatever the parent class is, you run that at this point in time. So you can see it's a little bit tricky and confusing to use that kind of direct inheritance. If you have like five or ten different states that all are animation states because they all play in animation in exactly the same way, then it becomes worth it to kind of have that. But we're literally only going to have Chase and death, so it's overkill. But Tay should mention that that's another way of doing things, as well. So on Inter, we basically want to make our character invincible. So we may want to do something like agent dot stats dot LI, and we want to make sure that this is set to false. So remember that when a stats controller is not alive, then can be hit is going to return false. So this will prevent us from accidentally hitting a character that is in its death state. They also do something like, say, function finished, and we declare this function so you can actually call it from the animation or other places. And we're going to say that the agent needs to u free. So agent Qu free. So this basically means the route Aosne the org. We're going to free it and everything inside of it. When we call the finished method. So now we can go to two D view animation player down here at the bottom. We're going to go to the death animation. We zoom in on the timeline, go to the end here. We'll do add Track, call method track on the death state. And then we're going to right click down here, insert a key, and we're going to look for that finished method we just create. So, this means that when our death animation is done, it's going to call finished finished on the death state is going to free the agent as soon as the frame is done rendering and everything else resolves. So usually you would call quarter, not free, free freeze immediately, and quarter waits for other operations to properly resolve. So this would actually reduce the amount of weird bugs you might get when you qu fare. Okay, so the last thing we need to do is jump into the enemy HSM. Let's create a new function for setup transitions, which will return void. So we want to add a transition from our chase state to death state when the death event is emitted. So when you declare an event here like this, this is just a string that our system is going to respond to we could go up here and create a constant death event. It's going to be a string name equal to we'll just do death lowercase, paste that down there, and we can connect to the stats on our agent like this. Let's say NPC dot stats dot Alive changed. We haven't created this signal, but I'll just go ahead and jump here, so connect. Underscore on NPC Alive changed. Okay, so I'm going to copy this down to the bottom function paste on NPC live changed, and this is going to have the new value. So alive is a boolean. Could say P for parameter if we like here. I usually like to do that. And this is going to be returning a type of void. So when the NPC is no longer alive, then I want to emit the death event. So if P alive equals false, then we are going to dispatch the death event. Okay. And when we add the transition, that means that this transition is going to happen when this death event occurs. Now, it's still giving us an error message because we don't have the Chase state or the death state reference here at the top. So we can just say at export var Chase state, which is a limbo state, and at export var death state, which is a limbo state. Now save the script over in the inspector. We can assign the Chase state and assign the death state. And now, once we call setup transitions, it's going to handle managing these transitions. I think we want to call that before initialize, or perhaps right after. Let's do it right after. So underscore setup transitions. We call that. We set up our transitions. There shouldn't be a need to create a death state to chase state because we don't have enemies coming back to life currently, though, you may later add that sort of thing. Certainly, that would be a common theme, especially for, like, undead enemies. Okay, so the last thing to make this work really fast, go to the spear scene, and let's jump into the hit box, or is it the spear? Right, the spear. And we want to take the final damage and set this to 100, so it will be one hit equals one orc defeated. Hit play, and let's see if this all comes together. So it's giving us an error. I haven't created live changed event on the NPC stats. Right click on NPC symbol, go to Lou symbol. Right click on the symbol, Lou symbol, right click on Stat Controller, Lou symbol. Okay, and here's our stats. Let's create the signal, so we could say signal live changed. And this is going to pass in the new live value as a Boolean. We'll come back here and assign more signals, but for right now, I want to make sure that this signal emits whenever a live value changes. So I'm going to say set value. So first, we're going to guard against if the value is the same as the value being set to it. So if a live is equal, two equal signs to the value will just return. Otherwise, a live equals value. And then finally, a live changed dot emit with the new value. So where do we want to calculate Alive? Well, a good place to do it would be in this HP setter. So whenever the HP changes, we want to check if the character is still alive. So that's just going to be live is equal to HP is greater than zero. Okay, so that's handled. Now we have the live changed event. This should all trigger when the character hits zero HP. Let's stop the game and re run it again and see if we hit it with a weapon, like so, does the character remove itself? Does it go to the death state? And we can see that the answer is yes. So it takes 100 damage. HP hits zero. A live gets set to false. The signal gets emitted. And then that means over on the enemy state machine that the transition triggers because this signal hit. This patch on the enemy HSM emits the internal event. So these state machine events are separate from signals. These are just for the state machine in Limbo AI signals in the greater context of Gadot. So we get that internal death event, and that makes us go from Chase state to death state. Inside of death state, we play the animation death on Enter we make sure that this is set to false here, though. In theory, you wouldn't need to do this, but I don't think it's going to hurt anything to make sure that if we're playing the death state, that the character is no longer alive, I think that's fine. And then we have the finished method, which we're actually just calling from the animation. As you saw before, the death animation down here calls it right at the end of our animation, and that is how we make our enemies become on live as soon as they drop to zero HP. So I know there's a lot of pieces coming together. I would recommend for this part if you haven't already been downloading the individual part snapshots that you do grab the one for this video if you are, you know, running into hiccups and see where maybe your script is a little off. So what we're doing right here with signals is the observer pattern. It's really useful when you want one piece of code to respond to a change in a different piece of your code. And something like C Sharp, this would be called events and then event handling. But in Gadot it's called signals. Essentially, they're the same concept, though. So it's observer pattern. If you want to look that up and read more about it. So that's going to be a wrap for this 18. Rotating and Aiming Projectiles with Weapon Loadout: Okay, so in this video, we're going to make it so that our spear rotates to face the right direction after instancing and that we can actually launch the spear in the correct direction as well. So how we're going to need to set that up is inside of our weapon two D script here. So in our survivor like game, we probably want to make it so that a character can have multiple weapons at the same time. Therefore, it actually makes sense to have some kind of manager for all of these weapons. So we can create a weapon loadout script that's apparent to this weapon two D, and we could use that as a sample way to resolve things like the player import for our weapons individually. So let's right click on the player node and add a new child node. I'll call this node two D, and then we're going to rename that to be weapons loadout. We're going to immediately parent weapon two D to weapon loadout here, and I should rename weapon two D to just be like spear weapon making it clear what it's actually holding. And then let's click on Weapons loadout and add a script. So this is going to be weapons loadout it can go in player, create and then at the top, we'll do class name weapons loadout. So this is going to be a manager script for all of the weapons that a player has equipped. So here we could just grab a quick reference to the player input. Okay, and now we can go to the spear weapons script, and we're going to insist that whenever we have a spear weapon, we have the dependency that the parent is a weapons loadout that'll be consistent across our game. So I think that the setup is fine. So I'll do var underscore loadout here inside of our weapons two D script. And this will be of type weapon loadout. So on ready, we're going to want to get that loadout. So say, underscore loadout equals get parent, and we should assert that the loadout does not null. Parent of a weapon should be weapon loadout. Okay, and it seems like I might have set the class name. Is it weapons with S? Yes. Okay, so that's weapons loadout here, not weapon loadout. Okay, so we have a reference to the parent loadout script. This does create a dependency, but it also makes it easy to access things that we need. So, in case we ever decided we wanted to change this pattern, I'll create a function down here at the bottom for Get direction. So function Get direction is going to return a vector two. And in this case, we're going to use the loadout to get that. So return underscore loadout dot input. Dot direction. And so this is how we resolve the direction, but we can just always use the G direction function in order to handle that. So then if we call, let's say, get direction in ten different places, but we change the loadout. So we don't actually use a loadout anymore or maybe the loadout doesn't use an input, then we just have to go in here once and change this. So maybe the loadout has a direct reference to direction, so we just change it like that, and then it would work again. But I actually need to change this a little bit because we're not going to use the direction. We're going to use the facing direction because remember, direction is the raw input. It can be vector dot zero, but we're never going to cast a spear a down at the character's feet in this style of game. So we actually want Get facing here, and we'll take that to Import dot facing. So we're really going to resolve the direction that the character is facing and instead. Okay, so let's take this Get facing and where we cast, we'll just do cast to get facing instead. And we move our character around, you're going to see that already, we can cast in the right direction, and this should include diagonals as well, like that. Okay, so that's half of what we need done right there. Now, we also need to rotate any projectiles we instance. So let's get that rotation angle. So inside of cast, under where we create the pact scene for the projectile, let's do VR rotate angle. And we're going to take the get facing vector. We're going to convert that to an angle here, and I think that's in radiance. If I right click and do Lou symbol, we can see returns the vector angle with respect to the positive X axis in radians. So that would be stuff like Pi divided by two kind of thing. Okay, but we want to rotate it so that the spear or other projectiles are facing the right by default as our base angle. So if it's facing up and the sprite, we want to rotate that Pi divided by two or like 90 degrees like this so we're going to add Hi, which is a constant divided by two, and that'll make it so that it's facing the right by default. Withut mathing it out, it's really more of a trial and error thing here. But let's get that rotation. So we're going to after we instance the projectile and we said it's global position, let's do projectile dot Rotate and this function takes radians. So we have our rotate angle. Really, I should rename that to be rotate ads. So I'm going to double collect that Control R, and we'll say rotate ads for radiance, replace all, and that looks a lot cleaner there. So save and run, and there we have our spears pointing the right direction. And this should also work with essentially any projectile. As long as those projectiles face upwards by default, if they don't, then you can rotate this sprite individually on the projectiles scene until they are facing the intended direction. So there we have our aiming and rotations for the projectiles. And to show one more thing, if I let go of the direction, then our direction is now vector 20, but we're still shooting to the right because facing will never be set to vector two dot zero. And that's why we use facing instead of the raw direction input. Okay, that's a big step done right there. 19. Adding Sound Effects and Music: Let's add some sound effects into our game. First, I'm going to start by having a launch sound effect, and then maybe when the enemies reach their death state, we have the option of making them play a sound effect there as well. I would probably stay away from on hit sounds because there's going to be multiple enemies getting hit on every single second, and that would just end up being, I think, too many sound effects at once. You wouldn't want to overwhelm your player too much. So let's just start with those two. So for the player spear weapon, let's say in the weapon to D, we'll add in a sound player. So at Export var audio player of type or audio stream player two D. We want it to be two D because these projectiles exist inside of the TD game world, so it makes sense to affect the audio based on the spatial positioning of the camera and the projectile and all of that. So we will take the audio player and we'll have to put it under here. So to add audio player node, we're going to zoom in here and right click on Spear weapon, add child node. So we want the stream player two D, add that in under there. And we want to assign a stream audio that we can play here. So let's find out sound effects. I'll collapse all the folders. Let's go into audio. Let's see. What was I using? I think we had, like, Blal sound effects. Okay, let me get my headset on here so that it's not coming through the microphone. Okay, so inside a, I think it was going with slash 04, so we could double click on that, go to the top right and preview. Yeah, I think that's what I was using in my demo project. So let's take the slash and put that into the stream here. So now we can just tell this audio stream player to play whenever we launch a projectile. So let's go into stream weapon. And when we have the projectile cast, we'll say that if the audio player is not null, then we'll tell it to play its current audio clip. So down here, after we instance the projectile, we'll say, I audio player then we'll say audio player do play, and that's basically all we have to do. The reason we have this condition here is that for some weapons, you may not actually want it to play any audio, so we will just skip that option here. Now, one thing I want to point out here is that we are giving extra responsibilities to this weapon script. So now the weapon two D is not only responsible for instancing the projectiles, but also telling the audio player to play when the projectile is instanced hiring its own reference to the weapons loadout and creating a timer, as well. So it's a few things going on now, I don't think that this is overwhelming and a problem for the game, but it is something to be aware of. If you notice your scripts taking on too much responsibility, then you might want to consider breaking it into multiple nodes with different scripts and having them work together as individual pieces rather than one giant mega script. But we're not there yet. The script is only 50 lines long. It's not really a problem. If you had maybe two or 300 lines of code, it would probably get to the point where you would need some sub objects, I would think. But don't worry about that too much. Most of the scripts in this course are pretty simple. I just want you guys to be aware of that going forward that a script can become too monolithic, and that can be problematic in the long run. We have a spear weapon. We need to assign the audio player for it to actually play audio. So assign and then audio stream player. Now if we play, we should get our sound effects. Okay, yeah, there we go. Every time we instance a projectile. Okay, now we want to play a sound effect when our orcs die. So let's go into the orc scene. All right, click on the orc, add a new audioStream player here. If we want, I could rename this to be a death audio player. We could say. Okay, so now we would want to find a suitable death sound effect. Playing around with it a little bit, I think I think enemy Death 01 is too much. Even flash oh two is a little much as well. The thing is, there's going to be enemies dropping like flies in this kind of game. So we need something very subtle or maybe even none at all. I think I'm looking for something more like the Step grass 03. Now, that might sound a little weird, but I think anything that's longer than that is just going to be too much to see how it goes, may even remove this altogether. So let's assign the Step grass oh three to the stream here, and to tell it to play that sound effect on death, let's go to the animation player. Down in the bottom left, we will select the death animation. And we'll add a new track, add track here, call method on Death audio player, and we'll right click at the start of this animation, insert key, and we want to play. So it's kind of like we're writing a script when we're defining these animation keyframes, except we have the timeline, so we can tell exactly when we want it to play that animation, and it's very easy to move it along to a different point in time just by dragging it to a different keyframe. So you could script all of this writing GD script code if you wanted to and just skip the animation player. But obviously, animation player kind of makes it a lot easier to do it visually. Okay, so now if we play, we should get both sound effects to play. So you can hear when the orcs dive, it does play that little sound effect. It's a little quiet, but maybe that's okay. If we think the spears too loud by comparison, we could even take the spears audio stream player and drop the volume decibel here by a bit. And hit play and run it one more time. You can even edit this while the game's running. So we could just drop this down. Okay. You can see it's already way quieter. Sea, one of the nice things about cado is you don't have to necessarily close the game to change your properties. And let's hit an enemy. Okay. And the volume difference between those is a little less pronounced. So it's probably good now. As a little bit of a bonus for this video, if you want to add in music, let's go to the world, and let's add in a audio strem player, audio stream player two D, just the default note. So stream player here, and then pick this one, Audiostream player. Okay, so with our audioStream player, go over to the right, and we're going to want this to be on autoplay. And I think to make it loop, we actually have to load it from the audio MP threes first. So let's close everything in the bottom left again, go to Audio, and we have the box jump song from the Th Red Hearts pack. So we'll bring that into the top right. It's actually not P three. It's a dot OGG, but both formats for music are supported in KDO, so it'll work fine. And then we want to take this and check Loop on. But we might actually need to do that in the bottom left. So double click the OGG here. Okay, and then check looping here. And when it gets to the end, it should loop the whole thing. Okay, so that's good. Okay, so the last thing before we play the game, I'm going to take the volume DB and put this at negative 20 because I think it's probably going to be quite loud, otherwise. So let's hit Play and see how it goes. Okay. So we can hear the music, and it's still pretty loud. So I'm going to take this to negative 30. Maybe even negative 40. Yeah, that's probably a bit more appropriate. So now we have the audio. We have the music, and we have a Enemy Death sound effect. So that's kind of the basics there for audio sound effects and music inside of the game, and we can keep going from there. 20. Combat System Singleton: Okay, so the basic projectile damaging and combat kind of works at the moment, but we don't really know how much damage is being dealt to the enemies in the context of the game. So one way we could do that that would also visually enhance the gameplay would be floating combat text. So whenever one of these characters takes damage on the hurt box, we can communicate with, let's say, a combat system to instance, floating combat text at the position they took damage every time they take damage. So this will mean there'll be a lot of numbers floating around the screen, but I think that works pretty well for the style of game. It's very action based, and we do want immediate feedback whenever we are damaging enemies. And we also want to see the benefits of leveling up our abilities, as well as our character gets stronger. To create our system, we're going to want to do that as a Singleton object, meaning that there is one reference to this class or this system throughout the game. And if we set it as auto load, it will always be there in the background, and it will be easily referenceable by name. You can also implement Singleton pattern in the script itself, but we'll do it the Gada way, which is a little bit simpler and easier to understand. So at the top here, I'll just create a new scene. We'll create it as other node, and this will be a base node. If you are two here, you can rename it to be Combat system. Then let's right click on it, attach a script, Combat system. We can save that either in the root or we can create a systems folder to save that into, which would probably be my preference, and I'll create that in there. Now we want to save this combat system into the systems folder. So combat system dot TSC save that. And now let's make it a auto load so that this node script can be referenced across our game just by using the name. So I'll go to project project settings. Then we have Globals here. And we want to add a new TSCN as a global. So click here, go to Systems and then do TSCN. You can also see that you can select a dot GD script as the combat system, and what that would do is create a new instance of the node script in your game as soon as the game launches. But if you use the dot TSCN, then you get the advantage of being able to use export variables because this is a very specific copy of the combat system. If I double click that here and add this as combat system node name, then now we have this combat system as a global Singleton and side of our game. And the difference between autoloading a dot GD script and auto loading the SEN directly is that if we do something like Export var spawn count, integer equal to ten, then we can set this over here, right? But if you instance a script that hasn't been defined as a scene or a node yet, then you wouldn't have any ability to actually customize properties like that in advance. Now, I'm sure there's ways to get around that, but that's one advantage of using dot TSE. Okay, so we have our combat system. We're going to want some kind of report damage function. So I'll just kind of draft that out. Here. Let's say function. Let's say report hit. I think that makes sense. Let's say we have a target that got hit, and we can say no two D could just be anything in the game that was able to be hit. Generally, that would be like a character body or a static body. But we'll just use the base class for flexibility. And then we have here the amount of damage. So P damage is an integer. This will return void. And then in this combat system, we will instance our scene for the floating combat text. So let's go up here to the top and we can say at Export var floating combat text scene. And this will be of type packed scene. And down here on Report Hit for right now, we'll just do a pass. Or if you want, you can even do a push error and say not implemented. This would give you a little bit more feedback if you ever forget to implement a script rather than just it would run the function and it would pass so there wouldn't be any error, but it also wouldn't do anything. So I think this is a little better during development, actually. Now let's jump over to our HRT Box two D script. So Control P, HRT Box two D. And then inside of here, we can reference the combat system. Okay, so combat system dot report hit, and we want the object that the stat controller is managing. So I don't think the stat controller actually has a reference to that yet. If we take a look at the orc scene real quickly, let's click on the Stat Controller. We can see there's nothing over here in the right side to get a reference to the Oc. We could just make that like a get parent kind of reference or we can make it an export variable. If you want to make sure that the connection is more absolute, then I would go with at export. Because that would mean that if for some reason you move the stat controller down one level, like you parent it to something else, then Get parent would no longer return the orc. So if we use at export, we can make sure it returns the c. We could say something like body or character, maybe even object, keeping it more generic. And then this can just be the object this controller is managing stats for, and I'll assign it in the top right to the orc here. Now, if we go back to the Hurt box, we can use the stat controller dot object here, and then we can pass in the damage P damage. Okay, so that'll give the combat system everything we need to know about where to position the text and how much damage to assign to the text. 21. Animating Floating Combat Text: Now we need the floating combat text itself. So let's create a new scene over here. Click plus and do user interface. And we're going to make the base node just be like floating combat text. But the actual text we'll assign as a child label. And this will help us out when we're using the animation player to animate because we don't want to change the root position. We just want to change the texts position relative to the root of the floating Combat text control. So, right click here, add a child node, look for label. And then let's right click on floating Combat text Adhild animation player. Okay, I think that's what we need here, so let's control S. And we can save this. Maybe we create a folder for UI. This is kind of UI still. So let's create a new folder. I'll type in UI. Okay. And then floating combat text dot TSC and can go right in there. So hit safe. Okay. And now we need a script attached to our root here. I'll right click on floating Combat text, attach a script, and we'll create that in the UI folder, floating combat text dot gD. We're going to want a reference to the label so we can manipulate it here. So control var label label. So we'll assign the label here. And we want to create some kind of initialized function. If you've used Gado, you probably know that there is a function underscore and knit here. That function is used for when a node is being created in the scene. But in this case, we need to wait until the object has actually been created already. So that's why we need to create. So if you've used Gadot for a bit, you might know that there's a function underscore and IT function. So this is a overridable function where it can run code before all the properties have been set for your system. You might use it to pass in optional parameters to set those properties when you are creating a node from a function call rather than existing inside of a packed scene like over here, which is what you usually do, you would just set up the scene and then instance the scene from a paced scene. So that's actually not what we want to do. We're not trying to customize the initialized function. We're trying to make the floating combat text run when it is already in the scene. So I just wanted to make that distinction there. Okay, so what we want to do in the floating combat text script is have the option to set the text on the label, and we'll just do that directly with a kind of more of a delegate function here with function set text. So we'll set the text on the label, but we'll do it operating through the root node floating combat text. So set text, and we'll say P text, which is a string, we'll return void, and we'll take label dot text and set that equal to P text. So one reason to do this is that our Okay, so, of course, we're going to want to set this whenever we instance the floating combat text scene. So you can give a class name up here so that we can easily get that set text method. So class name, floating combat text, we'll hit Controls there. And now in our combat system, whenever we report a hit, we want to instance our floating combat text scene. See, let's say Vara text is of type floating combat text. And we get this by instancing the floating combat text scene. So enter and then instantiate. Okay. And then we want to add that as a child. And now we don't want to actually add the text as a child of the P target because when the P target gets deleted from the scene, like the death animation plays, it would also remove its child nodes like the floating combat text. So because we have this global system, the combat system to handle the text, I think we might be able to get away with just doing add child on the system itself, and then we add the text node. Now we need to set the global position to the position of the P target. So text Global position equals p target dot global position. And if this bit doesn't work or we run into any other issues, then we may get a reference to, like, a canvas layer, the actual UI and position in there under some parent kind of similar to what we were doing with a projectile, so we just need a parent to hold all of the text. But the position should be wherever the enemy took damage is the idea here. Now we want to take the text and we want to set its text. So set text to a string of P damage. So P damage is an integer. We convert it to a string by saying STR and then wrapping the P damage in the parenthesis. And then we set the text to that string value. So if we set the combat system floating text scene here, so now I'm looking at the Singleton scene, the combat system, we can go over here and we can quick load our floating combat text. So we have that reference to the packed scene over here on the right, so it should be instantiate let's hit Play and give it a shot. Okay, so we're going to have the damage be hit. Okay, yeah, that's kind of what we expect. And you see, very important when the orch gets removed from the scene, the text does not remove itself. Okay, so now we just handle the animation for the floating combat text. So we did that in the animation player. Create a new animation at the bottom. New. And let's see. We can call it whatever we want. I'll go with float up, and then we need to add some properties to animate. So add track property track label, specifically the label because we want that offset position. So let's look for position here. Okay. Now, we can't see anything because there's no default text on the label, so I'll click on the label, and I'll just say DEF for default here. We may also want to assign a theme with a new default font that fits our Pixar game a little better. So we can do that by clicking on floating Combat text. Going to theme on the right side. Do new theme, expand this, select a default font, and I'll choose what we already installed at the start of the project five X seven, which you can see is a nice Pixar font. So and then if we want to reuse this theme, which we probably do. I'll right click and save this to the project, and UI sounds fine. So we'll just say game theme. Dot TRS for now. Okay, if we close that, you can see it's now referenced by the file name. Okay, now, a couple of things. On the root node, the floating combat text, we can see it basically has this giant box. We don't actually need it to be that big. So go to layout. And then instead of Anchors preset full rect, let's change that to reset. So now the control is just this little one point up here, and the label is its own size as a child. Then another thing on the floating combat text. I do want to take mouse and change the filter to ignore just so that mouse clicks will still go through on the screen, even if they're touching this floating combat text, I want mouse clicks to be ignored. So if there's any UI on the screen, this won't interfere with pressing the UI button. That's pretty important. And now we go back to our animation player, and let's keyframe the position. So I'm going to zoom in at the bottom. Let's right click keyframe the position, and let's move it to wherever we want the starting position to be. So the 00 is where our enemy took damage. I think I just want to make sure that it starts somewhere above that. So I'm going to w to move the text. I'll move it up here, and then we'll keyframe the position here. So go to layout on the right hand side, and then go to transform and keyframe the position. So I have it at negative 15 pixels on the Y. And then we can go to how long do we want this to last? Maybe half a second. You can also enable snapping if that helps. Okay, so 0.5 seconds. And then let's move it up here. So kind of like that and then keyframe it again in the right hand side, just using the keyframe button. Okay, and now it's going to animate between these two points. So if I go to the start and I play, you can see it's going to animate that position movement. We can change the full duration to 0.5 seconds as well. And then if we like, we could just have it ure at the end by doing add track call method track on the floating combat text root. And then right click here, insert and type in quarre. Okay, that'll make sure that the text cleans itself up after it is done in this scene without having to custom script anything else, really. We can just call it from the animation. Now, this would probably work, right, right now, but it's really basic. Let's test it. Just make sure it works. Okay, yeah, I didn't play the animation. So that's because I didn't set Autoplay here. So in the bottom right of the animation player, Autoplay on load. Okay, now we hit Play again, and we're getting our animation. Okay, so that works at a very basic level. It's not bad at all. But while we're here, why not make it more interesting? So I can add a track on the property track of the label, and we'll do, let's say, scale. Okay, so this will let us make it bigger or smaller as time progresses. So let's key frame it as it is at 00 time. Let's say about 0.15 seconds or I guess I'm snapping to 0.16 67. It doesn't really matter here. And you can change your snapping in the bottom, right, by the way, if you want to have a more customizable keyframe snapping. So let's go to label and then transform scale. We will take its size and bump it up. So maybe like 1.5 or maybe even two. We can start with that and scale it down if we need. So I'll keyframe this. So now it's going to go from 1.0 to 2.0 size. And then when it's fading out, we want it to go really small. So let's take it to, let's say, 0.1 size, and then key frame it. Okay. So now if we play our animation, it's kind of like this. Maybe the scale is a little bit too big on this second keyframe, so I'll just take it to let's say 1.5. I think that's right. Hit play. Okay, that works pretty good. And we can also take the position and offset it maybe to the right when it is reaching that peak size point. So let's take the position here with W move mode and move it off to the right or something and then key frame it. Okay, and now we'll play again. So now we can see how text pops out to the right and then back to the top left. Another thing we could do would be to change the color or the Alpha over time. So let's add a track on property track of the label, and we'll make this self modulate. So it only affects this label, not any child nodes. So I'll self modulate on the label. Let's right click insert a key at the start. So this will be our standard white with full Alpha. Let's go to 0.15 or wherever our key frames are snapping here and insert a key. 0.0 1667 for me. And then let's change the color to red or something because this is for damage text. And then let's go to the end here. Right click insert a key, and we'll change it to let's say zero Alpha. Can it play. So now it's kind of like that. I think it might make more sense if we swap the first keyframe and this one. So I'm going to drag this on the timeline. I'll pull the left over here to the 0.0 1667 point. This goes over here. So now it'll start at red and go to white. And we could have it end with a white color as well. So I'll click here, go to the value and change it to 255 for the green and blue. So it ends white though fully transparent. If I play a few times here, it feels very gradient. I mean, it really is because we're just traversing one color to another color over a period of time. So gradient would be like that from left to right. Like, it goes from red to white as it progresses across the screen, except instead we're doing it over time now. So kind of up to you how you like it here. You could just change this to be red, too. So it's just red all the time. The only thing that changes is the transparency. Then you'd have to set the third key frame point as well, kind of like that. Maybe we would want it to get darker over time instead, so we could take the values here and make it like a dark red, kind of like that and see how that goes. Yeah, that looks pretty good, I guess. Maybe the red is a little too vibrant, so I might take the initial one and make it kind of like that. And maybe it can be a little brighter at the peak. Let's try, okay, darker to bright, and then to dark again. So, that seems to work pretty good. I kind of like it. This is purely a personal preferences artistic thing. So really, whatever you like, go with that. Okay, so let's play and test it out, and we're going to have our damage text as soon as we hit the enemies. And it looks to me like it just moves way too fast. So maybe we need to double the duration of this animation. So if we click on animation player, we can just move these keyframes along, and this is one of the huge advantages of using a keyframe animation editor rather than, you know, hard coding it out. It's just easier to iterate and to make small changes to adjust it to be where you want it to be. So let's take these keyframes for Kframe. Put that at the end here at 1.0. This should go to 1.0 as well. And you can drag a box over multiple keyframes and move them all at once like that. So I can drag a box here, and we move this to, we could try like 0.5 seconds, whatever. Okay. Now, that's pretty slow there. So maybe we want this to be more like front loaded. Let's bring that over there. Hit play. Okay. I mean, I'm not sure. We'll have to give it a shot and see what it actually looks like on the screen. It seems like it takes a little too long to fade out. So maybe this does belong more like there. A little further along. And maybe 1.0 seconds is too long. I'll just kind of drag it there. So whatever this keyframe point is 0.8 seconds, that'll be the duration of the clip. Maybe the text is also too small. So I think one way we can kind of get an idea is to put a copy on the world scene so that we can see it right here as it is. Okay, so there's our Daf text. I think we can actually write click here and do editable children, and then we can click on the animation player and just test it and seeing here. If we switch to float up, does that work? Yeah, kind of does. Okay. Yeah, this helps a lot. So you can see really what it's going to look like in the actual gameplay, because we have the mock up scene here. Okay, so maybe let's take the position here, and I'm going to move this down a little bit. And it looks like we can keyframe here as well, so that's good. So do this actually get the 28 negative 31? Let's have it fade out a little faster. So I've never actually tried it this way, specifically. It seems like you can keyframe on the label, but you can't adjust the animation player directly when you're editing from this other scene. So there's a bit of a trade off here. I might change the self modulate on the first frame. And keyframe it there. Okay, so that works. That updates the animation player. And then I can jump into the floating combat text scene. And I want to take the modulate color and just move that over to the left a lot more. So it'll turn red faster, but I didn't really like it when it was red the whole time. Okay, and then I'll go here and I'll add another key frame. And in this, I'll make it full transparency. So it'll be visible on the screen for longer. It won't fade out until close to the end of the full animation. Let's play, and I'll try that one more time. Maybe here I also want its color to be white as well. So I'll do that. So just have a flash of red. One last thing in the bottom right, if you take the interpolation mode here, you can change it to cubic. And I'm going to do that for the position, the scale, and the self modulate properties. So if you look really closely here, it puts a bit of a curve on the left and right sides for that. So if that's working like other timeline editors, which I think it does, then that's going to mean that it'll be a ease in and ease out curve rather than a linear transition for the animation here. So let's test that here and see if that's the case. That's what I would expect coming from other things like DaventiRsolt video editor. And it does seem to have that easing here. So I'll change it back real quick just to compare. We take that to linear. Yeah, now the speed's just consistent. So I was writing my assumption. So that can give us a little bit more of a oomph to the animation because it has its slow moments and its fast movements now. If we hit play, we'll test that out one more time here, and I think that works right for right now. So I'll hit play. We'll test it out. I feel like the animation is a little long here. It just needs to have that popping moment where we can clearly read it. Okay, so once again, I'm going to shorten the animation just a little bit here. Pulling all the keyframes to 0.7 there. Animation ends at 0.7. Let's play one more time and test it. I think I'll just wrap it up after this. Okay, so the fading is now very fast. Okay, and then for the position when it's fading out, I want to move that as well. So let me drag this over here so I can see it. Click on the label, pork kind of maybe there. I'll keyframe the position, and then I'll drag this point over back to the right. I think this will just make the animation a little more subtle. Okay. Yeah, so now when it's fading out, it doesn't move quite as far. And I think that's a little softer to look at. So we could go on like this for a long time. I think you get the idea. Just customize the animation to whatever you think would make it feel good in terms of color, scale, transparency, positioning, et cetera. The main things for this tutorial, though, code wise, are just making sure you have the combat system. It's auto load. You tell it to report the damage, and then on that report damage, you instance the combat text, and you add that not as a child of the target node that took the damage, but as some systems node or just a persistent node that's never going to remove itself. And you remove the floating combat text in its animation by calling Q free. And that's basically the gist everything else is more artistic detail. 22. Enemy Movement with Player Tracking in Godot 4: Projectiles already have the ability to damage and slay an enemy, but our enemies don't actually walk towards the player. So for orcs to move towards the player, first, they need a reference to the player. It would be a little bit tricky to have a direct reference between the orc and the player initially because they are not existing in the same scene when the game starts. When we spawn enemies into the game world, they're coming from the packed scene. So they don't exist here until they're actually put into the scene by the spawning system. So what we could do instead is have a reference resource, we could call it player context, where the player sets itself on the resource, and then each orc is going to reference that resource to pick up on the player target as soon as they spawn into the game world. This would have some extra benefits of being able to swap out, let's say, the player target on all of the enemies by changing out the resource once. We could even have the combat system set the reference as soon as they spawn a copy of the orc into the game scene. So the spawning system could essentially choose which player each orc is going to go after, and that can have some added benefits over just making it a pure single ten reference, which you certainly could do, in this case, for the player. But that would also tie you down strictly to more of a single player game, which is fine for this course. But I'm going to try to show how you can achieve it with the resource method. So in the bottom left of our file system, going to collapse everything. And let's go into characters, and I'm going to right click here, create a new script. Let's call this player context. And for inherits, let's make it resource. Okay, create the new script and make sure you open it up in the script editor will give the class name player context. Okay, so this is just setup to be essentially a shared reference to a specific player inside the game. So what we need here is going to be var player. And if we have a player class, we'll make it extend that. So var player is of type player, and that might actually be all we really need here. For that resource. So let's go to the player script now, and inside of player, I'm going to export a reference to context. So let's do a Export var context, and I'll call this of type player context, controls to save it. And we can see in the right now we have a slot for resource. So let's create our new resource. And then I want to right click on it and save it to the project. We'll save it into let's say characters. We could put it in player if we want, so why not? And then I'll call it player Context here dot TRS, which is for resources. You might also want to move player context at GD into the character slash Player directory. Okay. And now our orcs need a target. So we can go to the orc NPC. You can click over here if you need to. We'll say at Export var target is a type player context. If you needed to, you could also make it like a target context if you just need to go a step more generic for this, but they're only ever going to have one target. So I guess making more specific by calling it the player context might be a little bit better here for our specific case. But feel free to make it a target context if you want enemies to go after inanimate objects or NPCs or something like that. And this is going to hold the reference to the player target that this NPC is chasing. So let's just set it directly in the inspector. So click on OC and then on the right hand side, we need to give it the player context. So I'll click here. Quick load, player context. And you'll notice that even though the orc and the player are two completely separate scenes, we can have them reference the same resource and our project. The resource exists on the file system, so we can reference that inside of our individual packed scenes. So when they actually get added to the same game world scene, they'll both have the same reference. So the player is going to set itself as the player on the player context, and then everything else, the NPCs are going to read from that. And that's how they're going to relate to each other through this context intermediary. So the NPC script isn't going to do anything directly right here. We're going to have that set up on the Chase state. But the last thing we need to do before we get to the chase state is going to be to go back to the player scene and inside of player on ready. First, we want to assert that the context doesn't null because it's kind of needed for our game. So assert context so the context resource needs to be set here so the player can make itself the player of the context. And then we're going to take context dot player equals self. Actually, this is going to air out anyway as soon as we don't have a context because this can't be null. So this assert might not even be needed. We can just cut it there and reduce the redundancy. So as long as we have this line and the player loads first, the player on the context is set, and then we load in all the other NPCs, we can have them use their hace state to move towards the player now. So inside of Ok, we're going to go to Chase state. So in the Chase state, we need to get that reference to the player. So we can get that as a reference to the orc, and then the orc has the player context here. So there's a few steps removed. So let's see. Maybe we'll say something like VR target, node two D here. So we have this local variable. And then whenever we enter, the Chase state we'll get a reference to that target. You can even do it on setup if you just want it to be a one time thing. I'll put it in Enter. So every time we enter the state, we'll get the new target from the player context. So underscore target equals agent dot target dot player here. Because we don't know what the agent type is of NPC, we don't know that it has the target and we don't know that the target has a player. Okay, so this bit is a little bit fragile because it basically makes the assumption that the agent is a NPC, and it makes the assumption that the target is set, and it makes it the assumption that player is still the property on that player context for getting the target from. So this line specifically could break pretty easily if you change some of your other classes around. You might want to just set up some asserts here also to just kind of be early warning signs. So we could say assert the agent dot target and maybe assert agent dot target dot player. Let's change this from agent dot Target to agent dot Target is an NPC. Okay, and I think that's good enough. It doesn't cover all the cases, but if we were to change this class to something else completely different, I think these two lines would catch that. Let's hit Play and see if we're still good or if we get an issue here. So agent was expected to be an NPC. Maybe what I want is agent Target as NPC. I might be just using the bound keyword there. So here, I actually want to check that the agent is an NPC. The agent dot Target is the player context. So we make sure that the agent is an NPC so we can operate it on it. And we're getting no issues now. So that's good. And the last thing we just need to do is to put in the movement towards that target location, and we're going to do that on function underscore update. So let's implement that. Okay, so we need to get the target position. So var Target position. Okay, so that's going to be assigned from underscore target dot Global position. And then we have the position of our agent. So var agent position. That's going to be equal to agent dot Global position. So an agent can technically be a regular node, I think, and that's why it doesn't show up here. Savara agent position. The reason it can't infer the type is because we don't know for sure that the agent has this global position property. So we do need to declare it there as a vector two. Okay, and then we want to get the direction between these two locations. Savara direction's going to be equal to the target position minus the agent position. And then we normalize this vector to get just a direction. So it normalizes the XY value to a scale of 1.0. It won't have anything like the distance anymore. It's just going to be the peer direction, which would look a lot like if you're doing input dot get vector for the keyboard input. That would also be on a scale 0-1 for both X and Y. Okay, now that we have the direction, we just have to move in the speed that we are assigning. So each of our characters are going to have a speed in the game, and we're going to get this speed from our stat controller. So I think one good way of setting it up would be to use the Blackboard. So if I click on Enemy HSM, we can go over to the Blackboard plan, and let's manage, and we will add a variable. Let's see stats. This can be a node path, and then we're going to assign the stats controller. So now we can use that in, let's say, our Chase script and get the speed from it. So on setup, we want to get that stats controller. So we could say var, underscore stats is a stat controller, and we will say underscore stats equal Blackboard dot Git var, and we're looking for the stats property. So from there, we want to get the speed. So var speed down here at the bottom again, which is a float is going to be equal to underscore stats dot Get speed. Or if we want to make that even more straightforward, we could just say dot speed. So now we need a runtime speed for our character stats. So we go into the stats controller, and let's create a speed property here. So for speed, a float is equal to 100.0. So this is basically the run speed for the character at runtime. We haven't set up any definition resources yet to control basically what the starting stats of each character like the orc or lady will put in a skeleton will be. So we're just building this up one step at a time. So now in the Chase State, we have our direction, we have our speed, we have our Delta, so we just need to move the character a certain amount. So when you're moving a character body two D, there's at least two ways you could do it. One would be to call Move and slide, and that would be on the NPC. So this agent is NPC thing up here, I think it would actually be better if we did Var NPC of type NPC. And instead, we say, underscore NPC equals agent as NPC. And then we assert that this NPC is not null. So this ensures that the agent is an NPC, but also we have a reference to the NPC so we can use its class functions. So that makes it easier to come down here and say stuff like NPC dot MOV and slide. You can use this, and this would also have the ability of sliding along any collisions that the enemies find. But we're going to have our enemies ignore collision for this game partially because it allows you to squeeze more enemies onto the screen, and also partially because it makes it simple but I'm actually going to do npc dot translate here. So this allows you to move without taking collision into consideration. So it has the downside of your characters aren't going to be able to slide along edges like water's edge or a tree or things like that that are getting in the way. But it also has the advantage of making the movement as simple as possible, which means you can squeeze more enemies onto the screen at one time. And I think that maybe this arguably fits a vampire survivor style a little bit more. So we're just going to make our enemies brain dead simple. So pc dot translate, we're just moving it in a direction by an amount. So we do direction times speed times Delta. Now, that would work fine, but you might want to take that bit out to one more variable above and save our move, which is of type vector two, and we'll set that equal to this bit here. So we do the math outside of the function call, and then we call it on the move vector two once we have that. This also would help with debugging because you can set a breakpoint there and then it would show you what the move value actually came out to before you call translate. Whereas, otherwise, if you just put this into there, you wouldn't know what's going into the translate call unless you jumped into here. So a little bit cleaner, a little bit easier. Okay, so I'm going to hit play, and we're going to see where we're at. We have the orcs running towards the player, and wherever the player is on the screen, they're going to try to go to that, which is exactly what we would expect. Now, they do move pretty fast, though, so I might go into Stack Controller and take the default speed to something like, let's say, 40.0, and at play. Okay, and I think that's a little bit more appropriate. In most cases, the enemy should move slower than the player so that the player at least has a chance to avoid them. We can see that they're all stacking on top of each other. Okay, so we'll hit play, and we'll see that the orcs are going to run towards the player. These trees don't have any collision setup, but even if they did, they would still run right through the player body. Now, the player is still colliding with the orcs as well. And that's because the orcs still have a collision shape and body, and the player is moving using move and slide. So it's kind of up to us where we want to go from here. We could move all the orcs onto a different collision layer using collision here. And I probably would do that right now. Let's set up some collision layers. So edit layer names. We'll say that one is world, two is player, three is enemy, four is projectile, and that should be good for now. We'll close that. So we want to move our enemies onto the enemy layer, and we'll turn off the mask so that they're not actually looking to collide with anything right now. So now if we hit play, our player will be able to walk right through the enemies, but would still collide with anything else. So actually, I don't really like that they stack up on each other. So even though Translate would be the fastest function to run here for Chase state. And this is definitely the set of code lines that is going to be the performance block for the project because you're having hundreds of potentially even thousands of enemies trying to move at the same time using this update script. And remember, almost every enemy is going to be in the Chase state all the time until they're actually defeated, and they're only defeated for a second. So you're talking about everything being in the Chase state at the same time update runs at every frame. So this is the part where if you need to optimize anything, this would be it. Let's add the collision back in for the orcs. We'll have them collide with themselves, and we'll see if that just gives us a better result. The orcs all being on top of each other isn't really ideal. So we'll put that back in. So instead of doing NPC dot translate, what we do is we say underscore NPC Velocity is going to be equal to this bit without the Delta. So I'm going to get rid of the Delta and we'll say new velocity. Okay. And then we set the old velocity to the new velocity. And then instead of calling NPC dot translate, we call npc dot move and slide. Once again, I want to iterate this takes about twice as much processing power from my tests. So if you could have 5,000 enemies on screen at the same time before you get too bad of lag, now it will be about 2,500. But if you can have the enemies space out, maybe you don't need 2,500 enemies on the screen at the same time because they all occupy a completely different position. So maybe this is the way to go. So let's go to the orch, and we'll make it so that it masks the enemy. It'll also collide with the player here. So it's looking to the player, the enemy. And let's also make it the world, but not projectiles. Okay. And then we want to go to the player now that we're actually working with different collision layers, and we'll set its collision layer that's under collision Object two D to player. And then we want it to collide with the world. And actually, I think just that. Thinking about it again, the orcs, maybe I don't want it to collide with the player. I want the player specifically to be able to walk through enemies so that it can actually escape. So the enemies will collide with the enemies and the world, and we'll hit play from there. We could even turn off world at some point. It kind of depends on what we want. Okay, so let's see. It looks like the enemies are not stacking on top of each other, which is actually probably good. They won't occupy exactly the same space. If we want them to be even more spaced out, we would just increase the size of that collision shape. Here, if I pause the game real quick and we go to Editor visible collision shapes, and we can go back in here. Oh, we have to restart the game for that, I guess. Okay, we'll restart, and then we'll be able to see the collision shapes. So this one down here, that's the collision shape of the orc. The big one is the hit radius, so we can hit them up high, but they can only collide at the feet because that's how we defined the collision shapes before. But yeah, with this setup, they'll be a little bit more spaced out from each other. And I think I'm liking. 23. Spawning Enemies on a Time Curve: You can see our enemies work. They'll have a little bit of a collision with each other. Which, you know, after watching this is definitely the way to go, rather than the translate method. However, we still only have four orcs at the same time. We want to be able to spawn a whole bunch more dudes, and if we're going for that survivor like style, we want them to spawn off screen and then go towards the player. So almost in a circle, we need them to just appear off screen and then just swarm the player in the center of the screen. So we'll need a spawning system in order to do that. Let's go to the world, and we'll add in a new system node here. I'll right click Add a child node as node, and then I'll rename this to be spawning system or sponnorSystem, if you prefer. Right click on it, attach a script. So we'll call it sponsorsystem dot gD and I'm going to save it in the systems folder. And then for our spawner system, we're going to need to declare a whole bunch of different properties that are going to be export bars so you can customize them in the inspector. So first off, we need the spawn margin, which is a float, and I'll have it default to ten. Forgot the far there. Okay, so the spawn margin is the minimum distance off the screen that the characters can spawn. That is in pixels. I'll be named this to be characters actually and characters up there as well. We might have a world where we spawn NPCs, not necessarily just enemies. So it helps to be more generic when you're not sure about the final use for your script because you might actually want it to be more flexible than just doing enemies. Okay, next, we want the spawn Extra range. Okay, so at Export VR span extra range, I'll have this go to 100. Okay, so this is the bonus distance that an enemy might spawn away from the edge of the screen in addition to the span margin. So if your spawn margin is ten and your extra range is 100, then that means the pixel distance away from the edge of the camera is going to be ten to 110 pixels. Then we need the definitions for our enemies that we're going to spawn. So this should be basically a setup saying what can spawn under what conditions. So let's say at export var random definitions, which is array of spawning definition. Okay, now we haven't defined this class. It's going to be a resource type. It's a definition we set up in the editor that we use during gameplay. So a resource is the perfect type for that kind of data. Okay, so that's going to give us an error because we don't have the spawning definition class name defined. Let's do that. Right now. I'll go into the root of our project and the file system. Let's say, maybe this belongs in systems because it's part of the spawning system. I'll right click, create a folder, and we'll say spawning. And then inside of here, I'll right click, create a new script, and I'll say here spawning underscore definition, which is of type resource. Okay, create that and then open up that script. Okay, and we need to give a class name, spawning definition here at the top, and we'll worry about setting that up a little bit later. So to continue with the spawning system, we need a parent for where we actually spawn our objects onto. So at Export var spawn parent is a node two D reference, the parent for any object spawned by the system. And then we want the frequency. So I'm going to make that a curve so that we can control the timer. As time progresses by just using a curve and then sampling from the curve. So rather than setting like ten different time periods, after 30 seconds, spawn every 0.5 seconds. And then after 60 seconds, spa every 0.4 seconds. We can just have it as a curve. And then, no matter when it's spawning, it'll just get its next timer amount from that curve. So if you're going to have a lot of values, it's easier to define a curve with a slope rather than going one by one and being like if X then Y in like a more of a dictionary style. So I'm going to say export var span frequency, which is of type curve. Okay. We'll come back to that in the inspector. Okay, the next thing we're going to want is a runtime variable. I'm going to call it var random span stats. And this is going to be of type dictionary. I want to give the dictionary key type and a value type. And the value type is spawn stats. So this is going to correspond the type of spawning with the stats for the spawning in a one to one ratio at runtime. So this is runtime specific, and this is editor specific. And the spawn stats is going to keep track of when the last time we actually spawned an object is and how many times we have spawned using this system. So let's create our spawn stats object. I'll right click in spawning and create a new script. We'll call it spawn stats. Actually going to have this extend ref counted. Now we can jump into span Stats, and up here, I'll give it class name spatts. And the two things I want to track here are the spawn count, which is an integer starts at zero and the last spawn time, which is a float, and we'll start it at Enfinit. This will be the last time that the object was spawned out. Okay, so now in our spawning system, we have this relation. So when we start up the spawning system, we want to take the definitions and we want to create a new span stats for each of the definition. So this handles the runtime data. This handles the editor details for our one to one setup here. So if we want to get the span stats, we can just reference the span definition, and it will immediately spit out the span stats that corresponds with that. And that's in a nutshell how dictionaries work. You have a key, and it'll give you a value, and it's very useful for relating two different objects together. So let's set up our system ready function. So let's say function underscore ready is going to return void. And we want to say for definition in random definitions, we want to take the random spawn stats, and we want to assign the definition key to our value, which is going to be a new spawn stats. So spawn statts dot n Okay. And now we have one of these objects for every definition, and they're linked in a one to one of spawning definition as the key and span stats as the value. So now it becomes very easy to look up the spawn stats for the definition. Okay, and let's say that the next spawn time is going to be equal to the spawn frequency, which is the curve, and we sample on the curve the elapsed time since the game has started. So we need a few more variables up here, actually. We're going to need the var elapsed time, which is the time since the spawning system has gone into the game world. We can create a timer variable. So VarspawTr is a float of type zero, zero. Since this is going to be an internal timer, it's fine to just actually have a node manage its own time tracking. So we'll try doing that with a span system rather than creating actual timer object into the game world. So the spawning system is going to handle its own timing for this instance. And then the spa next spawn time is going to be basically how long we're waiting for this next spa. Let's set up the timer function here, function underscore process, and that's going to be Delta as the parameter, returns void. We're going to take the elapsed time and add the Delta, which is the time since the last process call. So elapsed time plus equals Delta. And then we want to take the spawn timer and plus equals the Delta as well, since we're keeping track of how long since the spawn occurred. And we'll say I spawn timer is greater or equal to next spawn time, then we're going to do a random spa. So random span here, and then after that, we'll take the spawn timer. And rather than set that directly to Zal, I'm going to minus equals the next spawn time because there might be a little bit of remainder time, especially if we get to really small span time intervals. So I think it's a little bit better to make it more accurate like this. And then we'll say that the next spawn time is going to be a sampling from the frequency curve. So equals spawn frequency dot sample, and we're sampling the elapsed time. So basically on the XY curve, it's a graph where X is how long has elapsed since the system entered the scene. And then Y is how long to wait for the next span. So whatever the elapse time is, there'll be a new value we can get from the curve in order to set our next spawn time at. Okay, a few more lines, and let's do our random spawn function, so far random span. Return type of void. So what we need to do is get our spawning definition that we want to spawn. So that's going to be a random selection based on a weighting of each of the definitions to determine which object is going to be span or not spawn. Okay, I'm going to pass here for right now because the logic for the spawning is going to get kind of a bit more complicated. I just want to make it so that we can at least see it working in the game world for right now. So back out in two D view, let's take the spawner system and we'll assign this bon parent to the enemies node. Okay? So like that, it should be enemies. This bond frequency is going to be a new curve. So let's go on this curve, and we can define values. So if you can zoom in here, that'll help. The Min domain in the Max domain is going to be basically how many seconds into the game we have it. So you could say something like 300 seconds or 120 seconds. However long you think that the level should be, that would be what you'd want to make the max domain. So if you have a final time for your level and then after that, the level ends, then that would be perfect for the Max domain here. The X Y value, what we want to get whenever we sample the X value. So how long should it be between spawns? Maybe two as the max value would be then we go up here to the curve and we left click to set a point, and we just kind of control that. So in general, at the start of a level, you would want less spawns than at the end. So if we kind of pull this up here and say, maybe only every 1.5 seconds, a character spawns, and then we go over to the right and we left click and we pull down this keyframe point to something like this where we can say every maybe not 0.2 seconds. Oh, maybe ever 0.2 seconds. So every 0.2 seconds, an enemy can spawn. And we kind of have this curve from here to here. You can also click on the points, and there's a curve adjuster if you need to get a little bit fancy with this. So right now, if we sampled it, let's say at 75 seconds into the game level, that would mean that the spawn time would be about 1.2 here. We can see that from where the XY intersects on the line. So likewise, if we went all the way to 225, then it would be about half a second between enemy spawn frequency. So the difficulty gets harder as the level goes on. And we can just adjust this. You can even set a third point in the middle if you need like this. Maybe you need it to flatten out for a while and then get really hard. Control E to undo. And I think you can right click on a node to remove it from the graph. Most likely two points is going to be good enough because you can kind of control that with the handles if you need to change this rate at which it gets harder. But we could kind of do that. Now, I'm going to personally take this way over here to the left because in our testing, I don't really want to test past 2 minutes. I just want to make it fast paced so that I can kind of show how it's going to work later on. But in your actual game, feel free to have a much longer more drawn out spawn curve. So right now, after 120 seconds here, it's just going to be flat for the rest. And that's fine for me right now. Okay, now let's create a spawning definition. Up here. We'll add one. We need to create a new resource for the spawning definition. So new spawning definition, we'll right click and save that. Let's go into characters, enemies. And I can put this in the same folder where the orc is at because we're going to call it orc spawning. Definition or just orc spawning. I think orc spawning is fine. Dot TR, yes, we save it. But of course, this doesn't have anything associated with it yet. So we need to add in what packed scene we're actually going to instantiate whenever we spawn the orc. Okay, so in spawning definition, script. Let's add export the vR the scene, which is a type of packed scene. And we can also add an export var weight, which will be a float of 1.0. So we have the scene to spawn and the amount of preference to selecting this scene as the random spawn choice. So when we randomly select, we might have like three types, and they each have a different weight. So if one has a weight of three and the other one has a weight of one, then the one that has a weight of three has three times the odds of being selected as the one that has a weight of one. And that's just one way we can make it a little bit more skewed in the randomness. So it's not perfectly random. It's that each choice has a weight to it, and the weight determines the odds. In the scheme of the random selection. So we click on spawning system now. Ok spawning is now going to have a scene reference, so we quick load our Ok scene. Okay, now that gives us what we need. So for random spawn, I'm going to make it as simple as we can right now by getting the first definition in the list and then instancing that. So far instance, which will be a no two D is equal to the random definitions at position zero. And we're going to get the scene, and we're going to instantiate that. Okay, so we now have our Oc. That's the zero position and the orc spawning. So we'll take that and we'll add that to the random parent or the spawn parent. So spawn parent dot Ad child of the instance, we'll just have the instance go to position 00 for right now. We'll worry about its random positioning later on. And then we can see that the span management is already managed out here outside of the random spawn function. So honestly, that might actually be about all we need. For testing purposes, I'm going to take this and make it like 10 seconds over here, just so we can really see the spawning ramp up if it works, and we'll hit play. So let's see. Do we have any characters coming at 00? Oh, yeah, yeah, I can see them right there. Okay, so here's our orcs. You can see it's spawning faster and faster as time goes on. So we already basically hit the peak orc spawning, and our spears can just cut right through them because our spear has no max hits count, so it's very easy to win this game currently. But you can see how the curve makes it so we can easily ramp up the spawning as time goes on, and that's just a cool feature I really like a lot. It's much better to work with than defining in like at 10 seconds, now the spawn rate is 1.0, and at 20 seconds, now it's 0.9. So now you can just all values for all the time, basically be managed by this curve, and it's just much easier to kind of edit them. You lose precision, but it becomes a lot simpler to kind of give you a general sense of what you need for these values. And you can still, you know, click and set absolute values. So in points here, you can see that you can set the exact XY positions for those points if you need to get really technical. But in most cases, like I said, just two points on a curve is going to be good enough. So there's a lot to add into the spawning system still, but this is a pretty good start for what we need. 24. Spawn Off Screen with Calculated Offset: Okay, so we already have our spawning working. But the problem is that the characters are just spawning at the 00 point where we actually want them to spawn off the camera wherever our camera is looking at. So it always looks like they're coming from somewhere really far away, but really, they're just coming from a little bit away from what we can see. And then they're gonna swam the player rather than spawning in the 00 point. So let's work on that. Okay, so in our spanner system script, we're going to go back into it down at random span. So right now random spawn is not very random. Let's work on spawning the enemy off the screen. So we need to get a new location for where we're spawning the instance. So down here, let's add in a var spawn position. So that's going to be a vector two. And we're going to get that from Get spawn position off camera. So we want to generate a span position that's off the camera. So that's going to involve some math and the current viewport of the camera itself. So let's create our function, Get span position, off camera. So the first thing we need to do is to get the camera TD that's currently active, what our actual human player is looking at on screen so that we can stay outside of that when we spawn. So our camera is going to be equal to Get viewport. So basically, we're looking at what the player can currently see, and we're going to get the camera two D that the player is using to view the viewport. So we have access to that camera maybe it even makes sense to mark this as a camera two D. So we could have a guard check if we like here, saying that if the camera is null, which probably should never happen, but in theory, if it could, then we want to push an error that there's no camera, no camera two D found in viewport. Maybe that could occur if for some reason you're using a three D camera and there is just no two D camera. I guess that would make it trigger an error. So it's possible. Okay, so if that occurs, then there's a major issue in the game. We'll just return vector dot zero. That happens and we'll push the error and let the developer figure out, Oh, why is there no camera in the game. Okay, but assuming we have a camera, we can actually do the math. So first, we want to get the center position of the camera. Let's do VR camera center is a vector two, and we want the camera doc global position for that. Yeah, just like that. And then we want the size of the camera. So how big is the camera on the X dimension and the Y dimension. So that should be another vector two. So we'll say VR camera size of vector two is going to be equal to vector two, and we get the viewport size. So get viewport dot size, and then we want to divide that by the camera Zoom. Okay, so the reason for that is that our camera, especially in a two D Pixar game, if I click on camera two D up here, and then we look over here at the Zoom, then you can see that our Zoom may actually take the viewport and scale it in four or five times so that we can view our Pixar well. That's why we need to make sure that even if we're looking at, like, let's say, 12 80 by 720 pixels, if we zoom in by a factor of five, then our camera size is actually scaled down to those new values based on the Zoom. So that's why we need to make that adjustment. So next up, we have the VR camera rect, which is a rect two D type or rect two, rather. And this is a rectangular shape that represents the bounds of our camera. It's based on the size. So for the math here, we're constructing a new rectangle by doing Rectangle two and then parentheses. And our first parameter is camera center minus camera size. And then we multiply that by 0.5. And then for the second dimension, we just have camera size. So that gives us the appropriate rectangle box that represents the camera on screen. Next, we want to randomly select an edge that we're going to be using to spawn the enemies from. So our camera is a box, so it has four edges, and we can basically spawn our enemies up above, down below, to the left or to the right. So we just want to pick one of those edges, and because we want to even distribution, we'll just do it randomly. And then there's no waiting. So, in essence, it should just mean that they're coming from all directions pretty consistently. So if we say var edge, and we're going to have that be an integer value, and it's going to be equal to Rand I for integer. We're going to give it percent four? The percent sign here means remainder, so it's the remainder of dividing it by four, which means it'll either be zero, one, two, or three, and those represent our four edges. Okay, then we need an offset. How far away from the edge do we want to actually spawn our enemy? We don't want them to spawn directly on the edge because then it would be on screen visible when it's instancing into the game world, and that would look kind of funky. So let's say var offset, and that's going to be a float I believe here, equal to span underscore margin plus random float. Okay, so we're basically generating a random float here and we're going to multiply that by the span extra range. So random F can be 0-1 0.0. So a value of zero times our span extra range is going to give us an extra spawn distance of zero. And if it's 1.0, it'll give 100 because that's what we have it set to the span extra range property. And if we have something in between, then we're going to get some value of pixels 0-100. In addition to our span margin, giving us the total offset. Okay, so we'll say var spa position, and that's going to be assigned to a vector 20 here. Zero all caps the constant, rather. Okay. Add a few more lines. And we'll say if the edge is less than or equal to one, then we are treating that as the top or bottom. Then let's take our edge and we'll do a match statement with it so we can calculate the son position based on which edge we're actually using. So let's say match edge, and default, we'll push an error handled. Edge number, percent s percent edge. So having that is just kind of helpful if we forget to, for some reason, put in one of the cases. Now we can actually match the edge integer to a Enum value if we want to be a little bit more verbally descriptive here. So if we go up here to the top, I'm going to declare an enum, say enum edge, and we're going to have top as zero, bottom as one, left as two, and right as three. Okay, so now, if we go down here and we match the edge to those values, we could say edge dot top, if I'm not wrong. Hold on a minute. Seems like there's a bit of an issue here. Still, I need to put equal sign here for the camera rect. Okay, so that's going to work. Now we can come down here to where we were doing the Enum matching, and we want to actually put in the code here. So the spawn position X for top is going to be equal to a random float from a range. So we say Rnf underscore range, and we're going from camera rectangle dot position dot X to camera dot position dot X plus camera dot size dot X. So for a rectangle tutti, the position X is always going to be up here. So if you add the size X to that, then you would get over here on the right hand side. So if we're going to be going for the top, we want to position somewhere between here and here. So that's why we take the random value of the position and then the position plus the size. So that allows us to get somewhere between this line right here. And then we need to do the Y position, of course. So spawn position Y is going to be equal to camera Rect. Dot position, dot, Y. Minus the offset. So we're subtracting the offset because when you subtract a Y value, you're actually going up on the screen. So we have the Y position, which is the top of our rectangle. So, let me pull up paint right here. The Y position is up here, right? More specifically, it would be the Y value that gets us to the 00 point on the rectangle. Then you take the offset, which is going to be some random value up here. So our enemy is going to be kind of spawning in this area, roughly speaking, and this is based on whatever the Y offset was randomly generated to be. So we have our ten pixel margin, and then we have 110 pixels up here as our extra plus margin area, and then our enemies can spawn somewhere inside of here for the top value. Okay, hopefully, that makes sense. And then we just kind of do the same thing for all the other edges. So edge dot bottom. Is going to be precisely this line up here. And then the span position Y is going to be a little different. So we want spawn position dot Y equals camera rect dot position dot Y plus the camera rect dot size dot Y. So that gets us the bottom of the rectangle. Because now we're adding the Y size, which pulls us down to this bottom area. And then we just add the offset to that. So plus offset and note that we're adding the offset because we want to go further down underneath the camera box, but on the top, we subtract the offset. That's very important. Now we just do the same thing with the left and the right. So edge dot left, which is our two enum. And that's going to be spawn position dot X equals, camera rect dot position dot y minus the offset because we're going to the left on our box, so we want to offset to the left. That's it for the spa position X, and it's spa POS, not spa position. I abbreviated it. So spa post Y is now going to be equal to, and we take a random F range between the or camera rectangle, dot position dot Y. So that's going to be the top of our box. And then the other side of the random is going to be the bottom of our box. So that's going to be camera wrecked dot position dot Y plus camera wreck dot size dot Y. So when we add the size, once again, we're going down to the bottom. So our span position vertically for left and right is somewhere on this line right here. And then finally, edge dot right all caps. It's going to have the span position X of camera Rect dot position dot X plus camera we size dot X plus the offset. So same thing. We have a box over here and a box over here, and we want to offset to the right or offset to the left if we're spawning left or right. Let me double check that. Okay, position X plus size dot X plus offset. Okay, that looks correct. Of course, we'll test it in game. And then the span position Y should be the same because we're still dealing with that vertical picking a random spot between here and here and here and here for a for the left and right generation. Okay, so that should basically be our span position code. And then we want to return the span position. So this function is fine as it is. It's not too bad. If it got a little bit bigger, I probably would just take this out to another function. Honestly, we could because I felt the need to put this comment in the code. So if I wanted to, we could say function, underscore, calculate final spawn, which will return in a vector two. And then we put that here instead of defaulting it to vector two and putting all this down here. And we cut this to our new functions, so we're refractoring it. And then we're going to obviously require some parameters to go into this function for it to work. So first off, we need, let's say, camera rect, which is a rectangle two, and then we need camera size, which is a vector two Control S. And then we want to return the son position inside of here. So the var spawn position is a type of vector two. We go down to the bottom and we return the spa position out of here. Okay? Up here, we want to pass in those parameters, so camera rect and camera size. And then we need this over here. Finally, we also need the offset. So pass in the offset up here. Okay. And then we need offset as a parameter. So offset, and I think that was a vector two as well. Okay? So Control S actually was it just a flow? Oh, it was a float. Okay. So a one directional float. And once you save that one, most of these errors should go away. So we need to match on the edge, and then we need to return. We could also put in that edge parameter. So edge, which is going to be of type. We could just actually use the enum here. It should work fine because integer can just auto assigned to an enum as far as I know. So if we go up here and then we pass in the edge, Okay, that's going to come in here and it's going to work as a enum, which is kind of what we expect to match it with down here. So to go through enums a little bit, an Enum is basically a list of named values that correspond with integer position inside of this Enum. So the zero because this is the first is the zero position. So it's like the first item in an array. You could almost think of it like that. And then bottom is one, left is two and right is three. I think some languages allow you to assign custom integer values to a Eenum. I don't know if you can do that in GDcrip but that's basically the idea. If you don't mess with it, then zero is just top. One is just bottom, two is just left, and three is just right. But what this gives you the advantage of doing is that when you create a function like this, instead of saying zero, one, two, three, which is very obscure for what that actually represents, what is a zero? You can just replace that with edge dot top, which is the value of zero, but it's much clearer to someone reading the code what that is actually supposed to represent. Okay, let's see. So we have our Get spawn position off camera, and we had that here in random span. So now we actually need to set the position for the instance. So instance global position is going to be equal to the spawn position. That part's easy. Let's hit Play and see if it actually works. Cause that was quite a bit of code. Okay, so our characters aren't spawning on the screen. I see them coming from off the screen. Let's see, or any coming from the bottom and the right. I don't see any coming from the top or the left, and that kind of concerns me a little bit. So I'll see if I can find them. Okay. And I think what we should do for testing that. I'm going to close the game right now is go down here and we were missing them on the top and the left. So let's add a breakpoint here and a breakpoint here. So if, in fact, characters are trying to spawn from the top and the left, then those should hit. And then we can check the values that they're actually hitting. So I forgot to stop in the game, so it is going to just automatically hit this breakpoint. Hit Play and see if we can get one from the left. Okay, there I did see one coming from the top, actually. Okay, and one is coming from the left, supposedly. So it seems to be kind of going, right. Maybe if I bump up the spawn rate a bunch, it'll be easier to see for sure. So let's go to the world. Do we have the spanner system? I'm just going to take this right here. I'll remember the value. So it was 0.66 and 1.5, and I'm just going to drop this way down so we can get some very fast spawning for right now. Let's hit Play, and I'm going to test. So we'll just see a bunch of guys spawning very fast or we should. And yeah, okay, that looks mostly correct. Can we get a guy from the bottom left over here? 'Cause I want to see that occur, too. Okay, yeah, yeah, finally, finally. We got a couple guys over there. Okay, so I think it is working proper. If there's an issue, it's possible that the math was not double check to be correct, but I think it's going the way it's supposed to. If not, it's a little hard to tell. But yeah, I do see them coming from all directions. So I think we're good here. This math would be something very easy to get wrong, though, so I'll put it on screen once again if you need to double check. So we have our spawn position. We're getting that from G spawn position off camera. That's our function right here. So we get the camera center from the camera global position, the camera size, is from the viewport size, and then you divide that by the camera zoom. The camera rectangle is constructed from the cameras center minus the camera size times 0.5, and then the Y value for that is just camera size. The edge is random integer, and you get the remainder of four, which will give you zero through three. The offset is span margin plus random F times the span extra range. So this is somewhere 0-1 point with decimal points, and then we add the span extra range as some percentage of that. Calculate the final span using all those properties we just set up the camera rect, the camera size, the offset, and the edge. So we take that into here. We match on the edge. So zero, one, two, and three. And then for the X value on top and bottom, we do a random range between the left side and the right side. Using that math. For the Y position, we take the Y position at the top and then subtract the offset or for the bottom, we add the size to get to the bottom, and then we add the offset. And then for left and right, we do the same thing in reverse. So for X, we do the left side minus the offset, giving us further to the left. And then for the right side, we add the size to the position X to get the right side, and then we add the offset to go further to the right from that. Then for the span position Y, we do a random range between the position, which is the zero Y position, and then the position plus size, which gives us the bottom corner position in the Y axis. And so that'll make it so that the left and right spa is somewhere between the top of our screen vertically and the bottom of our screen vertically. But the position is offset off the screen, so we won't actually see them spawning. And we returned the span position so we can use that in this other function, and that's pretty much going to wrap that up. 25. Random Enemy Spawning with Weight & Time Conditions: Our offscreen spawning is working, essentially. There's a couple of things I want to clean up, and then we'll proceed with adding an skeletal enemy so that we can randomly select which enemy we're going to spawn based on a waiting system. So let's close the game out. And first off, we can see this label here, the floating combat text instance. We don't need that anymore because we're done editing the animation. So I'm going to delete that node. And then on the spawning system, for the demonstration, I took this way lower than it was before. So over here on the first point, I wanted to bring that back to the original, which was like, I think it was 0.66 seconds originally, and then 1.5. I'm going to make that 0 seconds and then 1.5 seconds as the spawning period. So we can actually just go down here and type in 1.50. Hit Enter, if you want to be precise with the setup, and you can see that the point while selected shows 0 seconds, and then 1.5 seconds as the spawn time. 0 seconds is the elapsed time here, if you remember. I think these four work enemies, we could just remove from the scene. So I'm going to delete them. So I'm going to select them, hit hit Okay. You can hit play just to test that everything still works, and we should get the enemies spawning off screen coming towards us. We don't have you can hit play if you want to double check and just make sure that the enemies are still spawning. We just don't need those initial enemies because the enemies are going to come after the game starts. We do want to give the player a couple seconds to, you know, acquaint themselves with the controls and so forth. Okay, so for creating the skeleton enemy, let's first clone the orc in me. So I'm going to search for orc and the project. We go down to orc dot TCN. I'm going to right click and clone it, so duplicate. I'll call it skeleton dot TCN. And then let's search the project for that skeleton scene we just created. Double click it. And then we'll zoom in here. So I'm going to take the sprite here and change it to the skeleton sprite. So let's quick load on the right, and we're going to look for idol, and there should be one for the skeleton. We may need to zoom in a bit. There should be one here for a skeleton enemy. And then on the top right, we can quick load, and you may have to zoom in a little bit to find it, but type idle and we'll look for the skeleton sprite sheet. It may be a little hard to see, but I'm going to select this one right here. If you can't find it, feel free to just navigate the file system and find it. It's probably in the skeleton base folder. Okay, now let's rename the root node to skeleton. And we're going to need to set up the animation player to make sure that it is playing the animations of idle skeleton and then Ron skeleton and skeleton death as well. So let's go to the animation player. And then in the bottom right, we basically need to change the texture for each of these. So the reset texture will go to the top right and then quick load the Idle sprite sheet we just selected. Right now it's already pre selected, so we can select that. And you might want to play and make sure it switches on reset to the skeleton and then change the texture for the other animations as well. So Idle we're going to click on texture, go to the top right. Quick load, choose the sprite sheet for the third time. Okay, and hit Play. And then make sure that's playing correctly. Looks good. Let's go to the death animation. Select the texture here. And because there's a lot of frames in the top right, it's a little hard to actually see what's going on, even harder than the idle animation. So I think it would be easier to just navigate to the art and then find pixel crawler free pack, entity, mobs, skeleton crew, base death, and then just drag the death sheet into the top right like that. Okay, hit play and make sure that it is playing correctly. So it seems like there's eight frames of animation here. So we do need to zoom in a bit like this and make sure that H frames will set in the top right to eight. Okay, then hit Play. Yep. Okay, that looks correct. And now we need to add in the seventh and eighth keyframes. So right click on frame at 0.6 seconds and do insert key, click on it, go to the top right, change it to six, do the same thing at 0.7. You can't snap there because we need to extend the animation. So make the animation 0.8 seconds long. Click on 0.7, S that keyframe and change it to seven for the eighth position is what we want. Okay, let's hit Play. Play one more time. There's our animation. Now move the death state finished to the end at 0.8 seconds like that, and there's our death animation. Okay, so we want to run. Should be quicker. Click on texture. And then in the bottom left, where it says skeleton base, choose the run folder, the run sheet over to the top right. Drop that in. Hit play, and we can see it's playing our run animation. It has the same age frames, so that's all we need to change. Okay, that's the basics of our skeleton. We can go to the stat controller. Aside from assigning stats, it's like run speed and HP and so on for our skeleton and a custom values for our orcs and CD that is the basic setup there for our two characters. So let's go back to the world, and then we're going to have the spawner system, right? So we have our random definitions over here on the right. We want to add another random definition, and then we're going to create a new one. So new spawning definition. Let's say that the weight for the skeleton is 0.5 so we'll get half as many skeletons as we do orcs. And let's quick load the skeleton scene, okay? And then we'll just check the sponsor system script. So open that up, and we'll see the random selection here. So right now it is not generating the random selection scene. It's just selecting the first one in the array. So we either need a bit of code or a function in order to pick which scene we're actually going to instance. So let's zoom in, and I'm going to say var scene is a packed scene, and we're going to want to select the random spawn from a list of randomly spanable definitions. So we'll say random definitions, save that. And then we need to create this select random spawn function. So I will do function, Control V to paste it in. And then we're going to say P definitions is an array of let's see what do they call it spawning definition. And we're going to return a single packed scene. So if P definitions is empty, then we can return null because there's nothing to select from. Otherwise, let's calculate from all of the total weights and then randomly generate a value between zero and the total weight to determine which scene we're actually selecting. So that's random selection, but it's weighted random selection. So let's say if our total weight is a float, starts at 0.0. And then for definition or D and P definitions, we're going to take the total weight, and we're going to increase it by the definition weight. Okay? So we do that for every definition. Now we have the total weight, and we need to generate a value between zero and the total weight. So far, let's say random value is a float. And we're going to take random F. So this is zero to one point oh, all inclusive, including decimal, and it's going to be times the total weight. So that'll give us a random value between zero and the total weight. And then let's say V checked weight flow is 0.0. So this is how much weight value we've tried to pass before hitting the random value. Once we hit the random value, we'll return whichever scene or whichever definition sne we're currently on. So for definition in P definitions, we're going to say that the checked weight plus equals definition dot weight. And if the checked weight is above the random value, then we'll return the scene. So return definition dot CM. Okay. And then if we get to the end and we still haven't hit anything, that's a issue, so we can push a error. Okay, so here I'm pushing an error. Was not able to find a definition at weight value percents, where checked weight total was percentS. And then I'm replacing those percents with the random value and the checked weight. So this kind of shows what we were checking, where we reached, and it's indicating that we didn't actually find a checked that was above the random value, which should always happen because the random value can only go up to the total weight. So this should not occur, but if it does, we have an error message. And then at the end here, we can just say return null because it wasn't able to find a proper definition. So this makes the most sense. It'll probably immediately break something indicating that something needs to be fixed in the math at this function. Once again, I want to emphasize this should never happen. If our math is correct. Okay, so we select the random spawn from our list of definitions, and that handles the weight and the random calculations. So if I run the game now as things are, then we should be getting half as many skeletons as we get orcs. But let's see. Do we get both of them? Because we should get both of them. So we have a few orcs spawning. Okay, so it actually turns out the code was working perfectly fine for select random spawn. I just forgot to use the scene. So we want to take the scene here. And instead of random definition zero instantiate, we just do sent instantiate. Okay, now we can go back into the random spawn and remove that Prince debug message. Okay? And if we play, it should work fine. It should actually use the one we randomly selected rather than always the first one in the array. So we should get half as many skeletons as we get orcs, but there's our skeleton. And as time goes on, you'll see a bunch more orcs then you'll see skeletons because of the waiting. Okay, so that's good. And that's basically the idea there. Okay, so last thing for this part, you may want to delay how long until a enemy definition can actually spawn into the game. For instance, at the start, maybe you only have orcs, but then after a while, you can add in skeletons. So we already have the elapsed time. So if we say a starting and ending time in terms of elapsed time when a definition can be spawned, then we can control when the orcs can spawn and when the skeletons can spawn. So if I go into spawning definition, we can add an export var here or starting time a float, and this can be 0.0 by default. And then the ending time it'll also default that to 0.0. So what we want to do with these two is start creating like a can be spawned method. So we could say function can be spawn, which of course is going to return a true false Boolean. And for this, we actually want to pass in the elapsed time because that's part of the condition. We have to check what time it is in the game or time since the spawner system has actually been created, rather. And then we'll use that to check if the elapsed time is within the starting time or the ending time. So if P elapsed time is before the staring time, then we'll return false. If ending time does not equal 0.0, or rather, if ending time is greater than 0.0, I think that's a little bit easier to read. Then we want to check if the ending time has actually been passed. So if the ending time is zero, we're just ignoring it. So and P elapse time is above the ending time, then we'll return false. Otherwise, we've passed all the conditions so we can return true. So that's basically all we need to do for a time based spawner, and we just check if it can be spawned. And if it's not able to be spawned, we remove it from the list that we consider for random selection. You would do something very similar. If you also want to, let's say, have a maximum number that they can be spawned, you would just pass in the number that has been spawned and check if that is above or equal to the maximum number that the spawning definition would allow. That would be helpful for something like a boss that can only spawn once. So you can just check, Oh, it can spawn one time. How many times does it spawn? Is it one or greater than don't spawn it. We'll see if we come back to that, but this should be all we need for writing now. So now, back in the spawner system, we want to basically filter spawnb from the random definitions. So I'm going to create a function filter spawnb which is going to take a P definitions array of definitions, and that's weapon. That's spawning definition. And then we will return our filtered definitions. So return an array of spawning definition. So we create our filtered list here. Var filtered array of spawning definitions, and that's going to be equal to a empty array. And then for DF and P definitions, we'll check if DEF can be spawned. I guess we need the elapsed time in here, but that's a local variable. So we can just pass it in directly to this. So elapsed time, as in we don't need to put it as a parameter to this function because it's part of the state of the object, which is the spawning system. So if it can be spawned, then we'll add it to the filtered list. So filtered dot pen definition. And then at the end, we just return filtered so here in random span, we do var spanable and that's going to be equal to filter spanable, which is going to take our random definitions as the parameter. And then instead of select random spawn from random definitions, we select random spawn from our filtered spanable variable. Okay, now that gives us the ability in the spawner system, if we go over to the spawning definitions, we can say starting time, ending time. Let's just say orcs are always active. But then for skeleton spawning, we can't spawn skeletons until 15 seconds, but then once we can spawn skeletons, we have a weight of two, so we end up seeing a lot of skeletons as soon as they're able to spawn. Then we can play. And if all the math checks out, we should see nothing but orcs initially. But then after 15 seconds, might be helpful to have, end game time or something to see we'll see if we need that. Then after 15 seconds, we can see the skeletons are going to start spawning because they are now allowed to be spanable because the starting time of 15 seconds has elapsed. Okay, so now you can see the skeletons start to come in. So we have our second wave of enemies, and we could just make the skeletons tougher. So the difficulty progresses as game goes on because we add in tougher enemies. And, of course, in the spawning system, we increase the spawn rate as well, like that. So you can kind of see how that would work. That's the nature of basically controlling the random spawning of our spawning system. That's most of what we actually need here. Good job keeping up with everything so far. Okay, so we might do a couple tweaks here and there later on if we need, but for the most part, that's our spawner system. So now we should start working on fleshing out the combat a little bit more and make our enemies actually able to damage our players would be a good next video. So let's go from there. 26. Player Hurtbox and Enemy Hitbox Setup: Part of our basic combat mechanics is going to be setting up the ability for the enemies to hit our player, which is going to involve setting up a hit box for the enemies when they run into the player, and we need to ensure that the player has a Hurt box, as well. And we'll probably add an invincibility time just to make sure we don't get too many hits when we get mobbed by an enemy kind of like that. Okay, so on our player, we'll create our Hurt Box nodes. So I'll right click on the player route, add a child node, and let's look for HRT Box two D. Enter, add it in. We'll assign the stat controller, which we haven't added to our player yet, so we can right click on player, add a child node, stat controller. Okay. And then we want to do some assignments. So object for the SAT controller is going to be the player root. And then in HRT box, we want to assign the stat controller. So our HRT box, you can see by the warning sign, we also need a collision shape. So let's right click on it and give it a child node. Collision shape two D. And then in the top right, we'll make this probably like a circle shape. Okay, so we get this circle shape appearing. We want to shrink this down to the size we want to be able to take damage for our players. So depending on how we want to do it, we might make it slightly smaller or slightly bigger than our collision shape. I'll go big for now, and then we can shrink it if we need to for game balance or the feel of things. So that already gives us the ability to take damage on the player. Now we need to give the enemies the ability to deal damage to the player. So on our orc scene, let's right click on Orc, add a child node, hit box two D. Okay. And then this is going to need a collision shape. So right click, add a child node, collision shape two D, and then in the top right, we'll make it a circle shape, and then we shrink it to where we want the orc to be able to hit targets. So I'll make it slightly bigger than the main collision shape for the actual collision. Then in our RT Box two D, if we check the script, we can see that we get the hit signal from the hit box hitting a RT box. So to deal damage, we actually need another script to deal damage on the signal hit and being emitted. Before that, though, let's add the collision layers for our hit box and our hurt boxes. So the hit box two D on the collision layer is going to be masking for the player layer. So mask of layer two because that's the player layer. For the layers which this hip box exists on, we can just turn off layer one, or we could set layer three for enemy if we want. But I'm probably going to turn off monitorable because this is a one way hip box. It only needs to detect. If it finds a hip box, it doesn't need to be monitorable by other nodes inside of the game, at least currently. So let's go to play and I'm going to do the reverse for her Box two D. Over here, we're going to turn off monitoring, and we're going to take the collision. We're going to make the layer as player. That's very important layer two here, we can turn off the mask. Then inside of here, I'm going to right click under Hit Box two D, and I'm going to add in a new node, add child node. This will be a node two D. And let's rename this to be something like collision attack. So when the orc collides with another enemy using this hit Box two D, it's going to do all damage as a collision damage. A lot of classic games have this sort of thing where an enemy just runs into you and you take damage. So we'll have to right click here and create a new script. So let's put collision attack in enemies. I think that's fitting. Create. I'll rename it Class Name collision attack up here at the top, Let's export a reference to the Hit box. So Hit Box is of type Hit Box two D. And then on ready, function under score ready, returns void. We're going to take Hit box, and we're going to connect to the hit signal. So it dot hit, dot connect, and we're going to connect on hit. So now we need, let's say, the at export var damage here, which is going to be an integer. We can default it to ten. So let's get the callback for the hit with on hit function underscore on hit. Now you need to right click on Hit and check the signal parameters. So we have the Hurt box two D here. Okay, so that'll get us access to the stats as well. So inside of collision attack, let's jump back. So we have the P Hurt box, which is a type Hurt Box two D. This will return void, and then we're going to hit the player. Okay, so on hit P Hurt Box two D, we want to take the P HRT box, and we want to tridal damage to it, and we're going to pass on the damage. And then that's pretty much it there because we already wrote that into the hurt box. So the Hurt box already handles reporting. I already handles setting the HP, which this will emit the signal for the HP changing if we need that. And it does the calculation. It also does the guarding to make sure that we can hit the target. So it just makes sense that we use this logic where. As long as we have collision attack, we assign the hit box up here and we set our damage. We can see if that will work. So I'll just try running into an enemy and see if our player gets the damage above it. Okay. I just need to be sure Oh, okay, yeah. There we go. Our player got hit for ten. We know it's the player and not the enemy because the spear currently does 100. Now, currently, there seems to be one issue, which is that enemy that is already dead seems to still be able to hit the player. It already took 100. I went to Death State, and the collision attack was still enabled. So we could handle that a few ways. One would be that we just check the stat controller and make sure that the stack controller on the orc the HP is above zero or the orc is still alive. Otherwise, we just completely ignore this attack. That would be one decent way. Another way would be we put it into the death state, and then we disable all the hit boxes as soon as our orc is considered dead. I think that's maybe too much responsibility for the death state. So I think it belongs a little better here in the collision attack. So what we'll do is we'll export a reference to our stat controller, export V stats stat controller. So what we'll say on it is, if not stats dot Live, then we're going to return. Otherwise, we'll deal damage. So basically, while the character is not alive, it cannot attack, but if it is alive, then we'll consider the collision damage valid if we do get that connection. So now if we go back into play here, we'll be able to walk at an enemy that had just been defeated and not take damage from it. Okay, so I need to time this right. Okay, I run into it and we see that we hit this, but I forgot to set the stats. So it might be a good time for an assert. So assert stats. So the stat needs to be set so that we check if the character is alive. And then this will basically already require us to have the hit box soda error, if we forget to set that in the inspector. Okay, so close there. Click on Collision attack. Let's assign the stats. Let's go into play. We'll do this one more time. Find an orc. And you can see that we don't take damage if the orc had already been defeated, one more time. Yep, seems to be working just fine. 27. Player Invincibility Timer and Custom Stat Definitions: Okay, so last thing for this video, we want to set up an vulnerability period, specifically for our player. So in Stat Controller, we can go in here. So in SC controller, we can create an invulnerability timer. So let's say down here V Invincible time and we can default this to 0.0. It'll be type float. So it's the time after getting hit that the object cannot be damaged. Okay, so with our invincible time defaulted to zero, that's going to be fine for our enemies. However, for a four hour player, we do want to have our custom value for the Invincible time. And we probably also want to be able to customize things like the HP, speed, Max HP, et cetera. So we're really getting to the point where we need to set up some kind of stats definition, where we can define what the starting stats for each character is going to be. So if we could create a new script for that, which is going to look a lot like our weapons definition, where we're just defining properties about the character and its stat block. So in the character's photo, alright, click Create a new script, and we'll call it Stat underscore definition. This will extend from resource and click Create, okay? And then search for that script we just created Stat definition, double click it. And we'll come up here to the top. So class name stat definition. So this will define default stats for a object type. So the stats we want is going to be basically mirroring what we have on the stats controller. So let's go into stat controller, and I'll just actually copy these over for right now into our stat definition. I'm going to remove all this setter code. Because we just want to purely define export editable properties that basically don't run any extra code. It's just pure data that is saved within the GIDoEditor essentially. So we don't need alive, and we will want a default for the HP and fencibT HP and MAX HP. So let's export all of these at export for MaxHP at export for HP. Over here, we'll do at export for speed and at export for invincibility time. You might also want to give them some comments. So MaxHP maximum health for the object, HP, starting health for the current object. Okay, and that will be that. So now we need a way for our stat definition to be loaded onto the stat controller. You could either put a load function in the stat definition, which kind of turns the resource into being a little bit more than just peer data. Well, we could put it on the stat controller and some kind of initialized stats method where it knows about the stat definition type, and then it can load all the values from an object of that type. So I'm going to do it that way. And that's going to mean we're going to need to create a new function down here at the bottom. Since the stat controller is going to need to call initialize on the stat definition anyway, because this is the node the node will handle when it is ready to set itself up. So either way, there would be some minor dependence on the stat definition. So I'll create the function, set up stats, and this will take a P definition, which is a stat definition. Well, return type void and let me add some lines here. So in setup stats, we'll just mirror the definition onto this node. Max HP is going to be equal to p definition MxHP. HP is going to be equal to p definition dot HP. Let's see what else speed is going to be equal to p definition dot speed, and invincible time is going to be equal to P definition dot Invincible T. So now we need to just do that on ready. So under invincible time, we'll add function underscore ready, and we'll say set up stats with our definition. So we need to export a definition on the node so that we can actually do that. So up at the top, we'll do Export VR Stat definition is a type stat definition. Okay. And then we just use this as soon as we ready. So set up stats with the stat definition. That's going to load all the stats into our runtime value. And this lets us customize the values for the base stats of our characters using something we can set up in the editor. So if we click on Stat controller, we go to the top right. I can create a new stat definition here for our Oc. And let's say that the Mx HP should be 40 and 40, and the speed is 25. So we save this now to the folder where we have the orcs. Click on Stat definition, save as, and we can put that in the enemy's folder. So Ok Stats. Sounds like a perfect name there. Save it. Okay, and that's how Ok Stat definition. So when it loads, it's going to load up with these values as soon as the character is ready. Okay, so that's basically that for the orc. Now, I also want to copy this hit box setup over to the skeleton, as well. So I'm going to Control C, go over to skeleton, Control V, paste it in. I might take the collision attack, and I'm just going to boost the damage to, let's say 25. Let's assign the stats to the stat controller, and the collision shape being the same works fine. Uses the same collision layers. It's stone in me trying to hit the player. So everything else is set up and just fine. And then we need to set up the stat controller. So stat controller here, go to the top or right. And create a new stat definition. Let's say the skeleton has more like let's say more like 50 health, 50 Health, a speed of 40 is fine. So we'll right click Save As, and we'll say skeleton stats dot TRS, save. Now we need to do that for the player. Go to the player, click on the stat Controller, go to the top right, do a new stat definition. Now, here I want us at the invincible time. Let's say a character is invincible for 0.2 seconds after being hit. We'll give the character a base speed of let's say 100 and the Max HP has 100. I think I'll leave that as the start. So right click on stat definition. Save as, and we'll save this in the Player folder. Playerstt dot TRS, hit Save. And now the characters should each have different stats. Last thing we need to do is actually use the invincibility timer. So on St Controller, we need to trigger Invincible with a method call. So let's create a function set Invincible here, which will return type void. And then we'll have a local variable for if it's invincible or not. So variable invincible is a boolean. This is going to default to false. If true, the object cannot take damage. Okay, so for can be hit, we want to say that the character is alive and not invincible. So I'm using this with the keywords rather than the logic symbol. I guess it makes it a little bit easier to read, in a sense. So if the character is alive and not invincible, then the character can be hit. And we want to invincible equals true, but we also want to start a timer here. And we'll say that this is equal to a new timer, equals timer dot new. And then we add that as a child to this object, invincibility timer, and we'll connect to its callback. So invincibility timer dot timeout dot connect on Invincible Timeout. So Control see this and paste it down here. Function on invincibility timeout. This overture and void. And we'll say that invincible equals false. Okay, so now we need to start the timer. Invincibility time or start, and we'll say that the time is the invincible time. That should handle the logic of whether we can or can't hit because the character is invincible or not. Okay, so for set invincible, we could check when HP is set that if the HP dropped, then we want to trigger the invincibility. But I think what might make our intent clear is to create a take damage function in this definition, as well. So we could do something like function, take damage, and let's pass a a P damage t here. We ta and void, and this is going to take HP and set it to P damage. And then we want to set the character to invincible. We could even say if invincible time is greater than 0.0, then we'll set invincible. On this object. And the HP is actually going to minus equal the damage here because we want to take its current value and update it. So this will still call the live equals HP is greater than zero here. If you want it, you could even make get live just a function that checks this, which would be fine as well, rather than doing this previous setup. But we already had that. It works fine. We'll go with that. So now, whenever we deal damage, we really want to call take damage on the stack controller. So let's find out where we're dealing damage, like the projectile might have it. Okay, so here we can see everything goes back to the Rt box. So let's right click and jump into this. And instead of setting the HP directly, we're going to say stat controller dot take damage, and we pass in the P damage. So we can cut away this line, and we actually don't need to calculate this because that calculation is automatically handled in the stat controller. So this kind of more illustrates our point that if our stat controller takes damage, it might be doing other things like setting invincible on the character because our character took damage. It's not even just that we changed the HP value. It's specifically that we took damage from a damaged source. Okay, so let's hit Play and see if it all comes together. We'll have to take damage from a few enemies, and then we'll want to make sure that we can actually only take damage once every quarter of a second. So if I shoot over here, we need a few more enemies for this to work. It might even be easier if I disable the projectiles. Okay, so I'm gonna run into these guys. I should only see damage occur once. Okay, 25. I think it is working. If I walk through all of these. I'm only seeing damage come up once at a time. To VLC for sure, though, I need to turn off the projectile. So I'm going to temporarily delete the spear weapon from the scene over here. You don't need to do that. I'm just demonstrating. Okay, so then I'm just going to go walk on to the enemies. Okay, so we only get damaged once. Now, this collision hit thing is only incurring on entering the area. So if we stand on top of the enemy, we still only get hit once. Okay? You can see even though there's a bunch of guys hitting us, we only get hit once every quarter of a second or so, like that. And our characters already done, so we can't get hit more. 28. Implementing Periodic Damage for Enemy Hitboxes: Fix the issue where HIP box can only damage once after another area. The HipxTD enters this hit box, but not when it persists inside of it. We can set a timer inside of our hip box to D to keep checking if the HIP box is still inside of the area, and then we can keep dealing damage to it periodically. Okay, so once we emit the hit, I can also add it to the tract areas and the script. So let's say var targets, which is going to be an array of Hurt box two D, which is currently nothing. And we'll say that after hitting, we do targets append, the P area. Okay, now we need to connect to the area left signal. So area exited dot connect. And we're going to use the callback on area exited. Okay. So function on area exited. We get the P area. So we want to check that if it's a HIT box two D. So the areas of type area two D. Okay, so we check if P area is Hurt box two D, then we'll continue. Now we'll check if targets dot has P area, then we're going to erase it from targets. Targetst erase the P area. Okay, so now the targets are only going to exist while the target Hurt box persists inside of our hit box area. We can check every period of time whether we need to damage the enemy again. Okay, so we could do that with something like, let's say we have optional timer, so at Export var timer timer, and then we'll connect to the time or timeout signal. So timer dot timeout dot Knect and we'll say on timer Timeout. Okay, so we'll put that call back down here, function on time or TO. This will return void, and we will try to hit that area. So on the time or timeout for Target and targets we do hit dot a Mt Target. So if the objects are still in the area after they've done their initial hit, then after every time or period, they will get hit again. So we go to Ok and let's look at our hit box two D. Okay, and maybe we want to be a little bit more clear about what this timer is for. So let's say this is actually a damage timer, and I'll update the property in the ready. Okay, so now for our damage timer, we want to right click on hipbox two D, add a timer. And we'll say for this that we do like 0.25 seconds. This will be autosrt, and then we just need to reference that in the Hip Box two D. I'll rename it to be damage timer so that the names match up as well. So click on Hit Box two. Assign the damaged timer. Now that's going to connect to the signal inside of the script. And for each object or target that is in the area when the timer elapses, it's going to trigger that. And because this is not one shot, it's automatically going to loop. So every 0.25 seconds, it's going to keep doing this and it's going to check the targets every single time. So that's probably good there. We just need the skeleton to have the same thing. I'm going to control C copy this timer over to the skeleton. And we'll Control V paste it onto the Hip box two D. Let's assign it in the Hit box two D and the top right, assign the damaged timer, save and run. Okay, and we can see that on some of the hipbox two Ds specifically like the projectile the speed projectile, it doesn't have this. So we need to be clear that we say, If damaged timer, then we connect to its signal. So this is optional, and I wrote a comment there to be clear about that optional timer to periodically emit hit signals on the targets inside the hip box two D area after a set time has elapsed. Okay, let's run again. And we'll just see if an orc can actually damage us twice. Well, that's gonna be a little hard with the spears there. Let's disable the spear, as well. So disable the spear weapon or remove it temporarily. Let's go back into the scene. Okay, and let's find an orc to damage us a few times. Okay, so here we have our k. Let's see how many times we can take damage. Yep, we definitely can get hit multiple times by the same orc. And that's probably too fast, honestly. So let's join that down. First, I'm gonna control Z to add the spear weapon back in. Let's go to the orc and I'm going to set its weight time to 0.5 seconds. And if it seems like the player takes too much damage as multiple enemies group up on him, you can still increase the invincibility timer, so the maximum number of hits per second would drop 5-4, depending on if you make it at 0.2 or 0.25 seconds timer. However you want to adjust 29. Player Defeat: Character runs out of HP, we do need to switch to a death state, and part of that is going to be stopping the attacks from proceeding, as well. And eventually, we'll put in a game over screen, but let's just get it to get to the end of the death state for right now. So over on the player scene, we have our move state. We want to put in a death state, and I think we can actually just go to the org scene, and we might even just be able to copy and reuse this. If I look into the death state script, I see nothing about it that would really be an issue except for maybe the quarter here at the end. Maybe we just want to notify a game system that the character has been defeated. So I'll just copy the script. We'll go over to player. I'm going to click over here and add a new Limbo state node. Let's say player defeated state. So player defeated state, right click, attach a script, and then we'll create that in the player folder. I'll paste this in. We'll rename the class name up here to player defeated state. Okay, let's check the name of the animation and make sure that's on the animation player. So animation player death, we have that there. Okay, so that's fine. If we go into the death state, then we're going to want to add a track called Method Track on our player defeated state. And if we go to the end here, let's enable snapping in the bottom right too, if that helps. So snap to 0.6, and then right click and sort a key, and we'll call finished on the script. Okay, so I'm tempted to use the already existing combat system and then do something like report defeated on the player. The defeat of the player is more of resetting the game or announcing game over. So it's not really a function of the combat system. So for the sake of separation of concerns, I may actually just create a new node for handling game management, and that might eventually include saving and loading, as well. We'll see. So if we go to the root of our project and systems, I would want to create it here. I'll right click and create a new let's say, script, and we'll call it game manager dot gD. I think that works as a name. Okay. And in game manager, we'll leave it as a node because we're going to make this an Auto oad Singleton. Why don't we just use a function report game over, so this could be called from anywhere as long as we reference the game manager. And when we report game over, let's just print, which is going to print strings to the console, and we'll say, game over player defeated. And later, we would replace this with UI so that we can actually switch to a new game or see the game over stats, et cetera. But this will at least show that it's working. Okay, so we want to take the game manager and create a scene so that we can edit export variables if we ever need them. So I'm going to write clicking Systems, create a new scene. This will be game manager dot TSCn. I'm going to right click here, change its node type to a node. We'll drag the game manager script onto it. Okay. And then we want to save it, go to Project Settings. Inside of Globals, we want to auto load the systems game manager dot TCN ad, and now it's a globally referenceable game manager symbol. So if we want to use that, we can just go back to our player and the player defeated state and finished. Instead of queuing free, we'll leave the character there. And we'll say game manager dot report game over, and that's all we need to do. One reason to not remove the player from the scene is that as things are right now, all of the enemies are depending on the player's existence so that they can continue moving. So as soon as you remove the player from the scene, then all the enemy scripts are going to start breaking. Now, if you want to fully flesh that out and allow the character to be removed from the game scene, then you will just need to check on basically each run of, say, the c chase scene here. You would just need to make sure that the target is still valid, and if it's not valid, then you would just return. So that could be something like if underscore target equals null, then you return, and then that'll just mean that it just doesn't move at all. So, you know, for fun, we could actually go to player and in the player defeated state. We can actually add in the agent dot q free, and we could just see how that would work. So if I just add in that check that Targets Not null for the chase scene, is it going to break or is it going to work here? So let's give it a shot. Let's see if our enemies can still continue functioning with no player on the screen. Okay, so we're going to get defeated. I haven't actually set up the transition yet for player HSM. So in player HSM, let's create another function, set up transitions. Okay. So this is going to require us to add a transition from move state to defeated state, and that'll be on the event defeated. Okay, so let's define those up here for the player HSM at export var move state, which is a limbo state at export var defeated state, which is a limbo state. And then we'll create a constant for defeated, which is going to be a string name equal to and quotations. Okay, now we need to update our variable names here for add transition. So snake casing for those variables. And we should only be able to go from move to defeat it actually because we don't have any resurrection mechanic in the game. So once we defeated, the games over, and that'll be fine. You could always add a transition back if you have some way of resurrecting. Now we want to call setup transitions inside of setup HSM, set up transitions there, and we can handle the transition by connecting to the player stats signal. So after asserting that the players not null, we want to connect to the signal on the player. So that really is going to be the signal on the stat controller. It could either directly reference the SAT controller or connect to the stat controller through the player though it doesn't have that setup yet. So I guess in this case, it would be a little bit more direct to just reference an extra at Export VR stats of the stat controller Okay, and then down here we'll say stats Alive changed dot Connect, and it'll be on Alive changed. So let's create that function at the bottom and callback. And this is going to have the P value, the new value of the live. The S return void. So if P value equals false, then we want to dispatch the defeated event. Okay, so that will handle triggering the transition from move to defeated state if we are in the move state. If you end up adding a bunch of other states, there's also the option of doing any state here. So this will make it so that if the characters defeated, then from any state, you can just transition directly to defeated state. That might actually be preferable thinking long term because maybe you would add on some kind of attack state where you play a different animation. You'd still want to be able to immediately switch to defeated rather than just being able to go from move to defeated when this dispatch occurs. Otherwise, if you dispatch when the character is not in the move state, then it may never go to the death state. So I think this would generally be the preferable way to do it. Okay, so now we need to assign everything in the inspector. So assign the stats controller, assign the move state, and assign the defeated 30. Testing and Enhancing Behavior on Player Defeat: To make it quicker, I'm going to click on Stats Controller. Let's take the HP and set it to ten, and I'll hit play. So if our character gets hit once, we'll immediately switch to the death state. Okay, so let's find an orc. Okay, there we go. Okay, so we hit the defeated state. But we can see that the agent doesn't have stats because we didn't require that on the character. So if we just want to be consistent, then yeah, we can add in stats to the player. I think it's okay to reference stats through the player because we would generally understand that the player is going to have stats, especially in an RPGs kind of game. Also, we can give rid of that speed variable right here. Can see that the player is still using the speed from the base script instead of the speed from the stats. So yeah, let's get a reference to at Export var stats, which is a stat controller. Okay. And then when we have speed, we want to use stats dot speed. Okay? And let's copy this and replace it down there as well. So consistently using it in our physics process for the player. Okay? If we assign the stats and the inspector, we can do that right there. And then if we look at the HSM, we no longer need a direct reference to the stats because we already have it through the player. So we can do player.stats.ai changed, and we'll connect to the live changed through that. So just being a little bit more consistent about how we do things across our scripts, though both options, of course, work, whether you do it directly to the stat controller or through the player. So let's go into the game mode and have a character get defeated here. So the characters removed, and what's going to happen is when new characters get created new NPCs, we assert that the target player needs to exist, but it doesn't exist. Okay, so, actually, we're just going to remove this assert because we can have character spawn into the game world and not have a target. So, if this is null, I think this might actually still run. And then so there'll be no target because the character's already dead. And then we come in here. When target is null, then we don't do anything for the Chase state. So I'll hit play. Okay, so there's that. The enemies just kind of stop. Yeah. Okay, so you can see that it says trying to assign previously freed instance. So what we do need here is one more check. We'll say I agent dot target dot player does not equal null, then we'll assign it. Okay, that seems to be good now. No more errors. Now, the characters are still running in place. So we may even put in a idle state for the enemies if we want it to be a little bit more believable because they've finished their job, so now they're just sitting around. So what we could actually do here on Update is we dispatch an event. Let's say this is the idle event. Okay, so we're going to dispatch Idol from the Chase state, right? So now if we go to Ok Enemy HSM, we want to add a transition from the Chase state to an idle state. So say add transition. Chase state to Idle state. And that'll be under the event Idle. Okay, so we need a Idle state. So I'll do at Export var Idle state, which is a Limbo state here. Let's right click Add in a new child node Limbo state. Okay, and this is going to be really similar to the death state. I'm going to go into Death State and copy everything up here into the new limbo state. So this is going to be Idle state here. Right click, attach a script, Idol State. And let's control V paste that in just rename this Idle state. If I was going to create more states, I'd probably start thinking about creating a base animation state at this point. But really, it's not that much code. We're duplicating here, so it's not really a big issue. And this is the simplest way to handle it. At least short run speaking. Okay, so with the alpiPasted for the Idol state, I just want to rename the animation here to Idle. It already gets the animation play from the Blackboard. We do want to cut this bit out about setting the agent to no longer be alive because that's not true. Basically just this. We have an animation. We get the animation play from the Blackboard, and when we enter, we play the animation. And the animation is idle over here. So now in Enemy HSM, we just want to assign the idle state. Aside from that, this should handle the transition. We do want to take the transition to death state and change this to any state. So whether the characters idling or chasing, we can transition to death if the death event occurs. That's important. And then we just want to make sure that we copy the idle state over to the skeleton. Paste it in here and assign it in the enemy HSM. So assign on the right, idle state. And aside from that, it should be good. So I'm going to hit play, and we'll see if we can get our character defeated. Okay, so here we go. Our character switched to the idol and that's it. So they no longer need a target. They're no longer chasing a target. They're just idling around in their idle state. 31. Creating Pickup2D Scene: Now we're going to move on to creating EXP pickups so that our player can gain experience points and leveling up. So there's going to be quite a few steps involved with this. The first is going to be to create our basic pickup script, a pickup collector script, and then making sure that our character can actually acquire those and then assign the XP boost to the player's current experience. So let's start by creating the actual game two D world pickup. So I'm going to create a new note here. We'll use two D scene, and I'm going to rename this to be pickup two D. All right click on it, and let's change the type to be a base of Area two D. And now if we click on the attached script button, then we'll get a pickup two D that extends Area two D. Let's say Objects folder, and then I'll create a new folder inside of here. We'll call it pickups. Then I can save the pickup undersquare two d dot GD inside of here, create the script. And at the top, we're going to give it the class name pickup two D. We'll save this in the Objects folder pickups. Pickup tdt TACN. So save that there. So this is going to be a world physical node that represents an overall object that can be picked up and applied to a character in one way or another. So that might mean directly applying it to the stats or in some other circumstances, it might mean you pick it up and you add it to a character's inventory. We're not going to have an inventory for the game, but we will have the ability to add levels to the weapon loadout or create a new weapon loadout for a new type of weapon. For the pickup two D, we want to create a T function so that we can take the object off the game map and apply it to the characters. So let's say function Tr take, which is going to need a target. So P target is a no two D, and we want to return Boolean whether this was successful or not. Okay, so I'm going to push an error here which says it's a virtual function, implement an extended script. Now, in Gudo 4.5, they're going to have abstract classes, which makes setting this up more concrete because they add some extra keywords for it. But because we're on Gudo 4.4, which is the current stable release, we'll just do it the old way, which just basically means that we have to remember that when we extend our pickup two D class that we need to manually override this with a new version of the function ourselves. With the same parameters and return type. For TriTek, we're going to return false because we're trying to take an object on this abstract class, which is the pickup two D. So for the different pickups in the game world, we want them to all have their own custom way of applying to the player or target node. So we'll need to implement a custom extending script for each one of them, L one for EXP and then one for a weapon pickup, or there might be a upgrade pickup. So those are all going to work a little differently, so they all need their own tritak method. Okay, so we have our pickup two D script here. Let's right click and add a child node. We need a collision shape. So for the collision shape here, I'll just go to this shape in the top right, and let's make it a circle shape. So you probably also want to default sprite here as well. So I'm going to right click here, Add a child node, and we want to get a sprite. So for an object that's just going to play one animation over and over again, no switching or anything, I think animated Sprite two D is a better option there. For instance, if a coin is spinning on the map over and over again and you just want it to autoplay that one animation, then animated Sprite two D is perfect for that. So we'll select that and hit Create. Now, for the icons in the game, they may not even really an animation. But if we're going to make this the default template, I want to open that up as a possibility. So for our animated Sprite, we go over to the animation over here. We'll create a new Sprite frames resource and open that up. So we have a different animation window in the bottom left. So I could just rename this to Idol, if I want. Let's go into art. We want to go into Raven Fantasy icons, and the full Sprite sheet, let's look into this and see if there's anything we want. So to access this properly from this animation window, there's this icon Ad frames from Sprite Sheet. So click on this. Then go to Art fantasy icons, fall Sprite sheet, 16 by 16. Really, you could use a bigger one if you want. Maybe I'll just use the 32 by 32. So as a little bit more resolution and open it up, and you can just select the icons you want. Okay, now for this to slice properly, we need the right number of rows and columns across our sprite sheet here. Or if you know the size of each icon, I think you can just go to the size over here and just put 32 30 Yeah. Okay, that's definitely the faster way of doing. When you change one, it'll automatically calculate the other. So we have 16 horizontal. Really, those are the columns, and then 128 vertical are the rows. So rather than calculate that, we just know that each one is 32 by 32. Okay? Now we can easily just pick the icon we want from this. So you can use anything you want as a EXP Gem I think in the template project, I was using this one over here. So kind of just a blue gem. We add that one frame. And this would autoplay if we just check autoplay down there, but you can see that this doesn't even need to play really. And it's probably slightly more performant if we turn off autoplay. So it's just showing this one frame and not trying to even loop anything. Pure assumption on my part, but let's just have that set there. And that'll be our default spike for Pickup two D. So for the pickup two D root node, we probably also want to set up the collision layers. So I don't think we have a specific layer for pickups. So let's add in a new collision layer. Just make it layer five and say pickups. Okay, close. And then we want to, of course, put the pickup on layer five, and we'll turn off mask. We'll turn off monitoring because this is going to be picked up by something. It's not monitoring for pickups. It lets the player that's picking up the object do the detection. So this is just monitor a bowl with layer five. And that's the base scene for our pickup 32. Implementing Experience Pickup System with Inheritance: What we can do in the bottom left is search for pickup again. This should show the pickups folder, and we want to right click on Pickup two D and do an inheriting scene. So that's at the top here, new inherited scene, which means that we're creating a new scene based on this scene, so it inherits all the same defaults from our parent. So we create that new scene, and you'll see that some of these nodes turn orange, which means that these are actually based off of the parent scene. You can jump into the parent by clicking right here. So you can have scenes within scenes. And we want to rename the root node to let's say EXP pickup two D. Because we want to be specific about what we're doing. And let's save this as a new scene. So XP pickup tod dot TCN, save that. And then let's right click on the root node and extend this script. Okay, so this is going to give us our EXP Pickup two D script, which extends from pickup two D. And then we'll go up here to the top and say class name EXP Pickup two D. So we want to give a variable for how much EXP we're actually going to pick up whenever the character uses Tri take on the pickup. So at Export VR is going to be a integer, and we'll default that to one. And now, if we look at the pickup two D script, we want to take this function and properly implement it in our inheriting script. So I'm going to copy this, and we're going to go over to EXP Pickup two D. I'm going to paste it and we're going to write our real tri take method. So the EXP pickup is going to apply the experience to the target if the target actually has experience. So let's say if ptarget dot stats equals null, then we're going to return false here. Otherwise, we can say var stats is equal to ptarget dot stats. And because we don't know what this stats variable actually means in the context of each script, let's assign it the stat controller hype directly. So once the condition is met, that this is a target that has a stats block, we can say stats dot experience plus equals experience, and then we'll return true. So after we pick up the experience, we don't want the pickup to be usable again, so I'll say monitorable equals false, and then we'll cue free on the object. So when you cue free, there's going to be some period of time before it's actually finally removed from the scene. So we want to make sure that nothing else walking into it at that singular frame could accidentally pick up the EXP pickup a second time. So I'm turning monitorable false here so that nothing else can detect. Just as a safeguard, and then we remove it from the scene completely. So this bit right here, I probably actually want to reuse across all my pickups. I'm going to cut this into pickup two D and then say function remove, which will return void, and I'll just do this wrap up step. So now, if I go back to EXP pickup, I just call remove, which is sourced from the base class. So this function is already written in the base class, and we can reuse it here. Then, like, yeah, anytime we implement the trite if we want to redo that, we can just do remove here. And for this to work, we need to add in the experienced stats to our stat control. So in the set controller, I'm going to create that EXP property. Okay, so we're going to have Vara experience and the soil default to zero as an integer. Okay, and kind of in line with the take damage, where we call the set invincible separately from setting the HP, I might actually prefer to not make a setter here. Rather to do function ad experience, and we'll give it an amount. So P amount, it's an integer and we'll return void here. So we want to take the experience plus equals P amount. And then as a to do, check for level up. And we can also say prints experience here. So we'll get the total experience as we're collecting items from the map in the console. And then to update the EXP pickup for that, I want to do stats dot add experience experience. Okay, so we're being a bit more explicit. So this function implies anything related to experience that needs to change on adding experience is going to occur, which means we would check for a level up and so on. And I guess this would have some benefit. Like we could change the experience value without automatically triggering or checking for a level up if we had the need for that later on. Instead of always doing that, no matter what, like we did with the HP setter, where it always updates your life. Both ways of doing things are valid to an extent. It's kind of about how you want to express your intent here. And do you want to hide the fact that setting experience would also trigger a level up by putting it in a experience setter, or do you want to be more explicit with add experience function? 33. Setting Up Collector2D and Fixing Player Animations: To get the experience onto our stat controller from the Pickup two D, we need our collector two D node, which is going to go around collecting pickups for our player. So let's go to the player scene. I'll right click on player. We add a child node. Let's look for a Area two D. So this area two D, I'm going to rename it over here to be collector two D. And then let's right click attach a script. So the script will go it could be in characters, so I'll actually put it in characters. There might be a case where we actually make an enemy able to pick up objects, but I don't want it to be completely player specific. So I'll put the script there. Let's create. Then in the top left, we'll give it the class name collector two D. What we care about for the collector area two D is when a pickup enters at space. So we'll say function underscore ready is going to return void, and we'll say area entered dot connect on area entered. Then we'll create the callback function underscore on area entered is going to receive a P area area two D as a parameter and return void. So if P area is a pickup two D, then we will do P area dot Tri take on our root node. So our root node is our player. We could export our root node here at export object root is node two D, I suppose. And then we'll send that object root here. As the TriTek object, the one we want to pass through. Now, in our inspector, sign the player here, and that might actually be all we need. So let's add a copy of our EXP pickup into the game world. We'll put a few in, really. So I'll right click on World. Let's create a node two D. I'll rename this pickups, and then I'll add a couple pickups onto the game world. So one, two, three, and then I'll put these all onto the pickups parent. We can see that these are quite large. That's fine for right now, but we probably are going to scale them down for the rest of our game. So before these are going to work, I just remember that we haven't set up the collider on the player. For collector two D, right click at a child node, and we want collision shape two D here. So on the right, add a new circle shape and shrink that to maybe right around there, a little bigger than the normal collision shape. Go to collector TD and take the collision. We're going to mask on the pickups layer and then turn off mask layer one world and collision layer one world. This should be a monitoring only script. And I did just notice that because these bright frames are different sizes for our animations, they are not lining up at the center properly. Like, the idol looks fine, but run is maybe one pixel to the right, and then death is a little incorrect there as well. So one way we can fix this to make sure they're all correct is to assign a position property to each of the sprikeFrames, which it looks like I actually already did here for the death position. So Idle has its own value. Let me see what happens if I actually remove those position properties. So do that for death Idol and run, okay? And then we'll remove it for the reset as well if it's there. Okay, so, yeah, it does look like we need to customize that again. Okay. So let's start with the run. I will position it right here at negative 30 and keyframe that onto that animation. We'll go to Idle. I'll hit W and move that down. So it's like right about there, negative 13 and keyframe it. Okay, let's go to run. Let's go to Idle. It seems like Idle is still one pixel low, so I'm going to move that up to negative 14, keyframe it. I think that's what we had before, but I'm going to take the run and move it one to the left as well and key frame it. So now if we switch between idle and run. Okay, that actually looks correct now. Let's go to death and move it over here to the right and key frame it. So that's ten pixels to the right. And now we switch to idle. Okay, death is one pixel to the right too much. So that needs to go to a negative nine, key frame it. So we have idle. Okay, so we need to go two more pixels to the left for the death and key frame it. So that's an eight, and then the idle is zero for X. Okay, all of these look like they're lining up now. Okay. So here's the actual values. Eight negative 14 for the death. Idol is zero negative 14. Run is negative one and negative 30. And then the reset, that should match the idol, which is zero, negative 14. So reset will take down to negative 14. Negative 14 is right there, Keyframe it. Okay, and that should fix any animation problems we're having with those sprite sheets. Okay, now back to the collector two D. Make sure you turn off layer one and the mask for layer one, as well. Okay, and now we can hit play and see if we can actually pick up those little icons. So we did get that one. Let's check the output. Did we get the experience? Yes, we did. We have one experience there. We'll go down here. We get experience two. Now, this one experience three, so we can confirm our pickups are working based on our console output. And our character got defeated in the background. But that's good that's showing that that game over system was actually working, as well. 34. Creating Enemy Drop Mechanics and Resizing Pickups: Our pickups. Now, we want to make sure that enemies actually drop them when they're defeated. We probably also quickly want to resize the pickup two D because it's double the size of our player at this point. So in EXP pickup two D, I can either change the animated sprite to the 16 by 16 version, which is convenient because we already have that, or I can take the node and scale it down. So I can make it like 0.50 0.5. And then that would be one way of making it shrink. But if we already know that size isn't going to be consistent in our game, it might just be making more sense to go into the base scene of pick up two D, change the animated sprite. And then we add the same icon, but from the 16 by 16 pack. So go into art icons full Sprite sheet, 16 by 16. Size it as 16 by 16 because this is the smaller sprite sheet. And then we'll control metal mouse reel, zoom in, find the sprite, add it, and remove the large one. So select zero and delete that, and then it'll go down to frame zero. Save that. And then back in this scene, you can save it as well, and it should be good on the worldview. Okay, so now it's the appropriate size. Okay, so let's go into the orc scene, and we'll add the ability to drop items. So in here, we want a script that is going to handle items that we can drop into the game, and we may want to randomly select from a list of items, and we can weight them kind of the same way we did with waiting the enemy spawning. So I'll right click on the death state since this is going to be directly tied to the death state in the sense that we drop items on death, and I'll add a child node. So do a regular node here. Then we'll rename this to be something like drops. Right click, add a script, and then over here, we'll create that. So we'll give it the class name drops. So we'll give it the class name drops. Okay, and in our drop script, we're going to want basically a list of items we can drop. So let's say at Export VR, we'll go with drop definitions, and this will be an array of drop definition. So kind of following the same pattern from before, where we have a editor defined resource, we can repeat that again so in the file system, we're going to want to go somewhere like the Objects pickups folder and right click and create a new script. And I am going to call this drop definition. And this will extend from resource. Create that. Now we can open up drop definition, give it the class name drop definition. And we're going to want at Export VR scene, which is a packed scene. So what are we dropping into the game world, the actual item drop? So because it's a packed scene, it could really just be anything that has a node at its root. You could drop enemies if you wanted to. So this packing just makes it flexible. Okay, and then next we want a weight. So Export var weight is afloat. We'll default that to 0.1, and that is a decent start. So we'll go into the drop script again, and we have our array of drop definitions so we can click on drops. And if you can't see the array up here and the inspector over here, you can just go to Project and reload the project. Okay, so now if you check the inspector, it should show up there as long as you didn't have any errors. So we'll create our first drop definition. So new drop definition, and we can make this a EXP gym with a weight of one, if we want. So quick load. And we're going to look for the EXP pickup two D enter, and that sets that up. So our orc is just only going to be able to drop the EXP Gems, and that's perfectly fine for our basic enemy. So we want to function we can call on our death state or our animation that would be able to generate the drops in the game world. So we could say function drop if you want, you could pass on a number of times here, like P times is an integer, and that can default to one, and this will return void. So we could say four time in range zero to P times incrementing by one each time. So this means we start at zero, we go up one time until we reach P times. So if P times is one, this will run once because it'll do zero, and then it'll hit P times and stop. So we'll do the colon there. Okay, and then after doing the loop, we need to generate a drop each time. So it would probably be best to calculate the total weight once and then we use it inside of here. So let's generate the weight. So before we generate the drops, we'll say four DF and drop definitions. We'll accumulate the total weight, so we need a variable above this. So far, total weight, there's a float starts at zero, zero, and that's going to be total weight plus equals DF dot weight. Okay? So we have that. Now, inside of this four loop at the bottom, we're going through it each time. We want to get our selected weight between zero and the total weight. So it's kind of repeating the same logic as other randomization we've done in this course. So say var selected weight is going to be equal to rand F, which is 00-1 0.0, and we'll multiply that value by our total weight, which will give us our selection. So now for each of the drops for definition in drop definitions, we'll add the weight so far. So far, current weight, which is a float and starts at zero, zero, we'll say that the current weight plus equals the definition weight. Okay, so that gives us what we're at so far. If this is greater than the selected weight, then we want to return this drop or add it to a drops array. So if the current weight is greater or equal to the selected weight, then this is the one we want to append on to an array. So let's create array up here above the four loop, and we'll say var drops is an array of drop definition. Now, key thing here, this can have multiple copies of the same drop definition. It's an array, not something else like a hash set. So we can have the same drop two or three times if needed, which is important. Say drops dot append the definition, and then we break out of that four loop, and then it's going to go again here. So once we get to where the current weight is equal or greater than the selected weight, we pick the drop, and we loop again for each time that we need to for this drop setup. So now we have all the drops down here, so we want to instance them into the game world. So let's say four drop in drops. We're going to create an instance which is going to be equal to drop dot seen dot Instantiate. And then we're going to add that as a child to some parent inside of our game world. So in world, we could just reuse this pickups node if we want. So I can take pickups. Let's go to the node up here on the top right, and I will add a new group. So we'll say pickups. I'll make this global. So for anything that's an item to be picked up from the world, we'll add that in as its node group. So when it's in a node group, it's very easily findable inside of our script. And we can actually get that pickups node as soon as our orc load, so we'll know if there's a problem. So we'll say var pickups parent, and that's going to be node two D, and we'll say function underscore already. Let me zoom in here. Pickups parent is going to be equal to Gtree dot Get first Node in group, and we're looking for that pickups so in the node groups over here in the inspector node groups, you can see pickups there is already one, and this is assigned to this pickup node. So as long as the pickups node exists in our current game world, it'll work just fine. We could go a little step further and pull this out to a constant variable. So we'll say pickup group here, all caps, and then constant pickup group is a string name equal to, and I'll say ampersand, Control V, paste the string. So as long as you have a node in the pickups group for this and you've assigned that node to the group inside of your game project and it exists in the scene, then this will work perfectly fine. You don't need to get any direct references, which would be tricky because the orc doesn't exist in the scene until it's been spawned. So this is a decent way of getting that parent node that we can parent it to. If we want to be sure we can even say, like, assert pickups parent and does not equal null, and then we'll say there must be a parent for items to drop. Okay, that's pretty clear. Okay, so we instantiate each of the scenes and then we want to add them as a child to the pickups parent. So pickups parent AchildEstance. And the location we want the drop to be in is going to be based on the node two D. So we actually need to take drops and change it to a node two D type. So then down here, we can say Instance global position is going to be equal to global position from the script. Okay, so that means back in our org scene. So now we want to change this node type, I'm going to right click and change the type, search for drops, and you should see it here. Double click. Okay, click on drops. Make sure in the inspector, you still have your drop definition for the EXPJM there. And we probably need to move this out of the HSM nodes here. Since this is a node two D, we want it to be a child of our main node two D, so I will bring this up here as a child of work. And that should probably be good. So we have the item or whatever we're dropping get instance into the game world. It becomes a child of the pickups group node, and then we give it the position of this drops node, which drops is just a offset, if any, of the orcs position. We don't need to change it, but we could just have a default as zero, zero there. That's fine. And now we can either call drops from the death animation or the death state. So if I go to animation player here and we open up the death animation, let's see. Death state. So let's see. We're calling finished death state. We could just put the drops as soon as the orc starts dying. I think that might be more the way most games go, where you see the loop before they despond from the scene. So at a track, call method on the drops node. Then down here at the first frame. Let's zoom in. All right, click Insert a key. We do drops. You can see the parameters work in animation player. So in the top right, we have our drop method name. Have the argument. So the first argument and the only argument is int value. So how many times do we want to call drops? We could say 50 for fun, and let's hit Play and see if that actually works. So this will put a lot of these EXP gems into the scene if it's working, but at least we'll definitely know if it works. So let's see what happens. Okay, I think they all just spawn on the same spot. So I'll go over that. And yeah, you can see we picked up 50 gems. 35. Generating Random Item Drop Positions: Ly, when the orc drops 50 of these items, they're all stacked on top of each other, if I check the output. So we got 50 XP there. And I think what we'd want to do is that when the object spawns, we either give it an offset or have it move away from the object. It's dropping from a little bit. So I think the quickest way to do this would be to generate a random offset position, and this will be a certain number of pixels away from the character in a random direction. So we can just add this in for the drop script. The top, I'm going to at export of our drop Max distance, which will be a float. And let's go with ten pixels by default. This is going to represent how far away from the drops node. That's this specific node that we're working on that the object can spawn at, and this is a maximum amount, so it can be 0-10 pixels effectively. So down here at the bottom, I'll create a new function. I'll call it function, underscore, get span, offset. And this will pass in a P max distance, which is a float, and we can default that to the drop max distance. So we don't have to pass a parameter. It can just be that by default. And then we want to return to vector two as the offset. So to get a final offset, we need the direction, and then the magnitude, the magnitude is right here in the P max distance. So let's say var direction is going to be an inferred type from vector two, and we're going to create a new vector two, so wrap it in parentheses here, we need a X and a Y. So to get that, we basically need to generate a random direction between negative one and 1.0. So I will say rand F, and I think we need range here because we want to go negative 1.0, not zero to one. So rando F range on negative 1.0 to 1.0. And then we need to do that a second time so we can just copy this line for the Y value down to here. And then I want to normalize the vector so that it's a pure direction. Okay, so now we get our offset. So var offset is going to be equal to direction times the P max distance. And then we return the offset, and that's basically it. So we take Get spawn offset here, and when we're setting the global position, we're going to do global position plus GitspawnOfset. The default is drop Max distance, so we can just use that as the default here. So you don't actually need to pass in drop Max distance, but you could. So it would just be the value overriding itself. Either way, it'll be the same, but you have the option of leaving it blank, and then that value will just default down here. I think that's all we need. So let's just go to play mode and see if those 50 items actually drop at different locations. So let's hit play, and we'll go in here. I'll shoot an arrow or spear at the orc. And we can see that they do all spawn in random directions. But I actually made a mistake. So we don't want to normalize the vector because I want the percentage of the distance to be able to go 0-100%. Okay, so because I normalized the vector for the direction, it's always going to actually give you the full max distance here. Th will just be the pure direction. But what I actually want is when it takes that direction, it'll be some percentage of the distance. And we might actually want to call this more like a random vector. And then we'll go down to offset down here and past that. So when it's taking the X, it'll be somewhere 0-100% of the distance on the X multiplied by the P max distance and the same for the Y. So this will give it kind of more of a random position inside of an imaginary circle. If you were to create a collision shape, it would kind of look like that for the possible span locations. So let's go into play mode, and it should be more correct now when we drop all 50 items at the same time. So there you go. They're all randomly generated inside of that circle. We're never going to actually drop 50 items at the same time. So this kind of looks kind of funky. But if there's like two or three drops, I think it'll work just fine. So to kind of demonstrate, I'll go to animation player, and when we do death, I'll take the drops function here and we'll change its argument value of how many drops to something more like three. I think that's more realistic. Let's hit play, and then we'll go into here. In most cases, I think most enemies are just going to drop one item. But if we do decide to drop three, then at least we can see that they have different positions, and it's obvious to at least see where they're at. Okay, so I want to wrap this up by just changing the drops to one. Each or will only drop one EXP up. 36. Implementing a Pickup Gravity Area: It's a little tedious if your player has to walk over each and every pickup that you want to grab, especially when you're going to be dodging all these orc enemies that span across the screen. So what we want to add in is going to be a pickup gravity area. So on our player scene, rather than adding the gravity area to our collector two D node, I'm going to create a separate two D area node for gravity specifically. The reason for that is because the collision shape two D here for actually picking up the items is going to be a different area than the one that is applying gravity. So that would be kind of confusing with the collector two D, having two area shapes here. So we're going to create a new node. I'm going to right click on player add a child node. This would be at area two D. We'll add it in here at the bottom, and I'm going to rename it to be say pickup gravity two D because this is specifically for items on the map that can be picked up. Okay, so let's right click on player add a child node. We're going to look for area two D, and I could call it something like gravity area two D. I thought about calling it pickup gravity two D, but we want to make the script a little more generic because there may be cases down the road where you actually want the gravity to apply to something that's not a pickup, and handling the pickup specific gravity is just managed by the collision shape. Handling the fact that it is for targeting pickups is just managed by the collision layers. So all you have to do to make it target pickups is to choose mask five here for the pickups layer, and let's turn off layer one for where it exists on and make it monitoring only. Okay, so that's our setup. Then we just need in the gravity area to apply it to all objects inside of here that it detects. So inside of the gravity area, we'll right click. Before we move on from the inspector, though, if we look at this gravity section, is a built in area two D gravity, but the thing is that applies to rigid body two Ds, which are full physics objects in the game, and we don't want 1,000 rigid bodies floating around the screen. So rather than using the standard physics for controlling the gravity on our pickups, we just want a simple script where we're just going to make it pull into wherever the player's center is. And not deal with rigid body two Ds at all. So essentially, it's a performance optimization. We don't need full physics for each of our pickup items. They just exist to be picked up by the player. So sucking them into the player directly is perfectly fine. So I'll right click on Gravity Area two D and we'll attach a script. Of course, I'll call this gravity area two D. Let's create that class name. Of course, gravity area two D. Okay, so while an object is within the zone, we want to track it and consistently apply a movement to it, basically translating another node two D across the screen towards the center point of this area two D, the gravity area two D. So let's have a local variable. I'll call it targets, and this will be an array of node two D is fine. On function ready, we want to connect to the signal of this area two D. So we're going to say area entered dot connect, underscore on area entered. We'll also connect to the area exit area exited dot connect on area exited. We need to create those functions now. Function underscore on area entered, and this is going to give us area two D as a parameter. So P area area two D, we be trying void here. Okay, so what are we going to do with the area here? We're going to move it towards the center point of our gravity area two D. This is going to work fine with the pickup because the pickup areas are at their root in area two D. So anything under it is going to be automatically move towards the center so we do require it to have that kind of setup. Otherwise, you would need a reference to the parent node of the scene. Whatever is your base of your pickup scene is the object or node, rather, that needs to move. So be aware of that. Because we're doing all the pickups specifically like this and pickups are exclusive to layer five, it will work just fine as it is. If you want to be extra safe and you want to limit it to only pickup scripts, then you could, of course, check if the P area is of type pickup two D. It doesn't seem to be necessary as it is right now, but that's something to be aware of. So I'll just put a little comment up there about what I just said. Okay, and since the P area pickups like that. I'll work fine as it is. So I will say targets dot a pen, P area. Remember, P area area two D is of type node two D, so that works fine up here. There's no need to be more specific than that because we only need to access the node two D functions. So on area exited, function, underscore on area exited. P area area two D, returns void, then we're going to say targets dot erase P area. Now, to note about the erase function, if the object you're trying to erase from the array does not exist in the array, then this will do nothing. If it does erase, then it will remove it. So there's actually not a need to check if it's already in the array. Because if it doesn't exist in the array, then this will already just do nothing. So there's no need to double check. Right. And then we need a physics process. So let's say function underscore physics process, and we'll say for target in targets, we want to translate it across the screen towards the center gravity area point. We need a speed at the top at export far gravity speed is a float. If I recall, this is going to come out to pixels per second. So I'll try something like 50.0. Then we need to calculate the direction between the target and our gravity area center point. So far direction is going to be equal to target dot global position dot direction two global position. Okay? And then we just need to calculate our move amount. So var move is going to be equal to direction times gravity, speed, times Delta. Since we're using translate, we need to factor that in directly here for the final move vector. We can say this is a factor two D, if that makes it more clear. And then we want to take the target and translate it across the screen by the move offset. This is essentially our gravity script right there. The last thing I think we need is to add a collision shape to our gravity area. So right click on the gravity area to D, add a collision shape to D. Go to the top right, a new circle shape and make this rather large so that we have the area where it's actually going to apply gravity towards the center. Okay, so I can play, and we'll go in here and you can see that our objects get sucked towards the center. So right now, the gravity is linear. We may want to give it a curve so that the closer it gets to the center point, the faster it's actually going to animate that translation towards the center. So if we go to gravity area two D, and inside of here, I'll say at export, let's say effect curve of type curve. We'll save that. Then in the inspector, let's edit this effect curve, new curve. And then we want X or the max domain here to be basically the distance from the center. And then the value here is going to be what percentage of the max speed you want to apply based on that distance. So let's say that the distance is like 100 here for the max domain. The closer it is the closer we want to get to 1.0. So let's take the first point and move it up here to 1.0. I'll zoom in here so you can see. And then I will just pull a second point down like this. Okay. So if it gets to 100 distance, it's going to be very weak at that point. And that would be measured in pixels, of course. So then with that curve, we want to go back in here and adjust the move by sampling this curve. So let's get the curve modifier, and that's going to be equal to effect curve dot sample. And we need to sample with the distance from our target to the gravity center. So vara distance is going to be target dot Global position, dot distance to the global position. Okay, so we just pass that distance into the curve modifier sampling, and then we take the curve modifier and multiply that by the Delta. So this will be some value between 0.0 and one. And that should affect the gravity based on this curve. The closer it is, the stronger it is. And over here, it gets weaker. Feel free to customize a curve if you need to change the shape of the animation a little bit. Okay, so I'll hit play here and we'll kind of test that out. It's a little hard to see because maybe I made the max distance here too big. So what you can do is you can check the collision shape and see what its radius is. So 27 pixels. So we're basically making a curve for up to 100 pixels, but the max of this radius is 54 pixels, 27 over here. Oh, really, no, no, no, 27 pixels because it's the center to the outer point, right? Not the diameter, of course. Yeah, I might want to take the radius here and just make it 30 pixels, a nice even number, and then we go to the gravity area. And then let's take the curve point here and move this to 30 pixels, as well. Okay, so that'll kind of be like our max distance on this shape over here. It doesn't need to be perfectly 30. Like, roughly here so we can see the shape a little better. Okay, now if I hit play and we go over here, it should be a little more accurate. Okay, now you can see the gravity really ramping up as it gets closer, so it's slow on the outside, almost too slow. So I might actually pull this up a little bit, and you can adjust the shape of the curve by pulling the handles, whatever you need to do. See, I kind of like that. We'll hit play one more time just to test. And yeah, I think that's right for gravity. So there you go. Scaling gravity. The closer your object gets to play, the faster it moves, and it'll suck up all the pickup items or really whichever areas you happen to mark on the mask layer here. Which in this case, right now is just pickups, but you could do that with other areas. 37. Building a Player UI with HP and EXP Progress Bars: Okay, so although the main combat is working in the game, we can pick up items like EXP pickups, but we have no UI indication of what's going on. We can't see our character's level, our HP, our EXP bar. So we're going to start by working on EXP bar and adding in a level counter to, let's say, the top left of the screen. And we're going to do that on a new node. So I'm going to go up here to the middle section, add a new scene. And we want this to be a user interface. So I'm going to call this player UI. Let's save it into the UI folder. And save it in there. Then I'm going to click on the player UI, and we'll add in, let's say, a panel container. So this is essentially going to be the background for our main player UI. Now, you say that the player UI stretches across the whole screen by default. So we want to go into layout on the right and change Anchors preset from full wrecked to top left. Okay, now, that'll shrink the size here to basically nothing. And then we want to do container sizing, expand on horizontal fill and vertical. Then under our panel container, we want to right click and add in a new, let's say, a V box container. A VBox container organizes its children vertically. So if you put two items in here, the one that is directly under it will go on top of the other in terms of its visual layout. So let's right click on the VBox container, and now I want a HBox container. So our HBox container, the first one, will go left to right, and this will be on top of any other children HBox container. So it helps us to kind of lay things out almost like a grid, but not exactly. Okay, and then as a child for the HBox container, we want to add in a texture progress bar so you can search Progress and you'll find right here. This allows you to, in the top right, set textures for the under texture, the over texture, and the progress texture. So as the values fill up, it will actually show more or less of the progress texture, and then under it will be the base texture, which shows in the background always. Okay, and then in addition to that, we want a label to the right of this. So I'll right click on HBox container, add a label. A basic label should be fine here. We can always change it to a rich text label later. Okay, and now for this texture progress bar, we want to go find the art for our progress bar. So let's minimize everything on the bottom right and expand the art folder. Let's go to Crimson Fantasy GUI. And I think it was under GI Sprite here. So in the top right for under, I'm going to create a new atlas texture and then expand that. And let's drag GI Sprite to the atlas here. So we can see the full atlas, pretty cool Gothic art style going on here. And we can select what we want our undertexture to be. So the under texture is going to be basically our bar where it is completely unfilled. So if I edit region, I can zoom in here, and I'll select this, but right here, like so, that can be our under texture hit close. And then for the label let's just say zero out of 100 HP for right now, just as a mock up. So now it makes sense to rename this HBox container, something like HP display. And let's duplicate it, and then I'll rename the second one to be EXP display. Go to the texture progress bar. And in textures, we're going to select a different one from Edit region. So let's see what do we like here as a EXP bar? We could use this basic one over here, but I think it might actually be kind of helpful if we could see different chunks. So with this one, we could see out of seven how close we are to actually leveling up. But you'll notice as soon as I change the value here for this one, the top one automatically updates as well because we're using a shared atlas texture resource. So what we actually have to do here, I'll close is right click on this texture and then do make unique. So when you do that, this texture will be unique. We need to go back to the first one and fix it, right click and then do make Unique, and we need to change the HP one back to its HP texture. So over here, we'll select that HP bargain, and now we can see that these are two separate things. Okay, so now let's also do the fill in for the top one. I'll go to progress here, new Atlas texture, and then we can quick load from the drop down menu, our GUI sprite. And now we want to edit region, and we want to grab the filled in health bar here. Okay, so that'll look something like that and hit Close. Now that we have that texture set, if you go to, let's say, value here and you type in 50, you'll see that part of the health bar is now filled up. But you also see that with this setup, this may partially be because the heart is kind of hiding things, but it doesn't really seem like it's accurately reflecting 50% of your total health here when the value is 50. So to get around that, what I was doing is changing the men value to be something like negative 20 to kind of offset this over to the right. So now you can see there's four visible health chunks out of eight. So that 50 value makes a lot more sense when you have that negative 20 offset there. So you might just need to kind of customize that. So obviously, when you characterize zero health, it's going to show zero is the value which will be over here. But what really matters is like the 50 out of 100 here and the 100 out of 100 there. So just to make sure we do value 100 for fully filled in health bar, and that looks good. We can say 20. We can still see our health. And even at five or ten, well, I guess at five, we don't have to see it necessarily. But like at ten, we can still see a tiny sliver. I think, as it is right now is pretty good. So we can actually just leave that default as 50. And let's work on the EXP display texture. Let's just right click on the base under texture here, copy, and then right click on the empty progress and paste it in. Then we want to right click here and make this unique and change it to the value we need. So Edit region, and we want to grab this filled in version of that bar. Okay? And we can type in 50 for the value for testing. Okay, and then we can see that's halfway filled in. So that's fine. We don't need to do a negative min value on this version of the texture. There's a bit of an issue here where our progress bar for the XP is red, just like the one on top. So what we actually want to do is so that this metal portion is tinted a different color. So how we can do that is by going over here to the progress tint and changing that to more like a green, kind of like that. But you can see that that actually tints the entire texture. So what we want to do is right click on the under texture, control copy, and then paste it on the over texture. Okay, so now only the pixels that don't exist on the under and over are going to be affected by the progress texture change. So whatever color you want the progress to be, you can just change it to that by just kind of manipulating the tint here. Can change the darkness, and that is probably good enough for your XP bar, to be honest, as the simplest solution without using a shader or anything. So if we go to the label now, we want to change this to say EXP. So zero out of 100 EXP. Okay, and then we want to vertically center these texture progress bars. I think you can select both of these at the same time if you hold Control down and left click and then go to layout container sizing and change it from fill vertical to shrink center. Yeah. Okay. And then that basically takes these, and it centers with respect to the other elements like this label over here on the right. So that'll look good. Then we just want to shrink the text. So what we might want to do actually, is just go to the player UI at the root here and change its theme if we quick load from the drop down menu our game theme. Then it's going to have that pixel art font, which is already by default, a little bit smaller. Okay, last thing for wrapping up our mockup, we probably want a margin container as apparent to the panel container. So right click on the roots, player UI, add a margin container. So search margin, and then pull panel container under that. Then now in the margin container, you can go to theme overrides on the right constants, and you can customize a margin. So I'll go with five for all direction margins. Okay, kind of like that. So that's probably a sufficient margin, but I actually got the ordering wrong. The panel container goes under the player UI, and then the margin container goes under the panel container, and then the VBox container goes under that. Okay, so the point is that we want the panel container to stretch the background image to the edges here, and then we want everything else to kind of be contained within that. So that's why the margin container has to be within the panel container, so the panel container can stretch out further than the margin container. It's just a ordering thing. So that's going to pretty decent. Let's put it into our world. So on our world scene, let's create a canvas layer to put the UI under. So right click add a Canvas layer node now. Okay, and the reason you would have a canvas layer here is it gives you a separate rendering area from everything else in the scene. So the UI is rendered completely independently of the Toti game world. And that would be important if you have, say, Toti game lights, and you don't want that to affect the UI at all. You want those to be rendered separately. So the lights for the world are different from if you have any lights for the UI, that would be a separate thing. And you just kind of manage them separately. So I will now want to add the player UI here, so filter your files, search for player Underscore UI, if that's what you gave as the name, Dragon drop the scene into the Canvas layer, and it's going to pop up right there in the top left of our UI. Before we hit Play, make sure player UI is under the Canvas layer. That'll be important so it doesn't also render in the game world, but rather as part of the screen. So it's there in the top left. It's actually quite tiny. That's pretty hilarious, if I'm honest. We can either upscale the UI here or we can make it bigger in the base scene. So maybe what I just want to do is take the canvas layer here and we'll just transform, increase the scale to, like, four X. You can zoom out. Look for the purple lined box. You might actually have to hide, like, the ground world for that to actually see it. Okay, there. So we have the purple box here. This is the full view port area. So your UI here is kind of with respect to that. So at four times scale, I think that works pretty good. Now I'll show the dirt and the grass again. We'll hit Play. And let's see if the UI is there nicely on the top left. And I think that size is actually pretty decent. I'm liking that a lot. We'll go with that for now, and then we'll work on scripting it out. 38. Implementing a Stat Based UI System with Signals: So for this UI to actually work, we need to hook it up to signals that are going to relate to our character Sp block. So that means we need like EXP changed signal or a HP changed signal. We want to make sure we update that. We also have another minor issue, which is that basically this UI is very big on the screen area where it's at currently. So I want to quickly add in a script where I can just make it so the Canvas layer will automatically show when the game starts, but we can optionally just toggle off when we're an editor. So I don't want to always have to manually turn on the visibility. What I'll do is I'll right click on the Canvas layer over here, attach a script. So this would just be canvaslayer dot GD. Let's save that in the UI folder Bin. Create. And I'll just say function underscore ready. It's going to return void, and then we'll say visible, equals true. And that is all we need for that. So now we can hide it when we're looking at our map view here and hit Play. And it should still pop up as soon as the game start. So now we can hide it from our map view here, hit play, and it'll still show as soon as the game starts. So in the top left corner, there it is. So that will be helpful while we're editing. Another way you could handle this would be you have a completely separate scene for your Canvas UI when you're editing that, then your world scene, and you combine them together in one parent scene. When you actually load up the game world, that would be fine. And maybe that would be the long term solution. But this is just a really nice, quick and easy fix. So let's work on connecting our player UI to the player stats. So let's jump into the player UI scene. I'll right click on player UI. And attach a script. So the playi dot gD can extend Control and inside of here. We'll just at an export R and we'll say context of the player context. Since we already created that resource, it's super easy to use. And the inspector over here to the top right, just quick load the player context. And if you remember from earlier on in the course, the player is going to automatically set itself there, so we can just grab the context dot player dot stats, and boom, we have a reference to the stats. We just need signals in order to update the HP display and the EXP display. Okay, so we can look up the stats. Let's see, stat controller dot GD. If we jump into that script, we have a live changed. Let's create some signals for HP changed, Max HP changed, and experience changed. Okay, so signal Mx HP changed. And you can see that this is a lot of stats that we're going to change with a very similar type. We might want to pass the object that was being changed, the old value, the new value, and the change amount. So we can actually create a class to encapsulate all of for any of our stats that are going to change. And this is a very stat based game system, survivor like, right, because you might have cool down reduction, you might have player movement speed, HP, attack power, all this kind of extra stuff. And those are all stats that could be represented inside of one object that we can pass into an event. It's also much easier if you just have one parameter so that you send that parameter and then they receive it as one piece rather than three or four. So to show what I mean, let's create a new script in our file system. So I'm going to collapse everything, and then we'll go into let's say objects because objects have stats. So I'll right click, create a new script. So this is going to be data that goes on a signal for whenever a stat changed. So a pretty clear name would be stat changed data dot gD. We create that. Okay, and let's open up that script. So stat changed data. Class name, stat changed data. And this is actually going to extend from ref counted, which is a object that when it's no longer referenced, it will automatically be deleted from the game. So references. And instead of extending no, this is actually going to extend from ref counted, which is a Gadot object that keeps track of how many objects are referencing it. And when no objects reference it anymore, it'll basically queue itself up for deletion, so you don't need to manually free it. It'll just be picked up and deleted by the engine. That, by the way, is also the default for when you don't extend from any class, that's the default class as well. Okay, so our stat changed data, we want to give it an initialization function, so function underscore a NIT. And when we create an NIT for a object, we can pass on whatever parameters we need. So I'm going to say P object, the object that we are affecting with our stat change. So no TD, the P Nu value, which I guess will be an integer for right now. There could be a float stat, in which case, you could change this type to a variant to support both integer and float. But I don't think right now there's actually going to be any floats going on in the game for stats like experience and HP. We've made those a concrete integer, which is probably preferable anyway. So, the new value we're passing in, and then we want the old value. And when we have the new and the old value, we can automatically calculate the change. So let's put in the state properties for this object. I'll say var object is going to be a node two D, the var new is going to be an integer. The var old is going to be an integer. The var change is also an integer. So when we initialize the function, we just take the parameters and we set the local variables to those values. So object is going to be equal to P object. A new is going to be equal to P Nu, old is going to be equal to P old. Now that we have the new and the old set, our change is going to be equal to the new minus the old, I think that is unless I have it reverse. Okay, so if the old value is 30 and the new value is 20, then the change is going to be negative ten, which is what we want here. So yeah, new minus old. So essentially what we're doing here is we're passing these four parameters into the signal as one St changed data object. And then on the signal receiver, the callback function, we can access everything inside of St changed to theta just by doing St changed data dot object, dot new dot dot CAGE. And we might not need all of that, but we have it accessible. Also, if we ever add another thing here like var Cookie, which is a texture tue or something like that, then we don't need to add this as an extra parameter. It's contained inside of this class. So you can modify this object while not having to change your callbacks or the sending signal parameter because you're just sending one object. Hopefully, that makes sense. It's easier to kind of modify as you go. Anyway, we have our stat changed data. So if we go back to our stat controller script, now we can create our signals. So Max HP changed is going to have the data of the stat changed data. Okay, and then we just do signal for the other one signal, HP changed data, stat changed data, signal experience changed. Data stat changed data. Now, the reason I would put a signal for each of these is because on the receiving script, we can choose which stats we actually care about. So, for instance, the EXP display over here only cares about the experience changing. So we're only going to connect to the experience changed signal and maybe a level changed signal as well to update the level label if that goes in the same display area. So we have those signals. We just need to emit them. So what I was doing before with, like, the HP setter here, if you're just going to emit the value changed, I think that's acceptable use of a set so we emit that the value changed and just kind of make it an observable property. So I'll say here, whenever we set the HP, we'll just say HP changed dot am HP. Now, if we're getting set to HP 100 when it's already 100 over and over again, we might want to skip that signal emission, so we can say if HP equals the value, then we just return because we don't need to update live here. We don't need to update the HP because it's the same value. So following that pattern, I'm going to basically do the same thing with MAX HP. So we'll do colon. Next line, set value. If Mx HP equals value, we return. Otherwise, Max HP equals value, and we emit Max HP changed. Oh, but down here, I forgot this is actually not just the at HP value here, but we actually need to create the stat changed data object. Data is equal to stat changed data dot new. And we're going to pass in this object reference up here for the stat controller. So object, and then we do the new value of HP and the old value, which we need to get first. So before we set the new value on HP, right before that, we do var Old equals HP, and now we put the old in here, and we pass the data to the signal emission. So HP changed dot MMI with the data, and that's all we need to do. And now it's going to have those four data properties inside of that object instead of just one, but it still looks super clean because we're just sending one object. Okay, now in MX HP, we want to do that again. So we'll say var Old equals Max HP, and then we'll do far data equals stat changed data dot u. So do the object, the Max hP and the old, and then we emit the HP changed signal with the data, and that's that. Okay, so following the same pattern, once again, down here in experience, colon set value, we can still guard against if the experience is the same value just in case. So if experience equals value, we return. Otherwise, we get the old old equals experience, and then experience equals the value. And then Vara data is equal to stat changed data dot w. Object experience old, and we emit experience changed with the data. Okay? And I might add this. Oh, yeah, okay, forgot the D there at the top for the signal. Control STA, and that should be good. 39. Scripting Dynamic HP and EXP Displays for Player UI: So we have the signal setup, and we have our context here and the player UI, which is going to allow us to grab those signals from the stats block. But we don't really need to do that directly in the player UI. We need to do that in the HP display and the EXP display. So I want to create scripts for the HP display and the EXP display down here. So I'll right click on HP display, attach a script, and we can put that in UI, so hp display dot GD. Great. We can just make this extend from container. We really don't care about what type of container it is because you can create a display using other types. Maybe you display your HB vertically rather than horizontally. So you can use a different base class, like container rather than HBox container, and you can still then make the actual node in HBox container. So just keeping things flexible is the idea here. And I'll say class name HP display. Okay, so in our HP display, we need a reference to the stats of the player controller. We could export the player context and then directly get that here for each of the displays. However, if we set things up like that, for each of your sub UI components, if each of them have an export property that you have to set, then it becomes very hard to drag it into the scene to reuse it because then you have to, like, right click on it and make it editable and make sure you assign all of the individual properties like player context. To every sub UI node directly. So another option, which might be preferable, is to have the root player UI handle injecting the player context or what's relevant from it, the stats controller into each of its sub UI nodes. And then you only need to make sure that the player UI knows about its subcomponents, like the HP display and the EXP display. But then you let the player UI handle the heavy lifting. So how that would look like in the context of our HP display, would be that we need our local stat controller. So if our underscore stats stat controller. So we make this local, right? And then we'll create a setter function so that we can set the stats controller from the player UI. So say function set stats, and this is going to take a P underscore stat controller. Which is the type stat controller. I'll return void. I put the P underscore for parameter so that it's very clear that this variable is a parameter of the function. In case you have another stat controller up here, there'll be no confusion between our local reference to the stat controller and our function scope, P underscore stat controller. So we're going to use this set stats function to do that. So do underscore stats is equal to P underscore stat controller. Okay, so we have our set stats method. It assigns the stats to the stats, which is private here because of the underscore. Hover, this is GD script, so it's not truly private. It's just marked that you should not access this from outside the class, but you still can technically. So we have our stats setter here. We want to connect to the signal now. So this is the HP display. And I want to say stats Hp hanged dot Connect, and we'll say on HP changed. And we can also say underscore stats MxHphanged dot Connect. And this can just be on underscore on Max HP changed. So technically, both signals have the same parameters, so we could just use one callback function if we wanted. But to keep it flexible, maybe there would be some code that we'd want to do differently, depending on if it's MaxHP or the HP changed, I'll just have two callbacks here, so function underscore. On Max HP changed, which is going to have the P data, the stat changed data, this overturn void. And what we want to do is refresh LUI. So I'll just create a refresh function here, and then we'll do the same thing for on HP changed. So we can just copy this down here and then remove the Max bit. So delete four times. Okay, that's that. Now we need a refresh function. So we'll say function refresh. And what do we want to do on the HP display? Whenever the HP changes? We want to update the slider value and we want to update the text label. So we want to basically get the percent healthy for percent health, and that is going to be a float equal to let's say, underscore stats dot HP, divided by underscore stats dot MAX HP. Okay, then with the percent health, we can assign that to the texture progress bar. So you can see now we need a reference to the texture bar down here. So the only time we're going to be setting up the reference to the texture bar is like the first time we're creating this HP display scene. Maybe we save this to a separate scene on our project, and the main scene that we actually put in the world is the player UI scene. But I think it's perfectly reasonable to export the texture bar and the label here. There's other ways of grabbing these, as well. But that's the method I'll use. So I'll say at export var progress is a texture progress bar. And then at export var label is a label. So when we're refreshing, we can take the progress and take the label, and we'll say progress dot value equals percent health. So let me see. That should be on a scale 0-100. And this calculation is actually going to give you a 0.021 0.0. So we want to multiply this by 100 to get an actual percent so times 100. And I'm not sure about the order of operations here. So I'm just going to wrap this part to make sure that this executes before this with certainty. Okay, and then the progress value is going to be our percent health 0-100 as a float. Then we need to set the text value on the label. So label dot text is going to be equal to the string. We could just say in quotations HP, percents, out of with a forward slash percents, and then we replace those values with a percent sign and pass it on an array of values. So the values we're going to use are going to be the current HP and the MX HP. So in other words, underscore stats dot HP and underscore statts dot MAX HP. So if you want this to be customizable, we can cut that out with Control X and replace it with something like health text template. Then up here at the top, we could say at export bar Health text template is going to be equal to, and then Control V paste that. This still has these percent as symbols for replacing. So we still do Health text template percent, and then the array of the two values in order, which is underscore stats dot HP, and then underscore stats dot max HP. Okay, so that will update the bar, and we'll update the text. So we also want to call this whenever we set the stats. So I'll say refresh up here to make sure that after we set the stats, we immediately update that text, as well. We need a colon up here to finish that function, and that is pretty much good. Now on the right hand side for the script, we do need to assign the progress and the label. So assign the texture progress bar and assign the label. Make sure you're selecting the ones under the HP bar. Now, that's going to be good for the HP display. We almost want the same script for the EXP display, but there's a couple differences. So I will control A, Control C to copy everything in this script. We'll go over to the EXP display. Right click, attach script. Exp display dot gD sounds good to me. We'll change the inherit type here to container, though, and then just select everything and replace it with a Control V pasting it in. Now we need to change the class name to EXP display. Our HealthText template. We're going to select that Control R to replace it with EXP text template. This Health text template, select the text. You can double click on it to select the whole thing. And then do Control R. Down here, we're going to replace Health text template with EXP text template, replace all, and then we want to change this HP right here to EXP as our default template. The last thing we need to do is change the connection of the signal to experience changed. We can completely get rid of the Max HP changed, and then replace this function name right here on HP change so you can select it, Control R, and then down here, we want to replace that on EXP changed or experience changed, whatever makes sense to you. We'll replace that. Get rid of the on Max HP changed data here to wrap this part up. Let's not update the progress bar yet, so I will comment those out. So for right now, we'll zoom in here and I'll replace the experience text template with underscorests dot experience, and I'll put experience over here again, too. So it'll say like one out of one or two out of two, but at least we can see it working. Now in the top right, make sure you assign the progress. To the texture progress bar, the label to the label. Then we just need the HP display and the EXP display to call their set stats function from the player UI. So inside of the player UI script, let's at ExportvRHP display of type HP display at Export var EXP display of type EXP display. And then function underscore ready, we're going to call hp display dot set Stats to context dot play dot stats, and we need to get that twice so we can even say var stats of stat Controller, then we set that equal to this bit. So I'm going to cut that, go over here, equal sign, paste it in. So we set stats on the stats. We do the same thing with EXP display XP display dot set Stats. And then we do stats. Last thing we need is to assign the HP display and the EXP display and the inspector so that we have that reference. So click on player UI, go to the top right, assign the HP display. And assign EXP display. Now, really, the HP display and the EXP display can be and probably should be their own self contained scenes that we instance a copy of in our player UI. So I'm going to right click on HP display, and we're going to save the branch as a scene inside of our UI, and then right click on EXP display and do the same thing. Save branch a scene inside of the UI. So now we can jump into these and individually customize these little displays as we need in the context of our greater player UI. So we have scenes within scenes, and then we take our player UI scene, and we put that into the game world, which is invisible right now because the Canvas UI is hit. But yeah, there you go. And if we at play, we'll see if anything linked up properly. Okay, so set stats and HP display is null because I forgot to assign them in the inspector. You can see right here these are null on ready, which means I forgot that at export. Also because this is checking, we don't need any asserts because it's kind of implied here that this is required now. So jump into the player scene, click on the player UI, assign the HP display and the EXP display. Anyway, now we can play, and we'll test it again. So we have ten out of 100 HP. If I recall looking at the stats, our Max HP was 100 and our base HP was ten, which obviously we get hit here. You can see the HP updates to zero. The EXP progress bar not working yet because we need to set up our level E experience requirements, which is going to create, like, a whole hierarchy of leveling up. But let's try real quick, setting the HP to 50 on our player. So I'll go to our player scene, and then I'll look inside the stat control here. And we'll set the HP to 50. Let me see. If I do that right there, can I tap back to the game? No, because the players already removed, I think. But by hit play, we should see the HP bar update here with the right progress value, but it does not. It shows the right HP value, but we have to fix the HP display real quick. Okay, so in the HP display, let me see what was going on here. The progress value is equal to the percent health. Let me set a break point here, and I'll hit Play, and we'll see what is actually setting that so the percent health is 0.0. Ah, right, because I'm dividing with integers. So when we divide by two integers, we're going to get an integer, which is going to automatically go to the floor of zero. But that's not what we want. We actually want it to return a flow. So what we could just do as a quick fix here is just say float on the Mx HP. So now this is going to be an integer dividing by a float, which will return a float. If we hit play, then we should see the right value go there. So now we can see percent health 50. If we hit play, the bar is showing properly there. Minus the offsetting for the value. So let me see. Did I offset the value there, HP display? We have the texture progress bar, and for some reason, it's up to 00 here, but I want this to be negative 20 so that when the value is 50, it properly shows in the middle there. You can see right there this is like four out of 8 bars. And the value over here is 50. I must have unset that at some point, but the kind of hackish fix for that is you just make the min value more out to the left so that 50 ends up in the middle. Because if this is zero over here, then 50 actually ends up as, like, the third bar. See, I make sure your men value is negative 20 there, and that should work for the HP display. Okay, so final test here, we hit play progress bar being set to 50. I can undo that breakpoint, hit play. And we can see our health progress bar is correctly in the middle, right there. Our HP is showing us 50 out of 50. Let's get hit. You can see our HP drops, the UI immediately reflects that, including the progress bar. So all of our signals are hooking up correctly. We're getting the experience set there, and we just need to add in our leveling up definitions so that we can level up our character. Before we completely finish with the video on our set stats function, there is one flaw here, which is that if we ever reset the stats with set stats, and one is already set here, we'll end up with duplicate signals. So we want to say here, if Underscore stats is not null before we call set Stats, then we want to disconnect from the signals, so we can say underscore stats dot MxHphanged dot disconnect. Underscore on MAX HP changed. And same thing for the regular HP. So we just copy that and do SATS HP changed, and then on HP changed. This is more of just making it robust. I never intend to set the SATS controller to something different, but just in case you do, this will make sure that it doesn't cause a horrible bug where you have two signals connected for two different stat controllers, but on one HP display. Now, do the same thing kind of over on the EXP one as well. So if you look at the EXP display, I'm just going to paste that in and we change the signal name. So sats experience changed, and then on EXP changed here. Okay, so that's what that should look like. Underscore SATs experience changed disconnect on experience changed. Okay, so that should be good for if you ever changed the SATS controller. Just wanted to show that to be a little bit more complete. 40. Debugging and Optimizing UI and Pickup Scripts: I wanted to clear up a few warnings and errors. I've been getting in the console with the project so far. So if you've been copying the code word for word, you may have run into these same issues. So pickup two D where it says you can't use monitorable equals false, normally because it's blocked and you have to wait for set deferred. So if you double click on that, it'll point out that in the remove function, I was trying to set monitorable to false. Well, actually just going to remove the monitorable equals false here because when you Qu free, it's removed at the end of the current frame. When you call set deferred, it's removed at the end of the current frame. So making it not monitorable at the same time you free it is not really going to help anything. The point was to make it so that if two objects try to take it at the same time, that it would not pick up twice. However, what we could do if you want to be extra safe. However, if you want to be extra safe and pick up two D, let's give it a var picked. And this will be a boolean that sets to false by default. So I'm just implying that it's a boolean there. I mean, if you want to declare it for sure, then you can do that. So this is picked. And if we try to take when this is true, then we'll allow it. Otherwise, we'll return false. So what I'm going to do with Tr take is I'm going to make this actually guard, and then we'll have a separate take function, which will be what the pickups actually implement, like the virtual function, what they currently do. So I want to do function, underscore T, and this is not going to return anything, so that's a return type of void. And I'll take this push error and bring that down here. So I'm going to return true here now. I'm going to change this to be underscore T. So we call the virtual function, and then we're going to guard against picking up a second time up here. So we'll say, I picked, return false. Okay, so there's our guard. And otherwise, we get here. After we take, we do picked equals true. And I'm putting that in here so that we don't need to worry about that Boolean and the inheriting classes. We just need to worry about implementing the take function. So I think this is a better setup. It will work well with ir quarter, and as long as we set picked to true after calling we're sure that it won't pick up a second time because we have this guard here. So now we just need to update our other pickup scripts. So search pickup. I think we only have EXP pickup so far. So I'll double click into that. We're going to rename this to underscore Take the return type is void. And instead of returning false here, if stats is null, we'll push an error. So push error. Percent has no stats stat controller. That's going to be percent P target. So we replace this with a node name there. And then we don't return at the bottom. We just call remove. And we need to make sure that we match the parents signature. So I probably need to pass the P target to the parent script. Okay, so let's go up. And yeah, we want to pass that P target. So P target we'll remove the underscore there because we're actually using it now. And then in here we do underscore P target is a node two D. Now that should clean up the errors in our EXP Pickup two D. And just to verify, we'll go into play mode. Okay, then the other warnings, let's go to that. In most cases, these are just unused parameters. So, in the case of something like pickup two D, you see that P target is unused, so you just want to mark that with underscore to make it so that it's marked as being unused intentionally. Let's go to the next one here. Camera size was never used. So the camera size was used actually to create the camera rect. So it's actually not needed here because in this calculate final spawn function, we used for the spawner system. The camera rect gets created, and then the size is pulled from that. So we can actually remove camera size as a parameter, cut that out. And then where we call that up here, just remove camera size as a parameter as well, and we simplify it to three parameters. So that would be a good case where that warning actually kind of helped us. And then, next here, if you have an update that doesn't use the Delta, which would be the case if you doing move and slide because move and slide doesn't need to do the Delta, you set the velocity, and then move and slide automatically handles with the Delta. So we underscore the Delta and that's it there. And this drop script, the name drop here is shadowing the function name. So we could change the name here to something like drop instance. And then wherever we use drop under it, like right here, we say drop instance instead. You just want to make sure that if you use a variable locally, you don't have that variable also declared outside of the scope in the class base, like the local variables. And you don't want it to have the same name as a function name as well. Then in each of these stat changed signals, the callbacks for that, I was just refreshing whenever the value changes, and I don't actually need to use the P data for these functions directly. So I would just underscore both of those once more on the EXP display script. Click there and underscore that, and that should get rid of all the current warnings and errors. And I'll hit Play. Okay, no errors for. 41. Leveling System with Experience and Stat Progression: Health and experience now, we need to add in some kind of leveling mechanic, and we need to have a leveling stat on our stats controller in order to manage the leveling up of our player and possibly other characters as well, but definitely for the player. So we're going to go into the step controller, and I'm going to add in a new variable here. We'll say is going to be an integer equal to zero, and we'll have a similar setup to experience down here where we'll just have a satur value where we'll say I level is equal to the value we'll return, otherwise, we'll just repeat the same pattern emitting the signal for level changed so that we can respond to that in our UI. So we'll say var old equals level, and we'll say level equals value. We'll say var data is equal to stat changed data dot u object level old, and we'll emit a level changed signal. With the data. Now, we need to create that signal, of course, so copy level changed up at the top. We'll create a signal level changed data, stat changed data. So now we can subscribe to that if we need it. Now we can fully implement add experience. So whenever we add experience, we want to also check if we're ready for the next level. I put that in here rather than the experience setter because I don't want too many random side effects by setting the experience, so I want to explicitly call the ad experience method, which levels up if the threshold is reached. Okay, so to do that, we're going to need to create a level definition object. So let's create a new script for that. Everything will go into let's say characters. Right click here, do a new script, and we will call it level definition. So level definition dot gD. It's going to extend from resource, create. Double click to open it up, and we'll give a class name level definition. It'll extend resource, and this will define a level up for a character. So what we need here is going to be the experience threshold. So at Export VR, we'll say experience needed. And this will be an integer. We could default to something like ten. And we probably also want some stats that we're going to level up. So for right now, we'll just start with HP and keep it simple, but you would do extra stats here later on as you work out they became mechanics. So at Export var HP, we'll say that's an integer, defaulted to ten. And we could say this is the bonus HP for leveling up. So you hit this level, your character gets this much HP. Now, how do we apply our stat changes to the character? You could do it one at a time manually, but a smarter way would be to create a method to do it for you. So let's say function apply, and we will apply this onto a stats controller, let's say. So P stats of a stat controller. Then we'll return void with this. So we want to basically update the sets. So P stats dot HP plus equals the HP amount. And before that, we also want to increase the MX HP. So p stats dot MX HP plus equals HP. It's important that you do the MAX HP before the HP because the current HP is capped at the MAX HP. So if a full health character increases its HP by ten, it actually won't gain the health unless the MAX HP updates first. So important ordering there. And that can be our basic level definition for now. So in stat control, it would be a decent place to define our level definitions. So I'll say at export VR level up definitions. And this is an array of level definition. Okay, so to check for the level up, we need to get the next level in the sequence. So let's make a function to get our next level if there is one. So down here at the bottom, let's say function, underscore, get next level. Which will return a level definition or null. And this is going to return the level up definitions dot Git, and we're going to get at index level. So the reason we're getting at index level is because level is actually the second place in the array. So if our character is level one, we are currently on the index zero, and we want to look at the index plus one, which is level because level starts at one. Okay, so to handle our add experience leveling up, we first need to get the next level definition for the next level. So like the level plus one, we need to get that level definition up at the top. So we're going to at Export, not in a way, but actually a dictionary here. So at Export VR level definitions, we'll say. And this will be a dictionary of integers that are assigned to level definition. So the integer is the level we're trying to reach, and the level definition is the stat changes that would have if it exists. So before we level up or try to level up, we have to first see if there's actually a level defined. So at the bottom, we'll create another private function function underscore get next level, and this is going to return a level definition or null. So we're going to return our level definitions dot GET at the level plus one. Our current level plus one, we want to get that and we want to return null. If we can't find it. So if we return null, then we'll skip leveling up. So in that experience, let's do VR next is going to be a level definition equal to get next level. So if next is null, we just return or you could even push a warning. Actually, yeah, let's push a warning. Character cannot level up to percent a percent level plus one because that level has no definition defined. So we could say string of level plus one over here at the end and finish it with another parentheses. Okay, that'll work. So, we take the level plus one. We're trying to level up to level two if we're level one, and we're noting that we can't level up because that's not defined. So you might want to have that as a warning, or if you're sure you don't can just comment that out or remove. I'll leave it in for now so that we can at least see when we fail to set a level definition. So the next level is null, so if next is null, then we'll return because we can't level up. There's no next level defined. So we just skip altogether. And then we want to check if the next level experience requirement is less than or equal to our current experience. So we can put that as part of a tri level function. So underscore tri level up, and we'll say on the next. So we come down here and we'll have our function. Underscore. Try level up. Underscore because it's private, and we pass in the next level level definition. Or actually, you know, I feel like this should be contained within that function, really. So this is going to return a boolean, and we'll have this here. So we just get the next level inside of this definition. And then we don't need to pass in a parameter. We just try level up after we add XP, right? It's just like this. So if we can't level up, we return false. And then we want to check if the experience is greater or equal to next dot experience needed. If that's the case, then we'd level up. But I'm going to reverse that. I'm going to say if it's less than the experience needed, then we return false. Okay, so we're out of the if statement again. And then if we get to here, we've passed the guard checks, so we can say level plus equals one. We'll subtract the experience needed from the current experience. So experience minus equals next level. Oh, sorry, next, dot experience needed. And then we will apply the stats of the next level to our current stats. So after subtracting the XP, we do next dot apply to self because we're working inside of the stat controller script, and then we'll return true because we successfully leveled up. Now let me think about this. I think I actually want the level to increase after the stats have applied, like this. So if we're going to say, have a character level up, and then we give them the choices for the rewards. So picking an upgrade at leveling up, which is a core mechanic and our survivor like, we want to do that after the base stats apply so that the UI will still update basically at the same frame, but just before we level up and emit our level changed signal, thus causing the UI to react and show us our upgrade choices. I think that's important ordering change there. Okay, so yeah, then the level of change signal is going to get emitted up here because of the setter. And that is basically our leveling up. If for whatever reason, you want to get the current level definition, that might be useful outside of the script. And this one might even be useful outside of the script. So I'll take Get next level, and we'll remove the underscore for pivting move that underscore and try Next Level two. And let's add in a function G current level. Definition or maybe just get current levels clear enough. And then we'll return level definition. So we will return the level definitions dot GIT, and we want to return the level or null. Okay. So those might be useful for other scripts that want to look into what's going on with the stat controller. So it makes some sense to make those public. By not underscoring it. That's why. Okay, so we have this stat property, and we can level up whenever our EXP increases, which should be automatic because this ad experience is going to get called from our pickup, our EXP pickup. So everything is going to flow nicely if it's working good. Except now we need to make sure that we're connecting to the level stat and the level up signal or the level changed signal, rather. And we're going to do that inside of the EXP display. So we need to look in a project for EXP and scroll down, find our scene xp display dot tSCN, open that up. Okay, so now that our leveling should be working, we need to go into the EXP display, and we need to make sure that it is showing how much EXP we need for the next level. So let's go in here. And when we refresh, we want to get the next level XP. We want to change this bit right here to the Mx XP of the stats if the stats have a next level that we can actually access. So you get rid of that bit and say VarnNxt is going to be equal to underscore stats dot next level. So we get the next level, and if there's no next level, then we'll say I next is equal to null, then we want to change the label dot text to be equal to, let's say, our max level string. Okay, so we need to define that off here at export var max level string, and that'll just be equal to something like max level in all caps. Okay, else, we're going to set the text to our EXP, our current number out of the next level number. So we've confirmed that next is not null, so we can replace this bit with next dot experience Ned. Now if I run the game, it will probably just say Max level because we haven't defined any level definitions. If we're at MAX level, then I also want to take the progress bar and set that value to 100. So now if we run the game, currently, that's going to give us an error because I haven't set the progress, so let's do that. So in our XB display, go over to the right and assign the progress bar. Okay, now we can run, and we should see a full progress bar. We're at MAX level. Okay, so picking up XB no longer matters. I can still good experience, but it's never going to level up anymore. Because level definitions are assigned in the editor, not during gameplay. Although you could do that, but that would be pretty weird, right? Like just defining end game that you can level up to now level 31, where before 30 was the cap, no, that's something you do on the editor. 99.99% of the time. So we have the progress value being set here. We're going to want to get a percentage between our experience and the next experience needed, and then we'll update the progress value here for when we can level up. So our progress is afloat and we'll set that equal to next dot experience needed divided by underscore stats dot experience. Now remember, we're dividing an integer by an integer, but we actually want it to be a float. So we need to take one of these and cast it as a float. So say float. And then on stats dot experience, we finish wrapping it with our parentheses. So we do that, and this should now give us somewhere between a 0.0 and a 1.0. But this needs to be basically converted into a percentage because our progress bar by default has a max value of 100, so we're looking at percentages. Either that, you need to scale the max value down to one. Both are actually valid. So here we can just wrap this with another parentheses and times it by 100 and that should give us our actual progress. So we're still never going to hit this because we need to define some level up definitions. So let's go into the player, and we'll click on the stat controller. We have our level definitions here. So we can add some dictionary key values. We want the next level of two to be defined here. So add two and a new level definition. Inside of here, we can just say XB needed ten HP ten. That's fine for starting. Add the key value pair. It's important that you remember to hit this. Otherwise it won't be in the dictionary. So we have our dictionary with one key at level two and a level definition to level up our character. Okay, one last little tweak we need to do is to make sure that our player defaults to level one. So let's go into the stat controller and where we have level. We need to make sure this is a one. So that way, when we check for level plus one of two, we'll find that level definition, and it should display properly in the UI. So let's hit Play, and we'll have our XB needed here. The progress bar didn't update, but let's see when we get an EXP, that goes up, and it's showing out of ten. And yes, simple error. I forgot to assign the progress to the progress dot value. So maybe it actually makes sense to change this to a different name. We're shadowing a variable. I bet if I check the errors here, you can see that yeah, I shadowed this with the progress texture bar name. So let's say percent leveled would be a much better name. And then we'll say progress dot value equals percent leveled. And now I'm pretty sure it should work. Let's Play. Go in there. We can see why EXP didn't love up there, and now it's saying one out of ten. That's interesting. So I need to set a breakpoint here to see what this showing. And let's go into play mode. And we can see it's saying 0.0 right now, but let's pick one up and we get 1,000. Another simple logic mistake, I should be doing the experience divided by the experience needed. So we reverse this. I'll take the experience needed and put this over here, and then I'll cut the stats dot experience and put this over here to the left. We could still make the float casting on the right. That's fine. Okay, so let's play once again. Test that? So, okay, now it's showing 0% leveled up. We pick one item up. We get 10% there, two, three, but it should be going all the way. So I want to check the progress stop value here and see why that's occurring. Okay, so here we have 40% leveled up. So the percent leveled is right. So we might actually need to check the UI outside of the game. So let's go into the EXP display here. I'm going to test setting the value to zero. Okay, that's correct. 100 is correct. It goes all the way to the far right. Let's say 25 50, 75. Okay, that looks correct. So let's take the value to zero. All right, play. Let's test that a little bit. Okay, remove the breakpoint. Go into play mode, and let's do one, two, three. So that is 30%? Maybe it is correct, actually. Okay, so we get four out of ten and then five out of ten. Okay, yeah, it was working fine. Let's get seven out of ten. Let's actually just level of our character to prove that it works. Okay, so we get nine out of ten, and then we have the skeleton spawning. We can get one more, ten out of ten. Okay, you saw we just got ten HP here, then we went to zero out of ten for the EXP needed. So the part about the next level should be returning something, but it didn't. So the reason that occurred like that is that our experience gets set to zero. It emits a signal, and then our level changed doesn't connect here. So after the level is changed, it doesn't refresh again. Okay, so the simplest solution is just underscore stats dot level changed, and we connect on level changed to refresh again. So we can copy this up here and disconnect it. So we'll create our Callback function at the bottom function, underscore on level changed. We're not going to use the data, so underscore P data. Stat changed data, we return void, and we will just refresh. Okay, that way, after the experience gets set to zero, right before we increase our level, that will refresh the UI, but then the level changes and we refresh the UI again. So let's show one more time that it's all working as intended. So I just will get enough experience to level up here. So for and then, five, six, seven, eight, nine. Skillsins don't actually drop anything yet. We'll fix that soon. And then ten. Okay, so now it shows max level, but we also increased our HP. So that refreshed our UI twice. Once when we changed our stats, decreasing the experience and another time when we increased our level, which now will show the next level does not exist. So we show Max level at our EXP bar as we would expect. So everything seems to be working. 42. Setting Up XP Thresholds and Signal Connections for Level Up Rewards: Point now where we would want to add in a rogue like upgrade system. So we level up our character when our character hits a certain XP threshold, and then we get to select from one of three options for how we want to upgrade our character. And this is chosen from a random list of available options. So for convenience's sake, before we get into this, I want to change the XP requirement, fitting Level two to one, just so we can trigger it very quickly. So if I go to the player SAN, we'll open up player dot TCN, and then let's go to the stat controller, I believe was where we were defining it. And the level definition, we have the first level here set at ten XB needed. Let's change that to one real quick. And then maybe I'll add in a couple extra levels. So we can do a Level three here as the new key, and then let's do a new definition. We'll just make that one need ten. So we'll add that key value pair. You can add as many as you'd like here. So this is basically just a giant list of the levels we're trying to set over here on the left and the stat level up definition over on the right. So level four, we can make, I'd say, 20 XP needed, and it boosts HP by ten. And just keep going as you'd like. So level five, let's say 30 X P and L level ten. I'll just kind of flesh this out so I don't really need to worry about it much later. Let's do level seven. So new level definition. We'll say 50 XP, add key value pair, seven, and we'll do a new level definition at 100 XP and add that in. And I think that should be good mostly for our prototype having seven different levels. Is definitely enough to kind of test things. So we have those level definitions set up on leveling up, it's going to emit the level changed signal from our stat controller. So we want to connect to that in our new UI, which is going to provide us the option of choosing one of three different items or level up features. And we want the game to be paused while our player is making that choice. So let's create a new UI. I'll add a new control node here, level up selection, I think is a decent name, and we'll add a new script to this. So level Uselection dotGD. We'll save that in the UI folder, of course. So UI, and then open and create. Okay. Also, control as to save the scene next to where the script is. So that's also on the UI folder. I'll just save it there. Okay, now we'll kind of start working on the script. I'll do class name level up selection. Generally, unless something is very specifically going to be a one time use script that nothing else needs to reference, I'll almost always create a class name, if that wasn't clear by this point. Just having a referenceable name makes working between scripts a lot easier. Okay, so the quickest way we can get the reference to the player's stat controller is going to be you guessed it. The player context. So let's add export var, the context, which is going to be the player context here. Control S to save that. And then in the inspector, we're going to quick load the one player context of our game, and that basically gives us the reference to the player and thus the stat controller. Okay, and then in function underscore ready, we want to go to the context dot player dot stats dot level Chang and connect to that with on player level changed, which will be a new function we create right below. So function underscore on player level change. And this is going to have the level change data object. So P data is what do they call it Stats changed data. So we only want to actually provide the UI if the levels actually going up. There may be some weird circumstances where we decide that the level could go down in the future. So to guard against that, we'll say if P data dot change is greater than zero, that we know it's leveling up. Okay, so when the level changes, we only want to respond to it leveling up, not leveling down. So we're going to say, if pdata dot change is less than or equal to zero, then we're just going to return here because it's not a condition we want to handle. Otherwise, we're going to level up the player. Now, for right now, I'm only going to write the code to handle leveling up one at a time, because in the context of the current game, that's the only way it would be possible. So I'm going to write at least the prototype of the script to only handle leveling up once at a time. If you for some reason picked up a 1,000 EXP object, and then that would trigger three or four levels up, that might be a condition you want to handle later on. But I think that's a little bit unnecessary here because we're only going to be picking up one XP at a time anyway. But for this script for now, we're just going to be handling one level up at a time. So let's create a function here. I'm going to call it start selection. And then this function, which I'll put above function start selection is going to provide us with a three options. It's also going to show the UI. So we can just start with a show call here. And then we also want to pause the game so we can say get tree dots paused, equals true. Okay, so since we're pausing the game here, that means it's going to pause all scripts that are set to only be active when the game is not paused, which is the default. So over on the right here under node process, we need to change the mode here to I'm thinking always so that it can always trigger stuff inside of the game, regardless of if it is paused or not. Because we also want to be able to open the script when the game is unpaused. So being able to call start selection during an unpause game will be important. So I think making this always actually makes sense. But we definitely want it to run while the game is paused. Later we'll need to come in here and generate a list of rewards that we're going to show to the player. We'll need to set up the UI a bit for that first. And we also want to make sure that this is tested so far so that it's actually popping up. Let's right click on the level up selection and add a couple UI components to it. So I will start with, let's say, a panel container, and that'll let us see it when it pops into the screen. Also, click on the root UI. And then we're going to change, let's see, layout to top left and capreset over here. So right there. I also want to make sure that mouse does not stop the clicks from going through, but we're actually going to ignore it for right now. There will be things we want to click on the UI such as buttons to make the selection, but we don't want the UI root to block those mouse and port signals. So let's see if we can get this to pop up on leveling up. Let's go to the root and I'll add in the level up selection. So level underscore up, and we'll put this under the canvas layer. Let's temporarily show the UI. Okay. And we're going to want that to actually be in the center of the screen. So take the level up selection and go to layout in the top right, and we're going to center it over here. So center and if we zoom out, then this should theoretically be in the center of the screen. So let's go hit Level two and game, and we'll see if that is actually working. So So I'm going to go over here. The game did pause. I don't see it showing up, but that is a good start. So at least we know that it's triggering on Lavote. So I'm thinking what might be causing the issue here is the Canvas layer scale is set to four. Let's make that one like that. Okay, and then I want a child for the Canvas layer, which I'll actually use for the scaling. Let's right click on Canvas layer and add in a control node here. So I'll take the other nodes, and I'll make the root. So this node will be the UI. Okay, we're also going to need to take the UI control, the root here that I just created. And we're going to take the anchors preset and make that full rectangle. So now that's going to stretch to the size of the viewpoint, we want to make sure that mouse is set to ignore so that this doesn't block UI signals. Okay, and then if we have the level up selection here set to the center, we can actually see that's the real center of the viewpoint. So here's the root UI control node, and then this is in the center. So if I hit Play now and we go and pick up a XP, then this would show in the center, though it was showing bit default. So I got to hide it first. And now let's hit Play. We go here, and you see it pops up as well as pausing the game. Okay, so that script's working. We need to resize our HPUI up here again. So I'm going to actually click on the UI here and I'm going to increase its scale directly. It might just be the most appropriate to scale the individual components rather than applying a global scale to everything. Then we can just size our play up selection appropriately. So another advantage of having the UI here is that if we want to apply a theme to everything under is that we can just go to theme in the bottom right and then quick load the game theme. So now everything will be using that Pixar font, even if we don't assign it directly to the level up selection. So sometimes you do want fine grained control over the individual themes. If you want a custom theme for, say, the level up selection, then you would just create a new one down here. And I think at the lowest levels, it overrides whatever is above it. So you can basically have a hierarchy of themes like that because not everything needs to override every single property as well. You can have a main theme, and then for very custom stuff, like how a slider bar looks, you can make that like a sub 43. Building the Level Up UI Layout and Animated Text: We're going to work on building out our UI template for the level up selection. So we already have a panel container. I think we should stretch this out, so I'm going to pull on the bottom right hand corner and move this a bit bigger. We're going to need a much bigger UI to actually show our selection. Now we're going to need a header. Maybe we show a graphic. Like, congratulations. You leveled up something like that. Then we need three column displays. Those will be VBox containers, which will show our options. So that'll be probably like a title text, a icon, a description, and then a selection button. And once the selection button is pressed, then the level up selection will process the selection, apply it to the player, close the UI, and resume gameplay. So let's right click on our panel container, and I'm going to add a margin container as the next level, add a child node. So we're looking for margin container. And then in theme overrides on the right, I'm going to take these margins and set them each to five pixels for now. That'll just make it look a little nicer so everything's not as clumped up together. Let's right click on the margin container and now add a VBox container. So we can organize everything else vertically. You can already see the margin container, kind of spreading the edges a little bit to the sides. We want to add to this probably our title text. So we can add this as a rich text label. Alright, click Add a child Rich Text label. The reason for making it rich text instead of a regular label is that rich texts, you can do things like BB code, which is good if you want to apply a rainbow effect or waving the text up and down. So it's just more powerful than a regular text label. I'm sure it technically has, like, a resource cost associated with it, but a few text animating on screen is not going to lag you. So let's put it in here level up as the text. We can see it's not really popping down here. So we need to take fit content over and check it to true. Now our text is going to show up in the center there. We also want to center this text, so we'll change the horizontal alignment here to center. Okay, now it's centered. Let's put an icon beneath this so we can show a graphic four hour level up. So I'm going to right click on VBox container, add a child node. Let's say texture rack. Add that and we can select a texture. So any graphic we want to use from our project will work. Let's see if we can quick load something. So if I search, let's say 64 by 64 to get the highest resolution texture, let's grab from those icons, one that we want to use and then scale it up even more. So I actually wanted to use this as part of an atlas texture. So if you did select it, right click it, then go to Copy. And in the dropdown up here, do atlas texture. Click on atlas texture, and then right click paste the atlas end. Now we can select which icon we want to use from this. So just anything you think looks good, there's a lot of options. And this is the 64 by 64 texture sprite sheet from the icons Pack. That was an obvious, so one of the three art assets we have for the project. So something like these twin swords or the shining sword could work pretty good. Anything that would be like a glowing human figure, kind of implying, Oh, I level up. I got super strong, like, for instance, specifically like this, or in the base project, I used this one, which is more of a lightning, but it could also work. Okay, so this time, I'll go with the one where it looks like a giant beam of light rather than the lightning. And I'll hit close. So we have our icon showing in the texture rect. The stretching isn't really how we want it, so we want to change the stretch mode to keep aspect centered, I believe is the best option here. And that'll work pretty okay, I think, following our level up icon, let's add in a text block, which we'll say to the player, we want to make one of a few selections. So let's right click on VBux's Container, add another rich text label in and then we'll do something similar. Check fit to content. And then for the text, we'll say, pick one of the level up options below. Since we do intend for the Pixar font to show up here in our actual display rather than go over to the world every time, to check how it will look with the actual game theme, let's go to the base UI node level up selection, and just assign the theme here as well. So in the inspector, quick load the theme, and then that will update our text. So now you can see that with this different font, the text actually fits on one line. That's a big difference. Let's click on the Rich text label two, and let's center that. Okay, next, let's take this title and make it a little more interesting by adding a rainbow and a wave effect with the BB code. So check BB code enabled over here and go to the start of the text on the top left. We're going to put in square brackets for BB code, rainbow and then space FREQ for frequency equals 0.5 space SAT equals one. That's the saturation. Space value equals one, and then end the square brackets, and you'll get a rainbow effect over here on the left. Now, to make sure that that ends, you go to the end of the text, and then you do square brackets Rainbow. And there we have our rainbow effect animating in the viewport. And we can also change the settings here. So if you want it to be less vibrant, try taking the saturation to 0.5, and now you have a more tame rainbow showing up there. I kind of like that, so I'll keep it. Now, let's also add in the wave effect. So hit home again to go to the start of our textbox, or you could just click up there on the top left. And then do another square brackets, and we're going to be typing in WA VE for wave space AMP for the amplitude equals 50. That's how high it goes between waves. And then space frequency or FREQ equals two. That's how often it's going to do a wave, and then end it with the right square bracket. Okay, and then you want to go to the end after the rainbow up here and do square brackets wave. That just means that if you add extra text after that, it's not going to have the wave effect because we ended the effect in our text block. So Control Z, we can see our text animating there. That's pretty neat. We might want the amplitude to go down a little bit, so I'm going to shrink it to 30. So if we watch our animation, we can see that the text animates pretty much to the bottom or the top of our label area. I think if we have an amplitude of 50 like we did before it even gets cut off. So that's not ideal. If it's too much going up or down, we can add some padding to this specific label by going to theme overrides. And then we want to go to styles normal and do a new style box empty because there's no background. Expand that, go to content margins, and then we can make the top and the bottom, a few pixels offset. So I'm going to try making the top negative three and the bottom negative three. And then what we can do is add a content margin here to the top and bottom. So I'll make it like a two pixel on the top and two at the bottom. Okay, and that is going to basically stretch our box to be a little bit bigger than it would have been by default. So that's another fix if you didn't want to just add a whole bunch of new lines in the text box. I guess that would also be another quicker option. But a little bit less precise, as well. So, finally, for this, if we'd like the text to be bigger, we can go to theme overrides and customize the font sizes here. So I think we want normal font size, and let's boost it to 24. Okay, that one actually looks a little blurry. Let's try 32, right? Okay, so I think for this specific font because it's Pixar there are very specific scale numbers that you have to set the pixel size to. So if I said it's something like 22, I guess it's not like a multiple of eight or 16, so it's not going to work. So let's do 32 instead to have that there so that it renders correctly. These overrides, by the way, if you want it, you could also spend time and customize it and the themes specifically. But if it's just going to be a one off like this, it might just be quicker to assign your custom values to this header specifically. Only if you're going to have a lot of UI across the game and you want to customize it in a centralized place, then doing a lot of theming makes sense. Rather than just using it for fonts, you can also customize the font sizes or add custom header 44. Designing Reward Selection UI and Final Adjustments: Alright, so now we need to create our vertical selection boxes. So that's going to be a sub UI. I'll go create a new node up here. This will be, let's say, it's a user interface, but I'll right click here and change its type to a panel container. We'll just make the root here a panel container. And this will be a full size by default, expanding everywhere. Let's rename it to be Reward selection. And I'm going to save it in the UI folder. Okay, now let's go to Lavop selection, and I'm going to put three of those in the hierarchy. So search for reward in the project and drop one of those onto VBox. I'll duplicate it a couple more times. And you'll see that the immediate problem is that there are three, but they're all vertical. So we need another HBox container to make them go horizontal. So right click on VBox container and add a child node HBox container, and put the three reward selections under the HBox container. Now you should see if you click on the HBox container is that they each have a offset position from each other. But right now they don't stretch across the screen. So we need to go to reward selection, and it's going to be layout, container sizing. We want expand on horizontal. And while we're at it, let's just do expand vertical as well. Control as to save, go back to the level up selection. And now we can see that they're kind of in a column sense. They each take even spacing of the space from left to right, and it fits our main panel container. So that's more like what we're looking for. In reward selection, let's right click on the top and add a node. So this is going to be a Rich Text label in the Rch text label, let's put the text. We'll say square bracket name, and I will check Fit content. We can zoom in here to see it. So for the reward selection, I'll click on Reward selection, and let's take the anchor preset and let's take the anchor preset and do top left, okay? And then under here, we need a margin container. Okay. So if we look here, so if we look here, there's no spacing between our labels and the edge of this panel. So let's add a margin container under the reward selection. Right click Add child margin container, and then make the Rich text label a child of that. The margin container will do theme overrides constants, and let's say maybe three pixel for the left, top, right, and bottom margins over here on the right. Now let's take the root reward selection, and I'm going to change its anchor preset to, let's say, top left. But let's also take the reward selection and stretch it out a little bit so that we can at least see what we're working with here. We don't need it to stretch the entire screen because that's misleading, but we do want to be able to see our vertical content. And we can go to level up selection to see how that's going to look at any point after the resizing and this UI container. So so far, it looks good. We want to center that title, though. So I'm going to click on Rich text label, and let's do horizontal alignment Center. Also, for the vertical container sizing here and the control, I want to do Shrink begin. So that'll make sure that it doesn't expand any further than it needs to. If we go to level up selection, we should see our centered labels here. Okay, now we need our icon placeholder. So I'll right click on Margin Container, add a texture rect, and then we can select an icon from our project. So let's do new Atlas texture. And here, I'm going to Quickload an atlas icon from the 16 by 16 pack. Let's do dit region and select a placeholder. So something for leveling up, something like this looks pretty okay. And I'll hit close. Now we need to make sure that this is actually organized properly, so we're going to need a VBox container under the margin container. So right click on the margin container, add a child nailed VBox container and make the title and the texture a child of that. Okay, now it'll organize properly. We need to make sure that this keeps its aspect ratio, though, so it doesn't look weird. So in the inspector for textuct change the stretch mode to keep aspect. Also should be keep aspect centered. Okay. There. That's looking more appropriate. We may also want to go to layout and set a minimum icon size. So here, I'm going to take the custom minimum size, and we'll say something like 48 by 48. Okay, that will kind of expand that. 32 by 32 is fine. So that just means if your icon is smaller than 32 by 32, it'll automatically scale up to that size. Okay, let's add a description text, so I can control C the rich text label and then click on VBox container, control V, paste it in. And we will put in square brackets description here. Obviously, we can see that that is pretty big for this area, so we may need to work on our font sizes later. And then, lastly, we want a button to select the option. So I'm going to click on VBox container, add a button. So here we're probably just going to want a regular button. Which does allow you to have an icon, so we can quick load a test icon, same way as we did before, at less texture, expand it. Quick load from the 16 by 16 sprite sheet. Do Edit region, and pick an icon out. So maybe something like the sword with a level up symbol makes sense. Okay. Now we can see our button as an icon. And then in the textbox, we could say choose indicating what we're doing here. Now if we go over to our level up selection window, we can see our three parts of the UI show up down here, we have three buttons that we'll hook up to choose from later on. We have our animated title. And that is more or less what our template is going to look like. It's just a matter of providing the options for selection, hooking them up to our player, and then upgrading our player once they make a selection. It's just going to be a matter of populating the list with what options are available and then applying that to the player once the player makes their selection. Okay, so you might notice there's a little issue where sometimes the edge of our icons kind of overlap with possibly the neighboring icons. So to fix that, we can go to Reward selection and check filter clip. For the button. So when you do that, if you go over here and you zoom in and out, that should fix that issue right there so you don't see any more overlapping pixels. Also, for the level of selection in the world, it might be technically centered here. But you can see that it's going to the down and to the right from that center point. So it's actually not going to appear centered on screen. To edit that, I think it would be a good idea to take our Canvas layer and right click save it as a new scene into our UI project. So I'll say gameplay UI Canvas as the name, and then we'll open it up. Okay, now, if we look into Hue, we can much more clearly see where our objects are positioned on the screen and their size relative to the viewpoint, rather than having the cluttered world view. So we can see, of course, that our player selection, UI, we need to click on that and probably double the size. So in scale, I'll just take the scale and make that a two. That might even need to go up to three. Yeah, let's choose three by default. Okay, so to handle this issue where our panel container basically goes to the right and down rather than being centered here. We want to jump into the love up selection scene and then go to panel container, I believe. And over on the right, we need to take the layout mode to anchors and then center it here. Yeah. Okay, there. Now we have what we're looking for. So this basically makes everything that's a sub object centered on that anchor point, where if we go to the Canvas UI now, it's going to be centered properly. Okay, really quickly, let's jump into the script for Llevo selection. And on ready, we want to hide our level up selection. So I'm going to type and hide here. And that'll make sure that when we start the game, it'll hide by default. Let's hit play. We're going to LeveOp by picking up EXP. And there we have our LevelUp selector pop up. We have three buttons to choose from. They don't do anything yet. We may decide later we don't want to pause the music, like what just happened, but aside from that, everything's working. The game's completely paused and our UI is showing. 45. Creating a Weapon Leveling System for Game Upgrades: For our level up system to work, we actually need some upgrades to populate the list here for our level up menu. So generally, that is going to be weapon upgrades. So we need to set up a leveling system for our weapons as well. So if we look at the player scene, we have the default spear weapon, and that has a weapon definition. So the weapon definition is kept really simple right here for now, but we're going to need to add in a weapon upgrade. But we'll add into here an array or dictionary of weapon upgrades so that we can progress to the next level and actually get how much damage this weapon is supposed to do at any given level so that when we launch our weapon, it's using the appropriate settings. And that can also include things like the cool down time or how many targets a weapon can hit. There's a lot of different properties that you can set up for your weapon system. So if we jump into the spear weapons script, we can go in further to weapon definition. You can see in here, I already templated a get cooldown method, which is going to return based on the level, the cool down for the weapon. So in here, if we define our array, and then we pass in the actual level of the weapon, then we'll be able to retrieve the correct set of stats for our weapon at its current level. And then we'll also need to generate a get seen method as well. So currently, this is just loading the spear scene directly. But when we are upgrading our weapon, we may add in the option to have completely different projectile scenes spawn but we don't necessarily want to do that at every level, so we'll be able to make it default back to the original weapon projectile and just upgrade the stats instead. So let's get to that by creating something like a weapon level or weapon level definition. So I'm going to go to the file system, and we'll go down to weapons. Let's right click and create a new script here. And then I'll call it something like weapon level, which will itself be a resource. So let's create that. Open up the weapon level script. We'll go to the top and create class name weapon level. So this defines the stats for the weapon at this particular level. So one weapon can have as many levels as you want, and it'll progress in power as it upgrades. So we can put in some stats here that we would want to be able to access for a different level of the weapon. So one obvious one would be the cool down. So we can say at export var, cool down, and this will be a float, and we can default to 1.0. Okay, so let's save that real quick. I'm going to close all my other scripts here, so right click and close all so that we can see what we're doing. Okay, and then open up weapon level and weapon definition here. Okay, so then those would be the only two we're working with up here. So we want, I would say, a dictionary, if we want to be consistent with our player levels. And at Export VR, levels, which will be a dictionary of integer and the weapon level. So for Git cooldown, we want to get the level, and then we'll return the cooldown from the level stats. So VR level is going to be equal to levels dot Git and we're going to be getting the P level. And return null by default. So now we want to remove the underscore for the P level because we're finally using this variable. Now, it can't infer the type here, so we do have to give it the concrete weapon level type, and then we're going to return level dot cooldown. Okay, so right click into weapon level and do Loup symbol. Okay, make sure that's actually there. So in get cooldown, it's going to find the weapon level. If it can't find the weapon level, it's actually going to error here. I could see us re using this line a lot. So I'm actually going to also put down here a function Git weapon level. Which is going to take a P level integer, and this is going to return a weapon level. So we can just return this. And then instead, what we can do in here is say, get weapon level. Pass on p level dot qual down. Now, one good reason why we would do this is that if we want to later add in some code, let's say there is no weapon level and we want to default to the previous level, or we want to portion error. We can do all of that inside of this function G weapon level function. And then in all of the other places we call Get weapon level, all of that extra code is going to be pre handled. So this just kind of sets it up if we need to do function get damaged, function g max hits, et cetera, then this will reduce our code later on and centralize it in this function. But for right now, especially when we're doing prototypes, I think I definitely want to know immediately if I forgot to set up a weapon level where we would expect there to be a weapon level defined on the weapon definition. So I'm actually totally cool with it just throwing an error as soon as we try to call dot quo down on a null weapon level. So that's why I'm going to leave it like that for now. Okay, now let's jump into weapon level and we'll set up some other properties we want to modify per level. Another obvious one would be the damage that a weapon can deal. So at export var damage, and we might actually do men damage to MAX damage if we want it to be a little more interesting. So if we say at export var men damage, and let's say that's a float, we can default it to 10.0, and then we have at export var Max damage, which will be a float. And let's just set that to 20 by default, whatever numbers you think is a good starting point, you're going to adjust. Pretty much always for each weapon level resource. As I've been mentioning, we probably also want maximum hits. So at Export V max hits, and that'll be a integer. I'll set it equal to 1.0. Oh, also, the men damage and MAX damage, I forgot we are dealing with integers in the game, so I'm going to change that to integer. So I'm going to change that to integer in both cases to be consistent. Of course, you could use floats in combination with integers. But I think in this case, when we always want the damage to not be a decimal, it's just better to be consistent across all of our scripts. Okay. And then, likewise, maximum hit says integer should not have a decimal point there. We're probably going to also want a speed variable here somewhere. So export var speed, and that will be a float. I'll set it to 100.0 as roughly pixels per second there. Then a quick comment there, as well. So now we have a bunch of properties. We want to set up the resource in our spear weapon definition. Okay, so let's zoom out, click on the spear weapon. Over here on the right, we have our weapon levels, so we set them up. So the level corresponds not with an array position, but the actual level of the weapon. So we want to start at one here. And then we want to do a new weapon level. Okay, so in our weapon level, this could be our base sets. Why don't we just add the key value pair, and that'll be the default for our level one spear. Let's add in two and then a new weapon level. And we want to increase the damage here. So I'll say 15 to 25, and maybe the speed is a little faster, so like 110 for the speed. The cool down, we can launch a little faster, so I'll say 0.9, and MAX hits will set to one still because adding an extra Max hit is a pretty big deal. That could be like doubling your damage effectively. So let's add that. And then let's do level three, new weapon level. We'll say 20 to 25 damage, the speed, maybe 115, and then the cool down sti 0.9, but we increase the max hits to two. So you can kind of get the idea here. We almost always want some of the numbers to go up. I should feel every level is an upgrade over the previous one. We don't necessarily have to change the upgrades on a linear factor. So, for instance, one level could be a massive damage boost, but the speed of the projectile stays the same. Anyway, add that, and we keep going as many levels as you want. Okay, so to keep going, let's do four. We'll put a new weapon level in here, and it might help to expand the previous weapon level so you know what you need to upgrade. So we start at 20 men damage. Let's make it 25 now and then 30 for the MX damage and the speed, 120, the cool down 0.8. The max hits two. Add a key value pair. Just keep going. Okay, and I just added in a fifth one for fun. So that one, you can see the stats right there if you want to copy. One key stat I actually forgot would be the number of projectiles spawned per cast. So let's do export var projectiles, and that'll be an integer equal to one. So the number of projectiles to instance per cast is a very strong property. So if we wanted a level to be extremely strong, then we could say, maybe level five. We have two projectiles per cast, and that will make the weapon deal a lot more damage for. Now, we don't want the projectiles to spawn at exactly the same time in most cases. One way would be to offset them so you can see where they're going to spawn at. Another option you could add in would be to delay after a cast occurs, how long before each projectile spans. So you could say, let's actually add both in as an option. So we could say at Export var offset or span offset. It's going to be a float. It's going to be equal to zero, zero. And then at export VR, spawn delay is a float, and we'll say 0.05 seconds. So that's going to kind of look like that. Spawn offset is the maximum offset for the projectile instant span position. So how far away from the center point can it be when it spawns? And then the span delay will be the time and seconds to wait between additional projectile spans, and I should say, after the first, that's the key value there. Okay, so in the next video, we'll work on taking these weapon levels and making so we can actually select to level them up inside of our UI. 46. Weapon Items for Leveling Up Weapon Power: Okay, so for the next part, we need to introduce the ability for our level up selection UI to get the item for the weapon upgrade so that we can level up the weapon that's actually active on the play. So the first step of that is going to be to create an options list or resource inside of our level up selection that we will use to populate the options down here based off of a random selection from the available viable options. I see here on the bottom left that projectiles is in the levels folder. I'm actually going to move that to weapons that just makes more sense. So, since there's nothing in the Levels folder, I'm going to rename that to be settings because what we're really doing is setting up a list of weapon upgrades or other upgrades that we can choose from in our game level. And we might actually differ this, depending on the character or we might differ this depending on the level that we're loading into our game, but we want to have the option to customize it either way. So we're going to create a new script, and I'll call this leveling options dot gD. This will be a resource, so we can edit it in the GIDOEditor. And then I'm going to go into here. And let's say class name leveling options. Earlier on in the course, we introduced pickups. Now, those pickups only exist currently as a scene in the game world, it gets picked up and immediately the EXP is added to the players. So we haven't had the need for a item specifically because that EXP only modifies a stat. But if we want something more complex, like a health boost or the ability to level up a weapon, then we need a more concrete item class inside of our game, and pretty much all RPG type games are going to have it at some point. So we want to at export a list of items that we can immediately use with our player. So I'm going to do export for items, and this will be a array of let's say item for right now. Now, we haven't created an item class. So let's create a folder in our bottom left for items. I'll right click, create a new folder. I'll call this items. And then inside of here, I'm going to right click, create a new script. And this will be item dot gD extending from Resource. Okay, so inside of here, we'll do class name item. Specifically for a survivor like game, we may not necessarily have items that exist in an inventory. So they still need to apply immediately. But how they apply is more complex than just the EXBPickup being an immediate assignment of experience. So to make it so that we can apply our items to a player or a target, at least, I'm going to create a function. We'll say function try apply, which is going to take a P target, no two D, and it's going to return a Boolean. So try apply because there might be circumstances where it fails, and it might be helpful somewhere in our code to have that feedback of if it succeeded or we may also want a can apply function so that we can check ahead of time if it's actually viable or not. So I'll say function can apply, and this will pass in the target, no two D and return of booleans. So by default, I'll say return false here because this is a base item. We can't apply it, and I'll just say return false here as well. So I'll just put it in a warning that we can't apply the base item to a target, and then let's create our weapon item because that's what we're most concerned with first. So I'm going to right click on items, create a new script, and this is going to be weapon underscore item, it's going to inherit from item as the base class, so we'll create. So weapon item here, we'll go to the top and do class name weapon item that is a node two D. This is going to return Boolean. So we need a way to access the weapons loadout of the character if it has one. So a simple way we could put it would be far loadout equals p target dot find child, and we're going to be looking for weapons loadout. Okay? And that should return a type of weapons loadout. So let's actually move that into its own function. So function get loadout it's going to return weapons load out. And then we're just going to return this instead, return ptarget dot Fine Child weapons loadout. So we need to pass on the Ptarget here. And then we can infer the type. So I'm going to do colon equals, Get loadout on PTarget and then we return whether that exists or not for now. And then we want to check if the loadout has the weapons definition that we're currently trying to apply. So we could do at export VR definition. And this is a weapons definition, a weapon definition, rather. Okay, after getting the loadout, we want to check if the weapon exists on side of the loadout. If it does, then we're going to see if there is room to level it up or if we've already hit the level cap. So inside of the loadout, we need to know what weapons are actually under it inside of this system. So on ready, we can get all of the preexisting loadouts, and then we'll add a method so that we can add a new one onto it that will append it to the array, keeping reference to all of its weapon children. So let's say var weapons here is an array of weapon two D, and on function underscore ready, which returns void, we want to get all the children that are of weapons type. So for weapon in find children, and empty quotations for the pattern string were not matching by name. But for the type, we're going to do weapon two D, and casing matters here because that is the class name type. So this will only return children through of type weapon two D. And then we can say weapons dot Penn weapon. And now when the script starts, we have all the pre existing weapons. We could say we have a function add weapon function, which will take a P weapon P weapon, which is a type of weapon two D, rather, Vitun void and we'll say weapons Penn P weapon. And we want to make sure that it is a child of this weapon. So we do that in one function. We'll say Add child of P weapon. That'll be good for right now. So let's go into the weapons item. We have the loadout so we can check if it has it already. So after getting the loadout, we want to get the weapon associated with this weapon definition. So let's say, our weapon is going to be equal to loadouG weapon, based on a definition. So we're going to say definition here. Now, let's jump into weapons loadout and let's do the function G weapon, based on the definition. So P definition. Is a weapon definition. We're going to return a weapon to D. And let's say for weapon in weapons, if weapon definition is equal to P definition, we're going to return that weapon. Otherwise, we return null. Since we only have a need to search through a few weapons, having a small loop like this is going to be perfectly fine, not really an issue. Also, if you want to limit the amount of weapons a player can have, then you could say something more like at export VR Max weapons is an integer of six at the top. And then we'll be able to check if there's space as well. So we could say function has space, and this will be a Boolean return type, and we'll just say return weapons dot si is less than the max weapons. Okay, so that's just a simple little helper function force. Now, back in weapon item, if they have the weapon, then we want to see if we can level it up. It might even make sense to declare the type weapon two D here, although it's already inferred from the weapon function call. Okay, so first off, if there is no weapon, so if weapon equals null, then we will return load out has space as whether we can apply or not. Because if the weapon doesn't exist in the weapon system, we just need to see if there's space to add a new one. Otherwise, if there is a weapon, then we want to see if we can level it up. So then we'll say return weapon. It could be something like C level up or has next level. I think has next level sounds like a good name to. So now we need that function inside of our weapons to D script. So in weapons to D, we want to use the level against the definition and just check if there's a next level. So down at the bottom, let's create our function. So function has next level. This will return Boolean. So let's say function next level, and that'll be a weapon level equal to definition Get weapon level of our current level plus one, which can return null. So we will return next level does not equal null. Because if there is an next level, then we can hit the next level. We can level up. If there's not, then we've either hit the level cap or we forgot to define a level. So we might have defined level seven, but did we define level six when we're level five? Because five can go to six, but we can't just jump to seven, at least normally. So that should handle our needs there for checking if we can apply. And then if we're going to apply, we'll say function, apply. And let me check the base class here just to make sure, we'll try apply. It might actually make sense to copy this over. Okay. And then we're going to override that down here just by re declaring the same function. So first, we need to get the weapon if we're going to apply. So Var weapon is a weapon two D, and this is going to be equal to loadouG weapon of our definition. So if the weapon isn't there, then we already fail. So if weapon equals null, we return falls. But we also need to get the loadout again. So Var loadouts going to be inferred type from G loadout P target. So we're getting the loadout on the target that we're trying to apply to. So once we have the weapon, we want to lavo it up. So we're going to say weapon dot tri level up, and that'll return whether we are successful or not. So we'll return that value. Now we need to define weapon dot tri level up and weapon two D. So let's go down here and the function, function tri level up, and we're going to return a Boolean. Okay, so we look up at the top of our weapon two D script. We can see we have the level and the weapon level exists in the weapon definition to be a little bit more efficient and just kind of be able to access what our current information is. Once we've already leveled up, we could have a local variable as well. We could say our weapon level is a weapon level. So on read, let's say weapon level is equal to definition dot Git weapon level at our current level. And then whenever we level up, we're also going to set that. That way, if we need to see the stats on our current weapon level, then we just access this value right here. So we basically find the value on our definition once, and until we level up again, we have access to the information in this weapon level. Okay, so now with TrevelU, we want to get the next level. So our next level stats is going to be equal to definition get weapon level at our level plus one. So if next level stats equals null, then we return false. Otherwise, we're going to say weapon level is equal to next level stats, and we say level plus one. Level plus equals one, and we return true. Okay, so there's our level up function. Okay, last thing we're going to do in this video for the weapons item is actually create it. So in items, right click, create a new resource, search for weapons item, hit Enter we're going to put that in items. So we'll say spear item. And if you double click on spear item, you can see we have a spot here for the definition. We want the definition to be exactly the same definition as our spear weapons definition. So if you click on spear weapon, you can see it has the weapon definition. Let's right click here and save this inside of weapons. So up at the root, we go to weapons, and we have spear definition there. We want to double click and override that. Okay? So here is our actual spear weapon definition, and this is now a shared resource. So we can click on Spear item, and we want to quick load that same spear definition. So now the spear item is going to be linked in that way to the spear weapon. So that we can reuse the spear weapon two D. Let's save this branch as a scene. Right click Save branch as SN. And let's put it in the weapons folder as spear weapon dot TCN. Okay, so now we could reuse that on enemies or anywhere else we need it. The key point is just that we make sure they are using the same spear definition that's very important or else the spear item will never be able to find the spear weapon through the loadout. Okay, now we've done all the groundwork. So next video, we'll set up the UI and get the leveling up to work for upgrading our weapons. 47. Creating a Weighted Item Selection System for Level Up Rewards: So at this point, we need to add a list of weapon items to our level up selection that we can choose from when we populate the window. So that's going to be where the leveling option script comes in, where for a given game world or level of the game, we just have this array of items we can assign in the level up selection. So in the level up selection script, we need to at export a VR for options, and this is going to be of type leveling options. Okay, so we save this script, and now if we go look at the inspector and two D view, so click Level Up selection, and then over on the right, we can create our new leveling options. Now, this is something that is probably very level specific. So you might have a level one where you have lots of options, and then level two, maybe it's a little tougher, so you restrict the item list, or it could even be per character class if you decide to go that route of restricting items to specific characters. Either way, this is going to be a resource you want to right click and then save into the project. So let's go into, say, the settings folder, since that's why I put the leveling options script, and then we'll put in level one leveling options and then save, so that'll be a dot TRS. And now we can assign an array of items. So I'm going to increase this to one, and we're going to quick load the weapon item of the spear item. And now that is one of the possibilities we can use inside of our level up selection script. In our hierarchy, we want the level up selection to pass the options, let's say down to this HBox container, which we could say is more like the rewards selection. So in terms of our hierarchy, we have the option set at the level up selection level. When we open up the menu to level up our character, our reward selections are going to be populated down here in this H box container. So each of the rewards were allowed to choose are going to pop up as an option down here. By default, that would be three. But really, it depends on how many items we have available. So we could rename this HBox container to be something like a rewards box container, and then right click and give it a script. So I'm going to attach a script, and we'll save it in UI slash Rewards Box Container. The reason I'm doing this is because the more you separate the different functions of your overall UI into separate smaller scripts, the more reusable each of those components are going to be because you're separating the responsibilities out. So the rewards box container is going to be purely responsible for populating the rewards that are going to go under it. So we're going to do a class name rewards box container, and then we're going to want to export a packed scene to instance each of these rewards selections from. So those aren't actually going to be there. And the baseUI, those are going to be cleared out and only instanced when we have a reward to actually populate. So let's create a function populate and this will take a options list. And we also want to know how many items can be selected from at one time. So I think leveling options would be a good place to put that property rather than the UI itself. So let's right click into leveling Options, Lookup symbol. And I'm going to say at export var MAX Underscore Choices, which is going to be an integer, and I'll default that to three. And while we're ahead, I do want to make one big change to this array of items. I'm actually going to change it to a dictionary of items so that we can apply a weight to each one. And that'll make it so that if we want to prioritize one item as being a possible option, then we can do that by increasing its drop rate effectively by giving it a higher float value or a lower float value for rarer items. Now, since I changed the type of this variable, that means I need to reset it in the level selection. So if we go to level selection and we look at our new dictionary here, we need to give the item. So quick load that spear item and then give it a value. We could say one if we'd like. So now this spear item has a chance of dropping of 1.0. And just like the other instances where we've done randomness with weights, that 1.0 is relative to the total weight of everything else on this list. So right now it's 100% because there's only one more than 1.0 weight, so it's one out of one. But if we add four more items, then it would be a 20% chance. And, of course, because we're giving three choices, it's going to roll three times. Once we select an item on our drop list, it's removed from the overall weights, as well. If we had five options, we'd be taking three of them, three unique choices, I should say. So let's go to the Rewards box container. Now we have how many we're going to actually choose from. Oh, and we want to get the total weight. List of options, making sure that each one is available to be used with the specific target. So let's make another function for that. I'll call it function, generate choices. And this we'll take the P options as leveling options and the P target as a node two D. Okay, so first off, we want to get the remaining choices from the list of options. So if our remaining options is going to be equal to, let's say, Poptionst item weight or items weight, and we're going to duplicate Okay, so the reason we're duplicating it here is because we want this dictionary of items with their weights. To be local to this function specifically, we do not want to mess with the original dictionary by removing items from that dictionary. All the leveling options should stay in the leveling options basically for the remainder of the game because that's a editor time definition, and this is our runtime remaining options. So we duplicate the list, and we're going to be removing from it after we make each selection or after we invalidate a potential option. So the duplicate here is important to make sure that we're not modifying the original dictionary. Efectively. Now we have the remaining options. We want to filter out all the options that are invalid. So for option in remaining options dot keys, why keys? Because the keys in this dictionary are the items and the values are the weights. So it might be actually helpful to declare the type up here, even though it is inferred from the items weight, it's better if we can see it on screen. So I'm going to say dictionary of item, and then we have the weight. Oh, wait, weight is afloat. And there we can see very clearly that the item is the keys. And the float is the value. For each of these items, we want to check, can the target apply it? Actually, it might be better to call this item over here. For item of type item in remaining options dot keys, we can say, if item dot can apply and because we typed it out here, we get access to the functions in the auto populated menu. So we want to see if we can apply that to P target. So if we can't apply it to pre target, let's invert that. So if not item Doc can apply to P target, then we're going to remove the key value pair. So when we do the dot keys, this is giving us an array. So this array is separate from the original dictionary so I think we can actually modify that dictionary without a problem because we already have the separate array of keys here. So this loop here, if we modify the dictionary, it's not going to be a problem. Normally, you want to be careful about that. Like, you shouldn't modify an array while you're looping through it, but in this case, this array is separate from this dictionary, so it should be okay. We'll see for sure when we actually run the code, though. So if we can't apply the item, then we want to remove it from the list of options because we can't use it. So remaining options dot remove or Ease, yeah. Okay, it's called erase here. And we have the key right there. The key is the item. So we erase the item from that, and that's going to remove that as an option. So after looping all the options, we've already filtered out all the ones we can't apply. So now we just need to make a selection based on the total weight, and after we choose each one, we remove it from the remaining options. So our total weight here is going to be a float equal to 0.0, and we need to loop one more time through the remaining options. So we could say like four weight afloat and remaining options dot values then we say total weight plus equals that weight. So I think that's a pretty clear way of writing that out. So now we've totaled the weight. We want to make a random selection between zero and the total weight and then see which one we picked. So VR random weight is going to be inferred type from rand F times the total weight. So we've done that a few times in the course so far. The total weight is our maximum value. 0.0 is our minimum value. And by taking basically 0.0 to one times the total weight, we're going to get some value between zero and the total weight. Okay, so now we have our random weight, and we want to pick an item out of the remaining options. And there's going to be a loop here, by the way, but we can just write it out for one item first. Okay, so now here four option in remaining options. So we're going to say if the current weight is greater or equal to the random weight, then we pick that option. So now we need, actually a current weight here as well. So our current weight is going to be a float equal to 0.0. So first, we want to add in the weight of the option. So current it's going to be plus equal to remaining options at this option. So we put in the key to the dictionary. It gives us the value, and it might actually be a safer way for that. So we could say, get option. And if there's no option, the default value is null. W would give us an error here. And maybe that's actually what we want because we expect it to be there. It's a big deal if we lock up the key value pair, and it just doesn't exist. So yeah, we'll just let that be an just like it would have been by doing the square brackets option. Effectively, it's the same thing except this get function allows you to apply a default backup with a comma and then default variant. So it's a little bit more flexible, but in this case, we do want a hard failure if the option just isn't there, because it should definitely be Okay, so we added the current weight. We want to check if the current weight is greater than the random weight. So if current weight is greater or equal to the random weight, then we're going to add this to the selected options list. We could just declare the selected options up here at the top. So var selected is a array of item because that's what we need to return at the end. So we're going to append the option to the selected array. So selected append option, and then we need to remove it from the list of options. And actually, I think how I'm going to do this is we're going to set a local variable up here. So if our next selection is item equal equal to null, we'll make the next selected. Equal to that option and then break. So we break out of the four loop. We don't no longer need the four loop. After that loop, next selected does not equal null. Then we're going to selected dot append option. Else, we want to break out of our entire four loop. So we need a four loop because we're going to do it multiple times. So let's see. So I think right about here, 44 time in Range, zero, CP, underscore options dot Max Choices with incrementing of one. So that basically means we go from zero to the max choices, going up one each time. When we hit the max choices, we stop the loop. So if the max choices is three, this is going to run three times. It's going to be zero, one, two, and when it hits three, it's going to be equal to the max choices. I'll stop the four loop. Okay, so basically we just take all that and tab it over, and now it's going to loop all of this code, right? So as part of appending the option, we also want to take the total weight, and we want to minus equals whatever the options weight is. So we'll say remaining options dot get option. I'm aware that this is calling this again, but that's not really a big deal. So this is going to get the float value of the weight. We remove it from the total weight, and then we want to come back up here. So there's also the other possibility, which is that there's nothing selected, which means there's nothing left to select. So we could just do early break out of the four loop. So we're talking about this for loop right here. And that's looking probably good to me. We just need to return the selected. So return selected. So at the end of this, we get up to three items. For each of those items, we want to populate the UI. So let's say VR up here, choices is going to be equal to generate choices on the P options, and we pass in the P target so that we can check each item against its target to make sure it's valid. So we have a choices here now. And then we want to create the bbard selection scene and assign the choice to it down here at the bottom. Since this bit is actually outside of this four loop, we want to append the next selected because we set next selected here equal to the option so that we could use it outside the four loop. So we want to say next selected here, and we want to get on the next selected as well. So one more thing I forgot to do down here is that after adjusting the total weight, I also forgot to remove the next selected from the remaining options. So we decrease the total weight because we're removing the option, and then we remove the option from the actual list that we are selecting from. So when it goes back up here now for the next four loop for the remaining options, it's not going to have that option there, and it will just completely skip over it because it's no longer in that dictionary, which is what we want. For the remaining options, we only want to loop through what is still a possible selection, gluting selections that have already been made. 48. Coding Reward Selection with Signal Propagation: From our Generate choices function, we have this array of choices. It might actually help us visually to declare the type right here rather than inferring it. So since we have an array, we want to loop through the array. So for each choice, we create an instance of our reward selection scene, and then we are going to apply the choice to that scene, basically set the choice on the scene and have that populate its sub UI with everything we need. So for choice in choices, we're going to create a Reward selection, which currently is a control, and that's going to be equal to reward selection scene dot Instantiate. Then we need to add it as a child to this HBox container. So add child of Reward selection. And then we'll say reward selection dot SethoCE and we pass in the Choice into it. Our Rewards selection, you can see over here, currently has no script. This function doesn't exist, so we need to create a function on the script, and we're going to do that inside of the packed scene. So we want to click Open and Editor. So we go into the original packed scene for this particular UI element. So on Reward selection, we want to click on attach a New Script, and we'll call it Uilashrewardselection dot gD. Create that. And side of here. We'll give it the class name Rewards selection that'll make it much easier to autopopulate its functions. And what we care about here is function set choice, and we're going to pass in a choice. And that choice is an item. I recall. So this will return void. And from there, we need to populate all these elements. So I'm going to take the Rich Text label, and let's rename it to be name label. Let's rename the text direct to be icon rect. Let's rename the second Rich Text label to be descript label, and let's rename the button to be choose button. Okay, that'll make it a lot more clear what these are actually supposed to do. Now that we have those setup, let's go into the reward selection script again and get a reference to each of them. Now, the way I've been doing it through the course, and I think even in this case, I still prefer is to do an at export var and we'll say something like name label. Is a type of whatever it is, rich text label. Okay, and then you can just assign that in the inspector. We know how to do that, right? But this does mean whenever you set up a copy of the script, you have to manually set the export variables. So just to show another option, you can do at on ready var, let's say, icon Rec and that's going to be a texture rect. And then you give it an eco sign and you set it to the path to its name. So if I say choose button, it'll automatically populate here. We can select that. And I think this needs $1 sign at the start. So dollar sign margin container, slash VBox container slash Choose button. Now, what this means is that on ready, it's going to automatically get this node path and assign it to the text direct. So we do not need to manually set it in the inspector. However, there is a major flaw here, which is that this requires the path to that item to be the same. So if your choose button is called anything but choose button, it's not going to find it. So that would be one reason that I might prefer to do an at Export because then if you happen to rearrange your nodes here, as long as you still have the reference set, it's not going to break your script. Okay, so that's why I don't generally use that, and I am going to switch back to at Export var icon Rect, which is going to be a texture Rect. And then we want at Export var description label, which is a rich text label here at Export var choose button. Is a button. Okay, and for set choice, let's just return pass for right now so the script can save and click on Reward selection. Go to the top right and assign each of those. So name label to name label, icon Rect to icon Rec, description label to description label. And because we have two Rich text labels, this is where having informative names like name label and description label really helps rather than them both being called Rich text label and Rich Text label two. Just want to point that out. And then finally choose button we assign to the choose button. So now we have a reference to all of those, and we can easily fill them with information from our item. Okay, so now to handle set choice, we can do the name label dot text, and that's going to be equal to the name of the item. So right now our item class does not have a name. The weapon item subclass does have a definition, and that definition has a name, but we might want a item specific name, and I think this would be the most straightforward approach to create a item name in the item class. So if we go up to item, we can do at Export VR display name, which is going to be a string name, and I will default that to unnamed item. So this is kind of distinguishing between the item and the definition of the weapon because an item isn't always necessarily a weapon, so we can distinguish those with two separate properties, at least for now. So now let's go back to reward selection, and we choose pchoice dot display name. Let me take a look at the spear item definition, and we can kind of see here. So the definition here is really set up to be more of a definition of the weapon, how it levels up and its damage value. So the item description sounds like something that should be separate because you might pick up a copy of that item on the world map, and it would say something like, Oh, this is a spear item. It adds the ability to have the weapon to your player, but it's not the definition of the weapon itself. So we do want to distinguish those. So we can say description label dot text is going to be equal to pchoice dot description. Now we need item to have a description. Let's copy that name, right click on item, Lou symbol. And we'll do at Export var, well, and we'll do at export var paste in description, which is a string, and that'll be blank by default. Okay, so now in reward selection, that's handled. Next, we want to do the icon Rect. So we could say icon rect dot texture is going to be equal to pchoic dot icon. Once again, the item is distinguished from the weapon definition. It should have its own icon that can be set. So I'm going to do at export VR icon texture. Okay, and we have the texture icon, so we can go back to Reward selection. Now, I do want to be careful here because if you try to set the pchoict icon when there is no pchoic dot icon, then you're going to be getting a null reference as soon as you try to reference this. So you can either manually check if the icon exists every time, or you could change this to pchoic dot Get icon and just have that safety built in. But then we have to add this function to the item class. So let's right click Lo up the symbol, and then I'll create our function G icon down here, function G icon, and this is going to return a texture or nothing. So if icon, then we return icon, else we return null. Okay? I think that's simple enough. This method gives us safety in case it's null, then we'll just return null instead of having an immediate error. So then the last one is a little bit more tricky here. We have the choose button. When the choose button is pressed, we want to make the selection with the choice. So we want the button to know about the choice. And then we pass that signal of the button being pressed all the way up to our level up selection so that we can apply the effect to the player and close out the menu. So here at the reward selection level, we want to bind the signal of the button being pressed with the choice that we pass in right here, the item choice, and we'll emit that as a signal on the reward selection, and then the level up selection will connect to that going further up the chain and we can basically know at the top level when any of our buttons are pressed and then apply the effects to our player. So we're going to need to do, let's say, choice button. So we're going to need to do choosebton press dot Connect, and we're going to connect that to a new function on Reward Pressed. But we're going to bind it with the P choice. So when we're binding it, we're basically passing an extra parameter to the callback that that function wouldn't normally have. So function underscore on reward pressed now gets the P choice, which is an item, and it's going to return void, and now we can keep passing this choice up the hierarchy. So we can have a signal up AR at the top, which will say signal selected, which will pass in a choice of the item. Now we just need to emit that signal, so selected. Dot I MIT, and we pass in the P choice, and now it can go up the hierarchy. So yes, normally, a button press has no parameters when you get it back. It just reports that the button was pressed. But by calling bind, we add in this extra parameter whenever that gets pressed. So now our button can give us that very critical information of what choice was actually being made. Technically speaking, we could also set the local choice variable here, and then pressed, we could just pass in the local choice for choice of items. So if I set that when I set the choice, that would also work. Two ways of doing the same thing, but now you know a much cooler way of using binding, so you can actually add in extra parameters. Also, this doesn't require the reward selection to keep maintaining that choice, though it definitely could, and it wouldn't be problematic here. Okay, so let's go further up the hierarchy. Now, in our Lavop selection, which let's see. We have the Rewards box container. So this is where we're actually creating the reward selections from. So here, I need to propagate the signal upward again. So let's say signal selected, which is going to pass in the choice item just like that. So whenever we populate with our rewards selection menu, and now that we have a class name, we can and should change this to reward selection. And we're going to connect that to on Rewards selected. So down here at the bottom, we'll create this callback function underscore on Rewards selected. We get the P Choice, which is the item we return void, and we're just going to propagate that upwards. So selected dot EMT. We're going to emit with the P choice. So that goes further up the hierarchy now. So that's at our Rewards box container level. Now on level up selection, we just need to connect to our Rewards box container. So let's Export var our Rewards container, which is a Rewards box container. And on ready, we want to connect to its signal. Rewards container dot selected dot connect on Reward selected. Function underscore on Reward, selected. Let me zoom in here a bit. And we're going to have our P choice, which is the item, and we'll return void. So we're going to pass for now, and we're going to need to apply the effect to the player and close the menu. We'll come back to that in a second. So we had our start selection method. What we want to do is basically reset the rewards box container. We do want it to populate, but we also want it to clear any existing items. So in the rewards box container, I'm going to take populate and make that underscore and generate choices. I'll also make underscore for private so that we don't accidentally call that from our main UI. So here we need to underscore because we're doing underscore generate choices. Now let's create the function we're actually going to call. So function setup choices. Yeah. Okay, that makes some sense. And we're going to pass in our P options, which is leveling options, and we're going to pass in P target, which is our node two D. Of course, we're going to populate with those options and our target. But before that, I also want to clear out this menu. So any of the children boxes, I need to either remove those before I populate, or I need to make sure that my populate reuses the existing UI and then updates them with the new choices. So because we're only going to have a few choices here, it would be a little overkill to worry too much about reusing every possible sub UI component. So the easier short term option would definitely be to just clear it, which is going to be for child and Children. We're just going to call child dot Q free. So now up here at the top, we can clear before we populate. Oh, and we need to finish a return type for the setup choices. So we'll return void. Okay. Yep. So now, setup Choices, we can call that in our level selection script. So start selection, we're going to do rewards Container dot Start Selection, and that's going to pass in the Options and the target, which is the context player. And this is the wrong name for the method. It was actually setup Choices. Okay, so we start the selection. We set up the choices. We wait for the player to basically pick a choice, and then we're going to get our callback. So at that point, we want to close out the menu. So we could say rewards container dot CE here. And I did make that a private method. So in the Rewards box container, I'll just remove the underscore here and here. Okay, so now it's called internally, but also from other scripts. So if you need to make a method public, you can do that. Technically, once again, GD Script doesn't prevent you from calling a method with an underscore on it. It's just the best practice for indicating the intent. So back in level of selection on Rewards selected, we want to apply the item effect to the player. We'll say p choice dot Try apply to the player. So that's going to be the context dot player. Then we clear the container and we close the menu. Okay, so let's see. We'll just do a hide here. And that might be pretty much all we need. So a little bit of cleanup here. Let's go to the Rewards box container, and well, let me show the two D view, actually. Here you can see that we have these three boxes here. Having those in the view here is okay as long as we free them before we start using anything. So if we go to Rewards Box container on ready, I'm going to want to call Clear. So function underscore ready. Let's call clear on those UI elements. So that'll mean these will get removed and they'll be recreated whenever the Rewards Box container pops up. We want it to be as optimal as possible, reusing each UI component is better than having to recreate it from an instance. But for something simple like this, you're only going to show once every I don't know, 30 seconds of gameplay, it should have almost no impact, and it's not really a big deal. Okay, so we still have the spear selection here. So if we actually try to level up, we should see one selection. It's definitely time to go ahead and give it a test and see if we actually get it to pop up and work at all. So let's hit Play. And we'll see how it works. Okay, so it looks like I forgot to set the rewards container here on the level up selection. So let's go to level up selection. Click on that. And in the hierarchy, we need to assign the rewards container rewards box container. So that really is the weakness of export properties, right? If you forget to set it, then there's really no indication there, other than you're going to hit errors at some point in the code. So we don't need an assert there because the connect to the signal is already going to throw an error as soon as it fails. Let's close, and we'll run again. Okay, so let's go pick up our item. Boom, we have only one option to choose from. So we have named item here. I think I just didn't set an icon or description, but you can see that the Get icon method, because we handled the null case, it's not going to give us an error. Whereas if we directly reference the dot icon property because that was Null, it would throw an error here. If we hit choose, is it going to apply to the player? So let's find out about that. It should close the menu. It does, but I forgot to resume the game. So we have to go back in here and make sure that after we hide the menu or maybe even before we hide the menu, we'll say Geth dot paused is equal to false. Let's save that and run one more time. So we come in here, we make our selection, Let's choose the item. Boom, it's already there. Now, it seemed to work, but the real test would be if we had a display for our weapon item levels up here, we'll work on that in a bit. For right now, the best thing to do would be to hit close, and let's go to our spear item. I'm going to hit level two, and we're going to change the cooldown to 0.1, because that'll be very obvious if we upgrade to that level. So let's hit play again. I'll go pick up our item, I'll hit choose, and we see that the cooldown doesn't update. So something is a little off in the code. So I think the problem is actually going to be in the weapon itself. I'm going to check here to make sure that we are actually assigning the level up, so breakpoint there. And let me see here on ready, we get the cool down definition for the timer. But you can see here we're setting the time for the timer here. But I bet you down here, we actually don't reset the timer to a new value. So, yes, that is a very clear problem. We need to say the timer dot. Let's actually just say start is going to be the new value. So we need to do the G cooldown thing. We need to get the definition dot Get cooldown of a current level. So definition, dot Get cooldown of our level. What this means is whenever we are resetting the timer, we're getting the current cooldown of our level. So if let's say halfway through shooting a projectile, we change our cooldown because our level went up. So this means the next time that projectile launches after it casts, we're going to reset the timer with our new value. So now it will use the Level two property. And I think I don't even need to have this breakpoint. I'm pretty sure that's the problem. Let's go ahead and hit Play. I'm going to go ahead and pick up our weapon, hit choose, and there you go. We basically have a machine gun spear because our spear hit level two, and this is going to be completely game brreaking. So I would recommend at the end, we just change the cool down here for our spear projectile back to 0.9 now that we know it works, and that'll be a wrap for this video. 49. Applying Weapon Level Stats to Projectile Spawning and Instances: Shown that we can upgrade our weapons through the reward selection on level up, but we need to make sure that when we level up our weapons that we are using all of the correct stats for the weapon, not just the cool down reduction. So let's go into the script for projectile. And we're going to see inside of here that we're not using the stats of the weapon level yet. We're using a built in speed, and the final damage is not done by any cut of calculation. It's just passing 100 as the local variable. So if you've been noticing on the weapon level definition, it's not using those stats yet, well, this is why. So when we create the projectile, we need to pass in the weapon level as well. We could have a totally separate function to do it, but I think it might actually be more practical just to put the weapon level stats into the launch function. So the launch function is meant to not only set the direction but also set up the projectile specific stats, but the launch is also meant to set up the launch specific stats for this projectile based on the current level of the weapon. So as an extra perimeter, we're going to want to pass in P level, which is a weapon level. So that'll be all the stats we need to bring into here. And instead of at the top having a speed here, I'm going to remove and we're instead going to say var level of type weapon level, and that's where we're going to pull all of the stats from. So when we are doing the physics process and we need to multiply by the speed, we'll do level. Dot speed. Okay. We'll add that in. And now on launch, we need to set the level equal to the P level. Okay, so that's how we pull the stats in, and then we just need to update our calls to launch. But there's other information here that's relevant. So, first off, when the hit box hits a rt box, we need to pull the damage out of the level stats. So here we need a random damage calculation between our men damage for the weapon and our MAX damage. So I'll remove this line, and we'll say var damage is going to be an integer, and that's going to be equal to a random integer range between the minimum damage and the maximum damage. So level dot Min damage, and then level dot Max damage that handles the randomness here. So then we have our final damage. I'll just say that's equal to the damage right now, and then we pass our final damage to the Hurt box. So now we have random damage based on the level and speed set by the level, as well. So if we look at a weapon two D script now, we're going to get an error for projectile dot launch because we need to pass in the current level of the weapon two D, which is conveniently set up here on weapon two D. So let's just say coma, weapon level here, and that just sends it from the weapon level to the projectile instance. So we can test those changes by running the game now. So we're going to go find our or enemy wherever they feel like spawning. Okay, there's one. And you'll see that we have random damage between our men and MAX. Currently, you can see that it's still passing through the enemy, there's our level up. So, in theory, this should be going a little bit faster in both the cool down and the damage. You can see the damages up there. So, for the most part, that seems to be working. But there's still a few things we have to adjust, such as how many hits it can do before actually deleting the projectile. So on hipbox hit, we actually want to increment how many hits we've done, and if the hits are equal to our MAX hits or greater, then we want to free our projectile. So up here at the top, we'll have a var hits, and this will be an integer. It's going to start at zero. And every time we hit success. I'll go to the start here and say var hit equals P hurt Box dot Tri Damage. And then we'll say, I hit, then hits plus equals one. And then we can also say, I hits is greater or equal to level Max hits, then we're going to free on our projectile. So now this can free if the timer elapses for its basically persistence in the game scene, which we need to replace with the variable here, too, by the way. Or if the hits hits its max hits, then it will free immediately, as well. So then up here, we also want to say that when we create the timer, we're going to grab the level's property. So we say level, and I don't think we actually set it yet. So we need, like, a duration. Let's create a duration stat four our weapon. Okay, so level dot duration. That means we need to jump into weapon level. Look up symbol, and let's create duration down here at the bottom. We'll say at export var duration is going to be a float equal to let's say 5 seconds by default. So that's the maximum number of time that the projectile can persist in the game world before removal, assuming that it doesn't get removed by something else like hitting or exceeding the maximum number of hits. Okay, so let's go ahead and hit play once again. So I'll hit play. There's our spear projectile, we're going to wait for some enemies to spawn. Okay? So we have that. And you can see, very importantly, as soon as one guy gets hit, the spear removes itself. So that's a huge change. And if we level up to level two, it should still be the same, but our damage goes up. And let's try to watch this projectile, and it will despawn after 5 seconds. So 45. Okay, there you can see the projectiles despawning themselves from the game world. That's very important because otherwise, they may never be removed, and they would just be taking up system resources for no reason. So we do want those to automatically remove eventually. Okay, now there's also a few other properties from our weapon level, the spawn delay, the span offset, and the projectiles. So we need to make sure that in our weapon to D, we're actually generating the extra projectiles, we're spawning them relative to the offset and any additional projectiles, have a delay after the first one gets launched. And then with the span delay, any projectiles after the first one, have a delay before they get spawned. Okay, so let's go to weapons to D. So each time we cast, we need the projectiles to spawn. So we need a four loop, let's say, four index in range zero to weapon level. Dot projectiles, and then we'll increment by one. So we just loop this a few times. After each projectile launches, we're going to wait Kit tree Create timer, and we need that weapon level dot Span Delay. Okay. And then we're going to wait for the time out. So after the first loop, we wait for the short span delay to time out before we create our second instance of a projectile per cast, and we're creating projectiles based on how many projectiles the weapon level allows us. Later, if we wanted, we could also mix this with, say, a bonus from the player. So if the player has plus one projectiles for all weapons, then we have a function to add those two numbers together to get the total number of projectiles for here. So then finally, we need to generate a random offset based on the offset distance of our weapon level. So for our offset, it's going to be equal to a vector two, and then we're going to do a random F range for the X and the Y. So random F range, negative 1.0 to 1.0, and then random F range, negative 1.0 to 1.0. And we multiply this by the weapon level. Dot offset. So this is going to give us a random value between the negative max distance on the span offset, and then the positive max offset here for the X, and same for the Y and the positive Y. So this will technically give a square shape because the X and the Y don't affect each other, so it can be negative 1.0 on both X and Y or positive 1.0. I think that's fine. We're just trying to have a simple offset. It doesn't really matter too much if it's a square or circle unless you want it to matter. So I'm going to go down here to the projectile and we're going to add in the offset. Okay. And then that will make it so that whenever we spawn the projectile, it'll have some offset if we have that set on the weapon. So if I look at the spear item, well, we can see that here we don't have any span offset. So we might want to set one. Let's see. What would be the maximum pixel offset I'd want to do? Maybe like five. And then we'll just do five for all of them. So open up each weapon level, and we'll do five. So this can be positive or minus five or anything in between for both X and Y. And then I'm going to run, and we'll see how this goes. So watch where the spears are spawning. And you can see that they're a little bit offset from each other, right? So it's not strictly in the same position every time. And then also makes it a little more interesting for some variance. Okay, now, what I want to do is take the weapon level one for testing, and we're going to make it to three projectiles, and I'm going to make this bondlay 0.05 seconds, which is already there. So let's play and run it, and we should get like a chain of three projectiles. One, two, three, one, two, three, one, two, three. So you can see here one, two, three. Oh, now, one little issue here, you can see that if I rotate after the first projectile launches, the other projectiles I also rotate when they are instanced. Okay, so for this rotate Rads, I want to calculate that outside of the four loop. I only want to do it once. Once the first projectile launches, all the other ones should be locked into that direction. So let's go ahead and hit Play. And now if I move around, but I rotate, you should see that all three of them are always going to go in the same direction, which is usually what you want, I think. So let's remove the extra projectiles for our first we can keep that custom spawn offset for all of these. If you want, we could even set that a default weapon would have a span offset, but that's kind of up to you if you want to go back and customize those weapon level defaults. Let me look through the weapon level script one more time and see if we've handled everything. So minimum damage, maximum damage, speed, cool down projectiles, maximum hits, offset, delay, and duration. Yes, we are using all of the variables from our weapon level, and that gives us quite a lot of customization for any of the weapons we want to create. 50. Building a Rotating Scythe Weapon: At this point, since we have a working level up system, we want to take it and add extra options to it. So now would be a perfect time to add a second weapon to our game. So this will be similar to the first one. We'll add a scythe, and the scythe will be able to rotate around the screen rather than going straight forward like the spear does. So if we open up the spear dot TSC and weapons, you can see it already has most of the components we're going to need for our Syth. So I'm just going to duplicate from this scene in the bottom right by right clicking on the scene and going to duplicate. So let's put in Syth here dot TSCN. Okay, so now I can search Syth in the project and open that up. Let's zoom in on the center. Some of the obvious things we want to change first, the name of the projectile, so we'll call it Syth. And then the sprite should be selected from the atlas texture for the scythe sprite. So I'm going to click here, expand the at, and we'll use the same wooden sprites. So if I recall, there's a good Syth in here we can use. So we'll select that and hit close. Now you can see that the hit box no longer makes sense because it has this curve here. So we want to click on our collision shape, and I'm going to pull over here and then expand it to the right something like that, or it could even make it more like a rectangular square, if I want to cover the whole edge of the blade. So let's do that. Now, if we instance this projectile, it's still going to work almost exactly like the spear, where it's just going to go forward. So what we actually wanted to do is to rotate around the center point. So that's going to mean we need an offset, and we need to rotate that offset around in a circle and that will give us our spinning animation. So let's right click on site. I'll add a new node two D, and then this node two D will be the parent of the Sprite and the hit box. So let's move those under here. And then I'm going to rename this to be Rotator two D, and let's right click and attach a script to it. So weapons Rotator two D is fine. Let's create that let's give it the class name rotator two D, and then we want to put in a couple of properties here. So let's give it a velocity for this rotator node. So remember, this rotator is a child of the base object. So the base object is going to have its own movement, and then this sub object is going to be controlling the rotation specifically. So they both move at the same time, but they're independent of each other. So at export var velocity, which is a vector two, and I'll have this default to ten pixels to the right and zero on the Y axis. So this is going to affect the movement on the screen. But we also want the node to physically rotate as well, so it looks like our site is spinning. So at export var rotation speed degrees, that'll be a flow, I'll default it to 360, which would mean one rotation per second in this case. And then we just need to update it on process or physics process. I'll do physics process to be consistent with other movement in the game. So let's say if our amount is going to be equal to degrees to radiance. So we're going to convert this rotation speed degrees to a radiance amount. So we need the rotation speed degrees multiplied by the Delta to get the amount of rotation since the last frame update, and then we're going to rotate by that amount in radiance. So we can even call this amount RDs if we want to be clear that that is a amount of radiance and I'll make it a float as well, just to give it as much clarity as possible. Okay, then we need to physically move the node, and that's going to be based on our velocity up here. So I'm going to say our move is going to be equal to velocity times Delta. And then we call translate on the move amount, and that is essentially our rotator script. So if you need to customize any values, you have the inspector over here to assign them. So now we want to create a weapon two D that can instance our sights. So let's look for the spear weapon if we even saved that to the project. So now let's look for the spear weapon. We're going to open that up. We'll take a little look here. So this is the two D node with a audioStream player which plays the slash sound when we instance a weapon. So I want to duplicate this and then create a Syth definition for our weapon. So let's duplicate the spear weapon scene as Syth weapon. So let's search for Syth again. Double click Open Syth, rename spear weapon to Syth weapon. And then over on the right, we need to create a new definition. So I'm going to zoom in here, new weapon definition, and this is going to be Syth. Our icon, we can just copy from the actual Syth projectile. So I'm going to go to the Syth scene and then we'll click on the sprite and click right click on the texture, copy it, go over to scythe weapon, and then paste it into the icon with right click Paste. Okay, now we need to define some weapon levels here. So let's create our set. We'll start with weapon level one, new weapon level. So how do we want our scythe to work? Maybe the scythe moves slower than a regular projectile. Maybe it's cool down is longer, so we could say like 1.5. Before Max hits, I'll put it at five, so the scythe can hit a lot of targets before it despws. For damage, let's also bump it to 20 to 30. And projectiles one. Okay, so let's add that key value pair, and then we'll do level two with a new weapon level. Expand weapon level one so I can compare. And then I'll do, let's say, 30 to 40 damage, speed of 55, cool down of 1.4. Max hits of six, and I'll add that key value pair. Let's do level three, so on and so forth, however you want to make the numbers. Okay, so here's the numbers I went with at weapon level three. You can pause if you want to copy. Here we have weapon level four, weapon level five. And weapon level six. And I think that should be sufficient for right now. Okay, so if I recall, I have not set in the weapon definition the ability to pick your packed scene yet. So in weapon definition, I want to add a G seen options. So for now, we'll default it to a export variable. So I'll say export VR scene, and this will be a packed scene. Okay, and then instead of Getsn loading the spear, we want to return scene. So because we're using a get seen method, we have the ability later to come in and change if there's some extra scenes we want to consider passing in rather than the base default. But this should be good for now, but we don't really need a second scene at the moment. Okay, so now we just need to make sure that in the scythe definition, we quick load the scythe. And then in the spear definition, so search spear definition. You double click that. And then we need to quick load the spear scene. Okay, and that should be good there. I think the last thing we need for our Syth is the Syth item. Now we want to find Spear item in our project, duplicate that and create a Syth item dot TRS. So search for Syth item. And that Syth item we're going to assign, of course, the scythe definition. Now, for our Sites to be added as a weapon to our player, we're going to need a reference to the weapon two D. That we can add to our player's loadout because that doesn't already exist on the player, so it needs to be added. Okay, so to add our reference to the weapon scene, we cannot actually use the pact scene because if we do that, it's going to have a cyclical reference because the weapon TD already references this weapon definition resource. So in this case, to get around that, we need to do a export file, and this will be in quotations, star dot TSCn, which means we can filter it by anything that has the extension dot TSCN at the end of it. And we'll say var weapon two D path, and this will be a string. This is the path to the weapon scene for adding two weapon loadouts like the player. Okay, so now, if we look at our spear definition, we can assign the weapon two D path, which is going to be in weapons, and then we have spear weapon in here, yeah. So that's what we need. And then we need to do the same thing with scythe. So search scythe. And it looks like I called it scythe weapon, but that was supposed to be site definition down here. The TRES is the definition. The scene is the site weapon. Okay, so double click on site definition and then assign it the path. So you can actually just drag and drop from here into there, and that'll assign it. So in weapon item now, if the loadout does not have the weapon already, then we need to actually add the new weapon at weapon level one to the loadout. Rather than returning false here and tri apply, we're actually going to create the new weapon. So we can say weapon is going to be equal to. Let's look at our definition weapon two D path, we want to instantiate that. So we're going to say load on the path, and then we instantiate. And that's going to be our weapon. So we need to add that to the loadout. So loadout Add weapon and we're going to add the weapon. If that works, we want to return true. So if we have our item we can now add it to the list of possible items for our reward UI to generate. So if I click on the level up selection scene and we go to the first node here over on the right, we have our level one leveling options, and we already added in the Spira item. So let's add in the site item. So quick load our site item, give it a weight of one, add it. Okay. And then let's hit Play and see what happens when we go over here. So you can see it says unnamed item, and we don't really see any details. If I hit choose, it's not actually launching the new projectile. So we still have some work to do for that, customizing the items and making sure that they properly load out, and we'll handle that in the next part. 51. Creating a Weapons Display UI for Game Inventory: Project really needs now is the ability to see what weapons we have equipped in the top left with the UI indicator. And when we level up to either upgrading the first weapon or choosing a new weapon, and I know these names and descriptions here as well. That will also pop up here so that we can associate what we should be seeing and what we're actually seeing. So that'll help us with the debugging, but it's also very helpful for the player as well. So let's go ahead and create a new UI scene, so I'll click Plus up here. So I want to be name this control node to, let's say, weapons display and I'll also right click and change its type to a panel container. Okay, that'll give us a background by default. So inside of here, I'm going to want a margin container and then follow that up with HBox container for organization. So right click Add a margin container, and then right click Add a HBox container. Okay, and then in here we're going to lay out basically an icon with a level indicator for each of our weapons left to right. Let's take the weapons display and shrink it to be much more like this. And then I'm going to save it to our project in the UI folder, so on the UI folder weapons display. And let's put a copy into our gameplay UI Canvas so we can see how that's going to look roughly. So search weapon, and then we're going to grab weapons display and position it under the UI. And we can see that's quite large there. So you can click here and jump into the weapons display, and let's shrink it down to maybe more like the middle of the screen. Note the blue box indicating our full UI size. So this is going to need to be quite a bit smaller, really. And I probably want to offset from the center as well. Let's do layout center by default, as well. Now we can go back on the gameplay UI Canvas. We can see our weapons display there. It's not really showing up like we expected to. So in weapons display, I'm going to take the anchors preset, and let's go with center by default or top center, rather. So center top. And we can kind of see the problem for why it shrunk down to nothing. So I'm going to take the custom minimum size up here and let's set a default. I'm going to say 200 for the X and then 50 for the Y. That'll just be so that we can kind of see it and adjust it on our gameplay UI. So I'm going to go over to the gameplay UI Canvas, and now we can position it where we need it. So I'm going to position it, let's say, out here to the right of our main health bar and EXB bar, and I'll drag it out like this. Okay. And then we can have a few icons showing for our weapon levels. So to know about that, we're going to want to use our player context so that we can show what weapons the player has. So in weapons display, let's add a script islas weapons display dot GD. And then we're going to want to export var the context, which is the player context here, and we can assign it in the inspector. So quick load the player context. And then on ready, we're going to want to use that to get reference to our weapon loadout. So function underscore ready, it's going to return void, and then we'll say context dot player dot. Do we have a reference to the loadout? We do not. So if we don't want to make that another built in property of the player and have that directly referenced on the player script adjust for this, we could say find children, and the pattern is going to be empty quotes. The type is going to be weapon loadout. So this is going to give us an array. We'll take the first result of this by doing square brackets in a zero, so that means array index of zero. And we're going to assign that to a loadout. So our underscore loadout is going to be a weapon loadout weapons loadout. And we're going to say underscore loadout is equal to the result of that and we do want to assert that that is not null. So assert underscore loadout does not equal null. Must have a loadout on the player to show for weapons display, percent name at the end so that we can get the name of this node. And then we want the class name weapons display up here at the top. This will display the weapons and the levels for the context character. So let's do another at Export var. This will be for the UIC. We're going to instance for each of the weapons in our loadout. So do a at Export var. Let's say weapon display, which will be of type weapon display, but we haven't created that yet. And to kind of indicate that this display also does more than just purely showing the data, but it also hooks everything up. I'm actually going to rename this to view. That'll help also differentiate from weapon display, the sub UI container. And then I'll rename both the weapon display. And then I'll rename both the weapons display dot GD and the TSCN. So this will be now Weapons View and up here, weapons view dot TSCN. Okay. Also jump into the view scene and rename the root node, as well. That's pretty important. So weapons view there. Now we need to create our individual weapon display so that it can go inside of the weapons view. So let's add a new UI to our game. This will be a user interface at the start. And I'm thinking that the root here, we may also want it to be a panel container. You can always make a panel empty, but having the ability to have the background is kind of helpful. So let's change this to a panel container. I'll rename the root to be weapon display. Let's right click on it, add a child node margin container, just in case we need that and then right click Add a texture wect to display our icon. We'll right click add a label. And I'm actually thinking I don't need a VBox or HBox container for this because I may actually want the label to write over the texture. For a lot of games that would have a texture and then text at the same time, it would actually write the text over the texture icon. You would see that a lot in inventory systems for the number of items in the inventory. Except in this case, we're talking about the level of the weapon that is equipped to the player. So as a test text, we could say level X here. Of course, we can see that the display is taking up the full screen now. So let's actually shrink that. I'm going to go to the base weapon display, and we'll take layout to Anchors preset top left. Okay, that should shrink it down dramatically. So now we can see it's just showing the text. Let's set a texture on this texture rect. So I will do a new atlas texture, and then we will quick load a atlas. Let's look for maybe the 16 by 16. Sprite sheet in our game for all the icons, and then we want to edit region and select one. So something that looks like a weapon. Here we have a sword that works fine, and we can close. Okay, we can see that the sword icon is there, but it's not respecting the aspect ratio, so we want to go to stretch mode and change this to keep aspect centered, I think. Okay, there we have that. And now we want to take our main weapon display container, and we want that to be a specific size as well. So I'm going to say custom minimum size 48 by 48. That'll give us a square here. And I'm going to want to take the label text, and we want it to be centered at the bottom. So vertical alignment to bottom over here on the you'll see that doesn't actually change anything because we need to expand the size of the label for the text to be centered at the bottom. So go to layout now, and we are going to want container sizing to fill, I believe. Yeah. Okay, there. So now it's going to show level X there on the bottom right. And now it's looking like we want a little bit of a buffer between our texture rect and our label. For right now, let's save this to our UI folder, our weapon display. And I think what we want is a second margin container. So I'm going to click on the first margin container, add in another margin container, and this one is going to be the parent of the texturect. So let's position that on top again so that the text shows over. Now, with this margin container, go to the theme overrides, and we want the bottom margin to be something like ten pixels. That way, the text is not going to show you know, over the important bits of our icon. Maybe we even need it more than that. So let's try 12. Maybe we even need it more than that, really. So that we can see how it's going to look more properly, let's apply our game theme to the weapon display. So click on weapon display, find theme and then quick load our current theme. Okay, so now that's a lot smaller and it's in the bottom left hand corner. I think I want it in the bottom right. So click on label, and let's take the horizontal alignment to right. Okay? That's looking pretty okay, but it's a little too close to the edge. So I want to take the parent margin container. That's the one right under the weapon display, and I want to set some margins. So let's go to theme overrides and maybe two pixels on each of these. Okay, that looks right. Except maybe the margin bottom. Maybe we don't need that. Yeah. Okay. I like that better. Alright, now, our weapon display is going to need a reference to the weapon two D node. And then whenever the weapon two D node levels up, we change the text here to show the new level. So let's add a script to weapon display. This will just go on Uilashwapon display dot GD. Okay. And this will be class name weapon display. And we need a reference to the weapon two D. I could just export it for right now. So this will be a var weapon of type weapon two D. I'll say set value, and this will be weapon equals value. Whenever we set the weapon, I think I'll just call a refresh function, and we will set that here. So function refresh. So to set the text on the label and the icon, we need to get an export reference to each of those. So at Export var texture, text direct of type texture Rc. And then we need at Export var label, which is of type label. And when we refresh, we want to say texrec dot texture is going to be equal to weapon dot definition dot icon. Yep, that works. And then we'll say that the label dot text is equal to weapon dot, if we have a name here, weapon name. No, we want the definition name because the name on the weapon is actually the name of the node that won't have spaces or anything like that, so it's better to get the resource name because that's a custom property we set. And then we want to say plus equals space plus a string of the weapon dot level. Okay? So that'll basically show the weapons name and then what level it is currently at. And just in case the weapon definition doesn't have an icon, let's make a get icon method. Which will work like we did with one of the other scripts before, which will just return null if that property happens to not be set. So let's right click on the definition. We'll go into that, right click weapon definition. We'll go into that. And then let's create our function G icon, which will return a texture two D. So if icon does not equal null, then we'll return icon. Otherwise, we return null, and this should prevent you from getting errors if icon happens to be null here. Okay, and we probably also want to try refreshing on R so function underscore ready, I'll try doing a refresh. That'll also help us to confirm that if there's no weapon two D set, it will work fine and just show blank. The idea here is that whenever we set up the weapons view, it's going to populate with weapon displays. We set the weapon, and that's going to refresh immediately. So let's go to Weapons view. And inside of here, well, you see that I was exporting a weapon display, but actually, what I want to do is export the scene. So this will be a packed scene that we can instance. And then we quick load on the right our display for the weapon, so weapon display. So on ready, once we get the loadout, I want to populate this with all of the weapon displays for each of the weapons that are actually in our weapons loadout. So I did something similar with the level of selection where I just clear and reset it each time, generating new scenes for each of the sub UI that need to be created. But here, I'll actually try to reuse it so that you guys can see both approaches. This would technically be the better use of your nodes by not having to delete them and recreate them each time. So I think that'll be helpful to know after the course. So one of the ways we can easily access all of our active weapon displays is going to be to have an array of them. So I'll say var underscore displays, and this is an array. I'll zoom in of weapon display. Let's also create our refresh function. So our function refresh is going to return void. And for each of the weapons in our weapon loadout, we're going to create a display if one needs to be created. Otherwise, if it already exists in this array of weapon display, then I'm going to reuse it by just refreshing the properties on it with the new weapon by setting the weapon on the weapon to. Okay, so for our refresh function, we want to loop through all of the weapons, and we'll do that using the index positions so that we can match the weapon loadout indexes to our weapon display indexes. So for IN range zero to underscore loadout weapons dot size, incrementing by one. Then we're going to first get the weapon. So our weapon is going to be a type weapon two. Equal to underscore loadout weapons at that index position, and we need to get or create the display. So I'm going to say var display is a weapon display, and we're going to set that equal to get or create display at an index position of I. And then on our display, once we've acquired that, I'll say display weapon is going to be equal to the weapon itself. And because the refresh is part of the setter and that script, it'll immediately refresh automatically. Now, I did talk a little bit about side effects and if you do the setter like this, it's not necessarily 100% clear that setting the weapon is going to automatically refresh the UI. But because that's so integral to the weapon display, I think it's okay for right here to just say, Oh, we set the weapon. Of course, the weapon is going to show the current weapon. But since weapon display is literally what it does, setting the weapon and implying that that would refresh what weapon it's going to show in the UI. I don't think there's much too problematic with that as a side effect. But it's definitely debatable. So now we just need to get or create the display. So function, get or create display with index parameter, and that's going to be an integer, and we're going to return a weapon display. So let's say var display of type weapon display's going to be equal to displays dot Get at index. Okay, so that might be null. So if display does not equal null, we just return the display. Otherwise, we need to create one right here and add it to the so display is going to be equal to the weapon display scene instantiate. And I typed that name in wrong. So right here, Control C, Control V. And then we want to add it as a child to this UI or rather it's going to go on the HBox container. So, hold on. I need a reference to the container. So export V display container, and that'll be any kind of container. We just need to export that on the UI. Then we'll take the display container and add the display as a child of that. We say displays dot apen vi new display. And then, lastly, we want to return the display, turn display. So it should be created here if it wasn't already in existence here. So that'll handle creating the new displays. Now, what if we remove a weapon? Then after we loop through the weapons and we have all the displays set up, we want to check if the weapons array is actually smaller than our displays array and then remove the extra displays. So say that we remove a weapon from the game, and we want our display here to refresh. Well, that's going to be a null reference on the display now. So we can say for display and display I display dot weapon is null, then we can just free the display. So display dot Q free, and hold on here. I need to have that underscore there because that's a private variable. So we have our display dot Q free. So we have our display dot Q free, and we want to resize that to our loadout SI. So let's actually get that as a local variable of our weapons size. Now we can reuse weapons size here and down here as well. Okay, so to walk through this function again, it updates and creates displays as needed to show all weapons, removes any displays that have null weapons, and resizes the displays array. So we get the number of weapons. For each of those weapons, we assign the weapon to its corresponding display position. So after we do that, we look at all the displays once again, and technically, we could loop from the weapon range to the end of it, but a little bit over optimization for not much gain because we're only showing like six items here. But if the display weapon is null, but here if the display weapon is null, which it could be if we remove a weapon, then we're going to queue free on the display and resize the set of displays so that this size is going to match up with the weapons. In our G or create display, if the display exists at the same index, it'll just return that and we use that. Otherwise, it'll create a new copy of the scene, and then we apply that under the HBox container. Okay, now in the inspector, we got to assign that HBox container. We could rename it to be Displays Container if that helps. So displays container, keeping the naming consistent. And we already have the weapon display s here. So this will work on Okay, so let's run it and see if it's working on run right now. So we run and we failed to find the weapon loadout because I think it might be called weapons loadout. So you can see Fine children is kind of fragile like that. So we have weapons loadout there. So let's try to find weapons loadout with the S, and then we'll run it again. Okay, so we get to where we have textre dot texture equals weapon dot definition dot icon. Okay, so then we hit another issue. I haven't set up the textrect in the label on the weapon display. So let's go into weapon display and rectify that. The top right, when you're on the weapon displacing, assign the texture wrectt double click, and then assign the label. Okay, and we'll run it one more time. See if that resolves the issues. Okay, so here, when there's no weapon, it's going to cause an issue. So maybe we actually do want it to set a weapon before we refresh it. So since the refresh occurs here when we set the weapon, I will remove the ready refresh. It's not really needed, and it actually just causes more of a headache here than we need. So I'll hit play. And okay, there we go. We have Spear one up there on the top left. I'm going to go over here and we'll select the new item. Now, this isn't going to work because we haven't set up signals. Okay, so a mistake I made, when you call Git on an index that doesn't exist in an array, it's going to give you out of bound errors. So what we should actually be doing is making sure that the index is in the array. So we'll say if displays let's say size is greater than the index, then we're going to return this bit here, displays dot Git Index. Sure. Then we can remove the variable declaration var display weapon display down here to when we instantiate the display scene, and we'll remove this line at the top. 52. Connecting Signals and Configuring Weapons UI for Dynamic Display: Okay, so we have a few more signals to connect on our weapons view and weapons display so that when we're in game, it's going to properly show the right weapons setup after we refresh the UI. So a very important one here is going to be when our loadout adds a weapon by calling the add weapon method that we're going to connect to its weapons changed signal, which I don't think I've even created yet. So let's jump into weapons loadou. I'm going to right click here, open up the symbol. Now we're on the weapons loadout dot GD script. So we have the add weapon method, and we want to admit a signal here so that our view can respond to that. So at the top, let's create our signal weapons change. If we like, we can pass the array of weapons into the signal. So weapons, which is an array of weapon two D. And now we just need to admit it down here at the bottom when we call add weapon. Weapons changed dot emit, and we pass in the weapons. Okay? Now back on our view script, we subscribe to so on ready, after we get the loadout, we want to do underscore loadout weapons changed dot connect. And we're going to say on weapons changed. We'll make that method down here at the bottom. So function on weapons changed. The parameter is the P weapons, which is an array of weapon two D, we're going to return void. Now, we're going to call refresh here. That's all we need to do because refresh already handles checking the weapon loadout and which weapons we have, what the size of it is. So we're actually not going to directly use this P weapons here, so I'm going to underscore it so we don't get a warning. And now before we hit play, I want to make sure that our items are actually defined properly. So in the bottom right file system, I'm going to search for item. We have the Syth item. So let's see if we go to the top right now, this is an unnamed item with no description, and it needs an icon as well. Also, it seems like I have the wrong also, it seems like I have the wrong item definition. That makes a lot of sense now, why the scythe weapon didn't appear. So let's quick load here the scythe definition. Okay, so that's set up properly, but we have to set our display name. Scythe. So for the description, I'm going to put a projectile that rotates around the screen and can hit many enemies. That's basically its main difference over the spear. And for the icon, well, let's jump into the scythe definition. I'll right click on the icon here and copy it and then close that and paste it into the icon here. Okay, now we need to do something similar on the spear item. So double click spear. Then we'll go to the top right. The display name will be spear. So the description can be a basic projectile that goes forward from the direction that the character, I guess, I will say, is looking. And then the icon, we just copy that from the definition over to the item icon. Just like that. And that should basically handle a lot of that. So let's play. I think the only thing now is that we haven't connected to a level up signal, but okay, we can see spear one in the top left. Let's go grab our scythe. So scythe one is now up there, and our scythe projectile is also launching. We can see it's rotating. Maybe it's movement speed is not really enough because we want it to actually go around in a circle around the player. But let's make sure it can hit. Yes. Okay, so it does seem to be hitting enemies. Real quick, in the generated choices, there's a notice about selected shadowing and already declared signal. So that's not great. Going to take the selected array and generate choices. This is on the weapons view script. This is on the Rewards box container here. I'm going to take this and let's control R. I'm going to rename it to selected items. I'm going to hit replace not replace all because remember, we don't want to mess with the signal names up here. So replace inside of this function only, and we want to go down to here. I should also match case and match whole words that'll help reduce the number of extra stuff that gets selected. So selected datapen is now selected items datapen and then we return the selected items. Okay. So that should handle that warning. Okay, so our site scene was not rotating in exactly the way that we would have expected. So one issue with the projectile scripts is that if I try to say, run this scene without launching it from a weapon two D first, that we're going to get this issue where it's going to hit that assert on ready. So what we could do instead of that for testing purposes, mostly, is to have a default direction. So I'm going to say that the direction is going to be equal to vector two dot right. So just have that default there. We'll remove the assert. And so I'm also going to export var the level so that we have the option of setting a weapon level in the inspector. It shouldn't be required for us because mostly we're going to be launching through the launch method, right? That's how we set up the projectile. But just in case we want to test it in the editor, I could set up a test level over here and the right so that we could define a default weapon speed. So let's hit Play. Okay, so now with that setup, I should be able to run this script, and we can see how it's going to move by default, which is pretty helpful for us right up there at the top. I could even create a test scene where we have a camera looking at the center of our so that'll help us further for our visual debugging purpose, so we don't need to run the full game and have to level up each time we want to test the scythe item. So let's create our test scene. So this is going to be a new two D scene, and I will save it at the root. So let's call it test weapons dot TSCn. Save that, and I will drop in the scythe weapon. So scythe weapon dot TSN. We just put that at the root there. I'll rename the root node. So test weapons, and then let's right click and add a camera to D. So this will have a Zoom of three. I think is good. And let's try running the scene by clicking the play current scene button. Okay, so the reason we don't see anything here is actually because we're not supposed to put the scythe weapon here. We're supposed to put the Syth projectile. So my mistake. So let's search forsythe dot Tscn drop that on the root. Now if we zoom in, we should be able to see that. Okay, that's good. And let's hit the run current scene button. So now we can see what it's going to look like when our scythe shoots to the right, and that'll make it much easier and quicker for us to debug that scythe. So now with our test scene setup, we can see how our weapons rotating and moving on the screen, but that's not exactly what we're looking for. So what we actually need to change here is back on the scythe scene, this rotator node, if we want the weapon or the sprite to rotate around a center point, then we actually need to move this rotator offset to the right while keeping our sprite and hit box at the same position. So I'm going to temporarily move these two off of the rotator two D so that they are not going to move with it, and then I'm going to move the rotator two D over to the right like this. Okay, so that'll give us an offset so everything around it can actually rotate around that point rather than just rotating around here in a circle. So let's bring this right in the hit box back into the rotator two D. Okay, good. And we might want to say up the X velocity to 25. Let's run the scene and see if that gives us what we're looking for. So, yes, but that's a little too fast, actually. So maybe we just lower that back down to the default of ten and run it again. So it's the rotation that's actually doing that. So we either need a slower rotation or perhaps we need this to be further out to the right. I'm not sure which. This is kind of just guess checking for me here. So just if you do happen to move the rotator with this sprite and hip box under it, just make sure you move those back to the 00 center point. And then let's run the test scene and let's see. I guess just 360 degrees is way too fast. Okay, so I'm going to go back to the sth scene and we'll lower the rotation degrees. Let's cut that in half, maybe 180. And then let's run our test scene. Okay, that's probably a lot more like what we want, but I've also made the distance of the rotator too far now. Okay, so I'm going to move the sprite two D out and the hip box out, and let's move the rotator back in quite a lot. Move the sprite and the hip box back under the rotator. And let's test it on the test weapons scene. Okay, so let's run that. Uh, almost. But maybe the speed of the projectile still too fast. I don't know. We need to test it out in game. So let's hit play, and this will actually run the main game because that's the main scene. So let's grab the spear the scythe projectile. And there's how our scythe weapon is looking at the moment. I'm not sure I'm super happy with the pattern, but at least it's doing the general concept. It's mostly a matter of we got to tweak the variables now. Let me try having the player get hit and destroyed. Okay, so he does still remove himself from the scene, but we're gonna have to create the game over screen. As part of the polish of the course. So I'm going to run that one more time so we get our scythe projectile. It's kind of like going up to the right. I'd rather it went more, like, centered to the right. Let me see if I can adjust that. Maybe that's the result of the X velocity. I'll try turning that off, and let's run the scene again. Okay, so now it's a bit like this. It'll loop back to the start here. Because the rotator has no extra velocity. And the direction it launches depends on whether you're facing left and right. So at least it's equal application to both sides. You might like this. You might not. It's mostly a matter of just tweaking the variables to get the pattern you want for the movement. So at least for right now, I'd say it's pretty acceptable. Let's level up the spear weapon here. And that is going to level up the weapon, but we don't have a level up signal connected here. Okay, so where we do that is on the weapons display. Let's jump into weapons display. When we set the weapon here, we need to connect the signals. But at that point, I would say this is actually putting too much stuff into a setter for the weapon. It kind of hides what it's meant to do. The refresher was okay. But I think if we're connecting signals and refreshing the UI, that's just too much. So let's create a function set weapon. Which will pass in a weapon weapon two D. Okay, so if weapon does not equal null and and we'll say weapon dot level changed is connected, and then we'll have a callback here, underscore on weapon level changed. Of course, this is going to give us an error because the level changed does not exist on the weapon yet. So let's jump into the weapon, look up symbol. We'll create a signal here, signal level changed, which you'll pass in the level, which is an integer. If that's all I'm going to do with this level, and I think it is, then we'll say set value. We'll say if level equals the value, we return wise, level equals value, and we can level changed dot a MIT. On the levels. So we're just kind of making this property observable, and that's all I'm going to change there. The reason for this guard is if we set the level to the level, we don't want to emit unnecessary UI signals because the level, in fact, didn't change. So we only need to do this update if we're setting it to a new value. Now, I did forget a colon up here at the top, so that will get rid of that error. Now in our weapon display, we have the level changed. We need to create on weapon level changed callback function. So function, copy paste on weapon level changed, and then we have the P level, which is an integer. We're going to return void, and we want to refresh the UI here. Uh, but what we're actually doing here is we're disconnecting from the old weapon signal if it exists. So we check if the old weapon exists here by checking if it's not null. So if the signal is connected, we need to disconnect it. Weapon dot level changed dot disconnect on weapon level changed. No parentheses. And this part is just verifying that the signal is actually connected before we disconnect a signal that isn't connected. So just safety guards. Okay, so we've prepared the old weapon to be totally disconnected, so we can change the weapon now. So we'll say weapon equals P weapon, and then we want to connect to this weapons level change signals. But we can also put a guard checking if it is already connected for whatever reason. This is a little bit excessively safe, but we could just say, if weapon level change dot is connected to this function down here already, and we want to make that inverse. So we're saying if it's not connected, then we need to connect it. So weapon dot level changed dot Connect, underscore on weapon level changed. And we also want to refresh, for sure. So let's refresh that. So when we set the weapon, we refresh, and every time the level changes, we refresh. So now we want to take this property and underscore and just make it private. So this will be more like a private variable. And we don't want to directly modify that. We want to strongly encourage anyone using this script to use the set weapon method instead. So I'm going to select here, and we're going to control and basically find and replace. So make sure you match case and whole words, and then let's change this to underscore weapon. I think we can get away with the mass replace here. So all 11 instances just replace all. That's Control R to bring up that menu. Now, of course, if we ran the game, then we would get an error because we're trying to set weapon, not underscore weapon from the weapons view. So if we go to the weapons view, now in our refresh, we want to call display dot set weapon on the weapon two D with the weapon weapon two D and remove this. So the whole reason for moving the reactive weapon setting code into this explicit set weapon method is so that we know that there's more stuff going on here than simply setting the weapon value. By the way, get rid of that refresh. And we can even remove this, but here at this point, it's basically just a regular variable. But we don't want to hide the fact that it is connecting and disconnecting signals and refreshing the UI. Can get a bit more explicit. So all of this code could have just as easily been under the weapon variable setter, but having this big block function written kind of makes it clear more what our intent is. And I know that that does kind of contradict myself when before I was like, Okay, you can just do the weapon setter and use the refresh. But because we're also doing signals, I think it's just too much stuff going on, and we don't want to hide that from anyone who is trying to set up the display. We want them to know, Okay, there's this big set weapon function. And that is handling all of this extra stuff. Oh, right. So let's go ahead and run the game, and I'm going to level up the spear to Level two. Ah, okay, so you can see there was another reference to the weapons variable, but we made it private. So we want to have a getter to get the weapon rather than accessing the private variable there. Let me take a look at this function and see if we even need it, though. Okay, so since the weapons view is handling freeing up the old displays, we do need the display dot weapon to be replaced with display dot Git weapon. So let's go create that function in the weapon display. So function here, Git weapon that's going to return a weapon to D. We're just going to say, if underscore weapon, we return underscore weapon, else we return null. So why bother having this Git weapon function when you could just get from the weapon variable directly? Well, we made this weapon private because we want to make sure that we encourage any developer to use the set weapon function explicitly. And because that's a private variable, we shouldn't be accessing it from outside of the script. So then we need a public Get weapon function to access that. So it's basically encapsulation and preventing other people from doing things with the script where they're not supposed to do that with the script. Now, once again, underscore is just a practice of indicating it's meant to be private effectively, but GD Script doesn't really have private or public as keywords. So you can't actually prevent anyone from accessing it. You can only indicate your intent with the underscore. Okay, so let's close that and run the scene one more time. I'm going to level up our spear. And let's get the spear upgrade. So you can see Spear went to level two right there and the top left. Of course, that UI is too small, I need to make it larger. So let's go to the gameplay UI Canvas, and I'm going to click here. I'll actually change the layout and we'll center it. So Anchors is going to be center top, and then I want to W offset it from there, kind of over here. Also jump into the weapons display scene. So if I go to transform here, I'm looking more like a 400 pixels by 75. I want to make sure that the anchor is still centered, so I can do custom here and then go back to center top that'll recenter it. Let's look at the gameplay UI Canvas. Okay, that's still good. I'm going to jump into weapons display from here again. And I think the main thing is that we just need to make sure that each of those individual displays, the icon is set to a minimum size. So if I look at weapons display here, let's take the minimum size and make it. We could even say 96, 96. Let's just make it big and then shrink it down if we need. So the text label here, do I need that to be bigger, as well? Maybe I do. Let's just override the font size here. And I'll make it like, let's say, 16. Okay, that's what it's at right now 32. Okay, so we'll try 32. Zoom out. Let's hit play. How does it look in the UI at the moment? Okay, that's a lot better. So I'll go get the other weapon. We have our scythe. So spear, scythe one. I think the only other thing is maybe a little bit of a margin container would help. So close the game, and in the weapons view, our margin container, let's do a theme overrides. I'm thinking constants of two on all sides. We'll hit play. Run it. And that didn't help so much. Let's do four pixels on all sides, I guess, and hit play. Okay, that's at least more visible. I'll make it six. So six on all sides, that should be plenty. Okay, so we go into play mode. Okay, now we have a bit of a barrier between our individual container and the whole weapons view. And that's looking a lot more solid. So it's showing the type of weapon we have and the level of the weapon, and that's probably the majority of what we actually need it to show. So I'm thinking we're good to go here. Let me level up for funzies. Let's get Syth Level two. Yep, Syth is leveling up correctly, as well. So that pretty much handles setting up the weapons view and its full having all the signals connected, correcting the items, making sure they have a description and icon, and just having that clear UI, where we can see what's actually going on with our player. 53. Creating Stat Boosting Items for Player Upgrades: So with our leveling system working the ability to choose a weapons item and upgrade our weapon loadout with a new weapon or an upgraded level of a weapon, now would be a perfect time to add in the ability to choose non weapon related upgrade. For instance, a step boosting item, which will permanently increase our HP on level up. And we could even make it item which has a rare chance of dropping off enemies if we want. In our items folder, bottom left, I'm going to expand that and let's create another type of item. I'm going to right click here and create a new script. And we could call this a stat Boost item dotGD. This will extend from resource. So it's kind of a definition type. And then in our Stat boost item, I'll open that up, give it the class name Stat Boost item. Oh, and this is actually going to extend from the base item class, not resource. So if we look at item, we have the Trapply and Can apply. So let's go ahead and grab those because we need to implement those in our Stat Boote. So I'm just going to paste those in here. So for can apply, we want to check if the target has, A, a stats block and B. Does that stats block have the stat we're trying to boost? We could create a bunch of different classes for, say, HP boost item, cool down reduction item. But instead, we're going to simplify that by having a export var stat name, which is a string name. And we'll set that equal to nothing by default. And then let's have a export var value change, which is a float. And let's set that equal to 0.0. So first things first, I want to get the Spblock off of the player if it exists. So I'm going to say var Stats is a stat controller, and we're going to get that off of ptarget dot get, and we're going to get that stat name as a property. So this can end up being null. So if stats is null, then we're going to return false here. Another thing we want to check is the value change actually set. So if value change is equal to 0.0, I'm going to push a warning. Can't boost stat if value change is set to 0.0 or let's actually say percent s to be a little more flexible, and then percent value change. And we'll return false here. So the reason for that is if we haven't set up the item, it's not really going to do anything, so we'll just skip applying it, and we'll put a warning out there so that we know that that's an issue we need to fix just by changing the value change property to something else. There's not a ready function to check these things like there would be with a node because resources never hit on ready. So this seems like okay place to put it. We'll just check it before we apply. Otherwise, I think we can return true. I think those are the two main constituents we're watching out for. So, the other thing I wanted to make sure, actually, was this bit. So we're seeing if the stats has the stat name, so I actually set this up incorrectly. We wanted to say if ptarget dot get stats here. So first, we're getting the stat controller, and then we want to check if stats dot G stat Name. Does not equal null. If that's equal to null, then we'll return false. So first, we make sure it has a stats block, then we make sure it has the stat, and then we make sure that we've actually set up this item correctly to change the value. Okay, so once if we pass all of that, then we can apply it. So down here at the bottom, I want to get the stat controller one more time, so I'm just copy pasting that from up here to down here. We get the SATs controller, and then we want to get the current value and then add the change to that value. So our current value is going to be equal to stats dot GET stat name, and then we're going to take that and increment it. So let's say, our new value, it's going to be equal to current value plus the value change. And then we set that value on the Sts. So stats dot set stat name to the new value, and I add a little typo there, so make sure those names are correct. And then we want to return true. So we can also check here, once again, just to make sure stats is still actually found. So if stats is null, we'll return false. And we might actually want an error here because we're supposed to check if we can apply right before we apply it. So push and error Stats controller stats could not be found on percent S, percent P target. So that's going to get the node and replace that there. All right. Yeah, so for stats that get name, we're basically changing this to a float for now. The new value is a float, and we set that back on the stat value. Now, some of our stats like HP are an integer. So when we set that back on the stats controller, it's going to cast it back down to an integer, and it's going to lose our float value. So the idea here is that if you're going to be boosting a integer, then just make sure that you use non decimal values in the actual stat boost item. So it loses a little bit of clarity in that we're just grouping integers and floats into the value change, but it gives us the flexibility of being able to handle the vast majority of our stats through one class rather than creating one for HP change, one for damage percent boost, et cetera. Just a lot less scripts that things could go wrong in. So let's create our HP boost item. And the items folder, bottom left, I'm going to create a new resource, which is going to be a SAT boost item. So this will go on the same folder. I'll call it HP Boost, underscore item dots, save it. Now on the right, we have our SAT boost item. So the SAT we want to change is going to be Max HP, and we'd want that to go up 50. Now, I do see a flaw here, which is that if we boost the MX HP, it's not going to boost the base HP as well. So we have to make a decision about how we want to handle that. One way would be that whenever we increase the MAX HP, we adjust the HP automatically in our stack controller script by that amount. So that's an option. Let me check the SC controller if that makes any sense. So search stack controller dot gD and project, open that up, and we can see we have the MAX HP thing. So we can see the change by doing the MAXHP minus the and we could just say here if the change is positive, then we affect the HP. However, I feel like that is really starting to clump up this setter, and there may actually be some items we want to change the Max HP, but not the base HP. So I think a slightly better way of doing it would be in our stat boost item, we actually have a dictionary of stats we want to change, and then we loop through them. That way, we can do MaxHP and HP at the same time. We could even have an item that boosts, say, Max HP, HP, and attack all on one go, and that's just going to require a little bit of four loop. So let's change this let's make it a export var Stat changes. And this will be a dictionary of string name with the float values. Okay? So we'll get rid of this now, and that'll give us some immediate errors. So we want to make sure each of these string names exist in the stat controller. So we could say four key in stat changes dot keys. We'll just check if the key is actually in that controller. So if stats Git key is null, it doesn't have the stats, we can't modify it, that's a problem. And then we can just do down here. For value in stat changes dot values. Then we'll check if the value is equal to zero, then we're going to have that issue there, so just replace those with value. Okay, so technically right here, we're doing two loops, and that's sub optimal. So a better way of doing this would actually be to just get the value from the key. So I'll say var value is equal to stat changes dot G key, and then we can check the value without the extra four loop. So I'll cut that and paste it here, remove the second four loop. That should make it a lot more efficient and easier to read because now we're only dealing with this 14 Okay, now down here at the bottom, we need to do something similar for key in stat changes dot keys. We need to get the value for each of these. So let's tab that over, so it's part of the four loop. And we'll say stats dot Get key. It's going to give us the current value. We take the current value plus the value change. That gives us the new value. So where do we get the value change? Well, we need to say var value change is afloat equal to stat changes dot Get key. Uh huh. And then we have the new value when we add those together, stats dot set. On the new value, hold on. Set name not declared in the scope because the set name is the key. So we want to set the key with the new value on the stats controller. Okay. Now, let's search item in the project. I'll open up the HP boost item. Now we have the ability to handle multiple stat changes in one go. So I will just do MaxHP and that'll be 50. I want to do MXHP before HP because I want to increase the MX HP before I try to boost the HP. So the HP will also be 50 here. So we increase the MX HP, and then the HP will go up 50. If you're at FHP, it should be at FHP a second time. And then let's call the display name here, HP boost. The description will be increases the maximum HP by 50. And then for icon, let's create a new atlas texture. I'll expand that. Let's quick load from our project, and I'll get the 16 by 16 sprite sheet. So let's edit region and pick one out. So zoom in, do we have anything like a health potion, something that makes sense for us. There's a drop of blood that could be viable. Or here's a heart. That's another good option. Okay, I kind of like this flour. So let's get this crimson blood red flour, and we'll choose that so that can be our item icon. And let's test this by adding it as a option in our player level up scene. So in level up selection scene, I'm going to click on our LevelUp selection. And we have our item choices here. I'm going to quick load a new item. So this is going to be our HP boost item. We're going to give it a drop chance of one and hit Add key value pair. Okay, so now we can hit play, and we'll go in here. I'm going to level up, and we're going to select our HP boost. Choose that, and you can see, oh, all of a sudden, we have 160 HP, and that's how level up. So you can see from here it would be very easy to increase other stats like attack damage, cool down reduction, what else? Movement speed, et cetera, for your character as you need to give upgrades. 54. Implementing HP Boost Pickups and Level Up UI Fixes: Wrap up with our upgrade system by creating a pickup that will allow us to pick up and immediately apply one of these items like our HP boost item. So we're going to want to search our project for the pickups. So we have our object pickups. Let's create a HP boost pickup. Start by duplicating the XP pickup two D scene. So I'll right click and duplicate that and then this will be HP Boost pickup, underscore two D, sure, and double click into that. Okay, so first thing I want to do here is change the sprite to be the same as on my item. So I'm going to click Animated Sprite twoD. Let's go to animation here and create a new Sprite frames resource so that we can set a different icon. I'll add frames from a sprite sheet. Let's go into art. Not crimson fanta see Raven icons, full sprite sheet, 16 by 16, zoom in. Make sure on the right, you set the size to 16 by 16 here so it can divide the sprite sheet appropriately. And yeah, we'll find that flower I was using. So click that, add the frame. And boom, there's our item. I can look at the XP pickup, but this is actually just a pickup that immediately adds experience. So this script actually skips the whole necessity of having the item. So what we're looking for is more of an item pickup two D. So let's rename our base node, item pickup D, and we're going to right click it, detach the script, right click attach a new script, and this is going to be a item underscore pickup, two D, this is going to extend from pickup to D. We create that, give it the class name item pickup two D. So this is going to apply the pickup item to the object that picked it up. So in the level UI, we immediately applied the item to our player. Here's just a little different. We have to pick up the item on the game map first, like we pick up the EXB and then it's going to apply the item effect directly to the player. If we add items in an inventory, that apply method would be more like add one of the item to the inventory array and go from there, but our game doesn't need inventories, so we're basically skipping. But our game doesn't really need inventories aside from our weapons loadout, which works a little different. So we don't really need to get into that for this project. So our item Pickup two D, of course, we want to export var an item of type item. Okay? So save that, and then we can click here and let's assign if we want, we can assign the default item. Maybe I'll just leave that blank, though. Let me take a look at the base pickup two D script, right click into it. So we need to implement the take function. So let's copy that into item pickup two D. I'll paste that in. We'll implement it in a second. I'm also going to do function underscore ready, returns void. And we're going to assert that item is not null. Each item pickup must have an item assigned to pick up. Actually, I think assert is a little too strong here. I would probably go with more like push error. So we can see what's going wrong in the console, but it's not going to interrupt the game flow because when we instance the item pickup, it's probably going to be off of a drop, and we don't necessarily want that to completely break the flow of our game testing. So I think push error is a little more appropriate here. So for we're going to remove the underscore from the P target. So first, I'm going to say, I item dot can apply to P Target, then I am going to apply it to the P target. So item dot try apply to PTarget. And if that happens, I will remove it from the scene quarter. Now, maybe it would be better if the T function also had a boolean to return false. So here, the nature of changing this to a Boolean means it would have to go back to the base class and change that. And then any class that also overrides the T function, it'd have to change it. So that's one of the flaws of depending too much on inheritance for your classes, rather than using a composition approach, like you see in the inspector right now, composition would be more like you have a bunch of different small scripts that each have their own single purpose, instead of having one base script extend a bunch of other scripts. So, for instance, player might have extended character, and then there might be another class above player like Spearman or dragoon, which adds more stuff onto that. But then if you get a lot of scripts directly depending on each other, a small change like just changing this to a Boolean might mean you need to update it in multiple places. So just something to think about. Not really that problematic here. Okay, so what we're going to do with this item pickup is put it on the game map now. So HP Boost Pickup. We have that. We've saved this to the project. Oh, really, what I think I want to make this do is make this a base scene so I'm going to rename it item pickup two D. Now let's search for pickups down here. So I'm going to right click here and I'm going to do a new inherited scene. And then in this inherited scene, I can assign the item. So I'm going to quick load our HP boost item and then save it back in item pickup as HP Boost pickup. Two D. And then that is the scene that we put into the game world. In a way, creating an inherited scene like this is really similar to extending a script and getting all of the base into your new object. But in certain circumstances like this, where we're just changing which type of item that scene setup has the HP boost pickup, having the HP boost item, but otherwise just being a normal item pickup two D. I think in cases like that, this actually makes a ton of sense. So we want to put our item into the game world now. I'm going to search for world dot TCN. Okay, I'll zoom in here. We have a player there, so I'm going to search for the HP Boost pickup and try to drag this into the game world. I could just put it in there, but let's put it under pickups first so that it has the right parent W to move it. Let's put it over there and maybe a second one over here just to make sure we can do it twice. Let's hit play, and then we're going to go get our pickup. So I'm going to walk over here, and boom, we have 150 HP. Here we can choose if we want spear or scythe. Interesting, it's not providing the option for the third pickup. Maybe I need to be added in. And then I'm going to go here and get our next HP boost. So there is our permanent Sp boosting item, and that works a little different than our EXP, which just directly modifies the stab. So let's check the level up selection, and I'll just make sure that the item is still there. Somehow it got onset, so I'm going to quick load the HP boost item value of one, added in. So now we'll hit play one more time. I'll just make sure it's working again. Get the HP boost from the level up, select that. HP goes up, we select here and here, so we get three HP boosts. That's working good. Very last thing I want to adjust, and this is a quick one is that for our reward box selections, they don't scale down to the same size. When one of them has a longer description. So I want to jump into the reward selection scene. Click on the root, and let me see here. One of these needs to expand all the way. So actually, the quickest way to debug this would be to hit play. And while the game is running, I'm going to go over here and we're going to test, change the size here. And then I'll tab back and see if it updated. Okay, so our HP boost area, let me check the root. This is set to fill expand. Maybe it's the description that needs to do and expand. So we can see the root here has expand and fill set already. So I'm going to check the margin container and make sure it's also like that. So we'll do layout container sizing. Fill. Okay, so another thing I could look at remote view to see the notes that are actually currently running in the game. And let's expand world Canvas layer down to UI. We have the level of selection. We have our panel container under that, the margin container, the V box container, and, the Rewards box container and our three selections. So why are these sized differently? So we can see if we look over on the right over here that the sizes for the Y, they're all equal. That's what we would expect looking at this. They're all stretched all the way down. So let's find the level where they're not. The smallest one is the third one. So let me expand that. Click on Margin Container. We can see the margin container. Is the 20 pixels full size, so that's not it. Let's go down to VBox container. The VBox container is 214. That's also what we would expect because the margin container has a margin. So I guess it is actually this description box. You can click on the nodes and actually see a box when it's selected here. That's kind of cool. I think that might actually be new. So we just want the description to push everything all the way down. Okay, so we know what we're trying to expand. So in the local view now up here at the top left, let's click on description. Ah, and there we go. There we go. So down here in container sizing, it's set to shrink begin. I want that to actually be fill and expand. Now if I I'll tap back to the game, it is going all the way to the bottom. Okay, so you can see that, right? These three are going down. I'd also like the icon sizes to be equal. So maybe we need to set a specific absolute size for the icons. I think maybe the spear icon was set using a bigger one, but I want to make sure that they all go to the right size. So if we click on icon Rec here, let's try changing keep size to ignore size. And I think that's making them equal now. Yeah, okay. It seems to be lined up. If you have any other issues, that would be kind of a debugging process you can use to kind of fix your UI while you actually have the game running. So it's pretty helpful because you can actually see how it's going to immediately update you can change your control inside of Gado and your local scene, and it will update immediately in the remote view. So that's actually one of the really cool things about Gudo. Okay, so with that, I think our upgrade system pickups, everything we need, in that regard is just working really good now. So that is really the bulk of the course up to this point. We're just going to have some polished things to go through, like saving and loading a score system, like how many enemies we've killed between play sessions, gilving a game over screen for when we actually lose. So we can go to our main menu and restart the game, that kind of stuff. But the core gameplay is pretty much there now. So that's going to be it for this video, and then we will go into the wrap up and polishing stage of the course. 55. Creating a Health Potion Pickup for Enemy Drops: I want to tack on one more video about doing pickups for your game items in Gudo. So I created the pickup for HP boost item, but I want to create an item that would be dropped by the enemies. This will just be a simple health potion. So I'm going to search for our HP boost item in the project, and let's duplicate the HP boost pickup. So right click and go down to duplicate and we're going to call it Health Potion. Two d dot TCN. Now we want to open up that scene, Health potion tod dot TSCn. So our item here is going to be called Health Potion two D, just to indicate it's on the two D game world. For the HP Broca item on the right, let's click on the drop down and then make this unique. So we're essentially taking the base and creating a unique copy of it, and now I'm going to right click here and save it as a health potion. So right click Save As, and then put it in the project and items, and I'll do Health potion. Underscore item TRS. Let's change the stat changes to only increase the HP by 25, and let's remove the Mx HP from this boost. Now for our display name, I'll say Health potion. And for the description, I'll say restores 25 HP, period. And then for the icon, let's select another icon from our region and find a Health potion. I'm sure there's a few of them in here. So yeah, closer to the top, we have more than a few. Really Take your pick here. I'll go with the typical looking one. And let's select that. And then we also need to grab that same icon for the animated Sprite two D. So click there, go to animation, do a new Sprite frames, and then selectelect from the frames. Art icons, full sprite sheet 16 by 16, zoom into the top set size to 16 by 16. And then control middle mouse reel zoom into the top and grab the same Health potion, which is right here. Should be only one selected. Hit add. Okay, and there's our Health potion. So this should already be able to work. Let me throw it into the game world as a copy. So we have our Health potion two D. I'll just put it in here for right now, and then we can just use the Health potion. So actually, the base health starts at 50. So if I go over here, you can see 50 goes to 75 out of 100. So that's working. Okay, so then we need to go into the skeleton scene, and we would need to modify our drops component, but we actually never set up a drops for the skeleton. So let's go to Oc. I'm just going to select it here and Control C, copy it over to the skeleton. Then in the top right, we have our drop definitions, which is already going to have the EXP drop with a weight of 1.0. So we want to add in the ability to sometimes drop a HP potion instead of the EXP. I'm going to add an element, and I'm going to click a new drop definition. Then just for testing purposes, I'll give it a five drop weight, which means 86% of the time it's going to drop a health potion, not a EXP, just so you can see it working. And then let's quick load our health potion two D. Okay, so for our drops that actually work, we also need to add it as a call on our death animation. So let's click on the animation player, go to Death, and we'll do a call on drops here. So at the start, let's add a call method on drops. And we're going to right click insert a key to drops with one time. That's fine. There was another mistake I made on the item pickup. When we push the error for item is not null. That should be actually pushing if it is null because we're trying to verify that we actually remembered to set an export item on the item pickup. That was a simple logic reversal there. Okay, now let's go ahead and hit Play and we'll try to defeat a skeleton. I'll just collect some items in XP here, get the site. And we'll just wait for a skeleton to pop up. In the meantime, we can see that the orbs are still just dropping their XP. Always, that's the only possible drop, and there we have a skeleton. So if we defeat this guy, 86% of the time or so, it should drop a health potion. So that time didn't. We'll have to defeat a couple more. And let's see. Do we get the health potion to spawn? Yep, there we go. Health potion. So we actually saw that both of them can spawn. Another health potion came from that skeleton, another health potion. So you can see that the weighting is definitely working the vast majority of the time we're getting the health potion instead of the EXP. So now that we know it's working, we can just go to the skeleton. We'll go to drops. Let's take the drop definition on the potion and make it a 0.1. So that means ten out of 11 times the EXP is going to drop, and one out of 11 times the health potion is going to drop, and that's kind of how the weighting works. You add in all the possibilities plus their weights, and your odds are your weight for that specific one divided by the total weight of all of them together. Okay, and that's pretty much how you add a health potion into the game. So you can see that once we have our stat item, our pickups that give us a stat item, and we can just apply them to the player and we can just set as a string name what stat we're affecting, it becomes really easy to modify a lot of different things on our character. 56. Implementing Camera Shake for Player Hit and Death Effects: We're now moving on to the polished part of the course, which is going to wrap things up by adding in some nice to halves. We're going to start with a camera shake on hit for our player to make the game a little bit more intense. And we'll also do things like add in a game over screen and the ability to track high score. So let's start off with the camera shake. Let's go to world. And on the world, we have a camera two D here. So we could just attach a script right onto the camera two D. So I'm going to click here, attach a script, and let's say shake camera two D. Underscore two D, I should say. And we'll create and we'll put that in, I suppose, the UI folder. So open that up and create. I'll give it the class name shake Camera two D. And let's work on setting this up. So, first off, we need a reference to our player like many other scripts so that when the player takes damage, we can have the shake effect trigger. So let's do Export var context of player context. So just like before with other UI elements, we get access to the stats through the context. And then let's add some variables to control our shake strength. So, first off, export var Max shake strength which is a float, and I'll set that to 10.0 by default. We can also have a shake duration. So export var shake duration, which is a float, and we'll set that to 0.3 seconds by default. And then I want a separate set of variables for when the character dies. So this should be an extra strong shake to give the impact of, Oh, you lost your player, you lost the game. So let's do Export Var death shake strength, and that's going to be a float up default to 20.0. And then we have at export Var death shake duration, which I'll set to a float of 0.6 seconds by default. So when our script starts, I want to get the stats of the player. So I'm going to say var underscore stats is going to be a stat controller here, and then we need function underscore ready. So we'll say context player dot stats, and we're going to set underscore stats equal to that. Let's assert that that's not null. G stats does not equal null. Must have a reference to the player stats in order to shake the camera. Okay, so when our camera is shaking all over the place, we want to remember its original position so that once we're done, we can return to that position. Let's have another variable here. This is going to be a vector two D, underscore original offset, which is going to be a vector 20. Okay, remember the equal sign there. So we're inferring it's a vector from vector two dot zero, and this makes it a vector 20 as well. Okay, so our original offset on ready is going to be equal to our current offset. So this is the offset of the camera because our script extends this camera directly. And then we need to connect to the stats controller HP changed signal. So underscore stats Hp hanged dot Connect. And we're going to connect that we're going to connect that to on HP changed. So let's create that function down here, underscore on HP changed, and that's going to have the stat data. So P data and this is stat changed data. This time when we have our callback function, I'm actually going to use the P data directly because it's very useful here for determining whether we're doing a regular shake or we're doing a death shake. So if the HP change on the event is greater or equal to zero, we just want to return because we're not shaking on a healing or if the HP doesn't change. So now we want to check if the event dot u value is greater than Zarel. So is the character still alive, essentially? So, if that's the case, then we're going to use the normal shake values. Otherwise, we're going to use the death shake value. So underscore shake strength is going to be equal to our max shake strength, and then underscore shake time is going to be equal to our hold on. Our Shake duration. Okay, so we need those local variables up here. So var underscores shake strength is a float and Var underscores Shake time is also a float. There's of course, going to default to zero. Oh, and when I put event here, I actually meant Pdta. So pata dot change and pdata dot neu. Okay, so otherwise, there's one other case here, which is that the health is below zero or equal or below zero, I should say. In which case we're going to set the shake strength to the death values. So underscore shake strength is going to be equal to the death shake strength, and the shake time is going to be equal to the death shake duration. Okay, now, there's one other thing here, which is that depending on how healthy our character is, we may want to increase or decrease the strength. So that's why I had MX shake strength here. Right now, it's always going to be that max of 10.0 I set up there. So I think a good way of doing this would be to check how much damage you dealt versus what the max HP of the character is. So if an attack does half of their health, you want that to be a pretty strong camera shake. But if it's just a damage over time tick, like, Oh, it does one damage a second, that's pretty inconsequential. You can just make that a very small shake or completely eliminate the shake altogether. So var, damage ratio is going to be equal to absolute value F, of the event dot hg. This is the amount of health we just changed, and that's going to be divided by the max of either one point oh. So while editing, I noticed that the math here is incorrect. Calling the max between one point oh and the stats dot MX HP doesn't make a lot of sense here because we actually want the ratio to be between the change and health, the absolute value of that anyway. And the MX HP. So really what we want to do is say var percent change. And what we really want this to say is the absolute value float of the p data dot change divided by underscore stats dot MX HP. So that gives us our ratio, which should be 0-100. But we want to clamp that down. So let's say clamped ratio now. I'm going to cut away this and let's say clamp F on the percent change, and we're going to force it between 0.0 and 1.0. So if for some reason our change value was actually greater than our total MX HP, like, let's say we took 9,000 damage and our max HP was 100, then that would actually be a percent change of 90. So we actually want to just limit that to either 100% or if we're going the other direction, it can't be below 0% change. So our total value is going to be 0-100, no matter how big or small the change was. Multiply Max shake strength by the clamped ratio. The last thing we got to do here to make sure this doesn't actually return as zero is take the MX HP and cast that to a float. So that's important because otherwise, integer divided by an integer would just drop the decimal point before we even convert it to a percent change. Now we can go ahead and run and make sure it's still working as expected, so I will find a guy to hit us. Okay, and we still have that screen shake, right? Of course, the death one will be much bigger. So because we changed the math, you may also want to adjust the max shake strength and make that stronger, like 20 or 30. And remember, this is always going to be a percentage of the max shake strength. So you need to factor that in when you're actually figuring out how strong the shake is going to be. So, let's see, at 30, max strength is kind of like that. Maybe a little strong. So I might even drop that down to 20. As more of a final number. Okay, now, finally, we actually need the process for the shake. So let's go up to the top here. After underscore ready, I'll say function underscore process Delta. So I'm just autofilling that. And then we're going to say, if the shake time is greater than 0.0, then we're going to actually shake the screen. So how we do that is with some math. So we'll say underscore shake underscore time is minus equal the Delta. So we remove the time from the time that we're elapsing here, and then I'm going to get the X shake and the Y shake value and apply that offset to the camera. So var shake X is going to be equal to random F range of negative 1.0 to 1.0 times the shake strength. So essentially, we're getting a random direction multiplied by our current shake strength. And then var shake Y, of course, you can imagine that's the same. Is going to be equal to random F underscore range, negative 1.0 to 1.0 times underscores shake strength. We take both of those, and we apply it to the camera offset. So offset is going to be equal the normal offset plus vector two, and this is going to be shake X and shake Y. So essentially on every frame update, the camera is going to shake to a new random position slightly off the original offset, and it'll do that until the shake time is done. So if the shake time is zero or less, so we'll say else, then we want to return the camera offset to its originals. So offset is going to be equal to original offset. Okay, and if I have this right, that is pretty much our script there. The last thing we're going to need to do is assign the player context and the inspector. So quickly over here, drop down, quick load, player context. And let's run the game and let our player get hit and see if it works as expected. Okay, so here's an orc, and there's our camera shake every time we get hit. And there's our camera shake on death, which is much stronger, as you can see. So essentially, that's our whole camera shake effect. Next, obviously, would be a good time to put in the GameOver screen so we can return to the game Start menu, which we also need to create. 57. Creating a Game Over Screen with Main Menu Navigation: One of the last things we need to add is a GameOver screen and a min menu screen. So GameOver is, of course, going to pop up when our character is defeated. So we'll need to access the player context again so we can check if I live is false. So first, I'm going to open up the file system and go to the UI, and we're going to find our main gameplay UI Canvas that'll make it a little easier to work on so that we can see where we're going to put the game over screen. So for now, I think I'm going to take the level up selection and hide that. It'll make it easier to work on. And let's take the UI. I'm going to right click Add a child Node. And so we want a new control this control is going to be centered. So in the layout, let's do layout position anchors and do center. Now, te click also go to mouse and turn filter to Ignore. And let's right click and add a panel container to this control node. So panel container. That'll be the background. We'll take the panel container and stretch it out. So zoom in, make sure panel container is selected over here, and let's expand that. So our panel container we want centered on our parent control. So let's go to layout now and do layout anchors center. That should put the parents anchors right here in the center, so that's good. It'll make it centered on the screen. Let's rename the parent node to be GameOver screen. Also, right click it and save it as a branch in the UI folder, GameOver screen. So now we can just jump into this and edit this purely. So our GameOver screen, we're of course going to need something like a rich text label as the title. Let's right click Add a rich text label. And I can just say GameOver inside of you. We'll probably want to make the roots based theme as our main game theme as well. So quick load the theme here, game theme that'll give us the Pix art font. Now we can also take the Rich text label, and let's say fit content. We also want to say centered text, and it appears to be too large as well. That won't be an issue, I think, after we add the VBox container in so, right, click on the panel container, add a VBox container. Move the rich text label under it. Okay, that fix the sizing of that since now it's controlled by the container. Let's right click on the VBox and add maybe a texture rat and then we'll get a sad icon for there. And then we'll get a GameOver icon. If we use texture and do atlas texture, we expand it. And then for the atlas, let's quick load our icons. We could just use the 64 64 here if we're going to keep that in the project. Edit region. Okay, and then we need to zoom out and just select something that would be good for, like, a game over. So there's a lot of options to choose from. I'm thinking this skull that kind of looks like it's sleeping. It's not too dark, it's not too cute, so I think that might work. Let me move this out of the way so I can see the region editor. I might need to actually change the size here. I'll try a step of four on the X and four Y. That way, I can stretch this out to the right, and then the left and the right are going to match each other. I'll also pull the bottom up a little bit. Okay, so with the step 44, that worked a little bit nice so I can get close here. We zoom in on our GameOver screen. I'm going to want I think filter clip. I think also for the stretch mode, we need to keep aspect centered, and that looks fine to me. So we will also add in a description box. So, right click on the VBox, add another rich text label, and we can say, you have been defeated. We try to try again. Sure, let's go with that, fit content. Yep. And then we just need a button here at the bottom to go to the main menu. So, click on VBox, add a child node button. This button will say main menu. I'll attach a script to it. So this will be a new button. Okay, next, let's take this button here and I'm going to rename it scene change button because we're going to go back to the main menu. Let's attach a script to it. So this will be scene change button dot GD. Create that. And we want to add Export var a same path. So we can actually make this an export file, and this would be quotations star dot TSCN Okay, so our scene path is a string and then on ready we'll connect to its pressed signal, so pressed dot connect on button pressed, and then function underscore on button pressed will just change to that scene. So let's do Gtree dot change scene to file, and we put in the scene path, and then that'll change our current scene to the main menu scene. As an important check, it would probably help if we say assert scene path is empty. Equals false and say there should be a seeing path to change to when this button is pressed. Okay. So one more thing I probably want is a margin container. Let's right click on GameOver screen, add a margin container. I'm going to put it directly under the panel container and then VBox under that. Okay? So now we can set values on the margin container and theme overrides, something like four for all sides, and that'll make it look a little nicer, giving a little bit of a buffer for the text. And our game over screen, we need to make sure that this will pop open the UI when the game is lost. So I'm going to attach a new script here. UI game over screen. Create that. Might want a class name here, game over screen. And let's get a reference to the player through the context. So at Export vara Context is of type player context. We'll say function underscore ready, and on ready, we'll do context dot player dot stats, and we'll get the Alive change. Let's connect to that and say on player Alive changed. So we have our function underscore on player Alive changed. I don't remember if that had any parameters, so I'll click on the symbol Alive changed, and we'll check, it has the live Boole so go back to the game over script, and we need that as a parameter here. So P Alive is a Boolean. This will return void. And if P Live is equal to false, then we are going to show our whole game menu here. Okay, now, our scene change button style isn't going to work because we have no main menu scene. So let's create a placeholder. Add a new scene user interface, and this will be main menu. I'll save it into the project. So let's just do that at the root of the project main menu, save it. And then in the game over screen, let's assign that main menu as a path to the scene path and the scene change button like this. You can just drag and drop. That's the easiest way to do it. Okay, also, before I forget, go to Game Over screen and assign the player context. So just quick load that player context resource. Now we want to go to the world. We can see the game over UI is right there. So let's see if we can just test it real quick. And make sure we can go to the main menu through it. So I hit Play. And it's right there. I'm going to hit main menu, and that gives us a blank screen because we haven't actually added anything to that scene. It's just a blank UI element, essentially. So that is actually working. I think the other thing we want to do is on ready for the game over screen. Let's just make sure it hides. So we'll say hide on ready. In our gameplay UI Canvas, we may also want to hide it there, but no need necessarily mean if it helps. But I'll just leave it visible by default so we can test the height. So it's hiding, and now we just need to go get defeated, and it should pop up if it's working correctly. So two hits, three hits, four hits, five hits. There's our game over pop up, main menu, and the flow is working. Last thing I think I need here is just to resize this. So I'll take the game over screen inside of our game play UI Canvas, and let's take the layout form and take the scale to three. That should be big enough. So I can test it one more time real quickly in game. Okay, so one hit, two it, three, four it, five hit, game over. And we get the main menu button. And that's the basic flow. 58. High Score Tracking and UI Display for Enemy Defeats and Survival Time: One nice to have that our game is lacking is a high score, being able to track how many enemies we've defeated in a game, and then remember that score in a safe file for a high score sheet that we could see in the main menu screen. So to implement that, we have to first start by counting how many enemies we've defeated whenever an enemy is defeated. So we're going to need some combination of reporting on the death state to a stats manager or high score manager, and then taking that value after the game ends and saving that to update the Safe file. So earlier on in the course, we added in a game manager Singleton and never really did much with it. In fact, the way I implemented the game over screen ended up just being using the player context rather than having to report on the game manager directly. So let's actually give the game manager something of a purpose so we can use it in the course. So we'll have an object inside of a game manager for game stats, and then we'll track that during the game. And then we set that whenever we start a new. So in systems, I'm going to right click and do a new script. Let's say it's game stats, which will be a ref counted because we're actually going to just make it a child of the game manager Singleton. So open up game stats, and then let's say if our enemies defeated, which will be an integer that defaults to zero, and we'll need a class name up here at the top. So game stats. And then in game manager, I'll have a function start, which will return void, and that's going to take our game stats and reset it or set it to a new object. So for stats of game stats. This is going to be stats equal to GameStats dot nu. Let's go with that for now. And when the world loads, I'm going to want to take the game stats and reset that. So let's attach a script to the world. All right click here, attach a script. We'll put it in the base scene. So create that and I'll say function ready, and we'll say game manager dot SAT. Okay, so whatever we need our game to load at the start, we'll just have that there. So as long as we are using the world script here, then we'll be able to manage that. We may also want to give this a class name. It could end up being a pretty major script if we were to continue with the series. So I think it deserves the class name world. So now, whenever we start a new game, it's essentially going to reset the stats. And then maybe report game over. We could consider going in here a little later and save high score stats as to do. Okay, now I want to modify our stats, and I want to report whenever our stats are modified. So in game stats, we'll create a signal for our enemies defeated. So signal enemies defeated, changed. We'll take a amount and integer as a parameter, and we'll follow a similar pattern where whenever we update the value, and we'll guard against if it's the same values. I enemies defeated is equal to value, we just return. Otherwise, we're setting a new value. So enemies defeated equals value, and then we say enemies defeated changed dot m with a new value. Okay, so just whenever we update the property, we report it with a signal so that we can observe the property. And I think that's about all we need to set up a UI. Okay, so now that that's there, whenever we have the Death state on, say, our orc or our skeleton, let's jump into the death state script. So on our Death state, I'm thinking on Enter makes the most sense for saying game manager dot stats, EMS defeated plus equals one, and that's how we will update that counter keeping track of all the enemies we've defeated. Now the last thing we need is a little UI in order to show how many enemies we've defeated and so inside of our gameplay UI Canvas, let's add another let's just make it a panel container. Add child panel container here, and we will put the layout as the anchors in the top right. I'll stretch it out a bit like this. Offset it. Let me name this to be Game Stats view. Then right click save a branch as a scene and the UI, of course, jump into the scene, and let's right click it and give it a script. So this will be Gamestts view. I can give it the class name, gamests view. And in the two D view where we edit it, let's add a label for our actual enemies defeated. So right click Add child node. We'll start with a HBox container, I think, and then right click Achil Node, Rich Text label. I'll just duplicate that. So the first one will be for showing the text, and then the second one will be the number. So this will be the stat label. And then the bottom one will be the amount label. Okay. So the SAT label is going to consistently show enemies defeated, colon, let's fit to content. And then the second one, I'll pour square brackets, zero. So that's just a placeholder. And I think we need these to stretch out so we can actually see them. So let's try going to layout, container sizing and expand on the horizontal. Yep. And then amount label, same thing. Go to layout. Container sizing, expand on the, expand on the horizontal. Okay, so now this is kind of what we get right now. Okay, so now take the game stats hit Q to go into select mode, and we'll shrink it to what we need. A margin container would also be nice here, so I'll right click and add a child margin container, and let's put HBox container under that on the margin container. Theme overrides constants and maybe four pixels on each side. That looks a lot better. I think that might be functional enough for right now. I'll go back to game UI Canvas and make sure that this is once again in the top right hand corner as an anchors, top right, and then offset it by hitting W to move it. Then just move it down off the corner a little bit. Okay. And then I need to in the script of GameStts view, I need to actually connect to the signal on the Gamests. So function underscore already. We are going to do game manager dot stats dot enemies defeated changed connect on enemies defeated changed. I'll take that name and make a new function down here, function on enemy defeated changed, which is going to take the P amount and integer return void, and we are going to update the value there. Okay, so basically we take the new amount and we set that to the label. So at Export VR. So anyway, we take our enemies defeated label, and we set the text equal to a string of the amount and that is pretty much that. We just need a colon here at the end. Maybe on ready, we want to assert that enemy's defeated label exists. Must set enemies defeated label to show enemy defeated count or mount. We'll be a little bit more precise. Now, click on the Game Stats View, jump into the scene. And inside of here, we want to assign that amount label. So click on assign and choose the amount label. And if I've got it right, that should be everything here. So let's play and give it a shot. All we need to do is defeat a couple enemies and see it pop up in the top right. So problem here is that stats is not initiated into anything, so I want to right click Lou symbol I guess the issue here is that the stats might not be set before ready runs on the UI, since that's the top of the world, I guess the children ready first. So a better way to do this, I guess, would be to say the stats are equal to gamestts dot u. So this will always be created when the game starts. And then we want to call stats dot reset. So we'll just make a new function for that. This will also mean that we don't need to reconnect signals because we're still using the same object. So it is actually a better way of doing. Let's jump into Game Stats and create our reset function. So function reset. We just take everything back to its default values. So enemies defeated equals zero, and that should be all we need for right now. So now stats should be set as soon as the game starts before world is even loaded. And then from there, we should be able to connect to the signal. Okay, let's try one more time. Alright, play. Okay, we got a count in the top right. Of course, it's way too small, but we can at least kind of see that. So let's defeat one orc and make sure it's working. And we got one there to show up if we kill another guy. Okay? There we have three. So yes, the count is working for sure. I just need to scale up the UI. So in gameplay Canvas, we'll just do that here. A simple transform scale of three. Move that over to the right here. And now we need to actually cut off a lot of this extra space. That's just taking up way too much for no reason. So I'll need a little bit of an edit there. Let's jump into the gamests view. The amount label, I'll actually change that back to zero without the square brackets so that before we defeat the first enemy, it doesn't have any wonky display. Okay, now for the size being too big, I think a lot of that is just that I manually set the gamests view size. So let's shrink that over here to what we actually need. So we can make these a lot smaller. And this second view does not need to be nearly as big as the first one. So there's no reason to have them take up an equal amount of space. I think what we might actually want is more of a flow container. Let me change the type of H box container to a flow H flow container. Okay, now I'll go down to the amount label, and let's uncheck expand here. And then let's try taking the custom minimum size and setting this to, like, 40 pixels, probably big enough, so I can hold up to the thousand digit at least. And then the rest of the space is going to be occupied by the enemy defeated. Let's also change the view on the base to our default theme of Quick load game theme. Yeah, okay, that's going to have a big impact, too. So now, if we look at it, I can actually shrink this down. I should have done that at the start, really. So something like that, that's our space for the label and the number over there. We can go to the game play UI Canvas and we can shrink this down dramatically. You can see that everything that's not that right side pixel just gets consumed by the text label on the left. So it's better if both of these elements aren't trying to take up an equal share of the view size, but one is just like a static size, and the rest just expands. And then I'll just put this a little bit more in the corner over there. We'll hit Play. And see how that turns out. Okay, that is much better, more appropriate size. So, of course, just to test again. We'll just defeat a guy, and that gets updated, so we have our enemies defeated count. So let's add one more stat, which is how long our player has survived in the game, another useful stat to have and good for our high score. So in our Gamests view, I'm going to duplicate this HBox container. Now we're going to need a VBox container so they can layer on top of each other. Right click on Margin Container, add a VBox. Move both of these H boxes on under it, like so, and now change the stat label on the left to time survived colon. And if we go into GameStatsVew, it's going to be a really similar setup. We'll just connect to the stats of how long we've lived and show that on the time survived area. So let's right click into Stats, look up symbol, and open that up. So we're going to have var time survived, which is a float equal to 0.0. We'll set the value on it. We shouldn't really need this guard. It's just an extra. So I'll just say time survived equals value. And then, of course, time survived, changed emit, time survived. And then we'll have that signal up here at the top. Time survived, changed. Which is going to be a time float. Okay, so the whole reason to have having this guard is just that if for some reason, something in the game set the value to the value it's already at, like, enemies defeated was 50, and now it's 50 again. I would trigger an extra UI refresh. But honestly, first off, that's unlikely to happen. And second off, it shouldn't really make a big deal out of anything. So it's more of just on a technicality basis. It's only changed if the value actually changed rather than really a problem for the game. So I just wanted to clarify that. Okay, now, our reset also needs to take time survived and set that equal to 0.0 because we're resetting the time. Okay, so let's see what else? In our game world, I'm thinking we want to increment the time. So when the world is running, we'll have a function underscore process. Going to take the game manager dot stats dot TV plus equals the Delta. But this can only really occur while the player is alive. So let's just do Export for the context at Export VR context, player context. You can see where we're using this context resource all over the game in different places. So having that as a saved resource, is extremely useful for being able to spread out the reference for all of our UI and other nodes that need to reference the player without directly having scene reference to the player. So hopefully, I've made it clear how helpful that can be. So we have the player context, and we only want to increment the time survived if the player is alive. So if contact dot player dot stats dot Live, then we'll increment the time. Otherwise, we're just going to skip over that. And that should be good enough for now I need a reference to the new amount label. So we'll say at Export var, Time label is a rich text label, and this is the the GameSts view, of course. So say game manager dot stats dot Time survive change dot Connect, on Time survive survived, changed. And we just have another callback down here. Function on time survived changed, gets the PT float. We're going to return void, and then we set the text on the label. So time label dot text is going to be equal to a string of the PT, and that's probably good enough. I'll just do an assert on the time label as well. Make sure it's set. Need the time label set to update the time survived. Okay. And in our Gamests view, let's assign that other amount label. We may want to rename these HBox containers. So the first container was the enemy's defeated container. And then the second one is the time survived container. So just clarifying which one's which. Let's make sure it's in gameplay UI. We'll go out to the world, see that it's still there. Hit play to test and run and see how that's going to look. Ah, okay, so I haven't set the context in the world, of course. So on the world node, we have to quick load the player context. Okay? And now, if we run, the time should be able to increment. And then we have the time up there in the top right. Now, this is mostly working. I think there's a couple issues here. One, it looks like we need to expand this a little bit larger vertically. Oh, nice. When the game's positive, it also stops. That's important. And then, secondly, I don't think we need to show all of the milliseconds. I think reducing this down to one decimal place would be a little less stressful on the eyes. You can see that the second and third decimals are flying. You can't even read it. So there's no point in showing that information. Okay, so to change our text setting function, we're instead going to do quotation percent one F. So this is going to give us the float value with one decimal position, and then we give a percent symbol so that we add the PT variable to actually set up that string. Okay, and now we can hit play and we'll see that the time only shows the first decimal. That is ten times more readable and less distracting. Major upgrade. So I think that is looking great for our game stats. I think the only thing I might change is put the time survived on top and the enemy is defeated on bottom and hit play one more time. I think that's just going to look a little better. Time survived. I think I want the most flashy one in the top right, and then the other more minor stats showing under it, the ones that are going to update less. Just a minor visual preference there. Okay. And just to show, we can still defeat orc, and that counters working. So the game stats are good in the gameplay at least. 59. Saving and Loading High Scores with JSON: Okay, now that our game stats are working, we want to take that and on defeat, we want to update the high scores and save that to a JSON file. So this would be basically the start of a save load system. And this type of game, specifically, there isn't so much a save state as just high scores, though, but the process would be really similar if you needed to save a game file. Basically, you need to serialize data to a file, and one of the most convenient ways to do that is JSON format. So for saving and loading the data, I want to have a save load object. Now, normally, I would make this a separate system, but the only time we need to save the data is when our character is defeated, and we're going to handle that through the game manager. So I'm actually just going to in systems, create a new script, and I'm going to call it save load. I'm not going to call it system because this is more of a component for the game manager itself. And this will be ref counted. So I'll create that script. Let's expand systems. Open it up. We could give it the class name save load. And we're going to need two functions here function save stats, and this is going to take the current game stats as a parameter. So stats of game stats, and we'll return void here. So this will take the current stats and turn it into a dictionary of data, and then we'll serialize that data. So VR data is equal to and I'll put in and I'll put in curly brackets here at Colon, stats dot two dictionary. Now, this method, of course, doesn't exist. We will write that in a minute. And once we have the data in a dictionary, this is a dictionary of data, by the way, I might make it more clear by declaring the type there. But whenever you see these curly brackets and GD script, you're talking about a dictionary. So next we turn that data into a JSON string. So that's very simple to do as well. We'll say var JSON string is going to be equal to JsunNU. So we have a new JSON object which allows us to call this string of Pi method onto our data, and that makes it in a JSON readable JSON string format. So now we need to open the file path. So we'll say var file is going to be equal to fileaccess dot OP. So this is another static class in the Godo library, and we need to give it a path and then file access of right. So we'll say Save File path, which we haven't defined yet, and then we want access here. So where are we going to get the safe file path up here at the top? We'll say var, save file path is a string. This will be equal to user slash LASH gametatt JSON. Okay, so what this Fopath basically means is it's going to save in the app data, assuming you're on Windows, for this specific game project, in the root of that folder, there'll be a new file called gamesatt JSON. So each game made in GADO has a user folder in Windows, that's the app data slash Gdolah the name of your project, I think. And then it's going to save GameStatst JSON at the root. So this JSON file is effectively your save file. For a survivor game, the say file is really just your high stats because every time you start a new game, you have a new character, you don't save and load the game like you would with a typical RPG. But the process is more or less the same. So if the file exists and we had to open it up for writing, correct? So if we don't find it, then there must have been an error. But if we do find it, we'll say file dot store string because we have a JSON string up there, right, so we just store the JSON string in the file, and then we do file dot Close. And that's how you write to the file. And otherwise, if the right operation is failing, then we want to push and error, of course. So push error is going to be failed to open sa file for writing, and I could say at path, percent s, and we'll do and the quotations. Then over here, percent save file path. So giving as much information as we can, and then we can debug from there if there's an issue. Of course, you have to spell porti error correctly. That would help, and that is the safe function. Okay, now we want to do our load Stats function. So function load stats, and we're going to have the stats object we're loading the data onto. So stats is of type game stats. Of course, we get this from the game manager later. And first, we want to open up the file for R to access. So VR file is equal to God, I cannot type right now. VR file is equal to fileaccess dot OP at the save file path. So we're loading and saving to the same file path. And then we have Fleaccess dot RED. Then we go. So let's say, if not file, so we failed to open, then we can say, push error, failed to open file for reading at Path percents, percent, save file path. We might actually even want to make this a boolean. So if we make it a boolean, then we can return false here, as in we failed the operation. That might be a little bit more helpful. Later, you can change Safsts to do the same thing if you want. And we're returning false, not literally the keyword so at this point, our files open for read access. So now we need to get the string from the JSON file. So our JSOtext is equal to file dot Get as text. Okay. And the reason that's not showing is probably because I did not type this here. So I could say file access. Let's see, JSON text should also be underscored there, and we can keep going. So we've got the text from the file, so we're done with the file. Let's close the file, filet close. If the file is empty, then we can return here. So JS dot text is empty. Then you can either return true or false here. It kind of depends on what you want. I'll return true, actually, so that we can indicate that the operation succeeded, but there was no data to load. So we'll say loads the stats data onto onto the game stats object. So if the file is not empty, though, then we need to convert it. So var JSON is going to be equal to Jsunt Nu. So we have a new JSON object, and then we want to parse the text. So let's say var error equals json dot parse on the JSON text. So if there's an issue with that, will return an error. So if error, then we want to push error, failed to parse JSON with error percents, and I'll say percent error. I think that might add in some extra useful information. And then we return false here because, of course, if it erred on parsing the file, then there is a major issue with our SAI file. It probably wasn't formatted correctly. Okay, so if there was no error, we know that the parse worked correctly, so we can say var. A data of dictionary is equal to json dot g data. So we're basically turning it into the GIDORcognized format as a dictionary. And then we'll say Varsats data is a dictionary equal to data dot Get in quotation Stats, I believe. And the default here is a empty dictionary. So that stats should match what we have up here. We saved the stats into the stats key value pair, and we need to retrieve it from the stats key value pair. So once we have our actual stats data, which is a dictionary, then we can do stats dot From dict for dictionary, and we're going to pass in the stats data. And that's all we need to do for our loading, so we return true. Okay, I think that looks pretty much right. So what we need now is a two dictionary and from dictionary method for our game stats. So if we open up game stats, let's say function to dict returns a dictionary, and then function from dict is going to take a data dictionary and return void. So to turn this into a dictionary, we need a VR dictionary like that is going to be equal to a new dictionary. And then, actually, you know, inside of here, we can just wrap this all in one thing and put each new key value pair on a new line. So we'll say enemies defeated is equal to enemies defeated. CD of colon for the key value pair. On the left, you have the string name and on the right, you have the actual value. So on the left here, we have time survived. And then colon, and we do time survived to the variable on the right, and then we return the dictionary. And it's really that simple. So next for from dictionary, we want to load the old values into the current values. So we're going to do enemies defeated, equals p data dot git, the key value pair enemies defeated. And then the default value, let's make it null so that if there isn't that value, we actually hit an error because otherwise we would load it to something like zero, and that would permanently override all of our save data. Kind of arguable which way you want to go here. Like, do you want it to immediately fail the save loading, or do you want it to succeed but then override the old actual data, which doesn't really sound very desirable, right? You don't want to override the player's progress. So maybe it would be more like you need to release a hot fix or something like that. If you run into this circumstance where the JSON just didn't load properly, you don't have this field, it's missing the name, something like that. So that's why I'm kind of going towards more like, Oh, it should be null. So when it tries to set the float to a null or the integer to a null, you're going to hit an error, and it's got to be fixed or otherwise going to break all of your players games. I think that is probably the way to go here. So time survived is going to be equal to pdta dot get on time survived as a string, and then the default value is Okay, so we have the game stats. Currently, the only game stats is our main game stats, our current game stats, but we want to also have a persistent game stats, our high score game stats. So I want to put a function in here to take the highest value when comparing to a current game stats. So I'll say function set to highest, let's say, set high scores. To the P current game stats, and this is going to be turn void. So we're only going to call this on our high scores game stats, but this is going to be basically we take the higher of the two values. So the enemies defeated is going to be equal to the max of enemies defeated or the P current dot enemies defeated. Okay, so we're just taking the higher value, and we're setting it to the integer enemies defeated. And we do the same thing with time survived. Time survived equals max. Time survived, and the P current dot time survived. Okay, so that's how we get the high scores. We just compare the current stats at the end of the game and see if that's higher than our current high scores. So we need the save load object, and we also need the high score game stats in our game manager. So let's open up the game manager now. Game manager dot GD. So we'll also have here the var high score stats, which is going to be a gameststnU and we'll also have here the var saveoad which is going to be equal to our save load dot neu object. So this is just a local component of our game manager. We might even make it private. Yeah, why not? Make it private. Because really the saveoad logic here, we only want to belong to the game manager. It's not really something other scripts should be touching on, so we make it private. So this function here, let's say function End Game, I think is a little bit of a better rename. That'll be turn void. So at the end of the game, we want to take our high score stats, and we want to set high scores with our current stats. And then we might want to reset the current stats, but I'm actually not going to do that because we might want to show in the main game menu what the last session scores were, so we won't actually reset until we properly start the game again. I think that's slightly better. Oh, and of course, after we set the high scores, we want to call save load dot save stats. So we're going to save the high score stats. And I think on ready. Yeah, on function Ready. And remember, this is Auto Load Singleton, so as soon as the game starts, it's already going to be loaded. And I want to do underscore save load dot load stats onto the high score stats. And yeah, that actually handles the rest. So the logic queue for saving and loading is basically hidden inside of that save load object or more like encapsulated, really. So all the save load is inside of here, but the game manager actually handles when it should save and load that data. And you can see this makes the game manager a lot easier to read because we break its functions out into different components rather than cramming everything in one giant game manager script. So this video is going on a little long, so I'll make the next video about building out the main menu screen. We already have the template for it because our game over button jumps to here, but we haven't set anything up here. So we'll connect this to the high score stats, have a display, a Start game button, and a splash screen background of some kind. 60. Designing a Main Menu with High Score Display and Game Start Functionality: Going to take the layout on High Scores container, and let's put it on the anchors of top or right. Then I can zoom in. I'll hit Q to move this position it about there. Let's stretch out our high scores container, and then we'll build out some other control nodes here. So I'll click here, add a child node. Let's say Margin Container. Same story with Margin container. We want a theme override, four pixels on all of those. And then to save some time, I guess we could go to the Gamesets view. And I'm going to copy paste that time survived and enemies defeated. I'm just going to control see all of and let's paste it over on the main menu. So inside of there. And I did forget a VBox container, so add a VBox container and then move those two text containers under it. Like that. And then that's roughly how it's going to look right now. Let's save the high scores container in its own scene, right click, say branchien scene, like so. And then I want to take the layout transform and triple the scale, like that. Okay, so when we do that, it's going to mess a little with our Anchors preset, so I'm just going to position this over there. No need to get it really perfect. Let's also open up the high scores container and add a title text. So zooming in here in the VBox container, I'll right click and add a rich text label. And let's position this on top of the VBox container. Check Fit content, and let's say high scores, but I'm going to preface that with, let's say, rainbow. End the square brackets, go to the end Rainbow. Of course, for this to work, we need to check BB code enabled, and there we get a default rainbow. So I think I want to lower the frequency and make that less dramatic. So let's say frequency equals 0.5. Well, let's go to 0.25 even. Yeah. That's a lot better. And then we need SAT for saturation. So let's say saturation of 0.2, maybe 0.5. Okay, that's a bit tamer. So that's good. And we'll do horizontal alignment center. Okay, that's not too bad. So in main menu, we can see how that's going to look. Back in high scores container, let's assign the right label. So Max defeated label is the amount label under enemy's defeated container. And then longest survived label is in the time survived container. Okay, save that, and back on our main menu, we need a texture background. So I'm going to right click on main menu. Let's add a texture act. I'll rename that to be background wrecked. We'll come back to that. Also, the main menu screen, we want to make sure we turn off the mouse filtering. So mouse on the right, we turn filter to ignore. That's important. Otherwise, the main menu would actually block the buttons under it. So let's add in a button now for our Start game. So add a button. Let's rename that to Start game button. And if I recall, I just have a script that can switch scenes immediately. So scene change button, I'm going to drag and drop this right onto the Start game button. And then we need to give it the scene path of the world. So in the top right, scene path, select world dot TCN, and now that button should be able to open up the world, which is our gameplay scene. This button is really small, of course, so we need to go to layout. And I'll triple the scale down here. Let's also position it in anchors and then say center bottom. We can zoom in here. I'm going to hit W. Let's offset it up to here. Let's give it the text Start game, and we may want to select an icon, as well. So let's do icon as atlas texture, Quick load, and atlas from our game project. I'll do 64 or 64. Dit region and just pick whatever icon you like for starting the game. So I don't know. Since this is a combat based game, maybe one of these swords would be good. So I'll just kind of grab this, make sure to get all the pixels. So I need to stretch this out to the left, hit close. Just in case I'll also check filter clip. We don't want edge pixels to get in there. Then that's basically our start game. Button let's W and make sure it's within the bounds of our main menu. Let's also go to Project, and we'll take the main menu and make that our game launch scene. So in general Run, instead of launching the world now, we're going to load. The main menu dots TSCN. Okay, now let's see kind of where we're at. I'll hit play. We have the high score up there, we have Start Game. Let's hit Start Game, which launches our main gameplay scene. That's what we would expect. And real quickly, actually, let's get defeated so that we can see the high score on our main menu. Of course, I renamed the Game Manager dot report game over, so we need to change this to game manager dot end game. That's on the player defeated state. Okay, now let's launch one more time. So I'll just make sure I get defeated by one of these works, three hits, four hits, five hits. And let's see. The players already been freed. It's on the World note because we actually freed the player. We also need to say here for the worlds. If process function, where we're adding the time survived, we'll have to say if context out player. And we'll have to say does not equal null. I think for previously freed references, if you just do Context Out player, that'll still hit an error, but if you say does not equal null, then that will get around that. Okay, so basically, if the player doesn't exist anymore, or the player is no longer alive, then we're going to add time survived to the game stats. Since the player got removed, this will handle that bit of it, and we should be good to go. Okay, so one more hitting play, making sure that that actually runs. Oh, we can see that the high score updated here, though, so that's good. We might need to resize the UI a little bit. As you can see, the problem here, it's showing like 12 decimal points, so we will change that in a minute. Let's see if we can get defeated faster than 7 seconds. Okay, let's go. One, two, hold on. Upgrade this gear, and it failed. Okay. So we got to find another enemy or two. There we go. 12 seconds. Main menu. You can see our longest survive now is 12.6 seconds. Enemies defeated two. And let's close the game and hit play. That will verify that the loading from file is actually working. So let's close and hit play. I may not have set it up yet. It's interesting to see if it has or not. Okay, so yes, when the game loads, the game manager is, like, one of the first things to load, and it already loads into the high score what saved high score from the file is. Okay, so as a cool time to show where that's at, let's do present app data in Windows File Explore, and then we want to find Godot. And then I believe it's App user data, the latest project Script Survivor's GD script. And right in here we have ARJSOFle. Okay, so let's edit in Notepad. And there you can see right there. Our actual game stats saved to a file. We have enemies defeated at two times survived at 12.6, and that is our saved game data, which updates whenever we get defeated if we happen to be any of those numbers. Okay, so for the high scores container, the longest survive text, we just kind of want to copy over what we were doing in the game stats view. So I'm going to jump into that script. And then I want to copy over this bit right here, which takes the flow and gives us a string that only has one decimal place. Go to the high scores container, and I'll replace that here with this. The PT, of course, is actually going to be the game manager dot high scorstts dot Time survived. Now if we at play, we get to the game menu, we should see 12.6 seconds over here. That's much more readable as our high score. So now for our background, I'll literally take a screenshot of the game. And you could turn off the UI for this if you wanted to do a screenshot. That would probably be a little more helpful. Okay, so let's also turn off the collision shapes before we take the screenshot. So debug collision shapes off. Okay, so let's go ahead and grab our site as an upgrade so we can show that. I'll leave a couple of these pickups on the game map. We'll wait for the skeleton to spawn, and then we'll see if we can get a nice screenshot in the game. One thing I would consider would be turning up the spawn rate if we want to emphasize how many enemies there are in the game. Okay, so let's screenshot here. I don't like that one so much. Let's maybe screenshot there. So, print screen to take a screenshot if that wasn't obvious. And I'm going to open up Gap. So Gap is a free image editor. It's kind of like Photoshop. But free. So then let's do File New, and I'll do 1920 by 1080 pixels. Open it. Control V to paste in our screenshot, and let's grab something like that. It's not really an image editing tutorial here, but really quick, I'm going to kind of paste a top bar in there. So using a black fill, then we can give it the text if we so desire. I'll choose a font. So let's go with pixel regular. You'd have to install that font if you want to use that. Okay, so I just spent a little bit of time creating kind of like a custom thumbnail, except we're going to be using that in the end game project. So just slapping on a black background at the top, some Pixar text, mentioning it's a GD course, mentioning it's a Do course, and then the Gudo icon. This probably won't be the final thumbnail for the course, but we can use it for the game menu screen for now at least. And I will open this up. I exported it to the project, of course. So where you have that saved, you can see I have the main menu screen here. So what I want to do and the UI, this is actually the wrong screen. We want the main menu. We go to background Rec. I'm going to quick load, and I'll search for main menu screen. Pop that in there. I may need to increase the size here, so let's go to layout mode, anchors and do full wreck. Okay, so with this and full wrecked now we want to make sure that nothing is blocked by this. So I think we need to move this to the top on the left. Okay, there the high scores show we can play. Okay, and we can play, and we should see it there. Script Survivors high score up here. It's a Gado course. We have the Start Game button and the Gado icon, which I mean, pretty solid for like five, 10 minutes of work, I think, of course, can be improved. Okay, so now at this point, pretty much the game is just about done. We have our splash screen there. We can start the game, the high scores, showing during gameplay for the current scores and on the main menu screen for the all time high scores. We can defeat the enemies. We can lose the game. We can upgrade maybe we want to do some cleanup, like removing these starting upgrades from the game Map. That's a little weird there. But that was just there for testing purposes. Perhaps we want to ramp up the difficulty, add new enemies in. We could have our main character, you know, showing the spear if we want that to be there rather than kind of armless. But, I mean, it works for what it is now. And we can level up multiple times. It's not ready for full release, of course. There'd still be a lot of work to do there, but everything is basically working as intended. Got the game over. There's our high score. Perhaps the main menu could use a soundtrack, that kind of so I might do one more polish video to just kind of clean up the course, but that is pretty much going to be it. So I hope all of you guys have learned a lot following along to the end. I know this was a pretty long course, 11 hours plus total edited runtime. So, I mean, if you got here and you followed everything along and your game's working as it is, I mean, good job because that was actually quite a lot. Okay, so thank you guys so much for watching to roughly speaking, the end of my script survivors course, GD script in Gadot 4.4. 61. Polishing Script Survivors ~ Gameplay Enhancements & Debugging: So in this video, we're going to try to add some polish to our script survivor's prototype. So first off, at the end of the last video, I have seven errors in the console. So that's the first thing we're going to take care of. Anytime you see an error, it's a good time to work it out, debug it, figure out what was going wrong. So we have item pickups that do not have an item actually assigned to it. So I think what would be really handy here would be to say percent South, and then we put that in here as percents. Okay, so you can see that the error message here isn't as clear as it could be. An item pickup needs to have an item, but we don't know which item pickup actually failed that. So how you can do that in the item pickup two D script is at the end of assigned to pickup, do a percents and then follow up the string with a percent self. So that will replace the percents with the node, which is going to resolve to a node name or in other words, which node actually didn't have the item assigned. Okay, let's run that one more time. I'll just play for a little bit, see if we hit any new issues. Okay, so since those item pickups and the health portion, it says that the item is not set on ready, which is weird because the at export definitely should be doing that. So if the item is equal to null, then we report an error. Oh, okay, right, right. So I made a mistake here. If that was an assert, that would work fine. But what we actually want to do is say, I item is equal to null, then we pushed the error. I was still in assert logic there. So let's tab that over. If the item is null, we push an error. Now, if we play, it should work, of course. Otherwise, it was going to always push an error. So you can see in the debugri down here, there's no issues now. I'll collect some items. Let's get enemies to drop them, and that should work just fine for that. So so far, it doesn't seem like there's any other new errors. I did notice a little bit of a UI layout issue here. So I think if I take the scythe here, it might kind of reposition this UI in some weird way. Oh, okay, that's what's happened. The HB box was actually expanding to get bigger. So to fix that, I either need to lock the size in place. Boy, I should just make it bigger and then move this over to the right. I think that would be better. Okay, so for this player UI, we need to expand it a little bit so that it has room to grill. So I'll go into player UI. And then let's take the root panel container, stretch this out to the right so that it has extra space. I'll go to Game Play UI. Let's move this over to the right. Now if I hit Play and we happen to upgrade our health, and it resizes this it should not actually push it further to the right anymore because it already has space to fit that full text label. And I do need to resize the weapons layout a little bit. I offset it a little incorrectly there, so I'll just line this up to the top there, we'll hit play, start game. Okay, and that's looking pretty solid. In the world, I do want to remove all these pickups by default. Those were just for testing. Those shouldn't be in the final prototype, so I'm going to cut those out with Control X, having them all selected. And I do think I want to make the standard game play a little more difficult. So I'm going to go to the spawner system, and let's increase the span rate. So I'm going to pull this down at the start to a higher spawn rate. Also add an extra point here, kind of like that. Adjust the handlebars, something like that. I just want to steeper curve so that we get more enemies to spawn faster so that I can show off a more intense gameplay. In the demo. Okay? And all play. Let's test it for a little bit and see how that goes. So I'll start the game. We want our enemies to come up to the center. Maybe the orcs are a little too slow. I might boost the movement speed of both the orcs and the skeleton. So there's really no need to make it easy here. Okay. So there we have our skeletons and the orcs. So I'll boost the movement speed of both of them. I'll look up the orc stat controller. Let's change the orc stats. The speed can be, let's say, 35, and then let's go to the skeleton. Sat controller speed of 60. I'll just make it kind of hard here, and then the MX HP can be like 100. All right. And the orc HP is 40. That's okay. Let's play. Start game. One thing you could add, I'm not going to do it here, but having, like, a enemy spawn burst at the start would make the initial game play a little bit more intense if you start the game by instantly spawning ten guys to come at you. That might help. But I think that's a little bit overkill for just the prototype. So the works do move a little faster, and the skeletons are quite fast now. So this will definitely be a lot more difficult. I do need to take the current HP and set that to 100 at the start. Okay, maybe the skeletons are even a little too fast, honestly. Okay, so let's fix the players starting HP, player, stat controller, player stats, HP to 100. And I'll nerve the skeletons a little bit. Let's say 50 speed. I think that's pretty fair. Okay, on the player level definitions, I'm also going to make the level up pretty easy. So I'll take level three and set that to five. Level four will be ten XP, level five, can be 15, level six, can be 20, and level seven can be 25 HP. And then I'll just add one more for 100. And that'll be like the one you don't really get to. In fact, let's just add one more. Let's say 1,000 new level definition. The reason I'm making it hard to go to is just so that it doesn't really happen. I kind of messed that up. That was Level 1,000 takes ten XB. That's not what I want. What I wanted, and let's delete that is level nine is going to be a level definition with 1,000 EXP needed, add that. So this just makes it so that I don't have to plan for that amount of scaling in the prototype demo. It would just be kind of overkill for right now. So let's hit Play, and I'll see how the game plays going. Start the game, and we'll wait for the first enemies to spawn. Okay, I think it's a little too easy at the start. I want it to be intense quickly. So I think what I will do is actually put in that instant spawn of ten dudes. So in the world, let's say at export V initial spawns, and I'll set that as N equal to ten. The world will have a reference to the spawner system, so spawner system. And then on ready, I'll say, I Initial spans is above zero. Then we'll say spawner system. Let me see. What were the functions called? Random spawn. And do I call that ten times? Hold on. Okay. Yeah, and it looks like I need to call that ten times or the number of initial spans. So for a time in initial spawns, we're going to call spaorsystem dot random span. So ten initial spawns, this should make us spawn ten dudes at the start of the game. Okay, don't forget to set the sponnor system in the world. So assign that sponnor system. Direct node reference, let's hit Play. And if this works, we'll have ten dudes at the start of the game, which should make it much harder. Okay, yeah, that is a lot of guys for the start of the game. So we just have to avoid all of them. That might actually be too hard, honestly. I'm going to nerve that to five, and I'll change that to five in the script, as well. I think ten guys at the start when you haven't even leveled up is it's just a lot to deal with a one go. Okay, so we have our five guys starting now. Let's get an XP. I'll upgrade with the Syth so that'll give us an extra way to hit enemies. Much easier now. And I'll just keep going here. Let's get some upgrades since we have the EXP curve made a lot easier, then we should be able to level up pretty easily. But I got to say, increasing the speed on the enemies definitely makes it a lot harder to dodge them, so that's something to keep in mind. Let's keep upgrading the spear. So I think at a certain level, the spear hits two guys at once. It's already there. Level three spear can hit two guys. Which is definitely a huge upgrade. I'm just kind of going through the gameplay here. The skeletons at 50 speed are pretty tough. 60 speed for the skeletons would definitely be too much. Let's just keep going with the spear. Based on the current settings, I actually have no idea if the spear or the scythe is better. Probably with large groups like this, the scythe would be better because, you know, it can hit all of them at once. But sometimes single target is important, too. I mean, we're just getting into, like, how to play the game now at this point. So that is a ton of skeletons, and at some point, they're going to kind of catch me. We can see a little bit of an issue, which is that kind of clump up too much together. So I do want them to not stack on each other, but I think their collision shapes are probably too big right now. This is kind of making them unable to move. So we actually dun into a nice issue we can fix. Let's pause the game, and then I'm going to take the skeletons. On all the scenes, let's take the collision shape, and I'm going to shrink that a lot. Let's make that like a two and then we'll go to the orc. We'll take his collision shape. This is the base character body collision shape, and let's take that collision shape, expand it, and set that to two as well. If we unpause the game, if that's working correctly, and I think it kind of is. I can't say 100%. Then because their collision shape is smaller, they'll clump less. But, yeah, it's still pretty hard when there's this many guys coming at you. If there was like, projectiles too, then you definitely have a pretty difficult game at this point. So I can get a couple more things. Let's upgrade the scythe this time. And let's just go for the game over screen. I could probably run around for a while, but we just want to verify everything still working with the game polish, fruity, solid prototype in there. And let's go to main menu. We have our time survived. I think I do need to bring that out to the left a bit just so that there's room for the decimal point in this case. Aside from that, I think we are looking pretty good. So on the main menu, so I'll just expand this out to the left a bit, get everything on one line. That'll look better. And maybe I put on the music on the main menu as well. So I'll right click at a node. Let's do audioStream player not two D, because, I mean, there's no two D world now. We're just looking at the game menu. Let's quick load our AudioStream, so.p3 because I need to go find it in the project. Okay, so let's add it in the audio now from the audio folder. We have three Red Hearts Box jump. We'll just use that for the main menu as well. I'm gonna put on headphones so I can kind of test this for myself. Let's do play. Oh, I didn't actually audio play. Okay, so let's do auto playing. Yeah. And then play. Okay, actually, I want to check the main game world, and I want to see how loud that is. It's -40 decibels. Okay, so I even want that to be a little quieter on the main menu. So let's make that -45 decibels. I'll hit Play. Test it out. Okay, and then we go to Start Game. It's a bit louder here. Maybe the endgame music I make a little louder than that, too. Okay. So the endgame world, I'm going to take that and set it to negative 35. I think I made it super quiet during the course because I just didn't want it to be an interference when I was talking over it. Okay, so let's hit Play. We can start the game. Okay, that's good. One other sound effect I might want to add is whenever we pop up the level up selection screen, I think that could use a sound effect. Aside from that, I think we're looking good. So let's go up to the level up selection screen. Alright, click Add a audioStrem player here. Let me see if we have some good level up sound effects in the project. So it's going to be under audio UI menu. Sure, let me check. Okay, use item seems like it would work. So I'll take the audio stream player here. We'll pop in use item. This will not Autoplay, so I need to go to the LevelUp selection. We'll add in a Export var pop up. AudioStream, which will be a AudioStream player. So we're going to say pop up audiostream dot play, and I just got to fix that export. Okay, and then be sure to add the pop up AudioStream in the export. So we have to add that in. We could do at ready assert if we want assert pop up AudioStream. Remember to set the audioStream player for playing level up sound. Okay, now let's hit play. We can go to Start Game. And let's test by defeating an enemy, we level up. And then we take our upgrade. The music continues. And I think that flows pretty nicely. It's definitely nice to have an actual sound effect for the veo up. So let's keep going with Syth this time. The Syth definitely putting in war here. It might be OP, actually. Okay, so Syth four, let's see how that goes. Definitely, it's really hard. That's a big deal. Since it's basically an AOE, it probably shouldn't be doing this much damage, let's be honest. Okay, I think I've seen enough there. I'm going to just buff the spear. And make spear more fun to actually use. So let's jump at player, the spear weapon. Let's jump into the scene definition. We can just buff it up however much we feel like, I think. Level one short. We can leave that as the defaults. Level two, three, four, five. Let's see. For level two, I'll just make it deal 20 to 30 damage, and the cool down. Yeah, we can do 0.9. Level three will be 30 to 35. I think the spear can also do two hits at level two. That's fine. And then level three, we'll do 30 or 40 damage. Okay, so the max hits two, and then we'll bump up the projectiles to two at level four, and this will be 35 to 45 damage at 0.8 cool down. And then let's do 40 to 45 damage. It can go faster. Let's make the cool down 0.6. The projectiles can be why not? Let's just say three projectiles. That seems pretty bonkers, but hey. And then I'll do a quick run through with the spear. Let's see if the spear can even compete with the scythe. Okay, more spears. That should give us two projectiles. That should be a lot better. Getting to Level four was a little rough. I'm not going to balance this too much, but I did want to make it at least bearable. Oh, God. The spawning is getting kind of ridiculous. Oh, I forgot to loop the music. Okay, open up the game world. That's pretty important. So game world. Let's open up that scene. We have the audio stream player. We want to take this music and check loop. So I think we need to do that in the project. Let's do three. Double click on this and enable looping. Alright. Close. And then autoplay. So on your audio stream player, let's check Looping on for that audio. That should also be on the main menu screen just in case. So here parameters, looping on, and then I'll unpause the game. So that won't start until we get back in there. So since we hit Level five Spear, we cannot select spear anymore. That's good to double check and make sure that, you know, it doesn't irr out by trying to level up to Level six. So we can still choose the scythe, of course. Okay. And I'm just going to let myself die at this point. I think Yep. Everything seems pretty much okay. Oh, and the high score screen here, if that's still not displaying correctly, let's jump into it. So for the high scores, to make sure that there's enough space here, let's set a minimum size on these. I'm going to set this to 60.0 and then let's expand the high score base over to the left. Okay, also take the amount label, and let's do a minimum size of 60, okay? And we can take the high scores and make that even bigger to the left. All right. And then hit play. And just make sure that the high scores don't go over the edge anywhere. So this would be good for at least one more digit, I think, without going over the edge and wrapping or anything. Okay, so at this point, pretty much, I think the prototype is relatively polished. I'm sure there are tweaks we can make in the code and content to be added to the game and just make it a full fledged game. But as far as the course goes, showing the basics of how to set all this up, a survivor like game and GD Script Gudo 4.4 I think that is pretty much about all there is to show. So I hope you guys really enjoyed following Long all the way to the end. There may be a few small update videos, but that is pretty much it. So thanks again for watching Iben Chris, and I will see you guys in my future video content.