Combat in Godot! | Thomas Yanuziello | Skillshare
Search

Playback Speed


1.0x


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

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.

      Intro

      1:44

    • 2.

      Setup

      0:32

    • 3.

      Lock On

      15:52

    • 4.

      Strafe

      13:47

    • 5.

      Attack

      20:46

    • 6.

      Weapon

      17:20

    • 7.

      Hit

      15:43

    • 8.

      Dodge

      14:43

    • 9.

      Block

      15:12

    • 10.

      Shoot

      17:43

    • 11.

      Enemy

      15:41

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

28

Students

--

Project

About This Class

This course is a continuation of Inventory & Shops in Godot!

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

In this course, we will cover locking on to a target, strafing locomotion, attacking, getting hit, dodging, blocking, shooting projectiles, and basic enemy AI.

You'll also learn useful skills for working with the Godot game engine, organizing, and designing your projects to be more scalable.  You will be learning how to code with GDscript, with everything explained in detail.  Our scripts will be written to be highly customizable and reusable.  All of the project files will also be available on GitHub if you need to review the project as it was after completing each lesson.  These videos were recorded using Godot version 4.3.

This course will be part of a series designed to teach bite-sized pieces of game development that can all be used interchangeably with each other.  So let me know what types of games you're interesting in learning how to make and I'll try to include them in future courses in this series.

Meet Your Teacher

Teacher Profile Image

Thomas Yanuziello

Indie Game Developer

Teacher
Level: All Levels

Class Ratings

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

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

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

Transcripts

1. Intro: Combat and video games can seem daunting and a difficult feature to implement. This course will provide you with all the tools and knowledge you will need to create a scalable and dynamic combat system in Gado. We will break the combat system down into individual mechanics and combine them to create a robust action combat system that can be customized to suit your needs. You will learn how to blend combat and movement animations together. Synchronize hit boxes with attack animations, implement eye frames into dodge animations, Lock on to enemies. Fire projectiles from arranged weapon, and control enemies with AI scripts. If you need any help, our discord server is full of other students and game developers who can answer any questions you may have. Click the website link in my profile to join. I'll be using a starter project made in God version 4.3 that is available on my GitHub using free assets from it dot IO. But you can follow along with your own project using different assets. To get the most out of this course, you will need a scene in your project with a character the player can control and at least one enemy. You may also want to have an inventory and equipment system capable of equipping characters with a weapon and a shield. For more information on these topics, see my previous course. Combat doesn't have to be an uphill battle. Let's draw our weapons and smash through combat together. S. 2. Setup: Hello, friends. Before we get started, you should have a project with a player character that can move around in an environment, and some enemies for them to fight. You may also want to have an inventory menu capable of equipping items, including at least a mala weapon, a arranged weapon, and a shield. Throughout this course, we will add controls, mechanics, and animations to our project for locking onto a target, attacking, dodging, blocking, and shooting projectiles. 3. Lock On: For our first lesson, we'll allow the player to lock onto an enemy. Let's get started by adding an input mapping for toggling the lock. Opening the project settings, switching to the input map tab. We can add an input event named Toggle Lock. Scrolling down to find the new input event, I'll bind the Q key on keyboard and the right stick button on my controller to this event. In the player input handling script, we'll need references to the character, spring arm, and camera, all set using add on ready. During the input function, after checking for pause, opening or closing the inventory menu, and whether the player's control has been disabled. I've divided the rest of the input into camera controls and character controls for better organization. Currently, the only camera control is rotating the spring arm holding the camera using the mouse. Let's also check if the toggle lock button was pressed. This will be a context sensitive input with multiple different possible functions. We'll need to know if the player is currently locked onto a target. So let's declare a variable to hold the currently locked target as a node three D, so any three D object in our game world could potentially being locked onto. We can first divide the functions of the toggle lock button into two possible categories, if the value of target is null or not, which we can shorten to just if target. Let's start in the s block where the player isn't currently locked on to anything. Here, we will need to assign the value of target to be the nerest visible target, and we can delegate responsibility for finding out which is the nearest visible target to the camera. In the case where there is no nearest visible target, the target will still be null. In many action adventure games, when pressing the togo lock button, but there is nothing to lock onto. The camera then resets to be behind the player character, which will be a function of the spring arm. If the player is already locked onto a target and presses the togo lock button, there are multiple cases to consider as well. If there are multiple visible targets, the player will lock on to the next nearrest visible target, besides the one they are currently locked onto. We can repurpose the same function of the camera to find the nearest target. But this time also provide the current target as an argument, and we will write the algorithm to ignore this target. In the case below, we can still pass the current target since its value is null, so we will have nothing to ignore. If there are not multiple visible targets, this function will also return null and the lock will be toggled off. Regardless of whether or not there are multiple targets, if the player is also pressing down, most games will toggle the lock off in that case. We can determine if the input direction being given is similar to down using a dot product of the two vectors, and checking if it is larger than 0.75. This will cover any directions close to down, but not reaching as far as the diagonals. We want to have the ability to toggle the lock from outside of the script, so it would be a good idea to make it a public function. Let's call it toggle lock and give it an optional parameter to force lock off with a default value false. The value of target will be set to null if it is being forced off. Otherwise, it can be set to the nearest visible target as determined by the camera, ignoring the current target if there is one. All three of these cases above can now call Toggle lock with only needing to specify that the lock is being forced off in the case of the down input direction also being given, and other scripts can also force the player to lock on or off for any reason. Switching to the spring arm script. Let's assume that the player presses the toggle lock button, but no targets are visible. We want the spring arm to position the camera behind the character. So we'll need a reference to the character the spring arm is attached to, which in my case is always going to be the parent node of the spring arm. If you're using a different node structure, you may want to export this variable instead and set its value in the inspector panel. To rotate the camera behind the character gradually over time, we'll use a tween variable of type tween. It would be a good idea to export a variable for the duration of the tween. I'll use one quarter of a second. We might also want a value for the preferred x rotation to reset the spring arms rotation two. I'll use negative 0.5 radians, positioning the camera slightly above the character, looking forward and slightly down at an angle. Let's also declare a variable to hold the tweens target rotation as a vector three and initialize it to be a vector three with the reset x rotation as its x value, but zero for the y and z rotations. Giving the function a definition, we can also accept an optional parameter for the duration of the tween using this as its default value so it can be overwritten. To rotate the camera behind the character, we can call a new private function to tween the spring arms rotation. Specifying that we want the y rotation to be the character rigs rotation plus a half rotation represented as Pi radians. Next, let's write the private function which tweens the spring arms rotation, accepting a target y rotation as a float, as well as a duration for the tween with a default value of the exported duration. We'll start by setting the y property of the target rotation to be the target y rotation accepted as a parameter. But to ensure that the rotation always takes the shortest path around the character, we should wrap its value to always be between the current rotation dot y minus a half rotation and the current rotation dot y plus a half rotation. If a tween already exists and is running, then we should kill it before creating a new tween. We can then tween the spring arms rotation to the target rotation over the duration. Next, let's let the camera detect visible targets. My camera doesn't have any scripted behaviors yet, so let's add a new script and put it in the players scripts folder. We will need a public function to get the nearest visible target, accepting an optional parameter of the current target with a default value of null, so we can potentially ignore it. This will return a node three D, and the default case will be to return null if we couldn't find a visible target. In order to determine which of the visible targets is nearest, we will need to maintain a list of all the visible targets. Let's declare another variable to hold all the visible targets as an array of node three Ds. Initialized to be empty. An easy way of knowing which targets are visible is to attach an area three D node to the camera. Let's call it target range. This area will be monitoring for anything on collision layer 11, which I've chosen to represent enemies. Make sure your enemies have their collision layer set to the same layer as is being masked by the lock on targeting. I will also need to reset the areas transformed to position it at the camera's origin. The area three D node needs a collision shape, and we can populate the shape resource with a convex polygon. Using a convex polygon, we can define the shape as a pyramid, which exactly copies the camera's field of view, but with a limited range. Since the camera's field of view, will take the shape of a four sided pyramid, we can define it with five points with the first point being the same as the cameras position. The other four will be positioned along the Z axis, the maximum distance of our lock on range. I'll use a maximum distance of 15 meters. Switching to top orthogonal view and hiding the environment for a moment, I'll just quickly adjust the positions of each point on the pyramid to approximately match the cameras field of view, 11.4 meters to the left and to the right. Likewise, in left orthogonal view, adjusting the y values of the points of the pyramid, 6.4 meters up and down. Until we have a pyramid shape, matching the cameras field of view, which reaches out 15 meters. I'll then switch back to perspective view and talk of the visibility of the environment back on. Anytime a body enters this area, connecting the body entered signal to the camera script, We can append the body to our array of visible targets. Likewise, anytime a body exits this area, the body can be erased from the array. Any three D collision body on layer 11, which exists inside this area will now be considered a visible target. In my spring arm script, I'll need any tag to use the get parent function. Running the game and switching to the remote scene tree, we can select the camera and view its properties in the inspector, including the array of visible targets. As the camera moves around the scene, the array of visible targets updates automatically to add and remove each enemy as they enter or exit the field of view. Pressing the toggle lock button won't actually target anything yet, but we can see how it repositions the camera behind the character. Before allowing locking onto a target, we might also want to check if the player's line of sight to the target is obstructed by terrain. Let's also add a ray cast three D node to the camera as well, and name it line of sight. Then grab a reference to it in the camera script. To find the nearest visible target with our camera, if the array is empty, its size is zero, then there are no visible targets to consider, and we should just return. If there are visible targets to consider, we will need to compare the distances of each one to the camera to find which one is the nearrest. To do that, we'll need a variable to hold the distance from the current target to the camera as afloat. The nearest distance as afloat, and the index of the nearrest visible target as an integer. We'll set the nearrest distance to infinite using the keyword I NF, and the nearrest index to negative one to mean not a valid index. If after iterating through the entire array of visible targets, the nearest index is still negative one, then no visible target was considered valid, and we should return null. Otherwise, we can return the visible target at the nearest index. If the visible target at index I is the current target, we can skip over it with the continued statement. For any other visible target, we'll need to calculate its distance from the camera. We can use distance squared since it's mathematically more efficient. If the current distance is less than the nearrest distance, then we'll need to check if the line of sight to this target is obstructed. First, we'll need to reset any rotation the line of sight currently has. Then set the target position of the ray cast to the global position of the target minus the global position of the camera and force it to update. But the position of characters is usually on the ground, which is easily obstructed by small bits of terrain and not always a good indicator of line of sight. Let's also add 1 meter up. If the line of sight is colliding with something and that something is the visible target, then the line of sight is not obstructed by anything, which makes this target the nearest visible target we have found so far. We can set the nearrest distance to the current distance and the nerest index to I before proceeding through the rest of the array. This will now return either null or the nearrest visible target to the camera. The line of sight will also be colliding with layer 11, looking for enemies, but also layer one to be obstructed by terrain. It would also be nice to let the player know what they're locked onto with the target indicator. Let's add a Sprite three D node as a child of the player input handler and rename it target indicator. For the texture, I'll use scroll bar blank pointer. But change its input settings to toggle on pre multiplied Alpha to make it look a bit smoother. And scale it down to a quarter of its size. Then expanding the flag section, I'll set its billboard flag to enable, so it is always going to face the camera. And the no depth test flag will always draw the sprite over everything else regardless of its position in three D space relative to the camera. The target indicator can be invisible by default since the player starts without a target to indicate. Grabbing a reference to the target indicator using add on ready. We'll then need to position it over the locked on target. So after determining what the player is locked onto, if there is a target. An easy way to have the target indicator follow the target is to repair it to that target. We can then move it a few meters up to position it over the target's head and set its visible property to true. If there is no target to indicate, we can repair the indicator back to this node and set its visibility property back to falls to hide it. Let's try it out. When we press the toggle lock button, the nearest enemy is targeted, and the indicator positioned over the target's head. Pressing the toggle lock button again, switch is to the next nearest enemy. Holding down and pressing the toggle lock button forces the lock off. If there's only one visible target that we are already locked onto, lock is also toggled off. We now have the player locking onto enemies in our game. In the next lesson, we'll change the behavior of the camera and the character while locked on. I'll see you in the next lesson. 4. Strafe: Hello, friends. In the previous lesson, we added a context sensitive toggle lock button to our game. In this lesson, we'll change how the player nodes behave while locking onto a target. Starting in the player script, once a target has been locked onto, let's emit a signal so that any other script can listen and react to the value of target changing, specifying what was targeted as a parameter. Using a setter, we can emit the signal anytime the value of target is changed for any reason. Now all three of the character, spring arm, and camera can connect to the signal, using it to set their own private variables for what is being targeted. No matter what the value of target is in the player input handling script, all of the other nodes will also have the same information, including if it is set to null. Writing a simple function for each that sets a local target variable so they could all access it easily. Once our character has locked onto a target, we would expect them to face towards the target instead of facing in the direction they are moving. In my script, rotating the character is being done in the physics process, which is telling the character to face towards the direction of the movement input. Let's declare a new variable at the top of the script, wants to face direction as a three. And replace input direction with wants to face direction in the function call. Then before this function call, we can check if the character is locked on to a target. If they are locked on to a target, the direction that they will want to face will be towards the target, which we can easily determine by subtracting their global positions. If they are not locked on, then they will want to face the direction of movement input, same as it was before. Now the character faces the locked on target, but their local motion animations don't look very good, since they are only blending based on how fast the character is moving and not the direction. In the character's animation tree state machine, they are in the jump idle state because there is no floor in the scene. The loco motion state is a one dimensional blend space, blending the idle walk and run animations based on the character's movement speed. Let's remove this state and replace it with another state machine. Reconnecting the transitions to the other states, starting with a transition from start to locomotion to make it the default state. I had the transition to jump start enabled with a cross weight of 0.3 seconds. And jump land to locomotion switching at end with a cross fate of 0.67 seconds. I'll omit the transition from locomotion to jump idle temporarily while working in the locomotion state machine, so the character will remain in the locomotion state and I can see the results in real time. Pressing the play button will force the character into the locomotion state and they will tpose because it's currently empty. Inside the locomotion state, we'll need to have two different sets of locomotion animations for when the character is locked onto a target and when they aren't and be able to transition between them. The default will be the same as it was before, a one dimensional blond space. Let's name it not locked on. Then we can add a two dimensional blond space as well. Let's name this one locked on. Adding a transition from start to not locked on makes this one the default. We can then transition between not locked on to locked on if the value of target is not null, which we can shorten to just target and give it a short cross fade. And return back to not locked on, if not target with the same cross fade. Inside the not locked on blend space. We can recreate it exactly as it was before, with a minimum value of zero blending idle, walking and running animations based on the character's movement speed. With that done, use the bread crumbs to go up one level to the locomotion state machine and press the play button on the locked on state machine to force the character into that state. Inside the locked on blend space, we'll blend together the animations based on which direction the character is moving relative to their forward direction, which is towards their target. I'll set the grid snapping to increments of one to make things easier. If we position our view to be behind the character and imagine that they are locked onto a target in front of them, then the y axis represents how fast the character is moving towards or away from the target. The x axis represents how fast the character is moving to the left or right, strafing around the target. Let's start by putting the idle animation in the middle. For the x axis, we'll want to put a strafing left animation on the left side and a strafing right animation on the right side. For the y axis, we can put a running animation at the forward position, which will generate triangles in the blend space showing how the different animations will be blended together. And adding a walking backwards animation in the negative y position, we'll also add more triangles. If we change the blend position, we can see how the character will look while giving movement input in any direction. Depending on the assets you're using, you may also have diagonal movement animations to add to this blend space. Sometimes this preview does not accurately simulate how the character will behave while the game is running. And you should always test it out by playing the game to make sure it looks good in the game. But before we do that, let's return to the root level of the state machine and add the transition from locomotion to jump idle under the condition that the character is not on the floor. I used to cross fade time of 0.3 seconds for this transition. We also need to update the part of our script, which sets the blend position of the blend space, which in my character script is happening in ground physics. Selecting the animation tree, we can copy the property path of the not locked on blend position and updated in the set function. But this only needs to be set if the character isn't locked on to a target. If they are locked on to a target, we will need to set the blend position of the locked on blend space instead. The locked on blend position is a vector two with both an x and a y, which we will need to calculate. So let's declare a variable to hold it named locked on blend. To avoid repeating the same calculations multiple times, I'll also declare another variable to store the character's relative velocity, which will be their x z velocity divided by their running speed. After calculating this value, the blend position of the not locked on blend space can be set to this vector's length. If the character is locked onto a target, we can find the x value of the blend position using the dot product of the rigs global basis vector x with their relative velocity. The dot product of two vectors results in a number representing how similar or different they are. So we are checking how similar the character's relative velocity is to a vector pointing directly to their right. Depending on how your character is set up, which direction is considered forward, you may need to multiply this value by negative one to reverse it. The y blend position can also be determined using the same method, but using the character's forward z vector. Next, while locked onto a target, the camera should look at the target, not the player character. In the camera's process function, ignoring Delta, If there's a target, we can tell the camera to look at the target and adding 1 meter up to not look at their feet. When setting the value of target, if the target is set to null, then we can reset the camera's rotation to revert back to the same as its parent node, the spring arm, setting its rotation to vector 30. We might also want to trigger the lock on function if the current target exits our visible range limit. Ether locking off or automatically switching to a closer target. So let's add a signal for when the current target exits range and emit it if the body which exited the area is the current target. We can then connect this signal to the player input handling script. Calling the toggle lock function. Depending on whether you want the behavior to switch to the nearest target or simply lock off, you may wish to bind a true argument for the force of parameter. In the spring arm script, while locked onto a target, we'll want the spring arm to always point away from the target to keep both the target and the player character in view of the camera. Let's add a new variable to hold that direction as a vector three. Then when the value of target is being set, if it's not null, then we can set the value of target direction to be the difference in our global position minus the target's global position. This will result in a vector pointing from the target back to the player character. We can then re use the tween rotation function, but it is expecting a float parameter representing a y rotation. We can get the y rotation, which will point in this direction using the trigonometry function, A t two, giving it the x and z values of our target direction. Once the target has been set, the spring arm will spin the camera around to be in the opposite direction from the target behind the player. But we want it to stay there. In the process function, if the value of target has been set and in parentheses, if either the tween does not exist or is not running, meaning the tween has finished and the spring arm is in position, then we can perform the same calculations, but just set the value of rotation y directly instead of doing another tween. This will keep the spring arm pointing away from the target, while locked onto the target, keeping the target and the player character in view of the camera. Lastly, in the player input handling script, we want to prevent the player from rotating the spring arm while locked onto a target. This is happening in two different places in my script, one for PC controls using the mouse, and another for controller support with the right stick. So I'll just wrap both in an if statement to not allow these inputs while locked onto a target. Let's try it out. When we lock onto an enemy, the camera focuses on the target, while the spring arm rotates to position the camera in the opposite direction. Moving the character, they rotate to face the enemy and animate fluid movements in all directions. The spring arm maintains the camera's position of being behind the character, pointing away from the target, while the camera continues to look at the target. The player's normal camera controls have been disabled while locked on. If the locked on target is out of range, then it will automatically switch to the nearest target or lock off if there isn't one to lock onto. If we force to lock off, then the camera refocuses on the player character, normal control of the spring arm resumes, and the characters locomotion animations return to normal. We now have our player nodes adapting their behaviors to being locked onto a target. In the next lesson, we'll have the player character perform attacks. I'll see you in the next lesson. 5. Attack: Hello, friends. In the previous lesson, we changed the behavior of the player nodes while locked on to a target. In this lesson, we'll add attack animations so the characters can attack each other. Let's start by opening the project settings, the input map tab, and adding a new input event for attacking. I'll use the left mouse button and the right shoulder button on my controller. Then in the player input handler script, we can check if the attack button was pressed when checking for character control inputs. If the attack button was pressed this frame, then we should tell the character to attack. But what if the character is busy? They could be jumping or performing some other action and we might not want them to be able to attack right this moment. Requiring the player to wait until attacking is possible before pressing the button would be quite frustrating. Most modern games will consider the button press to be valid for a short time after the button is pressed, which is known as input buffering. Let's add a time or node to the player input handler. Name it input buffer, and set its one shot property to true, so it will only count down once at a time. Grabbing a reference to this input buffer timer using add on ready, we can start the timer after telling the character to attack, then await its timeout signal. After the timer has counted down to zero, we will then tell the character to cancel the instruction that was given to attack. So at any moment while the timer is counting down, the character will try to attack when they are ready. The default wait time for the timer is 1 second, which is a reasonable amount of time for the attack input buffer. Another input we might want to buffer is the jump input. Following the same pattern as the attack input, we can start the timer, await its time out signal, then cancel the jump input. In the character script, let's start by creating a new code region for combat. Then move the player targeted function into this region. We'll need a public function to tell the character to attack and another to tell them to cancel the instruction. The jump function will also need a match and cancel jump function two. The main purpose of these functions is to set variables which will be checked by the animation tree so it knows which states to travel to. At the top of the script, we'll add more variables to indicate the character's intentions of what they want to do next, Buffered inputs as Booleans, whether they want to jump or want to attack. Then the attack function will set the wants to attack variable to true and cancel attack will set it back to false. Since the input is being buffered, we no longer need to require that the character be able to move or be on the floor, and we can delegate that responsibility to the Animation trees state machine. And we also don't need to tell the Animation tree state machine playback to travel to the jump start state. We will make this transition automatic if the conditions are met. But we don't want both of these variables to be true at the same time. The character will either want to jump or attack, not both. So anytime a buffered input is set to true, all others will be set to false, overriding the instruction. In the character scene, selecting the animation tree and switching to the animation tree panel, we can see the state machine. Let's change the transition leading from locomotion to jump start to be automatic under the condition that the character is on the floor and can move and wants to jump. Now the character will automatically jump as soon as they are able within 1 second of pressing the jump button. This is particularly useful for chaining jumps one after another, since the player will be able to press the jump button before hitting the ground. But looking at the jump state machine, let's consider how to incorporate the attack animations. We could transition from locomotion to attack and back again. But the animations would look bad if we want the character to be able to move while attacking. I wouldn't allow the character to attack while jumping. Instead, it would be better to separate animations that involve moving the character from those of performing actions. If your state machine hasn't been saved as a project resource already, click on the drop down beside Tree Root and select Save As. I've already saved mine in the character scenes folder and named it character animations. But I'm going to rename it to character movements. With the state machine saved, let's remove it from the character's animation tree, and instead replace it with an animation node blend tree. The animation tree panel now displays a graph, which will hold our blend tree, allowing us to blend any amount of animations together. Right clicking in empty space, select load. Then navigate your project resources to find your character movement state machine. The state machine we were using previously is now a node in the Blend tree graph, and the result of the state machine will be output by the pin on its right side. If we connect the output of the state machine to the output of the blend tree, then the animation will work just as it did before. But the purpose of the blend tree is to blend multiple animations together. Let's disconnect the pin. Then right click and add a blend two node. The blend two node will start with the animation connected to its in input pin and blend in a percentage of the animation connected to its blend input pin. The result of this blend will be output from the pin on the right side, which we can connect to the blend trees output. The animation we want to blend with the character's movements will be the character's actions, namely their attacks. We can add another state machine to the blend tree and name it action. Then connect its output to the blend two nodes blend input pin. Next, we'll open the action state machine and start the character in the idle animation. Transitioning from start to idle to make it the default action state. Next, we'll need a view of our character and to start both the movement and action state machines by clicking on the play button on their respective start states. Then return to the root of the blend tree. Our character still appears to be in the jump idle animation because the percentage of the action animation being blended into it is currently zero. If we drag the handle over to one, we can see the animation gradually change from jump idle to idle. Next, click on the edit filters button, and turn on enable filtering. Let's move the window out of the way so we can see our character as we filter individual bones, deciding which bones our action animations should be applied to and which it shouldn't. Turn on all of the bones which comprise of the character's upper body. Now the character's lower body, including their position, is being affected by the movement state. Jump idle, while their upper body is being controlled by their action state idle. This allows us to blend these animations together in three different ways. If the character is moving but not performing any action, then the blend amount will be zero, using only the output of the movement state machine. If the character is performing an action but not moving, then the blend amount will be one, and filters turned off, using only the output of the action state machine. If the character is both moving and performing an action, then the filters will be turned on. Using the output of the movement state machine for the character's lower body and the output of the action state machine for their upper body. Let's name this blend two node lower plus upper, since it's blending the upper body animations into the lower body animations. But if the blend amount is zero or one, then the animations in one of the state machines will not be run to save on processing time, which can result in some problems if we are relying on them to set properties or call methods. It can be useful to always have both state machines running at all times, even if the character is not performing any actions or not moving. To ensure both state machines are always active, we can set the blend amount to 1%, and when performing an action, increase it to 99%. We can assume that it should start with a blend amount of 1% since the character won't be performing any actions when first loaded. Back in the character script, since the animation tree is going to become much more complex, it would be a good idea to abstract its behaviors into its own script. We can remove the state machine playback variable from the character script and add a new script to the animation tree. I'll save it in the character scripts folder. In the animation trees new script, our main objective is to create the three animation blends we just mentioned. In the process function, we'll need to set the blend amount parameter like we did with the blend spaces. Copying the property path and specifying a blend amount. Let's make blend amount a private float variable with a default value of 1%, and pass this as the argument. We will then need to change the value of blend amount based on whether or not the character is performing an action. Let's store the action state machines playback in a variable. This time, using the self keyword, since this script is attached to the tree and using the property path to the playback of the action state machine. Then in the process function, we can check which node the action state playback is currently in. And if it is in the idle state, then that means the character is not performing any actions, and we can set blend amount to 1%. Otherwise, they are performing an action and we can set it to 99%. But this will be an instantaneous change which might look a bit jittery. So instead of setting the variable directly to 1% or 99%, let's just move it toward that number by a small amount over time. Using the move toward function, we can start from what the variable currently is, moving it toward what we want it to be by Delta. This will make the transition take 1 second, which is a bit long. So let's multiply Delta by a new variable to hold the blend speed, which I'll give a default value of four. The transition will take one quarter of a second. To turn the filters on or off, we will need access to the blend two node directly. So declaring a variable to hold it, which is of type animation node blend two. We can access it starting from the animation trees tree root, using the get node function. This function accepts a string name, which we can specify as the same name we gave the node in the blend tree graph, lower plus upper. Be careful to get the spelling exactly the same. We aren't allowed to turn the filters on or off while the animation tree is processing. So Let's make a public function to let the character script do this for us, telling the animation tree whether or not the character is moving. If the character is moving, then the filters should be enabled to blend the upper body actions into the lower body movements. If the character is stationary, then filters can be disabled to only use the action animations. While we're here, let's also add two more functions to set the blend amounts of the locomotion blend spaces to keep them abstracted from the character script. Set locked on blend, accepting a vector two parameter for the blend amount, setting the blend amount parameter of the locked on blend space. And set not locked on blend, accepting a float parameter for the blend amount, setting the blend amount parameter of the not locked on blend space. Back in the character script, we can see that the property paths to the blend amounts are no longer valid, since they don't refer to the movement state machine. Abstracting these functions, we will keep any changes to the animation tree contained within its own script. In the normal physics process, We'll also need to tell the animation tree whether or not the character is currently moving. However, we are now assuming that every character must have this script attached to their animation tree, and that they are using the same animation tree. So let's save our new animation tree as a project resource. I'll put it in the character scenes folder, along with the character movement state machine. Then update each of the other characters to use the new animation tree and attach the script to it, so they will all function in the same way. I'm only using the Barbarian and skeletons in this project, but the night mage and rogue should also be updated if you're using them. However, this creates a new problem. If every character shares the same blend tree resource, then changing the enable filters property for one character will change it for all characters. We can get around this by giving each character their own unique copy of the blend tree. To avoid having to replicate every change in every character's blend tree every time, I'll use a simple shortcut. In the animation tree script, during the ready function, I'll set the tree root property to be a duplicate of the blend tree. Passing true as an argument will also duplicate any sub resources this resource is using. Since the blend tree, this animation tree is using as its root is no longer the same one it was originally assigned. We will need to assign the values of the action state playback and the blend two node after this duplication. Now every time a character is loaded, they will make their own unique copy of the blend tree and be able to change their filtering behavior without affecting other characters. Let's return to the characters action state machine. Our character doesn't have any equipped items yet, so let's transition them into unarmed idol. For now, let's make the condition for this transition be if they are targeting an enemy or if they want to attack using a short cross fade, and they can transition back if they are not targeting an enemy and don't want to attack. Then to keep things organized. Also add another state machine named unarmed attacks, and transition into the state machine if the character wants to attack. They can then transition back to unarmed idol at the end of the attack animation. Editing the unarmed state machine. Let's start with unarmed male attack punch A. This will transition to either end or to unarmed male attack punch B at the end of the animation based on whether or not the character wants to attack again. We can repeat this pattern transitioning to either end or unarmed male attack kick at the end of the animation, based on whether or not the character wants to attack yet again. And we can create an endless cycling combo by transitioning back to the first punch as long as the player keeps pressing the attack button. When they stop pressing the attack button, the state machine will transition to end, which will return back to unarmed idle. We might also want to restrict how the player moves while attacking. For example, it wouldn't make any sense for the character to move at all during the kick animation, since their feet should be busy. I'll zoom in on the animation track display to better fit the length of these animations. Let's add a public function to the character script to restrict movement, accepting a Boolean parameter for whether or not the character can move. We'll then set the value of the move variable to be the opposite. In the character's kick animation. We can add a call method track calling a method on the character's root node. Calling the restrict movement function at the start of the animation, passing true as the argument, restricting the character's movement while kicking. And another key at the end of the animation can restore the character's ability to move by changing the argument to false. But for this to work, we need to return to the blend tree and add functions to the blend two nodes filters so that if the character is both moving and performing an action, the actions functions will be called. L et's try it out. Our animations appear to be working as they did before. As soon as we tell the character to attack, they transition into the unarmed idle state before performing an attack. If we keep cicking, they will keep attacking repeatedly, cycling through the different unarmed attack animations. It is even possible to move or jump while attacking, and the character will use the appropriate animations for their upper and lower body, but they are not allowed to move or jump while kicking. If we lock onto a target, the character transitions into the unarmed idle state and locking off transitions back to the normal idol. We now have our character performing unarmed attacks. In the next lesson, we'll add weapon attack animations. I'll see you in the next lesson. 6. Weapon: Hello, friends. In the previous lesson, we blended upper body actions with lower body movements, allowing our character to perform unarmed attacks. In this lesson, we'll add a variety of weapon attack animations as well. Let's start in the characters animation tree where we already have unarmed idle and unarmed attacks. Following this pattern, we'll add the two handed idle animation. In this asset pack, there are no one handed or dual wheeled idle animations. But we can just add two new copies of the idle animation and rename them. From each of these idle animations, we can then connect to attack state machines that work just like the unarmed attacks. Connecting from each of the idle animations to the attacks if the character wants to attack, and returning at the end of the animation. We only need a way of telling the character which set of animations to use based on their equipped weapon. In this project, we have a script named enums, holding all of our enumerations, to which we will add another weapon type. Starting with unarmed as the default, then listing out each of the different weapon types that need different animations, one handed mala, two handed mala and dual wheeld. It is important to know that the enumeration is just a numbered list, so each of these values are actually numbers, starting with unarmed being zero, one handed is one, two handed is two, and dual wheel is three. In the character script, we'll add another variable named attack animation with a type of our new enumeration. We can export this variable for testing purposes. L et's change the character's attack animation to something other than unarmed. I'll use two handed melee. Back in the action state machine, we can set the transition condition to move from idle to unarmed idle to be if the character's attack animation is zero, which is unarmed in our enumeration, and also at least one of the other conditions by putting them in parentheses. The return condition will then also be the opposite. Either the attack animation is not zero, or both of the other conditions are met. Adding transitions to each of the different idle states, we can set their conditions to be the same as unarmed idle, only using a different value for the attack animation. I'll give each of them the same cross fade time as well. Then transitioning back to idle under the logically opposite conditions. Inside each of the attack state machines, we can use the attack animations the same way we did with the unarmed attacks. There are four different one handed attacks, creating a cycle of different animations as long as the character wants to keep attacking. Repeating the same process for two handed attacks, which only has three different attacks. And the dual wheeled state machine also only has three attacks. We can test this out in the game, since the characters attack animation is set to two handed, they use the two handed attack animations. We can tell them to use one handed attack animations or dual wheeled animations as well. Now, we only need to change the variable when equipping items to the character. In the custom resources, we will need a more specific class to describe a weapon inheriting from equipment Giving it the class name weapon, we can add an extra exported variable to store the type of weapon it is, and we can use the same enumeration as the type. Then any of the item resources in the project that are weapons can be reclassified as weapons, giving them a value for their weapon type, so the character will know which animations to use when equipping them. For demonstration purposes, I'll update the x to be a one handed weapon. Remember to repopulate any fields that are not carried over when changing the resource script. The grade as as a two handed weapon, and the dagger as a dual wheeled weapon. In the int or whichever script you're using to equip items, we will need an exception for if the item being equipped is a weapon. And if it is either a two handed weapon or a dual wield weapon, then we will need to remove anything the character has equipped in their off hand slot. C hecking first if the file, progress equipment array has anything equipped in the offhand slot. I'll then use that to index the item buttons in the player's inventory and remove the E for equipped from the button. Then also set the equipment arrays value to negative one or empty. I'll then tell the character to dof any equipment in their off hand socket. In the character script, the character will need to know what they are holding in their hands. Let's add variables of type node three D to hold their main hand and off hand equipment. Our character donning a weapon can then also have some extra logic added for if the item is a weapon. The character's main hand item can then be assigned to this weapon, and the attack animations for the characters can be set to match that of the weapon being equipped. If the item is a dual wheel type weapon, I'll also add another duplicate copy of the weapon in the character's off hand socket. And assign it to the off hand variable. When doffing a piece of equipment, if that equipment is being removed from the character's main hand, then we can set the main hand variable to null and also check if they are currently dual wielding. If dual wielding, I'll tell the character to do the item in their off hand as well. And then I'll set the attack animation to unarmed. Likewise, if the socket being doffed is the off hand, then we should set the offhand variable to null. We no longer need to export the attack animation, since it will now be set by the equipped weapon. Now the character starts using unarmed attacks. But when equipped with a one handed weapon switches to using one handed attacks. We can equip them with a shield, then switch to a two handed weapon, and the shield will be automatically removed. The character now uses two handed attack animations. Switching to the dual wheeled daggers, the character has a dagger in both hands and uses the dual wheeld attack animations. Un equipping the daggers, the character returns to using unarmed attacks. The last thing our attacks need are hip boxes. Collision areas for detecting if the attack actually hits an enemy and dealing damage. Opening up the weapon scenes, we can add hit boxes to each of them. Using an area three D node, which won't be monitoring or monitorable by default, nor have any collision layer or collision mask until they are told to do so. Since these character models have short limbs and small weapons, their attacks don't reach very far. So I'm going to be very generous with their hip boxes, making them larger than the actual weapons. Creating a new script for the weapons, inheriting from item. I'll replace the script attached to the weapons root node and open it for editing. We can tell the weapon to grab or reference to its hit box using add on ready. Then add functions to set the hit boxes collision mask to a new integer. And another that will activate or deactivate the hit box by setting its monitoring property. The character holding this weapon can now specify which collision layer their enemies are on and activated hit box when swinging the weapon. The character model will also need a hip box for unarmed attacks. Just like the weapons, it doesn't need to be monitoring, monitorable, nor have a collision layer or collision mask and will have an oversized collision shape. But we don't need to position it right now and can delegate that responsibility to the animations. In the character script, we can grab a reference to the unarmed hip box using add on ready. Remember to add a hip box to every character or use the get node or null function to avoid errors. Let's also export a three D physics layer to hold the collision layer of this character's enemies. This will add a grid of numbers in the inspector just like the collision layers in the area three D notes properties. This way, we can specify that this character will be trying to hit things on the enemy Hurt collision layer, which I have said to be layer 18. Meanwhile, enemies using the same script can set their enemy collision layer to be the player's HRT collision layer. During the ready function, we can set the collision mask of the unarmed hip box to be the enemy's collision layer. Likewise, anytime a weapon is equipped, we can set its collision mask to be the enemy's collision layer, so it will know which layers to mask regardless of who is holding it. We'll then need some functions in the character script to activate and deactivate the hip boxes of the weapons during the attack animations, accepting a boolean parameter to specify if the hip box is being activated or deactivated and an integer for which hand? And I'll give it a default value of one to mean the main hand. Using a match statement, we can create three separate cases, activating the hip box for unarmed attacks, main hand attacks, or off hand attacks. If unarmed, we can set the monitoring property of the unarmed hip box to active. If main hand or offhand, then we can tell the weapon to activate or deactivate its hit box. Our characters animations can then have tracks added to them to activate and deactivate the hit boxes. I'll just demonstrate with one attack of each type, starting with one handed male attack chop. Adding a call method track to this animation, we would generally expect the hit box to be activated just after it starts swinging. Adding a key in this moment, we can change the Boolean argument to true to activate the hit box of the character's main hand weapon. Then deactivated at the end of the swing by changing the argument to false. This will prevent enemies from walking into the weapon when it isn't moving from being harmed by it, and only deal damage when the character is actually swinging the weapon with force. The process is the same when wielding a weapon into hands. Adding a track, then key framing function calls to activate the hit box at the start of the weapon swing and deactivating it at the end of the swing. When dual wielding, we will need to specify which hip box is being activated, whether it is the main hand or the off hand, by also changing the integer parameter to two, if the hip box being activated or deactivated belongs to the off hand weapon. And when unarmed, we will not only need to activate the hip box, but also position it to be in the correct location. Using a property track to key frame the hip boxes position property. We'll then change the integer argument in the function calls to zero to indicate that we want to use the unarmed hip box. It's a good idea to make sure the hip box is in position at least one frame before activating it. It helps to use orthogonal views to position the hip box exactly on the character's fist from at least two different angles. Then click on the key icon to key frame this position in the animation. We won't be able to test these hip boxes until the next lesson when we add hurt boxes for them to hit. The hip box will not only activate with the animation, but then also follow the character's fist. Remember that you'll need to complete this process for every attack animation. We now have our character attacking with different animations for each weapon type. In the next lesson, we'll allow characters to get hurt and take damage. I'll see you in the next lesson. 7. Hit: Hello, friends. In the previous lesson, we added hit boxes to our weapons and attack animations. In this lesson we'll add Hurt boxes to our characters so they can get hit by attacks. A characters Hurt box is just another area three D node. We'll be adding to the character models. They will not be monitoring, but will be monitorable by the hit boxes. They will exist on either the players Hurt box layer or the enemy's heart box layer appropriately. But don't need to mask anything since they aren't monitoring anyway. For the Hert boxes collision shape, another capsule is very common, but it is usually smaller than the character body collision shape and entirely contained within the character model. It wouldn't feel fair to the player to get hit by something that doesn't appear to make contact. Keep in mind that the weapons are using oversized hip boxes, so the hert boxes being undersized would also be reasonable, but I'll make mine fit the character's head and body. I'll then copy and paste the same hert box into my enemy character scenes, changing their collision layer to the enemy HRT layer. Then in our weapon scenes, I'll use the axe as an example. We'll need to connect the hit box's area entered signal to the weapon script. If this weapons hit box enters a character's Hurt box, then we will need this weapon to inflict damage to that character. For now, let's add a public variable for this weapons damage. When the collision happens, we'll need a reference to the character that the Hurt box belongs to. In my project, that will always be the parent node of the Hurt box. We can then tell the character to take damage, passing the weapons damage as an argument. It might also be nice to know the direction the damage is coming from. So we can use the difference between their global positions normalized. To keep all of our weapons information in one place, its damage should be added to the resource file as an exported variable, and I'll give it a default value of two damage. Each different weapon resource file can then specify how much damage that weapon will inflict. Remember to connect the area entered signal for each weapon scene as well. Likewise, our character's unarmed hip box also needs a signal connection to tell the character to deal unarmed damage. Getting the Hurt Box's parent and telling them to take one damage and providing the normalized direction. Then in the character script, when we create the weapon instance, we can set its damage to be the same number from the resource. I. While we're here, let's also write the take damage function in the combat region. Accepting a damage amount to take, as well as the direction the damage is coming from, giving it a default value of vector 30. In order to take damage, our characters will need health. I'll add an export category for the combat variables. Then declare one for the characters maximum health as an integer, which I will set to five. And their current health also as an integer, which we can set to their maximum health using add on ready. Let's also declare a couple of boolean parameters for whether or not the character is dead, and if the damage being taken was delivered from behind, and also grab a reference to the character's rt box using add on ready. When taking damage, the character's current health will be reduced by the amount of damage received, but we don't want to allow it to go below zero. So we can use the max function to either set it to this calculation or zero, whichever is higher. The from behind Boolean can then be set to true if the dot product of the damage direction with the character rigs forward basis vector is a negative number. If the character's current health after taking damage is zero, then they will die. Otherwise, they will be hit. While the death animation will be automatic, the hit animation can be handled by the animation trees script, and we will tell it whether or not the damage received was under or over a certain threshold. This way, we can change the hit animation based on the amount of damage received. We may also want to track a character's health or when they die. It would be a good idea to broadcast both of those events as signals. Declaring a signal for anytime the character's health changes, we can use a percentage of health remaining as an argument and another signal for when the character dies. Then emit the health changed signal if they take damage and the die signal if the character dies. To calculate their percentage of health remaining, we just divide their current health by their MX health. But since both of these values are integers, the result will also be an integer, so it will only ever result in zero or one. We'll need to cast at least one of them to afloat first before performing the division, so the result will also be afloat. If we no longer want dead characters to be impacted by collisions, we can also set their collision layer to zero, their collision mask to one, so they will only be affected by terrain and set their H rtbach monitorable property to false. It is also possible that a character might be hit or killed while performing an action. So we should also keep track of which actions being interrupted may cause problems and correct them in a private function. For example, we should probably turn off all of their hip boxes if their attacks were interrupted, which we can also do in a separate function, checking if references to each exists before turning them off. In the characters animation tree, we will want the character dying or being hit to interrupt and override all other animations. Since this blend tree is already saved as a resource, we can remove it from the animation tree and replace it with a state machine. At the very root level of the state machine, we will start with our characters death animations, since those will have the absolute highest priority over all other animations. Then add another state machine for the hit animations. We will transition from hit to death A if the character is dead. With a short cross fade. Or alternatively to death B if they were also hit from behind giving this priority by setting it to zero. There may be no need for our character to transition back from being dead, but it doesn't hurt to add the transition with the condition being that they are not dead. Inside the hit state machine, we'll do a similar thing with the hit animations and add our blend tree as the default animation here. Enabling the transitions to the hit animations and returning to the blend tree at the end of their animations. With a cross fade both into and out of the hit animations, With the animation tree state machine updated, we will save it as a project resource. I'll name it character animations. And for the final time, every character can be updated to use the new state machine. In the Animation trees script, we will need a reference to the hit state playback. Also, since we updated the format of the tree, we will need to update all of these property paths. We will now access the blend node by getting the hit node, then the blend tree node, and finally, the lower plus upper blend two node. Giving the get hit function a definition, we can receive the Boolean parameter as whether or not the character is getting hit lightly. Then tell the hit state to travel to either hit A if they were hit lightly or hit B if they were hit harder. Let's try it out. If we punch the skeleton, they react to being hit lightly, equipping an axe and attacking them again. They are hit harder. A third hit and they die, falling backwards since they were hit from the front. We can equip the great ax, which I've set to have a damage of five and kill another skeleton from behind, and they fall forward as they die. It would also be nice to be able to track the player character's health on the UI. Let's add a control node and call it health gauge. I'll put it behind everything else on the UI. This will need a text direct to draw the border, a color erect as the fill, and another text direct if we want an icon. I'll use slim slider frame as the border image. It's very large, so I'll scale it down to one quarter of its size. And I'll use icon, small heart full for the icon. Resize it and position it where it looks nice relative to the border. I'll then set the color for the color ect to match the color of the heart, and set its position and dimensions to fill the gauge. I'll reset its size property, anchor this to the bottom left corner. Clicking and dragging on the size x property of the fill, we can see how the gauge can easily be set to any percentage. Attaching a script to the health gauge, we can just call it gauge to reuse it for any other gauges we might want. We'll grab a reference to the fill color ret using add on ready, and also declare a variable to hold the size dot x of the fill when the gauge is completely filled, which is what the size dot x property is set to when first loaded. Then write a public function named set value, accepting a percentage float parameter. All we need to do is set the size x property of the color t to be the percentage multiplied by the maximum size. Connecting the player character's health change signal to the gauges set value function, it will now automatically track the player's health. To test the health gauge. I'll just tell the character to take two damage every time the player presses the run button. Now we can see the character's health gauge deplete, and the character dies when it is empty. I'll then reset the take damage line back to run. We now have our character getting hit and taking damage from attacks. And the next lesson, we'll allow characters to avoid getting hit by dodging. I'll see you in the next lesson. 8. Dodge: Hello, friends. In the previous lesson, we allowed our characters to get hit by attacks, take damage, and die. In this lesson, we'll help them avoid damage by dodging. Let's start in the project settings, the input Map tab, adding an input event for dodging. I'll use the space bar or the right face button on my controller. In the player script, we can use the same methods as jump and attack to buffer the dodge input. In the character script, we'll need a variable for whether or not the character wants to dodge. But a dodge isn't just a simple input. It also requires a direction as a vector three. In the combat region, we'll need a dodge function and a cancel dodge function, setting the Boolean variable appropriately. And with any input buffered, all other buffered inputs will be set to false. Since we're already storing the input direction in the character script, we can check if that is vector 30 at the moment the character was told to dodge. If not giving any input, I want the character to dodge backwards. I'll use the character rigs bases forward vector, multiplying it by negative one to reverse it. If we are giving directional input, I want the character to dodge in that direction, but always with full force. I'll normalize the direction to remove magnitude from the equation. In the characters animation tree state machine, navigating through the death and hit animations into the blend tree, and finally, the movement state machine. We can add the dodge animations in a two dimensional blend space. Transitioning from locomotion to dodge if the character wants to dodge, and back to locomotion at the end of the animation. Both with a cross fade. Since this is connecting to locomotion, the dodge will only be performed while the character is on the floor. You may also want to have aerial dodges connected to the jump idle state. Inside the dodge blend space, I'll change the grid snapping to factors of one. Then add Dodge forward, Dodge backward, Dodge left and dodge right animations that will be blended together so the character can dodge in any direction. The animation tree script will need to be able to set the blend amount of this blend space as a vector two, the same way we set the blend amount of the locked on strafing animations. Back in the character script, when telling the character to dodge, we can tell the animation to set this blend amount. But the dodge direction is a vector three in global space, whereas the blend amount for the dodge is a vector two in the character rigs local space. We can calculate the blend amount by comparing the dodge direction to the rig spaces vectors using a dot product. I'll need to reverse the x blend amount multiplying it by negative one. If we try this out, the dodge animation plays three times and the character isn't actually moving. The animation repeats because wants to dodge is true for one full second, but the animation is only 0.4 seconds long. So it will repeat as many times as it can until the wants to dodge variable is set back to false before the 1 second is up. We will need to set the variable to false once the dodge has been performed successfully and also apply some velocity to move the character. Adding another function, much like applying jump velocity, we will apply dodge velocity and call this function from the animation. If this function is called, the character has successfully dodged, and we can set the variable to false to prevent more dodges from occurring from a single input. Then we will set the character's velocity to be the dodge direction multiplied by Dodge force. This will create a sudden impulse of velocity, moving the character rapidly in the direction they are dodging. Creating another exported variable for the character's dodge force. I'll set it to eight. May also want our dodge to have eye frames, short for invincibility frames. This is a duration of time when the character can't be harmed by attacks. Just like we have a function being called by our attack animations to turn the hip box on or off, we will also have a function called by the dodge animations to turn the Hurt box on and off. We should use set deferred to set the monitorable property of the Hurt box to the value of active in case this is happening during a collision event. Since it's possible that we may want to call this function as a result of a collision and we can't turn colliders on or off while they are being processed. In the characters animations, we will need to add function call tracks to each of our characters dodge animations. I'll first turn off the character's Hurt box. Then when the character looks like they start to move in the animation, I'll apply the dodge velocity. Once they've had some time for the Dodge velocity to move the character out of the way of the attack, I'll turn the HRT box back on by changing the argument to true. Then repeating the same set of function calls for each of the dodge animations. While it shouldn't happen with this code as it is currently, it might be possible for the character to be interrupted while dodging, meaning the het box might never be reactivated. Depending on how the game functions, you might want to include activating the character's box in the interrupt actions. Now that the dodge velocity is being applied, I don't really like how the dodge animation actually moves the character rigs root position, resulting in some abrupt movements, both leading into and coming out of the dodge. So opening up the animations, I'll remove the root key frames from the dodge animations. Keeping the character rig rooted where the character should be as determined by the character body's physics. And then I'll repeat this for the other dodge animations as well. Running the game, we can view the remote scene tree, find the character's rt box, and pay attention to the monitorable property. Dodging sets the monitorable property to false, then resets it back to true, and the character animates the dodge more smoothly with the velocity thanks to the removal of the root keyframes. With the dodge now animating and functioning properly, let's now consider if we want to allow the character to attack and dodge at the same time. Also, should the character be allowed to attack or dodge if they are being hit? If we don't want to allow this behavior, we will need to track when the character is attacking, dodging or being hit. Let's add some more boolean variables to our character script is attacking, is dodging and is hit. All of these will need to be exported to be accessible to the animation player node. Using property tracks, the hit animations can set the is hit variable to true at the start of the animation. And back to false at the end. Then in the animation tree, when transitioning from locomotion to dodge, we can check to make sure that the character wants to dodge and naught is hit, and naught is attacking. Before transitioning into any attack states, we can check not only if the character wants to attack, but also if they are not hit and not dodging. We can then repeat the process of setting the Boolean variables in the dodge animations. Each one setting the is dodging variable to true at the start and false at the end. Again, with every attack animation, setting the is attacking variable to true at the start of the attack animations and falls at the end. In case attacks or dodges are interrupted, we should also set these variables back to false. There is also the possibility of the character dying interrupting the hit animation. So we'll set the is hit variable to false here. In order for the is attacking variable to be set by the action state machine, we will also need to enable the filter for it and also the hit box position. In order to test if the dodge is actually working, we'll need the enemies to start attacking. If you haven't set up the enemy attack animations yet, an easy way to do it is to copy the characters animation player and paste it into the enemy scene. We can then tell the animation tree to use the new animation player. And as long as the enemy has the same bone structure as the character, the animations will all work the same. Otherwise, you will need to set up the enemy to have at least one attack capable of activating a hip box, masking the character's t box. To make the enemy's attack, I'll just attach a blank node to them in the level scene. With a simple script, telling them to attack. I'll make a new folder for enemy scripts and call it attack. After grabbing a reference to the character as the parent of this node, I'll tell the character to attack in the ready function. Since the variable will never be set back to false, this is all that's required to have the enemies endlessly perform attacks. Let's try running the game with visible collision shapes on. And view the remote scene tree where we can see the values of all our new variables. The character will wait for an attack to finish before performing a dodge and vice versa. If we let the skeleton hit the character, they take damage and are unable to attack or dodge until the hit animation is finished. And we can dodge the skeleton's attack with our new dodging action. We now have our character dodging to evade attacks and restricting when the character can perform certain actions. In the next lesson, we'll allow characters to block incoming attacks with a shield. I'll see you in the next lesson. 9. Block: Hello, friends. In the previous lesson, we helped our characters avoid taking damage by dodging attacks. In this lesson, we'll allow them to block attacks with a shield. Let's start in the project settings, the input Map tab, and add an input event for blocking. I'll use the right mouse button and the left shoulder button on my controller. In the player script, the block button doesn't need to be buffered, since it behaves more like the run button, changing the character's behavior while the button is held down. Sending a signal to the character to either block or not, based on whether the button is pressed or released. The character script can then store this value in a variable. Wants to block. But unlike the other variables, there's no real need to buffer the blocking input. Then add a function in the combat section that sets this variable. In the characters animation tree state machine navigating to the action state machine. We can add the block animation and transition to it if the character wants to block with a short cross fade. Then transition back to if they don't want to block. Getting a view of the character and blending in the action state, this animation causes the character to raise their shield. Once this animation is complete, we can transition to the blocking animation, which should be set to loop to keep the shield up as long as the player holds down the block button. This will also return to idle if the character no longer wants to block with a short cross fade. We might also want to be able to transition to the block animation from some of our other idle states, such as the unarmed and one handed idle states. To keep the number of transitions lower, I'll first combine the idle and one handed idle states by logically combining the transition conditions to reach the one handed attack state. If attack animation is one, they want to attack and are not hit and not dodging. Then return to idle at the end of the attack animation. Deleting the one handed idle state, I'll then rearrange the other states to be a little more organized. Now, either idle or unarmed idol can transition to block, if the character wants to block, and transition directly back to unarmed, if the character's attack animation is zero, and they either have a target or want to attack, and they don't want to block. With a priority of zero. Now, either idle or unarmed idle can transition to block if the character wants to block. The block and blocking states can skip idle and return directly back to unarmed idle with a priority of zero. This way, the character doesn't have to go through two different idle states between blocking and attacking. If we try it out, our character can transition to the block animation when pressing the block button and transition back to idle when the block button is released. But it would make more sense to restrict the character to only use this animation if they have a shield equipped. In order to do that, we will need another resource script. Inheriting from equipment, giving it a class name of shield. If you want your shields to have different variables, we can define them here. Let's add a variable for the amount of damage reduced by blocking with this shield with a default value of one. Accessing the Barbarian round shields custom resource. We can change it to be a shield and change the damage reduction amount if we want to. Remember to repopulate any properties which are reset as a result of this class change, and update all shields to use the new class script. In the shield scene, we'll need to be able to store the variables from the item resource in the shield just like we did with the weapon. Adding a new script, It'll just have a damage reduction integer variable and a dummy activate hip box function, which will ignore the Boolean argument and do nothing. It will become clear why this is necessary in a minute. We can then replace the script attached to our shield with the new one. In the character script, let's add another variable to the equipment category for whether or not the character has a shield equipped. When donning a piece of equipment, if the item is a shield, then we will set it to be the off hand equipment, pass along the damage reduction value from the item resource to the shield, and set has shield equipped to true. Since the off hand variable is now holding a shield instead of a weapon, interrupting the character's actions will deactivate all hip boxes. And since the off hand is not null, we are telling it to deactivate its hip box. But we have told the shield to ignore this request. Then when doffing a piece of equipment, if the socket is the offhand, then we can assume that the character is no longer holding a shield. Then when telling the character to block, we can add the condition that the character must also have a shield equipped. The inventory script will also need some more conditions when equipping a shield as well to prevent the character from equipping a shield when their off hand is already occupied with a two handed or off hand weapon. This process will vary depending on how your inventory and equipment systems work. If the selected item is a shield, and the character has something equipped in their main hand, and the character is currently using either the two handed or dual wheel animations, Then I'll unequip the main hand socket and tell the character to dof the equipment in their main hand. If we try it out. The block button is now ignored if the character doesn't have a shield equip. We can equip a two handed weapon, then equip the shield. The two handed weapon is unequipped automatically, and then the shield is equipped. And now the character can block with the shield. Next, let's consider how the blocking input works with our other actions. If the player is blocking and decides to either attack or dodge, then I would want those actions to take priority interrupting the block. But if we just set the wants to block variable to false, then the player will have to release the block button and press it again to continue blocking. Instead, we can declare another Boolean variable. Let's call it interrupt block. If the character wants to attack or dodge, then this variable can be set true. And when the attack or dodge is done, we can set it back to false to allow the character to continue blocking automatically. I'll simplify the add dodge velocity function to just call cancel dodge. Then in the animation tree, we can add the conditions to transition out of block or blocking to include if the block is being interrupted. And not allow the transitions back to block until the block is no longer being interrupted. Now the character will stop blocking while attacking and automatically return to blocking as long as the block button is still held down. And the same also works for dodging. The last thing we need to do is actually reduce the damage of incoming attacks while blocking. For that, we'll need to know if the character is blocking. Since blocking isn't limited to a single animation and the animation loops, there's no way we could simply tell the animator to set a boolean variable to true or false. Let's add the block hit animation. Enabling the transition to this animation from blocking and returning at the end of the animation. What we contract to know if the character is blocking is which state this state machine is currently in. If it's either in the blocking or block hit state, then the character's shield is up. The block animation is also quite long. It includes not only raising the shield, but also a cycle of blocking animation as well. I don't think that the character should be considered to be blocking until after the shield is fully raised, but I don't want to wait for the block animation to finish either. So In the character's animation player, I'll reduce the length of the block animation to make it stop after the shield is raised at one third of a second. In the animation trees script, let's add a public function to check if the character is blocking, returning a Boolean. First, storing the name of the action states current node in a string variable. We can return if it is either blocking or block hit. And we'll also need another function to play the block hit animation, telling the action state to travel to the block hit state. Then in the character script, at the top of the take damage function. We can check if the character's animation is currently blocking, and that the direction that the damage is coming from is the character's front. Using a dot product of the damage direction with the character rigs forward basis vector. If the value is a positive number, then the damage is coming from somewhere in front of them. This would cover a wide angle though, a full half sphere in front of the character. Using something closer to a quarter or a half, we can limit it to a cone shape. Under these conditions, the attack should be successfully blocked, reducing the amount of damage by the damage reduction of the shield. Using the max function to cap it at a minimum of zero. We can then tell the animation tree to play the block hit animation, and if the amount of damage has been completely reduced to zero, I'll just return since the character isn't taking any damage. Let's try it out. Equipping the shield and blocking the skeleton's attack. The damage is reduced to zero and the block hit animation plays. Taking damage from the side or the back, the character still takes damage. We now have our character able to block attacks with a shield from the front. In the next lesson, we'll give them a ranged weapon that can shoot projectiles. I'll see you in the next lesson. 10. Shoot: Hello, friends. In the previous lesson, we allowed our character to block attacks with a shield. In this lesson, we'll give them arranged weapon capable of shooting projectiles. This asset pack does not contain a bolt for the crossbow, so I'll start by making one in the crossbow scene. Starting with a rigid body three D node, renaming it bolt. I'll position it where the base of the bolt will rest inside the crossbow with its blue z basis vector pointing forward where it will be shooting. Next, I'll add a mesh instance three D node as a child, populating it with the cylinder mesh. Reducing the radius and height, rotating it to point forward and positioning it to be forward along the z axis. I'll just give it a basic material and set the albedo to be a brown color. Then add a basic three D node as another child, so it will be placed with the same position and rotation and rename it amo socket. Then reparent amo socket to the crossbow retaining this position and rotation. Reparenting the bolt to the amo socket, so its position and rotation relative to its parent is now zero. The bolt can now be made into its own scene, which I'll put into a new folder for projectiles, and then delete it from the crossbow scene. The crossbow can now be loaded simply by adding a bolt as a child of this node. The bolt will need a collision shape. I'll use a capsule with the same dimensions and position as the cylinder. Expanding the solver section, I will need to have contact monitor enabled and max contacts set to something larger than zero. If we want the bolt to collide with the terrain, it will need to mask collision layer one. And I want my bolts to move slowly so I can see what they're doing. But don't want them to fall down due to gravity. So I'll set their gravity scale to zero. Attaching a script to the bolt, we can name it projectile, so it can be used for other types of projectiles in our game and save it in the item scripts folder. As soon as a bolt is instantiated, it will be loaded into the crossbow. And we may want to pass along information from the character or weapon to the ammunition at this time. Let's create a public function to do that. But for now, we'll just set its freeze property to true since the rigid body doesn't need to do anything until after the weapon is fired. To shoot the projectile, we will apply an impulse force sent by the weapon as a vector three. At this moment, the rigid body can start applying physics to move the bolt through the game scene, setting its freeze property to false. We no longer want the projectile to follow the transform properties of its parent, the crossbow. It should be re parented to the game scene where it can move and act freely. In case it isn't pointing directly toward the direction, it's being fired, we can tell the bolt to look at a point in space that is its own global position minus the direction of the impulse force. Then apply the impulse force to the bolt to get it moving in that direction. Connecting the body entered signal from the rigid body to the script, this function will be called if the bolt makes contact with anything on collision layer one, the terrain. If that happens, the bolt can stay exactly where it made contact, setting its freeze property back to true, but deferred since this is happening during a collision. It is very important that anytime you implement a feature into a game that instantiates a large number of nodes, that you also implement some way of cleaning them up. Otherwise, you could end up with very large amounts that cause frame rates to drop and eventually crash your game. A common practice with projectiles is just to give them a maximum lifespan. Let's add a time node to our projectiles and set it to count down one time. I'll set it to 10 seconds. Then grab a reference to the time or node using add on ready. We will start the timer when firing the projectile. If it doesn't end up hitting anything, we still want to clean it up, so it doesn't just fly off into the distance forever. Connecting the time up signal to the projectile script. It will cue the projectile to be freed from the game scene. Back in the crossbow scene, we'll need a new script for this weapon. We can name it ranged weapon and put it in the item scripts folder. Replacing the script that is attached to this weapon with ranged weapon and give it a reference to the custom resource file for the crossbow. We can export the ammunition as a packed scene, a prefab that we can use as a blueprint to instantiate as many as we need. Then assign the bolt as the ammunition for the crossbow in the inspector. We'll need a reference to the ammo socket using at on ready and a reference to the ammo that is currently loaded into the ranged weapon. It might also be nice to have a damage bonus that the weapon will add to the shots fired from it as an integer. The amount of impulse force the weapon applies to its ammo when it is fired as afloat. I'll use a low number like ten so we can see the bolts moving through the air easily, and the collision mask of our enemies so that information can be passed from the character th the weapon to the ammunition. When equipping the weapon, characters are setting the hip box, collision mask of weapons, so we can easily reuse the same function to set the mask variable. And just like we did with the shield, we can add a function to activate the hip box, ignoring this parameter, and just return. Since the weapon has no hip box of its own. The weapon will need to be able to shoot the projectile with a public function, and we'll let the character specify the direction they want the projectile to go as a parameter. All the weapon needs to do is pass this function call along to the amo, multiplying the direction by the weapons force. Then set the loaded amo reference to null since the weapon is no longer loaded, and a reload function, which will instantiate a new bolt, assigning it to the variable. Then add it as a child of the amo socket, giving it the correct position and rotation required to sit nicely in the crossbow. We can then pass along any information the ammunition will need to know before it is fired, like the weapons damage and the collision mask. So let's go ahead and add these as parameters to the onload function in the projectile script. Our character script will need a variable to know if the weapon they are holding is currently loaded. And two new functions, reload and shoot. Reload, will just set the variable to true, and then tell the main hand weapon to reload. For now, shoot will just set the variable to false, then tell the mainhand weapon to shoot in a direction, passing the character rigs forward basis vector. If the weapon is unequipped, since it is being completely removed from the scene tree, the ammo will also be removed along with it. So I'll just reset the weapon is loaded variable back to false at this point. Our weapon type enumeration, we'll need two more entries for one handed range of attacks, which will be number four, and two handed range attacks is number five. In the crossbows custom resource, I'll change the script from equipment to weapon. I'll change its weapon type to one handed range, lower its damage, and repopulate its icon. And likewise change the heavy crossbow to two handed range. In the characters animation tree, we will need more actions for range attacks. This state machine is already getting quite large and messy, so I'll wrap the range attacks inside another state machine. Transitioning into this state machine, if the characters attack animation is either four or five, and they want to attack, and they are not hit and not dodging. Then go back to idle at the end of the animation. And I'll blend both transitions with a bit of cross fade. Inside the range to attack state machine, we can add animations for shooting and reloading. Both for one handed and two handed range attacks. We only need to play the correct one once and then exit back to the action state machine. So they will all transition at end. Transitions to one handed will require that the attack animation be set to four. And two handed set to f. Whether the shot or reload animations play will be based on the value of weapon is loaded. Now that the animations can be played by the animation tree, we need the animations to call functions in the character script. During the shoot animation, we will add a method call track and scrub to the frame before the character recoils from the projectile being fired. Then add a key frame to call the shot function. Likewise, during the reload animation, We will try to select a frame when the character has finished reloading the weapon. And add a key frame to the call method track to actually reload the weapon. If we want these animations to lock the character out of being able to perform other actions, we'll need to set a boolean variable like we did with the attacks. I'll just reuse the is attacking variable. And repeat for the two handed ranged animations as well. Let's try it out. We can equip the crossbow to the character, and pressing the attack button once, we'll have them load the crossbow with a bolt. Pressing the attack button a second time, they fire the crossbow forward, and the bolt shoots out until it hits something in the environment, where it stops and destroys itself after 10 seconds. Now we need the bolts to deal damage to enemies. Back in the bolt scene, we will need to add a hit box to the bolt. The hit box needs a collision shape. I'll use the sphere. I'll position it at the other end of the bolt and give it a smaller radius. Just like our other hip boxes, it doesn't need to be monitoring or have a collision mask yet. Grabbing a reference to the hip box using at on ready, let's also export a variable for the bolts damage. I'll use a default value of one. When the bolt is loaded into the crossbow by the character, that would be a good opportunity to pass along information, such as any damage bonuses and the collision mask. The weapons damage bonus can be added to the bolts damage, and the hip box's collision layer can be set to the character's enemy collision layer. When the projectile is shot, we can tell the hip box to start monitoring for enemy herd boxes. Connecting the hip box area entered signal to the projectile script. We can tell the Hurt box that this projectile hit to tell its parent character to take damage. From the direction of the Hurt boxes global position minus the Bolt's global position normalized. The bolt can then be to be removed from the scene. And if the bolt hits terrain, it should no longer be monitoring to deal damage, so we'll set it to false deferred. In the character script, I would like to make absolutely sure that when the character is locked onto a target, that the projectile is fired directly at that target. So the shoot function will split into two different behaviors based on whether or not the character is locked onto a target, calling a separate function to shoot at a target, passing the target as an argument. But the weapon should only fire at the lockdown target if the character is facing them. So we can check if the dot product of the character rigs forward basis vector and the normalized difference in their global positions is a positive number. The closer to positive one this is, the more similar they are, the more the character is facing towards them. I'll say something larger than 0.75 is close enough to shoot at the lockdown target. In the ranged weapon script, giving shoot at target a definition, accepting a target as a parameter. We can now calculate the direction as the difference of gulple positions normalized and multiplied by force. Since this will use the weapons gubal position, not the character's gubal position, the direction will be more accurate. And I'll add 1 meter up to the target's position to not aim at their feet. Let's try it out. Equipping a ranged weapon and locking onto a target, I'll first just face off to the side, and the projectile still shoots forward, since the character isn't facing the target. Moving to face the target and firing a second bolt, this time it shoots directly at the target and deals damage. We now have projectiles being fired from ranged weapons, hitting enemies or the environment. In the next lesson, we'll allow enemies to detect players and fight back. I'll see you in the next lesson. 11. Enemy: Hello, friends. In the previous lesson, we added range attacks that shoot projectiles. In this lesson, we'll allow enemies to detect, chase, and attack the player character. In the graveyard scene, we added a simple node that tells the enemy to attack constantly. Let's remove that and start building a more complex version to only attack when the player character is in range to be hit. In the skeleton minions scene, we'll add a child node to it of type node three D, since it will need to access three D positions and rotations. I'll name it aggression. Then add an area three D node, which will represent the enemy's attack range. I'll give it a collision shape and use a sphere. Adjusting the size of the sphere to be a little bigger, I'll give it a radius of 1 meter, then position it to be 1 meter up from the ground and 1 meter forward. The attack range area will be monitoring for collisions masking the player's herd box. L et's attach a script to this node and put it in the enemy scripts folder. Since the purpose of this script is to control a character just like the player input handler node, we'll need a reference to the character being controlled by this node. With how I am structuring my scene tree, this node will always be the direct child of the character node, so I can access the character by calling get parent. Connecting the area entered and area exited signals to the script, I'll rename the parameters and give them types for clarity. When the player characters Hurt box enters the attack range, we want to tell the enemy to attack. And when the Hert box exits the attack range, we can tell the enemy not to attack. We can save this branch as its own scene and save it in the enemy scenes folder. Then delete it from this enemy scene. E Back in the graveyard scene, we can attach our enemy aggression node to any or all of our enemies to make them automatically attack the player character if they enter the attack range. Let's try it out. The enemies are not attacking yet, but we'll attack if we enter their attack range and stop when we leave it. Next, we'll allow the enemies to see the player character from a further distance. Opening up the aggression node scene, we can add another area three D node to represent an enemy's vision. Adding a collision shape to this scene, I'll use another sphere with a much larger radius. I'll use 5 meters and position it to be ahead of the enemy and a little bit up off of the floor. This area can be monitoring for the player character's collision layer. I'll use layer nine. Then add a ray cast three D node to be their line of sight and also move this up off of the floor. This will be looking for the same layer as the field of vision, but also be obstructed by terrain. Since I used layer nine, which is the character body and not the t box, I'll connect the on body entered and exited signals to the aggression script. When a body enters the field of vision, I'll store it in a variable, Let's name it target of type character body three D. But we'll also need to confirm that they have a clear line of sight too before becoming aggressive. So we'll need a reference to the cast. And we can store whether or not the enemy has seen the player in a Boolean variable. Let's also grab a reference to the enemy's field of division now too. A. Riding the process function, we can start by checking if the value of target has been set. If it's null, then the enemy can't see the player character. We should just return. If the enemy has not yet seen the player, then we can check their line of sight to the target. Setting the target position of the raycast to be the difference in their global positions, adding a meter up off of the ground. After forcing the raycast to update if it is colliding with something and that something it is colliding with is the target, then the line of sight is not obstructed by terrain. We can set the has seen player variable to true. If the value of this variable is true, then the enemy's behavior will change, and we will have them chase the player character. When the player character exits the enemy's field of vision, if the body is the target, and the enemy has not yet seen the player, then we no longer need to check their line of sight, so we can set target to null. This aggression node is a child of the character node, which is not rotating. So the field of vision and attack range will not rotate with the character rig. We can use the character reference to grab the rig rotation, accessing the y property, and assigning it to this nodes y rotation. So it will follow the same rotation. We might also want a way to tell this enemy to stop pursuing the player character. In which case, we can set the target to null, has seen target to false and tell this character to move with no direction. Good time to call this function would be when the enemy dies. So overriding the ready function. We can connect this character's died signal to the stop function. When the enemy first sees the player character, we can also connect the targets died signal to the stop function to stop the enemy from trying to kill the player character after they've already died. I'll also use this same function to turn off this enemy's vision, setting its monitoring property to false. In order for the enemy to move toward the player, we first need to make some changes to our level scene. Let's add a navigation region three D node to the level. Then re parent any nodes that will be used to generate our navigation map as children of this node, including the ground and obstacles. With that done, select the navigation region node and populate the navigation mesh with a new navigation mesh. Then click on the Bake navigation mesh button. The navigation mesh will cover the terrain with triangles that form an area where the characters will be able to walk. Expanding the navigation mesh resource, we can see its properties. The only section of concern right now is the agents section. Agents are our characters who will be using this navigation mesh to walk around the scene. Taking a quick look at our characters collision capsules, I have given them a height of 2.6 meters and a radius of 0.75 meters. I'll copy those values into the agents height and radius in the navigation measure resource, then rebake the navigation mesh. The mesh now leaves more space from walls and obstacles. In order to use the navigation region, our characters will all need another node attached to them, a navigation agent three D node. Make sure you attach one to every character. Grabbing a reference to this node using add on ready. We can then write a function in the character script, which can be used to tell any character to navigate to any location in the navigation mesh for any reason. This is also very useful for many things outside of combat. Accepting a target position as a vector three, we will set the navigation agent's target position to be the same location. The navigation agent will automatically use the navigation mesh, assigned the same navigation layer to produce a path to get as close to the target location as it can. Calling the agents get next path position, we'll return the first way point along this path as a vector three. We can then subtract the character's current global position to get a vector pointing from where we are to the first position along the path. If we multiply this by 101 to remove the vertical component, then normalize this vector. It is now functionally equivalent to an input vector produced by our player input handling script when the player tilts the analog stick or uses the WASD keys. So we can assign this to the input direction variable. And the physics process will handle the rest, telling the character to walk along this path, which will navigate them to their target. It would be a good idea to move everything that happens to a character when they die into a public function in case anything other than taking damage can cause them to die. When a character dies, we should tell them to stop moving. And when they are being told to move or take any action, we should ignore those requests if the character is dead by returning. Back in the aggression script, we only need to tell the enemy to navigate toward their target once they've seen it. Before trying it out, let's choose one enemy. I'll use the skeleton warrior and select their navigation Agent three D node. In the D Bug section, click the enable Toggle to be able to see the path rendered in game. Approaching the first skeleton, as soon as we enter their field of vision, the line of sight immediately confirms that they can see us, and the navigation agent finds a path for the enemy to walk toward us. Then attack us. Defeating this enemy, this time, let's attract the attention of the rogue. Since they're behind the gravestone, the line of sight is not confirmed and the enemy remains in hiding. And if we approach the warrior, we can run around the open grave, seeing how the navigation agent will automatically update the path in real time, to go around the shortest possible path to reach the player. We might also want our enemies to be able to wield weapons. Back in the graveyard scene, let's attach another basic node to one of our enemies, and name it equip. Attaching a script to this node, I'll put it in the enemy's scripts folder. All we need to do is grab a reference to the parent node, the character, and export a weapon to equip to this character. Then in the ready function, tell the character to do the weapon. I'll tell my skeleton warrior to equip a great axe. Then copy and paste this node onto another enemy and change the weapon to something else. I'll give the rogue some knives. Each of our enemies scenes will need to have bone attachment nodes added to them. Following their left hand slot and right hand slot. We can then populate the characters equipment socket array with these nodes to be used when equipping weapons. Lastly, it would be nice to not have to reload the game when we die. So let's connect the player characters died signal to the game manager script. After fading to black, I'll just tell the scene tree to reload the current scene, resetting everything back to how it was when the scene was first loaded. Let's try it out. The skeleton warrior is equipped with the great a and is using the two handed attack animations. When we die, the entire scene gets reloaded. And going up against the rogue this time, they are equipped with the knives and use dual wheeled animations. We now have a complex combat system in our game with a variety of mechanics that can be adjusted to fit the needs of your game.