Transcripts
1. Introduction: Hey phone. In this course you'll learn how to create a third
person male combat system, Unity with features like combos, counterattacks, intelligent
enemy AI, and all that. We'll design the
system in a modular, data driven, and scalable way
using cleaning practices. You'll not really learn how to build the system in this course, but you'll also learn lots of valuable game
programming concepts that you can use
throughout your career. The combat system that
you're going to create will be a free flow
combat system. That means the enemy won't
attack the player mindlessly. Instead, they'll attack
in a coordinated way by circling around the player
and attacking one at a time. This is the type of
combat system used in most of the modern diggings
like assassin screed, Batman, Narcosis, Marvel,
Spider Man, et cetera. The combat system that build will also be easily
customizable. If you want to use
Ppunge and kick attacks instead of Swat attacks, you can easily do that without changing a single line of code. Let's look at what we
cover in this course. We'll start by making a basic
third person controller. We'll not use any
assets for it, fill, build it completely from
scratch because it's a good way to learn
the fundamentals of gameplay programming. Then we'll start building
the combat system on top of our third person characters
fill. The ability to attack. The attacks will be architecture using scriptable objects. Ingenuity'll make it designer friendly and designers
will be able to create and edit attack combos without changing any code. Then we'll start
building the enemy AI. We'll act the enemy I using
a finite state machine. It's a technique that's
been used for building AI in games for
the last 25 years. It's used in popular
games like Last offers, Batman Archom series
and many more. We'll build a finite
state machine from scratch and create
our enemies using it. We'll start by creating
simple behaviors of the enemy like staying idle
and chasing the player. Then we'll implement
more advanced behaviors, like circling the player and attacking her in
a coordinated way. We'll also implement
more advanced features like counter attacks
in the system. Throughout this course, you'll learn lots of game
programming skills. You learn lots of intermediate
level C sharp concepts like generic classes, dictionary link
inheritance, et cetera. And you'll also learn to
use mathematical concepts like technometry
dot product cross, et cetera while
building the system. You learn to design
gameplay systems while keeping the usability
and scalability in mind. The skills and techniques
that you learn from this course will
be really helpful for you if you want to pursue your career as a
game programmer. All the models and animations used in this course
are completely free. You'll be able to follow along the course without
spending any extra money. The course also comes with the completed combat system project. You can use it as a reference
while following along. This course is not for
absolute beginners. You have no a
little bit of Unity and C sharp to be
able to follow along. But if you know the basics,
you should be good to go. Who am? My name is Uttldleep. I'm a software engineer who
has been building games and applications professionally
for the last six years. I'm really excited to
share my knowledge with you and help you in
your came de journey. I hope you will join
me in this course.
2. Section Intro: In this section will build a third person
controller from scratch. Since we're making a third
person combat system, the first thing that we need is a third person controller. And we'll build it from
scratch in the section, because building a third person
controller is a good way to learn the fundamentals
of gameplay programming. If you're someone
who already knows how to make a third
person controller, you might feel like
skipping the section. But I highly recommend
you to watch this. Because in the section I'll be explaining some
important concepts like root transforms of the animation and how to set up the root motion of the
animation and all that. I recommend you give this
section a quick watch. Even if you are familiar with building a third
person controller. By the way, you may
skip this if you wash the third person
controller section from my unit Parco and
Climbing System course. Because in that course I am building this same third
person controller. If you wash that, then
you may skip this. Otherwise, I highly
recommend that you don't skip this section.
Let's get started.
3. Project Setup: Hey phone. In this video I'll start by creating and
setting up the Unity project, in which we'll build
a combat system. I have my Unity up
opened over here. I'll go ahead and
create a new project by clicking on this
new project button. Here we have to
select a template. For the template,
I'll select the RP. This is because we'll be using the shader graph to create
shaders in this course. For that, we need RP or HDRP. It won't work in the
standard three D project. I'll choose three
DRP as a template. I'll just name this project
something like Combat System. By the way, I'm using
2020 0.3 as the version. You can use any future version, and it should work fine. Let me go ahead and
create the project by clicking on the
Create Project button. Okay, so it'll take some
time to create the project. I'll just pass the
video and I'll get back to you
once it's created. All right, so the project
has been created. The URP project comes
with some example acids. We don't need them. We're going to build everything
from scratch. Let me go ahead and
delete the example acids. And I'll also delete the polo containing the example acids. Okay, then we can also delete the tutorial info and the read me file.
We don't need them. Next, let's create a new scene
inside the scenes photo. We already have a
scene over here, but I want to create
a new one I could create and select scene
to create a new scene. Let me just name this
something like test scene. I'll double select to open it. Here I rather new plane game object under three D
object. I'll select plane. This will act as the
ground of our scene. All right, let me just reset its position so
that it's at the center. I also want to make this
plane a little bigger. I'll chain the X and Z scale
to something like five. All right, we have a
much bigger plane. Next, I want to change the
material of the plane. If we use a plane
material like this, then it'll be hard to understand if our character
is moving or not. Let me create a new
material for this. Under the Materials folder, I'll right click and
create a new material. I'll call this
Material Checkers. All right, under the surface Input we can change the
texture of the material. So if you'll con on the base map you can select the texture
that you want to use. I'll just use this
default checkers texture that comes with Unity. All right, now we can track and drop this material
to the plane to apply it. It's applied now, but the
checkers are a bit big. I'll increase the tiling to ten to make the
checkers smaller. That looks good. Now we have set up the
scene to work with. Next we can start building our third person
character controller.
4. Camera Controller: He. In this video
we'll implement a third person camera that can rotate around
the player like this. All right, so let's
start creating it. Let's implement the third
person camera controller. First I'll add a
capsule to the scene, which we can use as
the player for now. All right, let me
name this player. I'll double click
on it to focus. Let me just place
it above ground. All right, now we want our camera to look
at the splayer and rotate around it. Right? Let's go ahead and write
a script to achieve that. All right, in the scripts foldo, we already have a script called
Simple Camera Controller, but we are not
going to use that. We're going to build our camera
controller from scratch. All right, let me go
ahead and delete it, then I'll create a new script
called Camera controller. All right, let me get rid of all the default
code in the script. In what you want to do is first we want to place
the camera behind the player at a
distance like this. All right, this is the player
and this is the camera. We want to place it
behind the player at a distance in the camera
controller script. First, we need a reference to the object that we
want to follow. Here I'll create a sized
variable of type transform. I'll call this follow target. All right, next we want to place the camera at a distance
behind our Pollo target. Right in the update function, we can set the position
of the camera to follow target dot position minus the distance by which we want
the camera to be behind. Right subtracts follow target
position by a new vector. This vector will be zero
in both x and y and the z. We should provide the
distance for now. Let's say we want the camera to be five units behind the player. I'll just put five for the Z. All right, let's core hurt
and see the result of this. First we need to attach the camera controller
script to our main camera. All right, let me just drag
and drop it over here. Then we need to assign
the Pollo target object. Let me just assign the
player as the follow target. Now let's go ahead
and run the game. All right, So you can
see that the camera is placed behind the
player at a distance. We can actually minimize the game view and
take a look at it. From the scene view, you can see that the camera
is behind the player. It's sad, position
is minus five. Since the players
position is zero, it's placed five units
behind the player, right? Just like we specified
from the script. That's working, but it's not good to hard code numbers
like this in the code. We should make this a variable and expose it in
the inspector so that the designers can easily tweak these values without
touching the code. Right? Let me create a
new variable for that. All right? I'll make
it a sterilized field so that it's exposed
in the inspector. And this will be
a float variable, I'll just call it distance and set its value
to five by default. Okay? So now in here instead
of hard coding five, I'll use my distance
variable. Okay? So now if you go to here, we can change the distance value from the inspector, right? So let me just put
it back to five. All right, now we are placing the camera behind
the player at a distance. Next, we should rotate the
camera around the player. When we move the mouse, right, we should rotate the camera both horizontally
and vertically. But first, let's handle
the horizontal rotation. How can we rotate the camera
around the player like this? For that, all you have to do
is just rotate this vector. This is that vector. If we rotate this vector by
multiplying it with a rotation, then it'll be rotated like
this based on the angle. Let's go ahead and
try this out in red. To rotate a vector we just have to multiply it
with a rotation. Let's say we want to rotate
this vector by 45 degrees. For that first, we need to create a
rotation of 45 degrees. I can do that by
using con oiler. All right? In which
axis should be rotated? The rotation here is
horizontal, right? If you want to rotate
something horizontally, then we should rotate it
in the Y axis, right? In the anion oiler, I'll put zero for the X, 45 for Y, and zero for the set. This will create a rotation
of 45 degree in the Y axis. Then to rotate this vector, all you have to do is just multiply this coaterion
to this vector. All right? And this will rotate the
spector by 45 degrees. Let's score T and see
the result of this. All right, I'm going
to play the game. Now you can see that the camera is still
behind the player, but now it's at a 45
degree rotation, right? So this will be more clear if you look at it from
the scene view. All right, so if I just go to
the top view in the scene, you can see that the
camera is placed behind the player at a 45 degree angle. All right, that's
working as expected. Next we want to rotate the camera around the
player like this, when we move the mouse
horizontally, right? That is pretty
simple to achieve. What we can do is we can create a float variable over
here called rotation Y. Then in the update function, we can add the
mouse x input into the rotation by vab. All right? I'll say rotation
y plus or equal to input dot t axis, mouse X. All right, We'll be adding
the joystick in the future, but during the implementation, let's keep this simple
and use the mouse. Now in the cotton oiler, we can use rotation y instead of just hard
coding fortify. All right, so let's go to
it and see the wrestles. So you can see that
as I move the mouse, the camera is rotating
around the player. But right now it's not
looking at the player, but it's still rotating
around the player. All right, so you can get a better view of it
from the scene window. Okay. Yeah, you can see that the camera is rotating around
the player in a circle. Okay, So that's working fine. Next we should make the camera look at the target
when we rotate around it. Right? That's pretty
easy to achieve. All we have to do is set this rotation as the
rotation of the camera. All right, first let me store this rotation into another variable so
that we can reuse it. I'll call this variable
target rotation. All right, here I'll multiply the vector with
the target rotation. Then we can also set this as
the rotation of the camera. I'll set the transform rotation
to the target rotation. Okay, now the camera should
also look at the player. Let's go ahead and test it. All right, now you can see that as rotate
around the player, the camera is
constantly looking at the player that's working. Now we're done with the horizontal rotation
of the camera. Next, we should also
rotate the camera vertically, like this, right? This is going to be just like
the horizontal rotation. The only difference will
be for vertical rotation, we should rotate it
around the x axis. Here I'll create another
variable called rotation x. Then we should rotate
the camera vertically. When we move the
mouse up and down, right to the rotation X, I'll add the mouse Y
input or light level is copy this line and
change it to mouse Y. This might be a bit confusing to some of you because we are adding mouse Y to rotation X and mouse X to
rotation Y, right? But the reason for this is because for horizontal rotation, we should rotate
around the Y axis. Vertical, we should
rotate around the X axis. If that's confusing to you, take a minute or two to think about it and you'll
get a hang of it. All right, Let's continue with the vertical rotation here. I need to add rotation X to
the X of the cotton oiler. Now we should also be able to rotate the camera vertically. All right, let's go
ahead and test it. Now if I move my
mouse up and down, you can see that we can
rotate the camera vertically. Okay? But in the case
of vertical rotation, we want to clamp it
to some limit, right? You should not be able to
rotate all the way around the character like this, right? We should set the limit to how much we can
rotate vertically. All right, we can easily limit that by clamping the rotation
x value clamp available, we can use mathftclamp. What this function will do is it'll take a min and max limit. If the value goes
beyond the limit, then it'll just clamp
the value to the limit. In this case rotation excess, the value that we want to clamp. And then we should define
the min and max limit. I'll create variables for the
min and max limit and I'll make them sterilized
field so that we can tweak it from the inspector. Okay, I'll call the
first variable in vertical angle and let me set it to something like
minus fortified by default. All right, and then I'll
duplicate this line. By the way, you can
duplicate a line in vital studio by using
the control shortcut. All right, I'll duplicate the min vertical
angle variable and create another one called
max vertical angle. I'll set this one to
press fortified degrees. All right, now we can
clan the rotation x between the min vertical angle and the max vertical angle. Okay, so scored and tests. By the way, I'll just make sure that the min and max
angles are correct in the inspector and I'll go
ahead and play the game. Okay. Now, I can't move the
camera above this limit. I'm clammed over
here. If I go down. Okay. This minimum angle
is a bit too much, but I'm clammed over here. I can't go beyond this point. Right? The clamping
is working properly. We're done with the
vertical rotation, But there is actually
a problem right now. The camera is
actually looking at the bottom of the player, right? It's not pretty
obvious right now, but when you bring in an
actual character model, the camera will be
looking at the feet of the model and that will
look really weird. The camera should actually
look at the chest, the head part of the player. We can easily achieve
that by adding an offset to our
target position. Here I'll create a new vector two variable and I'll
call this framing offset. Okay, Then instead of using the polo target
dot position directly, I'll add the framing offset
to the Pollo target position. All right, let me show this in a variable called
focus position. This is the position to which
the camera should focus. Okay, We can't simply
add these two like this, because photo target
position is a vector three and the framing offset
is a vector two, right? We can't directly add them here. I'll have to create
a new vector three. Then I'll have to
pass framing offset X and framing of the taught by. Okay, so that solved the error. And now we have
focus point here. Instead of using the follow
target dot position, I'll use the focus position. Let's go to Unity and tests. All right, Right now it's
still looking at the bottom because we didn't set the
offset in the inspector. Let's try changing the
framing offset from here. Here you can see as I
increase the Y offset, our focus position gets
higher and higher. So let me just set it to
a value like one for now. This is further when we bring
in an actual character. All right, so that
looks much better, sex. We also need to limit the
minimum vertical angle. This is a bit too much. We should not be able to go
below the ground, right? Let me try setting it to
something like -20 By the way, here you can see that
the framing offset has been reset to zero. This is because we changed its value when we were
in the play mode. Whatever changes that we
make in the play mode will always be reset when
we exit the play mode. All right, let me set
it back to one now. Let's go ahead and test if
the vertical angle is fine. Yeah, that's okay. Now we're done with the vertical and horizontal movement
of the camera. All right. There is one
more thing I want to add. Before I stop the video. I want to be able to control the speed at which
the camera moves. That is pretty easy to
achieve when we are updating the rotation x
and rotation variables. From here, we can multiply the mouse input
with the speed value. All right, I'll create a new float variable
called rotation speed. And let me just set
it to two by default. Okay? And then I'll just multiply the rotation speed
with the mouse input. All right. So let me
also do it in this line. Okay, So now we should also be able to control
the rotation speed of the camera from
the inspector. All right? It's true right now, but if you want, you can increase it
to a higher value. Next, I want to hide the mouse. When we are playing
the game right now, you can see the mouse when
the game is being played. I want to hide that.
To hide the mouse, we have a property
called cursor visible. I'll set that property
from the start function. Let me create the
start function. Over here in the start function, I'll set the cursor
visible property defaults. This will make the
cursor invisible. Then we can also lock
the cursor by setting cursor lock state to
cursor lock mode lock. All right, so now we
won't see the cursor when we play the
game, All right? You can see that the cursor
is not visible anymore. Now if you want to
stop the play mode, you should first
press the escape key, so this will make the
mouse visible again. And then you can go
ahead and stop the play. All right, so just
keep that in mind, Now we have a decent
third person camera. But there's one more
thing I want to add. Some people prefer the camera
direction to be inverted. Right? Let's add a setting for that in our camera
controller script. Here I'll add two new
polling variables. The first one will
be called invert x, the second one will
be called invert y. If the invert x is true, then we should invert
the x rotation. We can easily do
that by multiplying the mouse input with minus one. Right? What I'll do is I'll create two more
variables for the invert value. First one will be
called invert x v, the second one will
be invert y al, okay? And we'll set its value
from the update function. If the invert x is true, then the value
will be minus one, and otherwise the
value will be one. All right, let me store this
into invert x val variable. Then I'll also do the same for
the invert val. All right. Now we can multiply the invert X val with
the mouse X input. We can also multiply invert Y
val with the mouse Y input. Okay? So this should
allow us to invert the camera movement
from the inspector. I'll turn on invert
X and invert Y. All right, now when we test, the camera movement
should be inverted. All right, so now we have a basic third
person camera set up. So I'll stop the video here. And in the next video we'll
implement character movement.
5. Character Movement: Everyone in this video will implement character movement. It's going to look like this. The character movement will
be related to the camera, just like we see in all
third person games. All right, let's start
the video First, I'll replace this capsule
with an actual character. When implementing
character movement, we won't be able to
see the rotation properly if we're
using a capsule. All right, let me switch this
with an actual character. I'll be downloading
the character from a website called Mimo.com This site has lots of
good characters and high quality animations and
they're completely free. I'll open up Xm.com You'll have to create an
adobaccount to use this. All right. I'll go to the character section and
download a character. The character I want
to use is Erica. All right. This is the
character I want to use. Click on it and then click
uses this character. Once you do that, you'll be able to see the character
in this window. By the way, you don't have
to use this same character. There are lots of different
characters to choose from. All right, I'll just go
with this character. Let me click on the download
button for the format, to select Px for Unity. Then we want the pose
in the post itself. Just make sure these two
options are correct. Then you can click on
the download button and the character will be
downloaded as an FPX filed. All right, so I've
saved it to my textop, Now let's go ahead and import
the character into Unity. Under Assets, I'll create a
new folder called Models. This is where we'll put
all the three D models. Next, I'll drag the FPX file that I downloaded into
the models folder. Okay, so the model is imported. So I'll drag the model to the scene so that we
can have a better look. All right, let me just get
rid of the capsule now. Yeah, you can see
the character model, but it doesn't have
any textures on it. Fix a, select the model
In the material stap, click on the extract
textures button. All right, so we need to specify a folder in which the
textures will be extracted. Here, I'll create
a new folder for that. Let me call it text. All right, And I'll
select this folder. Okay, so now we're getting
this warning saying that no texture should
be marked as novel map. Here, we just have to click on the Fixed now button and it'll automatically
fix it for us. All right, so now we
have the textures arson. Next let's implement
the script to move this character inside
the scripts folder. I'll create a new script
called Player Controller. All right, let me open
it up in Visual studio, and I'll also get rid of the
default code in this script, we should move the player based
on the input of the user. Right. First I'll get the input
from the update function. Let me create the update
function over here. Then I'll get both horizontal
and vertical input. All right, let me
store the horizontal input in a variable called H. Then I'll also get
the vertical input, and I'll store it in
a variable called V. All right, so
we have the input. Next, let me create a vector
based on these two inputs. I'll call this
vector move input. All right, so this will
be a vector three. The x value of the vector
will be the horizontal input. The y value will be zero, and the z value will
be the vertical input. All right, this
is a direction in which we should move
the player here. We have to do one more thing. We have to normalize
this vector. So I'll use a
normalized property to normalize this vector. What this will do is this will set the length
of the vector to one. If we don't do this, then
the diagonal movement will be faster than the movement
in a single direction. That can be solved very easily
by normalizing the vector. Okay, now we have
the movement input. Next, we should move
the character based on this input, move the character. I'll update the
transformed position. To the position. We can add more input so that the character will move in
the direction of the input. Then we should be
able to control the speed of the moment, right? For that, I'll create a
variable called move speed. Let me just set it
to five by default here we should multiply the move speed with
the moment input. All right, so now the character
will move in the speed. And finally, we should also multiply this
whole thing with timed or delta time so that the movement is
frame rate independent. Okay, now the character should
move based on our input. Let's score Unity and test it. All right, so first we should assign the player controller
script to our player. I want to attach it to
the model directly. Instead, I'll create an empty game object
here called Player. All right, let me
reset its position. Then I'll make the model a child of the
player game object. Okay, I'm doing it
like this because in the future we'll have to add more things to this
player game object. All right, now we can go ahead and attach the player controller
script to the player. Then in the camera script, the photo target
reference will be missing because we
deleted the capsule. Now let's go ahead and
assign our player. All right, now let's go
ahead and from the game. The camera is moving around
the player, just like before. Now when we press the WASD keys or the arrow keys, the
character should move. All right, so the
character is moving, If I press the arrow key,
it's moving forward. If I press the down arrow key, it's moving backwards and then it's also moving left and
right when I press those keys. All right of the movement
is working as we expected. But this is not how the character should move
in a third person set up. Right now, when we press
the forward input, the character is moving
in its forward direction, but it should actually move in the camera's
forward direction. In a third person controller, the movement of
the player should be related to the camera. So to achieve that,
all you have to do is multiply our more input with the rotation of
the camera, right? This will make the movement
flate to the camera. First, we need to
get a reference to the camera from the script. Let me actually
create a variable for the camera
controller over here. All right, then I'll catch a reference to it from
the awake function. Since the camera and player are attached to
different objects, we can't simply use the
ket competent function. Instead, we'll have to get the reference to the
cameras game object. First, we can get
the reference to the cameras game object by
calling camera main property. Then on that we can call ket competent and get
the camera controller. Okay, let me cast this to the
camera controller variable. Now we can multiply the move input with the
rotation of the camera. All right, so I'll
get the rotation from camera controller
transformer rotation. And then I'll multiply
it with the move input. Okay, I'll store this in a
variable called direction. I'll just say IR for a shot. Then here I'll use the movement direction variable instead of the movement input. This should rotate
the movement input in the camera's direction. But there is actually
a problem with this. Our character should
only be able to move in the exit plane, right? It should only be able to
move in the horizontal plane. When we multiply the move input with the
camera's rotation, we should only take the
camera's horizontal rotation, we should not take the
vertical rotation. If we use the vertical rotation, then the player will also be
able to move up and down. We don't want that unless you're making a player that can fly. Let me go to Unity
and show you what happens when we use
the complete rotation. All right here, the movement
is related to the camera, but the player can
also move up and down. If the camera is
facing up like this, then the player will
be able to fly. All right, so we
don't want that here. Visually take the horizontal
rotation of the camera. We can get the
horizontal rotation from rotation y, right? What I'll do is in the
camera controller script, I'll create a new property
called plan a rotation. This will be the rotation in the exit plane or the
horizontal plane. All right, in this property
we should return a coternion. Soil create a coternion
by using cotoniler. We should only consider
the y rotation. We should not consider
the extortation like we do over here. Soil pass zero for the extation, then I'll pass rotation
variable for the y. Then I'll pass zero for the Zt. This will give us the plan ar, rotation of the camera. Now from the player controller, we can simply multiply
the move input with the planar rotation. All right. By the way, if you're not familiar with
properties in C sharp, I'll explain it real quick. Properties are pretty
cool feature in C Sharp. Usually in most
programming languages, if you want to return something
like this from a class, you would create a
public function. Usually you would create
a public function, get plan rotation. Then from that
function you would return this value, right? So this is how you usually
return something from a class. But in C sharp, we have a cool
feature called properties. Properties are just
like a function. The only difference
is they won't have this parenthesis that
a function has, right? You don't have this feature in most of the
programming languages. I just want to give you
a quick explanation in case you don't know
what properties were. By the way, if you
want to use a function over here like this,
it's shortly fine. I just like using properties
since they are shorter. All right, let me get
rid of the function now. The movement is only multiplied
with the planer rotation. The character shouldn't be
able to move up and down. All right, let's go
unity and test it. All right, So the character is not able to move up and down, but it is moving
relative to the camera. Now when I press
the forward key, it's not moving in its
forward direction, instead it's moving in the forward direction
of the camera. Right? That's working fine. Next, when moving the character, the character should
rotate and face the direction in which
it is moving, right? We just have to set
the rotation of the character to make it
face in the right direction. From here, I also set the
rotation of the character. We want the character to
face in this move direction, right, But more
direction is a vector. For the rotation, we have
to pass a quaternion. We can actually get the rotation from a direction vector by using quaternion
rotation function. We have to pass the
directional vector in the parameter
of this function. Okay? This will make the character phase in the direction in
which it's moving. But we should only
update the direction when the character
is moving, right? How can we check if the
character is moving? For that, I'll create a new variable called
move amount over here. This variable will be a sum of horizontal and vertical input. If the move amount is
greater than zero, then that means the character is moving and we can
update the rotation. All right, but we can't simply add the horizontal and vertical
input like this. We should remove the sine of the horizontal and
vertical input, right? We don't care about the
direction of the input, we only care about the amount. If the horizontal
input is minus one, we should remove the
sine and make it one. To remove the sine from a value, we can use the math
at ABS function. Here, ABS stands for absolute, since this function returns the absolute value by
removing the sine. All right, and let me also do
it for the vertical input. And now if the move amount
is greater than zero, then that means the
player is moving. We can move the code to set
the rotation inside this if we can actually
move this line. Also, because there is no
point in trying to move the character if the
input is zero, right? I'll actually move these two
lines inside this condition. All right, now the character should face in the direction
in which it's moving. Okay, so let's score
Unity and test it. All right, So you can see
that the character is facing the move direction, okay? But the problem is the character
is rotating instantly, right? That looks really weird. We don't want the
rotation to be instant, we want it to rotate smoothly. So let's look at how
we can achieve that. To make the rotation smooth, we should not set the
rotation directly like this. I'll this rotation in a variable
called target rotation. All right, here, instead of setting the
rotation directly, I'll store it to the
target rotation variable. Then outside this condition, we can slowly change
the current rotation of the player to the
target rotation, right? Slowly change one
rotation to another. We have a function
called rotate towards. All right, in the
first parameter, you have to pass
the from rotation. Here. The from rotation will be the current rotation
of the player. Then two rotation will
be the target rotation. Finally, in the third parameter, we should pass the amount by which we should
change the rotation. Using the third parameter, you can control how slow or fast the rotation
change should happen. I'll actually create
a variable for that called rotation speed. All right, I'll set its value
to something high like 500. By default, the reason
is because the function expects the rotation amount
as an angle in degrees. Here, 500 is the
angle of rotation. All right, now we can pass
it as the third parameter. Then we should also
multiply it with time delta time to make it
frame rate independent. Finally, we should set
the value returned by this function to our
transform rotation. All right, so this will smoothly rotate the player instead
of rotating it instantly. By the way, let me
split this into two lines because I don't like lines that
are really long. Okay, now let's go ahead and test if the
rotation is working. I'll just select the player and make sure that the
rotation speed is 500. All right, now let's
go ahead and test it. Okay? Now it's
rotating smoothly. You can actually play
with the value of rotation speed and find
a speed that you like. I think this is fine.
We successfully made our character move
based on the input. Next, we should implement animations when the
character moves. We'll implement that
in the next video.
6. Animation Setup: In this video, we'll set up
the animations that we need. We'll look at a concept called Human Art retargeting Unity, which will allow us to reuse the same animation
on multiple models. First, let's download the
animations that we need. I'll be downloading all the
animations from Maximo, the same place from which we
downloaded our character. Here you also have animations. If you click on this
animation stab, you can browse all the
animations available in Miximo. All right? By the way, before you start selecting and downloading
the animations, make sure to select
the character that you downloaded in
the previous video. In my case, the
character is Erica. All right, so I'll select
the character and click on use this
character, All right. So make sure do this before you start downloading
the animations. This is important because when you download the animations, Ixia will automatically make it compatible with the
character that you selected. All right, now let's look at
the animations that we need. First, we need an
idle animation. Here, I'm going to
search for idle. You have lots of variations. I'll just go with a simple
title like this one. All right. By the way, you have few settings over here, you can play with it
if you want to modify the animations For
this animation, the hand of the character is
really close to the body. If you want to have more space between the hand and the body, then we can increase
the character space. Let me increase it to
something like 68. All right, that looks better. I'll go ahead and download this animation while do you have a few settings
over here for the format. Make sure you select
Px for Unity, just like we did
for the character. Then you can download it with
the skin. Without the skin. If you select with skin, then it'll also download the
character that we selected. In our case, we have already
downloaded the character. We don't want to
download that again. I'll set it to without skin. All right, then for
the frames per second, leave it at 30 And leave the
key frame reduction at none. I'll Right to make sure
these settings are right. And then click on
the download button to download the animation
as an FPx file. I'll just name this
Idly and save it. Okay. Next we need a
walking animation. So let me search
for walking again. We have lots of variations. Let me try this one. All right, This one looks okay for
the walking animation. Make sure to check the
in place checkbox. All right, once we check that the character will
only play the animation, she won't actually move while playing the
walking animation. All right, in our game
for character movement, we don't want animation to control the motion
of our character. We want our code to control it. We wrote the code for it
in the previous video. We should check the in place checkbox to remove any
motion in the character. All right, so now I'll go
ahead and download it. I'll use the same
settings as before. Let me just name this one Walk. Finally we need an
animation for running. Let me just search for run. Okay, I'll just go
with this one again. We have to check the
in place checkbox to make sure that we don't have any motion with the animation. All right, now I'll go
ahead and download this. I'll call this one run. All right, now we
have the animations. So let's go ahead and
import them to Unity. Unity in the Assets folder, I'll create a new folder
called Animations. Then I'll import
three animations that we download it right now. Okay, now you can check if
an animation is working by selecting the
animation and then selecting the animation
tab at the bottom, you should be able to
review the animation. But right now it says, we don't have a
model and we have to drag a model to
this preview area. Let's go ahead and drag our model into the preview
area of the animation. All right, so now if you
press the Play button, you can see that the animation
is working on our model. All right, but the reason why this animation worked
with this model by default is because Maximo made this animation compatible
with our character, since that was the
character that we selected in Maximo, right? The problem is if we bring in another
model to our project, the animations might
not work on them. Let me show you by
importing another model. All right. This is another character model that
I downloaded from Maximo. Let me drag him to the inspector and I'll check if the animations are working on this model. Okay, so if I drag this model onto the
preview of the animation, you can see that when I
press the play button, the animation is not
playing on the character. These animations won't work on other characters.
That's a problem. We don't want to download
separate animations for separate models. We want to be able to
reuse the same animations. Luckily, it has an EC
solution for this. Before we look at the solution, let's look at why
the animation is not working on different
models in the first place. If you expand the game object
of our character model, you can see that it
has bones under it. All right, so it has bones for
all the parts in its body. Now if you look at the second character,
it also has bones. But the problem is the names of these bones are
different, right? Usually the models
that are created by different artists will
have different bone names. But the way animation
works is by modifying the position and
rotation of these bones. If you expand the Ok animation, you can see the animation
clip over here. And if you click on it, it'll open the animation clip. In the animation window here, you can see what this
animation is doing. This animation clip is modifying the position
and rotation of the bones of the character and that's how it's
achieving the animation. Right here, you can see that this animation uses
the bone names of this character, right? The bond names of
the pod character is different and that's why the animation is not working
on the ipod character. If you rename all the bones of the ipod character and make
it as same as this character, then the animation will also
work on the ipod character. But that solution is
really tedious, right? We have lots of bones
for a character, and renaming each and every
one of it is really hard. The good thing is
Unity provides us with a solution in which we can solve this with
just a few clicks. The solution is
humanoid re targeting. Basically what we'll do
is we'll map the bones of all the characters to a standard set of bones
that Unity can understand. All right, we'll do that for all the characters
and all the animations. Once that's done, the animations will work with all
our characters. All right, let me
show you how to do humanoid re
targeting for this. First we have to
select the character, then you have to go
to the tab here you have to change the
animation type from generic to humanoid. All right, this works
by creating an avatar. Avatar is a thing that maps the bones of our character to
the standard set of bones. In the avatar definition, we have to select
Create from this model. Then all you have
to do is hit Apply. All right, you can see
the tick mark over here. That means Unity was successfully able to
map all the bones. In case Unity was not able
to map it automatically, then you'll have to click
on the configure button. This will show you
all the mappings that were done by Unity. All right, you can
see that over here, if some bones are missing
from the mapping, then you'll have to manually
drag and drop it over here. All right, so I click on the done button
to go out of the avatar. You can also see
Avada over here. This is my character inside it. We have Avada now. All right, now we should also change all our
animations to humanoid. Let's go ahead and
do that first. Let me just select the work
animation in the rig tab. I'll change this to humanoid. Then for the Avada
definition we have to select copy from Avada in
the case of animations. Then we have to select the avatar that we
created earlier. All right, let me go ahead
and assign that avatar. Now we just have to hit Apply. All right, we have to do
this for all the animations. We have to make them humanoid. Let me select the
other two animations and also make them a
humanoid animation. All right, now the animation should work on our
Erica character. All right, then it'll also work on the bot character if we turn it into a humanoid, let's go ahead and
do that in the tab, I'll change it to humanoid since this is a model I'll
create from this model. That definition then I'll apply. All right, so it was
successfully able to create the avatar
and map the bones. Now the animations should also work on the
board character. So let's go to the animation tab and preview the animation
on the board character. Okay, so the animation
is working fine. That's really easy to set up. All you have to do is just
change your characters and animations to humanoid when you import them to your project. If we preview our animations, you'll see that we
have a slight problem. Let me just make this big so that you can get a better view. All right, here at the bottom of the player's
feet, you can see a circle. The circle is moving right. If the circle is moving, that means the animation
has some motion in it. But in our case, we don't want to use the motion
of the animation. We want the character
movement to be controlled from the
code completely. We have to get rid
of this motion. By the way, if we drag
our other character, the main character that
we are going to use here. Also you can see the
circle is moving. This motion is
called root motion. It's really useful
in some cases. For example, for an
attack animation where the character is jumping
and swinging at the sort. We could move the character
from the animation itself instead of moving
it from the code. But in the case of idle
walk and run animations, we don't want to
use root motion. We want the motion to be
controlled from the code itself. All right, the problem is when we turn our character
into a humanoid character, it'll automatically set
up the root motion. That's what's causing this
motion in the animation. All right, we can easily
fix this by using these three options here for the root transform rotation,
Root transform position. By root transform position exit, we should change all
of them to original. All right, all of them
should be original. And then we should also select Bacon to pose for all of them. All right, now you can see that the circle is
not moving anymore. It's stationary. These
options are really useful. We'll explore these
options more in the future when we implement
attack animations. But for now, just keep in mind that if we don't want
to use root motion, then check bacon to pose
for all three of these. And then also change there
based upon two original. All right, I'll go
ahead and apply. Now the circle is stationary now let's go ahead and do this for the other
animations also. Unfortunately, we
can't do this for multiple animations at once
by multi selecting them. We'll have to do it
one by one first. Let me do it for
the run animation. All right, so I'll
check bake, interpose, and change based upon two original for all three
road transforms. All right, then I'll hit Apply. By the way, we also want
these animations to. Let me also select loop, time and loop to loop these
animations while we're here. All right, now let me hit Apply. Now let me do the same
for the idle animation. All right, let me also check loop time and lopo so that
the animation will loop. All right, finally let me also turn the loop time and loopose for the walk animation. Now we're done with the
set up of our animations. Next, we can use these to animate our character
when moving.
7. Adding Animations to Character: In this video, we'll
look at how to add animations to our character. Your character movement will
look like this once you add animations. Let's
look at how to do this. We'll play the animations
on our character. We have to create an
animator controller. Let's go ahead and create that. On the Assets
folder, I'll create a new folder called Gain. All right, this is
where I like to put all my prefabs and scriptable objects and
animated controllers. Basically all the things
that a designer would want to use inside this folder. I'll create another
folder called animator. I'll create my
animated controller. Over here, you can see
animated controller. I'll name this
character Controller. Okay, so now we can double click on it to open the
animated controller. Animated controller
controls what animation should be played on
our game object. We can drag all our
animations in here and then define what animation should be played based on the parameters. Let me drag one of our
animations in here. I'll just drag the
run animation. Let me rename this to run. The first animation that
you drag into the animator. Controller will be the default animation
that will be played. If I also drag the
other animations, you can see that their
color is different. The run animation is in
orange color, right? That's because this is
the default animation. Now if we attach our animator controller
to our character, then our character should the
run animation by default. To attach an animator
controller to the game object, we have a component
called animator. All right, in this component we can specify which animated controller
that we want to use. I'll assign the animated
controller that we created. Then we also need to assign
the avatar of the character. The avatar of this character is called Erica Archer, Avada. Let me assign that. Now
when we run the game, our players should play
the running animation. All right, you can see
that it's working fine. Now let's also try attaching
our animator controller to the second character
just to make sure that the animation
is also working properly. On the second character,
on the Y board character, it already has an animator
component attached to it. Unity Dust is
automatically when you turn a character into
a humanoid character, also over here you can see that the avatar has
automatically been assigned. Okay, we just have to assign the animator controller
the Y board character should also play the
running animation. All right, it's also
playing the animation. Our animations are working
fine on all the characters. Next, we don't want our character to simply
play the running animation. We want to play
different animations based on the speed in which
the character is moving. If the character is not moving, then we want to play
the idle animation. And then if the character
is moving slowly, then we want to play
the walk animation. And then if the character
is moving fast, then we can play
the run animation. All right, the
animated controller, there are different ways to control what animation
should be played. But in the case of
movement animations, I want the animations
to blend smoothly. For example, instead of just switching from the walk
animation to the run animation, I want to blend between
them based on the speed. To achieve that, I'm going
to use a blend tree. Let me go ahead and delete
these three animations. All right, now I'll create a blend tree to blend
between our animations. All right, I'll name this
blend tree Locomotion. And then we can double click on the blend tree to open it. In the blend tree we can
add multiple animations. And the blend tree
will blend between these animations based on
the value of this parameter. All right, in our case, we want to add three
animations here. I'll add three motion fields. Okay, so the first one I'll
assign the idle animation. And by the way, we
can't directly dragon drop the Px file of
the animation here, we can only assign the
animation clip to it. All right, so we have to
assign this animation clip. The first field we have to
assign the idle animation. Then to the second field we have to assign
the Ok animation. All right, finally
to the third field, we'll assign the
front animation. Okay. Now in the preview window, if you play the animation, then the character will
be in the idle animation. But now if you change the blend, then it'll slowly
start walking at 0.5 It'll play the
walking animation in the actual speed of
that animation, right? That's because here
we have thresholds. The threshold of idle is zero, walking is 0.5 and
running is one. All right, when it's at zero, it'll be in the idle animation. When it's at 0.5 it'll be
playing the walking animation. When it's one, it'll be
playing the running animation. All right, but the
cool thing here is when the blend is
between 0.5 and one, it'll play fast walking
animation like this. This is a blend between our walking and
running animation. This is why blend tree
is really useful. Also, if I try setting
the blend somewhere 0-0 0.5 then it'll also blend between the idol
and the walk animation. All right, you can
see the character is doing a really slow walk, which is not really
useful for us. But I just want to show
you how the animation is. Blend the blending
between the walk and the run animation that is
really useful By the way, you can also change
the threshold of different animations. Right now it's not editable because the automate threshold
checkbox is turned on. If you turn it off, then you can edit the
threshold values. I'll just set the
threshold value of the walking animation
to something lower like 0.2 The reason is because I want my character to start playing the walking animation as
soon as it starts moving. This is just a
personal preference. You can play with this value
and set it to one that you like based on how you want
your third person control. All right, now we
just have to set this blend parameter from the core and the character should automatically play
the correct animation. All right, but first, let me actually rename
this blend parameter to something meaningful. I'll call this amount
that makes sense. If the amount is zero, then the character should be
playing the idle animation. Then as we increase it, the character should start
walking and running. Right now, let's go ahead and set this parameter based
on the character's movement. Let me open the player
controller script. Okay, so to set a
parameter to the animator, first you need to get
a reference to it. So I'll cash a reference to the animator from
the away function. I'll just use the cut
component function to get the reference
of the animator. Now we should set the amount
parameter in the animator. We're already
calculating a value called amount over here, so we can set the same
value to the animator. To set a value to the
animator, pick animator set. When you type set, you can see that we have
lots of options. In this case, the
more amount parameter is actually a float
value, right? So here we have different
types of parameter. More amount is actually float. You can see that its
value is floating point. To set the more
amount from code, we can use the set
float function. In the first
parameter, we should provide the name of the
parameter that we want to set. In this case, the name of the
parameter is more amount. Then in the second parameter, we should pass the value
that we want to set. In our case, we are already calculating
the value over here. Let me just pass that as
the second parameter. Okay, by the way, one thing I want to address is the more amount parameter
should be 0-1 right? But here, the value that
we are calculating, it can be greater than one. For example, if both horizontal and
vertical input is one, then its value will
be two, right? So we have to make sure
to clamp this value 0-1 to make sure that the animations are
selected correctly. All right, clamp a value 0-1 We have this function
called clamp 01. All right, we just start to put the value in this
function like this. Okay, now our character should play the animation correctly, based
on our movement. Let's score Unity and test that. By the way, let me get rid
of the Wipeout character. I just brought it in
just to show you that the animations will work
on multiple models. All right, so let's
go ahead and test. Now when we move, you can see that our character is playing
the animation correctly. All right. Our third
person control is looking so much better when
we add at the animations. But if you look at how
the animation stops, it's stopping
really fast, right? It doesn't look very realistic. The reason for that is because when we start
pressing the input, the more amount becomes zero. Immediately, when the
more amount is zero, the character will start playing the idle animation immediately. We can easily make
this smoother by adding a damping when
setting the more amount. If you look at the third
parameter of this function, you can see that we have a
parameter called dam time. If we add this parameter, then the animator
will smoothly change the parameter to this value instead of setting it instantly. All right, for the dam time, I'll pass a small value like 0.2 Then when we
pass the dam time, then we should also pass the delta time in the
fourth parameter. So I'll pass time delta time
in the fourth parameter. Okay, so this should smooth
out the animations for us. So scored humity and test. All right, so now you can see that the animation is
not stopping instantly, it's slowly coming to a stop. Right? So yeah, now we have a pretty decent
third person controller. A small problem that
we have here is the camera is going
below the ground. So let's fix that by reducing the minimum vertical
angle of the camera. Okay, let me try setting
it to minus ten. All right, so now the camera
won't go beyond this point. Okay? Yeah, our third person control
is looking pretty good. There is one thing
I missed to do while setting up the animations. So if you look at
the ideal animation, you can see that it
is 8 seconds long and has 250 frames.
That's a bit long. And we don't want the idle
animation to be that long. If you look at other
animations like run and walk, you can see that they
are much shorter. Having such a long animation
can cause problems for us in the future when we
transition to other animations. We can make this shorter
by reducing the end frame. We can just drag the slider
to reduce the end frame. I'll just make the end frame 30. All right, now the animation
is only 1 second long. Let's apply the change,
Let's just test to make sure that everything
is working fine. All right. So things are working
just like before. I'll stop the video here. In the next video, we'll
look at how to set up collision and gravity
for our character.
8. Collisions and Gravity: Right now, we don't have
any collisions in our game. So let me add a cube
to our scene so that we can test collisions. Okay, let me just make
this cube a little bigger. All right, now if we try
walking through this cube, you can see that we don't
have any collisions. We need to handle
that. To handle collisions, we have two options. We can either use a rigid body or we can use the
Character Controller. For this project, I'm going to use a
Character Controller. Let's look at the documentation of the character controller to understand what it
does In the docs, you can see that the
character controller allows moment constrained
by collisions. It is not affected by
any external forces. This is an important point. If we are using a rigid body, it'll be affected
by external forces. We don't want that for
the player in our game. That can cause problems. In the future, we're going to go with a Character Controller
instead of rigid body. All right, let's
go back to Unity and let's add a
Character Controller to our player game object. Let me search for
Character Controller. All right, now in
the scene view, you can see that there
is a capsule collider. The character controller
comes with a collider. We can adjust the size of the collider by
changing these values. I'll change the height
to something like 1.7 since that's the
height of our character. Next we need to add an offset in the y so that the collider fits the
height of the character. Usually, I like to set the y a little above the
half of the height. The half the height is 0.85 I'll increase it a little
and make it 0.9 All right, this will make sure to keep
the player's feet grounded. Next, we don't want the radius of the
collider to be this big, so I'll set it to
something small like 0.2 Okay, One more
thing I like to do. I like to add a little z
offset somewhere around 0.08 The reason for this is because our player will always face the direction
in which it is moving. Collisions will mostly
happen in the front side of the player I like to of the collider
towards the front side. All right, we have set up the
character controller next. When moving the player from
the player controller script, we should not move it directly by updating the
player's position. Instead, we should use
a move function of the character controller
that will make sure to constrain the
movement by collisions. We should use
character controller move function instead of this. First, let's get a reference to the character
controller. All right? I'll catch a reference
to it from the function. Okay, so now to
move the character, I'll use character
controller to move function. And for the motion vector, we can pass the same
value as before. Let me just cut that
and paste it over here. All right, I can get
rid of this line. This will move the
character just like before, but it'll also constrain
the movement by collisions. So now let's coinity and test if the collisions are working Okay. Now you can see that we
can run to the cube. All right, so the collisions
are working fine. Next, we should handle gravity and make the player fall when the player
is in the air. Right now if we
place the player in the air and if we run the game, you can see that the player is not affected by the gravity. She can basically
walk in the air. All right, the
character controller is not affected by any
forces like the gravity. We have to manually apply the gravity to our player from the player
controller script. To apply the gravity and
make the player fall. First we should check
if the player is in the ground or is
in the air, right? We should really make
the player fall. If the player is in the air,
the character controller has a field to check if the player is standing
on the ground or not. The name of the
field is grounded. This T if the character is
standing on the ground. But the thing is I find this field of the
character controller to be really buggy. Sometimes it can
return wrong values. I don't like using this.
I like to manually check if the player is in the ground
or not biasing physics. If you look at the
implementation of the third person
controller in units, data assets, you'll see
that even in this project, they are not using the grounded property of the
character controller. Instead, they are checking it
manually by using physics. Let's also check that
man bioing physics. Let me get rid of this line. All right, below the
up plate function, I'll create a new function
called ground check. From this function, I'll
use the physics check sphere function to create a small sphere at
the player's feet. And check if there
are any colliders overlapping in that sphere. Okay, here you can see the
description of this function. It will return if there
are any colliders overlapping in the
sphere that we defined. Okay, to create this speer, we have to pass a few things. We have to pass the position, the radius, and a layer mask. The layer mask is optional, but it's really important to use a layer mask when you're
doing any physics operations. It'll make the operation
a lot more efficient. If we provide a layer mask, then this function
will only check for colliders in that layer. Okay, I'll define
serialized wheel variables for these three varus so that we can control them
from the inspector. All right, so first let me define a variable for
the radius of the speer. I'll call this
crowd check radius. Okay? I'll set it to something
like 0.2 by a default. Next for the position, we don't need a separate
variable for the position. We'll be using the position
of the player itself. But it would be nice to add
an offset to the position. I'll create a vector for
the position offset. All right, let me call
this crown check offset. Next we also need to define available for the layer
mask that we need to check. I'll create a layer mask over here and I'll just
call it ground layer. Okay, now in the check sphere
function for the position, we can pass transform
dot position. But we also need to
add the offset, right? What we can do is
we can transform the offset point based on the
player's current position. We have a function for that
called transform point. What this will do
is this transform the position from the local
space to the world space. And it'll transform it relative to the transform that
we're using here. Okay? In this case
it'll transform it. Related to the
player I transform, transform point for the point. I'll pass the ground,
check off it. Okay, we have passed
the position next. Let's pass the radius. All right, and finally
we'll also pass the ground layer. Okay? So this will check if the player is standing
on the ground or not. It's actually create a variable to store the value
of this function. Here, I'll create a balling
variable called is grounded. Okay, And I'll store the value returned by this function
into the grounded variable. All right, so now
all you have to do is call the ground check
function from here. And after that we'll
know if the player is grounded or not from
the grounded variable. Okay, So let's actually use a debug dot log so that we can test if the player
is grounded or not. Okay, so next I'd like to visually see the
sphere in the scene. This will, allows to set
the offset and radius properly as if there are any issues with
the ground Check. It allows to understand
what's happening. We can achieve that
by drawing a gizmo. Gizmos are the shape that
you see in the scene. For example, if you
select the player, you can see the collider,
that's actually gizmo. We can draw a gizmo like that for the ground
check sphere. To draw a gizmo, we can do
that from two function. The first one is rogsmos, second one is ragsmos selected. Rag ism selected will
only draw the gizmos if this game object is currently selected. Let's use that. We can draw gizmo by calling
gizmos draw spear function. All right, for the center, we can pass the players position offset by the ground
check offset. For the radius, we can pass
the ground check radius. Okay, This will draw the Chismo. We can also specify the color of the gchismo by setting the color before
the draw function. I'll set kismotat color. I want the color to be
something like a green. In the RGB value of the color, I'll pass zero for red, one for green, and
zero for blue. Okay, and I also pass 0.5 for the alpha because I want the spear to be a
little bit transparent. All right, so now it should draw the ground check spear when we select the player
in the scene. Okay, you can see that it's drawing the
ground check sphere. Let's ad just these values. 0.2 is okay for the
ground check radius. For the ground check offset, I like to put the y value
at half of the radius. I'll make it 0.1 All right. I also like to give
a little bit of offset in the Z so that the sphere covers the feet
around 0.07 should be fine. All right, so now the sphere covers the feet of the player. Next need to assign
the ground layer. What I'll do is I'll add a
new layer called obstacles. All the obstacles like the
ground and the cube over here, all those will be in
the obstacle layer. All right, so let's assign the plane to
the obstacle layer. I'll also assign the cube. Okay, now for the ground layer, we can just pass the
obstacles layer. All right, that's
all we have to do. So let's go ahead and check if the ground check is working. All right, right now since
the player is in the air, you can see that the
scrounded value is false. But if I go to the scene view and bring the player
down to the ground, you can see that
it becomes true. So our ground check
is working properly. Now we know if the player
is grounded or not. Next, if the player
is not grounded, we should apply the
gravity and make the player fall right. What we have to do is to the move function of the
character controller. We should also pass the vertical
movement of the player. All right, if the
player is grounded, there won't be any
vertical movement. And otherwise we should apply a vertical movement to make
the player fall down. Okay. The first thing I'll do is I'll move this line out
as the condition. The reason is because the vertical movement should happen. Even if we are not pressing any input for the moment, right? Even if you're not
pressing any keys, the player should fall down. I'll move outside the condition. Then what I'll do is I'll put the movement vector in another
variable called velocity. Let me define velocity
over here. All right? And then we can use the
velocity variable over here. Now we can also set the y component of the velocity
if the player is falling. All right, if the
player is not grounded, then we can apply
a velocity over here to make the
player fall down. By the way, guys, if you
don't know what velocity is, velocity is just the
speed of something at a certain
direction. All right? If you say a car is moving at 40 kilometers/hour
that's a speed. But if you say it's moving at 40 kilometers/hour in
this specific direction, then that is the velocity. It's just a speed
with the direction. All right, we have to set the y component of the velocity
to make the player fall. If you know how gravity works, you'll know that when
something falls, it'll not fall at a
constant speed, right? Instead, its speed will keep increasing
throughout the fall. To be more accurate,
every second the speed of the object will
increase by the gravity. We can't set a constant value
here for the y velocity, we need to keep increasing it. What I'll do is I'll create a new variable to keep
track of the y speed. So let me create a float
variable called y speed. All right, then if the
player is falling, then we should keep
increasing the speed by the gravity, right? If the player is grounded, we
don't want to do anything. Now, we want to increase it if the player
is falling, right? We want to increase it from the L's part of this
condition, okay? If the player is falling, then we should add
gravity to the Y speed. So we can get the gravity in our game by using the
physics gravity property. We only want a Y
component of the gravity. I'll just get the Y. This value is actually set from
the project settings. If you could edit and select the project
settings in physics, you can see that
we have gravity. This is where the gravity
of the game world is set. All right, here, if
the player is falling, we want to increase by
speed, by the gravity. But we should also multiply
this time, delta time. This is because the Y speed should increase by the amount of gravity every 1 second, right? But the update function is
not called every 1 second, it's called multiple
times in a second, right? We have to make sure to multiply the gravity by the delta time, which is nothing
but the value that has passed since the last frame. Now we are increasing
the y speed. If the player is not grounded, let's set the y speed
to the y component of our velocity, all right? And then if the player reaches the ground
and is grounded, value becomes true,
then we should reset the y speed back to zero, right? But what I'll do is instead
of resetting it back to zero, I'll reset it to a
small value like -0.5 The small value will make sure to stick the character controller
to the ground. All right, this should make the player fall down if the
player is not in the ground. Let me just scratch
rid of the debut lock. Let's go ahead and test
if the player is falling. All right, you can see
that the player fell down. The gravity was
applied correctly. Let me also try placing
the player over this box just to test how
it falls from such objects. All right, first we'll
fall to the box. We can move from here, but if we move past the
ledge of the box, then the player will
fall towards the ground. Okay, so if that's
not working for you, there are a few things
that you have to check. So the first thing to check is the value of the
ground check radius and the radius of the character controller
should be almost same. If you make the
character controller radius a higher value, then you'll have a bug where the ground detection
will not detect any ground. But since the character
controller is bigger, it won't allow the
player to fall down. All right, so that's one
thing to keep in mind. The radius and the set
offset of the ground. Shakespeare and the
character controller should be similar. They don't have
to be exact same, but they should be
in the same range. All right, next I just want to organize the player controller inspector a little. I want some segregation between the speed variables and the
ground check variables. We can easily do that by adding a header above the
ground check variables. Let me add the header attribute. I'll just call this
ground check settings. Okay, now there should be some segregation between the variables in the inspector. Okay, so yeah, we have handled collisions and
gravity in this video. All right, but right
now the player can still move and play the back animation
when she's falling. We'll be fixing
those in the future, but right now, this
is a good start. Next, let's set the skin with property of the
character controller. This is a really
important property if you go to the documentation of the Character Controller, and if you switch to the manual, the manual will have more
detailed explanation when compared to
the scripting API. Here we have a
detailed explanation of all the properties. If you look at the
skin, skin width allows other colliders to penetrate the character
controller a little. This is used to reduce jitter and it also prevents the character
from getting stuck. Recommends setting this to
ten percentage of our radius. In our case, the radius is
0.2 The ten percentage of 0.2 will be 0.02 All right, I'll set the skin
with 2.02 Next, we should change the y of the center based on the
value of the skin width. Earlier I told you
that y should be a little above half of the height for the character's
feet to be on the ground. If you want to be more accurate, we can set the center to half of the height
plus the skin width. All right, half of the height is 0.85 the skin width is
0.02 If we add that, we'll get 0.087 This will place the
feet of the player on the ground perfectly, so let's go ahead and test to make sure that
it's working fine. All right, so you can see that the feet is planted
on the ground perfectly. Okay. I'll stop the video here. In the next video, we'll add controller input so that we can control our player
with the joystick.
9. Controller Input: In this video, we'll set up controller input so that we can control the
player with joystick. If you connect the joystick
and desk the game, you can see that we
are able to move the player using the
left and locks stick. But we are not able to control the camera rotation using
the right and locks stick. All right, if you look at the
player controller script, here we are Using the horizontal and vertical
input to move the player. It automatically sets up the horizontal and vertical
input for the joystick. That's the reason why we are able to move the player
with the joystick. You can actually
see the input set up in the Project settings. All right, so if you
go to Project Settings and select Input Manager, here you can see all the inputs that Unity defines by default. Here we have horizontal
and vertical. Over here we have another
horizontal and vertical input. Right, They are
defined two times. The first one is defined
for the keyboard. Here it uses the
left and right keys. The second one is defined
for the joystick. All right, here it uses the
x axis of the joystick. Like this, we should set up all our inputs for the joystick. We want to be able to
control the camera using the right and
locks stick, right? If you look at the camera
controller script right now, we are just using the
mousex and mousey input for controlling the camera. Instead of this, we should
set up a new input over here. And we should set it up for
both mouse and joystick. Here, I'll create two new
inputs called camera X and camera Y. I can just duplicate this mouse input to
create a new input. We can try, click and
select duplicate element. All right, I'll name
this as camera X. We want this to be
controlled by the mouse. We'll create another one
for the joystick here, the type should be
mouse movement. I'll change the axis to X axis, because this is camera X. All right, now we
can just duplicate the camera X and we
can create camera Y. Here, the axis should be Y. All right, next, let's create camera X and camera
Y for the joystick, as well as just duplicate
this camera Y element. Let me rename it to camera X. The type of this we
should be joystick axis. For the axis, we'll
select the fourth axis. The fourth axis is
the horizontal axis of the right and locks stick. All right, now in
case of joystick, we also need to tweak a
few settings over here. Let's look at how the
left and lock stick is set up for the
horizontal input. Here you can see that
the sensitivity of the input is one for
the value of the dead. They're using 0.19 We can also do the same for
the right lock stick for our camera X input. So I'll change the
sensitivity to 10.1. Will be a perfect
value for mouse, but it'll be really low
for an lock sticks. Let's change this to
one. We'll also change the value of dead to 0.19
this will be the dead zone. If the value is below it, then it'll be mapped to zero. All right, we have set up the camera input
for the joystick. Next, let's duplicate this and
create the camera Y input. All right, the Y
axis of the right, an lock stick is the fifth axis. Let's change the
axis to fifth axis. All right, that's
all we have to do. Now we have a new
input which can be controlled by both the
mouse and the joystick. Let's use this input for
rotating our camera in the camera control instead of using mousex and mousey fee, camera X and camera Y. All right. Now we should also be able to rotate the camera using the right analog stick. So let's go ahead and test that. All right. I'm also able to rotate the camera by using
the right analog stick. Okay, now we can also control our third person
controller using a joystick. I'll stop the video here and
I'll see you the next video.
10. Performing Attack: He run. In this video, we'll start building
our combat system and we'll make the player
perform the attack animation. When we press input, first we need to find the
animation for the attack. So let me go to
Mixmo.com and with the Erica Archer character
directed icon animations. And I'll search for, okay, we want a slash animation
for our attack over here. We have lots of
slash animations. I'll just use this one. Okay, so this is
one I want to use. Let's go ahead and download it. Okay, make such a fix for
Unity for the format. We can download it without the skin because you
already have it. Okay, go ahead and
download that. Once it's downloaded, we
can import it into Unity. Here in the animations folder, I'll segregate the animations into different folders to
keep things organized. Firstreate folder
called locomotion. These three animations
will go inside Locomotion. Okay? And then I'll
create another folder called combat and begin bringing our new animation
into this folder. Let me drag and
drop it over here. Okay, we have to rig this animation to
be able to view it'll. Go to the rig tab and change
animation type to humanoid. I'll copy from Maamionucela because that's what we used in mix for previewing
the animation. Okay, let me go ahead and hit a P. Now we should be able to preview animation if we just drag and drop our player
into the preview window. Okay, the animation
is a bit weird now, so we can fix that by enabling the root motion of the
player's animator. Okay, so now it
looks much better. Next, if you look at the
animation settings here, we can change the based
upon to original. For all the root transforms. This will take the root motion from the original animation. Okay, We can remove any unwanted root
motion in the y axis. We can also remove
any unwanted rotation by selecting ba interpose. Okay? If you want weak interpose for the
exit root transform. I guess that won't be a
problem because I don't mind if the player moves a
bit forward, general attack. With this animation, the
player is not moving forward because after
performing the attack, she's coming back to
the original position. So that's why we have green
over here for the loop match. In this case, it really
doesn't matter if we turn on the baking to post
or not for the exit axis. But for most attack animation, we want this to be turned off. Because we want the player to move in the exit plane while
performing the attack. Okay, so let me go
ahead and apply this. This is how our
animation will look. In order to play the
attack animation, we have to first add this
into our animated controller. Let me open my animated
controller here. I want to add my attack
animations in a new layer. From here, we can click
on this plus button. We can add a new layer
called over right layer. Yeah, I like to keep all
my attacks animation in the over right layer. Basically, pup on this layer to any animation that is currently being played
in the base layer. Okay, so to do that
we have to click on the Settings button and
increase the weight to one. Now since the override
layer is below the base layer and since
its weight is one, it'll completely
override any animation that's being played
in the base layer. Okay, now in here first cell, create a empty state. Let me just call this empty. In this state, no
animation will be played. And we want to overwrite what's being played
in the base layer. Okay? Most of the time the overright layer will
be in the empty state. But when we want to
perform an attack, we'll play an animation
from this layer. Okay, let me go ahead and copy our animation into
the overright layer. I'll just to, to
keep things short. Okay, And once this
animation is complete, we want to make a transition
back to the empty state. All right, so this is how I
want to set up my animator. By default we'll be playing
the locomotion animation, but when we perform an attack, we'll start playing the
slash animation and we'll ate the locomotion animation that's being played
in the base layer. Okay, we're done with the
set up of our animator. Next let's go ahead and write the script for
playing attack animation. When the user presses an input, the scripts folder here, let me segregate
the scripts into different folders so
that it's organized. It's a first Ilgtd
folder called these. Two scripts are going to
be inside the player. Okay, recompile the code. Next I'll create another
folder called Combat system. All right, all the scripts for the combat will be
inside this folder. We'll start by creating the script for
performing the attack. For that, I'll
create two scripts. First create script
called Meal Fighter. Then I'll create a script
called Combat Controller. Okay, let me explain why
I added two scripts here. The reason for
that is because we want to build a combat
system in a modular way. Because we want to reuse as
much code as possible between the player and the enemy
characters, right? Since both the
player and the enemy has common actions like
performing attacks, combos and all that,
we want to reuse the code for doing that for
the player and the enemy. Okay, So to achieve
that what I'll do is I'll write all the
common logic for the combat system inside
the medi fighter script. The code for performing
the attack and enabling the sword collider,
performing combos. All those will be inside
the Medi Fighter. The Medi Fighter
script will be used for both the player
and the enemy. Okay, this will have a code
for performing the attack, but it won't have the code to determine when the attack
should be performed. Okay, the code for that will be different for the
player and the enemy. In the case of the player,
the attack should be done when the user
press an input, right? But in the case of the enemy, it's not controlled by
the user's input, right? It's controlled by
the AI of the enemy. Splitting our code into different components like
this will allow us to reuse our code instead of fighting the same code
for different objects. I'll right. That's one
of the main principles of the clean coding practices. It's, do not repeat yourself, which means we
should not have any duplicate code in our project. We should try to reuse as much
as code as we can because it'll allow us to easily make changes to our
code in the future. Okay, let's start by implementing the
merely fight descript. Here I'll create a public
function called try to attack. All right, so this function will try to perform an attack if the character is not
already performing an attack or if the character is not
performing any other actions. Okay, so first singular variable to keep track of if the player is
in an action or not. So here I'll create a bulling
variable called inaction. And I'll say it
defaults by default, okay, from the try to attack function if the player is not already
performing an action. Then we can go ahead
and perform the attack. Right? To perform the attack, we just have to play this
animation from our animator. First, we need to get a
reference to the animator from the Lei Fighter crap reference
from the apague function. All right, now we have
to play this animation. And we have to play
it by using its name. Right? I want to play this
animation by using its name. When we build
attacks and combos, the user just has
to provide the name and we'll use that name
to play the animation. Okay, play an
animation by its name. We can use two functions. We can either use
the play function or we can use the
cross fate function. In this case, I'm going to use the Crossphd
function because it allows to slowly
transition to the animation we want to play instead of
playing it immediately. Okay, So in this function, first we have to pass the name of the animation
we want to play. That is slash, right. Next we have to give the
transition duration. So I'll just give a small
value like 0.2 All right, 0.2 doesn't mean 0.2 seconds, because in the crossphate
function we are actually passing the normalized
transition duration, okay? Not the transition
duration in seconds here. When we pass 0.2 it means that we'll take 20 percentage of the current animations
time to transition to the animation. Okay. We also have another
function called crossphate. In fixed time, this function takes the seconds
for transitioning. In this case, when we pass 0.2 it'll take 0.2 seconds itself. But I like to use cross weight instead of cross
weight and fixed time. Because if our current
animation is a bit long, then it'll take a little more
time for the transition. That works really
nicely for most of the animations, all right? But when you use
cross fate function, there's one thing
you have to note if your current animation that's
being played is too long. So for example, if your idle
animation is really long, if it's like 10 seconds, then the transition
will look really weird because it'll take a
lot of time to transition. Okay, 20 percentage of 10
seconds is 2 seconds, right? 2 seconds will be
really long for a transition to the
attack animation, and it'll look really feared. Just keep that in
mind. If you're using the cross state function, then your idle animation
has to be a short one. In our case, our idle
animation is just 1 second. That won't be a problem for us. Okay, calling this function, we'll play the attack animation. When we play the animation, we also have to sit in
action to true to make sure that we can't perform another attack while this
attack is being played. Once we complete playing
the attack animation, we have to set the inaction
back to false, okay? But how can we know when the attack animation
will be complete? In the unities animator, there is no way to know when an animation will be complete. Here after this line, if we just set the
inaction back to false, it won't wait until the
animation is complete. It'll turn this immediately. Okay, we can't write like this. Instead what we'll have
to do is we'll have to manually wait for the
duration of the animation. Okay, if you look at
the slash animation, this animation is
1.5 seconds long. What we can do is, after
we play this animation, we can wait for 1.5 seconds. Then we can set the inaction
back to false, okay? Whenever we want our code to
wait for a period of time, we'll have to put our code in a special type of
function called cotine. Corotine is a special
type of function with Nuberator as
its return type. All right, here I'll create a corotine
function called attack. We'll run these two lines
from inside our cotine. Okay, so if you have no
idea what corotineus, you can go ahead and
read documentation. But basically corotin is
something that you use when we want to make our code
wait for some time. Or when you want to execute our code over a period of time. Okay, So you can go ahead
and read more if you want. In our case, after
playing the animation, we want to wait for the length
of the animation before setting the inaction
back to Ford's right. So to make the code
wait for a few seconds, we have a function
called wait 4 seconds. Okay, So this is actually a
class, not just a function. That's the reason why we
have the new cured at start. All right, in the
parameter of this, we can pass the seconds
for which we want to wait. Let's say if you want
to wait for 1 second, we can pass one over here. But in our case, our animation is
1.5 seconds here. We can just pass 1.5 seconds. All right, obviously
we don't want to hard code this value because slash is not the only
attack that we'll have. We'll have multiple
attack animations, right? We have to programmatically
find the length of the animation. We'll
be doing that soon. But first, let me show
you how to make a code. This is a function for making a code to actually
make this function. Before going to the next line, we have to use the yield
Return keyboard in front of the function, okay? So now we'll wait for 1.5 seconds from here before
going to the next line. All right, so after waiting for the length
of the animation, we can just set the
inaction variable back to faults, okay? Now, the inaction will
only be set to false once we complete playing
the slash animation, okay? But we don't want to hard
code this value over here. We want to find the length of the slash animation
programmatically, right? For that what we can
do is we can get the state of the animation that is currently being played. From that we can get the
length of the animation. Okay, So if you type animated, do get state here we
have two functions. Get current animated state info and get next
animator state info. Next animated state
info function is the one we want to use. Okay? The reason for that is because when we call the
cross fate function, we know that it won't play the slash animation
immediately, right? Instead, it'll
slowly transition to the slash animation from
our current animation. When this line is executed, our animator will be
in a transition state. In the transition state, the current animation will
be our locomotion animation. The next animation will be
the attack animation, right? That's the reason
why we are using the get next animator
statement for. Okay, and here we have to
pass the index of our layer. If you look at our animator, the attack animation is
in the second layer. And since you want
to pass the index, the index will be one, okay? The index, first one will be zero and the second
one will be one. Just like an array, I'll
pass one for the index. I'll just show this inevitable, called an state, okay? And from the anim state we can get the length
of the animation. Now we are programmatically finding the length
of the animation and we'll wait for
it to complete before setting the
inaction to faults. Okay? We also have small
issue with the screen. We call the cross
fate function Un won't start the transition to the new animation immediately. It'll only started after
the current frame. Okay. We have to wait for one frame before we call the
get animated state function. Otherwise we'll get
the incorrect state because the transition
has not yet started. Okay, so we have to wait for one frame after running
the cross weight function. Fortunately, there's an easy
way to wait for one frame. We just have to
write shield return. No. Okay, What this will do is this will make our corotine wait
for a single frame. All right, this is all you have to do to perform
the attack animation. Now from here we can
call the attack corotine And perform the attack animation if the player is not
already in an action. Since the attack is corotine, we can't just call it like
we call any other function. Instead we have to put it
inside a special function called start cotine. Okay? So this is how you call a
corotin from a normal function. All right, So this
is all you have to do from our merely
fight descript for performing the attack. Next we should actually call the strike to
attack function when the user presses
an input, right? That code can't be written
inside the male fighter script because that code is specific
to the player, right? In the case of
enemy, the condition for performing the
attack is different. What we'll do is we'll write that code in the
combat control script. Okay, from here first you need to catch the
reference to the male fighter. I'll do that from
the awake function. Okay, and then from the update, if the attack input is pressed. So I'll call the
kit buttoned down function and I'll check
for the attack input. The attack input is not
defined by default, We'll have to
define it manually. I'll open up the input manager
from the project settings. Here we have lots of inputs, but we don't have one
that's named attack. But if you look at
the Fire one input, it uses the left mouse button and the left control button. We can use this one
for the attack, we can just rename it
to attack and use that. All right, so back in our code if the attack
button is pressed, then we have to
call male fighter, tried to attack function. Okay, this function will perform the attack if we are
not already in an action. This will make sure that we
won't perform the attack multiple times when we
spam the attack input. All right, yeah, this is all we have to do
to perform the attack, to score humanity and attach these two new
scripts to our player. Okay, I'll attach
both the male fighter and the combat controller. Now if you test the game, if I press the left mouse button or the left control button, you can see that the
player is attacking. Okay, so this is actually
a swath animation, But we don't have a sword yet, we'll be adding that soon. But let's implement
the attack properly. This works, but we still
have to fix a few problems. The problem is we can
move by the attacking. Okay? We shouldn't be
able to do that, right? We can fix that issue
by not allowing our player controller
script to move the player while the mele
fighter is in an action. Okay, we can just
check the value of this variable from the
player controller script. If it's true then we can prevent the code
for the movement. Okay, we could just make this variable public and access it from the
player control script. But it's not a good practice in object oriented programming
to create public variables. The reason is because
if we make it public, we'll also be able
to set its value from other classes, right? But we don't want
that the value of inaction to really be set
from the Lei Fighter. What we can do is
we can just make this a property instead
of making it a variable. To do that, we can add
a getter and a setter. All right, so this is our property with a
getter and setter. But we should only
be able to set this from the current class, right? We should not be able to
change its value from outside. What we can do is we can make our setter private, like this. All right, now our inaction
is actually a property. And by the way, according to the naming
conventions in sharp the properties has to start with a upper case letter, okay? So we have to make
an upper case. So we can just rename
this property by right licking on
it and selecting, rename by the way, you can also use the
control R, short cut, okay? And I'll make the upper case, that will automatically
change the name in all the places where we
use that property, okay? Now, since the inaction
is a property, we'll be able to
access its value from the player control script. Okay, from here
first we have to get the reference to
the Mealy Fighter. I'll go ahead and do that. All right, let me catch the
reference from the awake, then from the start of
the update function. I'll check if Mele Fighter
taught in action is true. If that's true I'll just return and I want to execute
any of the code below. Okay, this will make sure that we can't move when we
are performing an attack. Let's continuity and test now. Now, even if I hold the moment input while attacking,
the player won't move. She move once the
attack is over. Okay, another issue that
we have to fix here is if we attack while holding the moment
input, it won't move. But soon after the
attack is over, it'll go back to playing the running animation even if the moment input
is not pressed. Okay? So that is
happening because the more amount parameter of
the animator is not zero. When we were starting
the attack, right? Once the attack is over, we'll play the
running animation for a short time before setting
the more amount back to zero. We can easily fix that by
resetting the more amount to zero while inaction is t here. Let me just add braces
because we have multiple lines in
the F from here. We can just set the more amount variable to zero if
we are in an action. All right, now if it, we shouldn't have that issue. Okay, so the attack
is working properly. All right, next we should add a sword to the player's hand because they're playing
a swath animation. So we'll be doing that
in the next video.
11. Reacting to Attacks: Here in this video, we'll add a sword to
the player's hand. We'll also add an enemy
character and we'll make him play this hit reaction animation when he's hit by
the player's sword. Let's implement this first. Let's add a sword to
the player's hand. I'll go to the
Unities Asset store. I'll find a sword
asset that we can use. Let me just search for
a long sword over here. Okay, And this is a
sort you want to use. It's a free asset.
Just click on it. And since I've already added
this asset to my account, I'll have this open
in Unity button. But if you open this
for the first time, you'll have to add this
asset to your account. After that, you'll be
able to open it in Unity. Okay, Click on
Open Unity Editor. This will open that asset
in Unity's Package Manager. All right, it's taking
some time to load, okay? Not open the asset from
the package manager, but we can see the
asset over here. If you can't see
it, just search for long swat and you'll
get it, okay. Now after selecting the asset, we can just click on
the Import button to import it into our project. Okay, go ahead and click Import, and this will import
it into our project. All right, the sword will be in this folder here on the prefabs, we'll have a long swatter. Okay? But the Swat has
a weird pink color. Whenever you see
this color in Unity, it means that Unity is not
able to recognize the shader. Okay? So if you look at
the long Swat model, you can see that its material is also pink because it
uses a standard shader. Okay, and since you're using the universal
render pipeline, it won't be able to
render standard shaders. So we'll have to
convert the shader into a universal render
pipeline shader. All right, let's find this
material in our project files. For that, we can click over here and click on Select Material. Okay, there are two
materials for the Swat. We have to convert both of
these to use the UR pat. That's pretty simple. We just start to select these
two materials and click on Edit under Pipeline,
Universal under Pipeline. Click Upgrade Selected Materials to Universal under
Pipeline Materials. Okay, this will convert the material to use the
Universal under Pipeline shader. Now we can see the
materials on the sword. The sword is not pink anymore. Okay, let's add the sword
to the hand of the player. By the way, there are
two game objects here. There's a parent game object and there's one for the mesh, we only need a
single game object. We can just copy this one
and paste it over here. And then you can just
delete the prefab. Okay, now let's place this swat into the
player's hand first. Tell rotated in the x axis
by around 90 degrees. I'll just type in 90 over
here to rotate it perfectly. Okay, let's move this and place it into
the player's hand. We can actually use
the front view. Just click on the Z gizmo and then click on the center
to make it orthographic. Okay, and if I just swum in, now we can see the player and
the Swat in the front view. From here we can easily place
it into the player's hand. Okay, now let me switch
to the side view by clicking on the Xchismo. All right, let me just make sure that the Swat
is in the player's hand. Yeah, this looks fine to me, so I'll click on the center to change it to the
perspective view. Let me just make sure that
this is placed perfectly. That looks fine, but I think the size of the sort is
a bit big right now. We can actually go ahead
and reduce its scale if you click on the Sort from
here we can find its mesh. This will give us the FPx file from the model tab
of the PX file. We can reduce the
scale factor a bit. I'll change it to 0.9
to reduce it a bit. Okay, so yeah, that
should be enough. Now, if you look at the sword, you can see that the
colliders don't fit perfectly because we change the scale factor of the model. We can fix that by removing these colliders and
adding them again. Let me just minimize
the material. Let me go ahead and add
a new boxlider to it. Okay, we don't want the box
collider to be this fed. Let me go ahead and
reduce the excise. Okay, that should be enough. We only need one
collider for the sword, we don't need another one
for the handle over here. Okay, Next I'll also turn on the trigger check
box of the box collider. Because we want the
sword to be a trigger, we don't want it to
really collide with other game objects and act as a physics body
turning on trigger, we'll make sure
that this collider will only be used for triggering events and it'll be ignored
by the physics engine. Okay, so next, even though we have positioned the
sword in the player's hand, the sword won't move when the
player's hand move, right? So to make it move along
with the player's hand, we have to make the sword a
child of the player's hand. Right? If we expand our model inside the
hips, we have spine. Let's keep expanding until
we find the right hand. Here we are, right shoulder, right arm, right forearm. And finally we have
the right hand. Okay, we want to make the sword a child object
of the right hand. So let me just drag and
drop the sword onto the right hand object
to make it a child. And by the way, I'll just name this sword as long
sword instead of long, short mesh. I like that better. Okay, so now the sword should move along with
the player's right hand. So let's try testing it. Okay, the sword is moving
the player's right hand, but we have a problem, since the player's hand are
pretty close to her body. The sword is going
through her legs. Right? So let's go to the scene view to take
a better look at it. Okay, Yeah, if I woman, you can see that the hand is like freely close to the body. It's actually a bit
inside her leg. The sword is also going
to pass through the leg. Okay, so this is actually a problem with the
animation fixes. We'll have to find a
better idle animation. Or we'll have to modify
this animation somehow. Maxima has a really cool
feature that will allow us to chain the character's arm
position in an animation. So let me go ahead and show you the cat character selected. I'll search for idle animation. Okay, this was the idle
animation we were using, right? In this animation,
you can see that the hands are pretty
close to the leg. But we have a slider in Maxima over here called
character arm space. If we increase it, you can see that it's the hands
away from the body. This increases to
something like 70. All right, so that
should be nuporous. Let's go ahead and
download this animation. Okay, and once it's downloaded, we can go ahead and switch the current idle animation with the new one that
we just download it. Okay, so I'll just lead the old idle animation and I'll import the new one
that we just downloaded. Okay, so we have to rig
this animation first. So let me go ahead and do that. If we just drag of the player, we should be able to preview it. Okay, just to be safe, let's also change all the
base upon to original and check pak interpose for all the road transforms
just to be safe. And make sure that we don't have any root motion
in the animation. Okay, now we go to the animator inside of
our locomotion, Bluntre. You can see that
the first animation is missing because
we deleted it. Let's go ahead and assign our
new ideal animation here. Now things should
work like before, but with our new animation
that has wider arm space. All right, so you can see that the idle animation looks a lot better when the arms are
placed away from the body. Okay, yeah, you can see that the sword is moving
along the player's hand. If I perform the attack now
the attack animation looks a lot better with the sword
in the player's hand right. Next, let's bring enemy
characters to our game and make the enemy play a hit animation when
we swing the sword. All right, again, I'll go to Maximo to
find an enemy character. All right, under characters
I'll search for Paladin. There are also other
characters in this style. If you want to use them, just go through them and use
the one that you like. You don't have to use
the same one as me. Here we have two models. One of them comes with props like the sword and the shield. I'll just double the
one with the props. We might want to use the
sword in the future. Okay, so let me go
ahead and download it. So yeah, when
downloading a character, make sure to download
it in Depots. Okay, I have already downloaded this
character to save time, so let me go ahead and import
the character to Unity. So we'll import it to
the Models folder. Okay, so this is the character. It doesn't have
textures right now, but we can score the material stab and
click on Extract textures. And I'll actually put this in a separate folder and
I'll call this Pella then textures just so that it defined the textures in case we want to modify
them in the future. Okay, now it's prompting
me to fix the novel maps, so I'll just fix our enemy
character has textures now. All right, next we have to rigging the animation
type to humanoid. Since this is a character we have to use to
create from this model, for the other definition.
And hit apply. Okay, so the character
should be rigged. Now let's look at the character. It has an animator
attached to it. Here we can assign the same animator controller
that we use for our player, make it play the idle animation. Let's try testing it now. Okay, so yeah, I can see that this character comes with a sword and a shield. But right now it doesn't do anything when we
play our attack animation. So we have to add a collider on the enemy character
and we have to detect the collision between
the players sword and the enemy's collider. Okay. Let me go ahead and add a
capsule collider to our enemy. All right, I'll change the
height to something like 1.8 because that's
the average height of a human to places in the center. I'll set the y of the
center to the half of 1.8 which is 0.9 okay? So the radius is big right now, so let me reduce it
something like 0.3 okay? I'll actually relate
this character to something simple like enemy. And we can even unpack the prefab just so that we can create our own
prefabs in the future. Okay, now the enemy
character has a collider. We have to detect the
collision between the players sword and
the enemy's collider. When handling collisions,
we have to make sure that the players sword can
affect the enemy's collider. Similarly, on the enemy sword can affect the
player's collider. Right? It'll look really weird. If the player swings the sword, the player itself plays
animation because a part of the players sword went through the player's body during
the attack animation. Right. To make sure
that doesn't happen, what I'll do is I'll add multiple layers first and
then one for the enemy. Then I add a layer
called Player Hit Box. This will be the layer in which the player's sword
will be assigned. Then I'll create a hit box
layer for the enemy also. Okay, so the player is going
to be in the player layer. And we don't have to change all the children, just
this game object. So this object only then the layer of the
enemy is going to be enemy again. I'll say no. And then we'll put the players sword in the player
hit box layer. Okay. And the enemy sword should be in the
enemy hit box layer. But the implementation of
enemy attacks would be done later so we don't
have to assign it now. All right, so with all these objects assigned
to separate layers, what we can do is
we can tell Unity that the player hit box surely collide with
the enemy layer. Okay, if click on Edit and
go to Project Settings. Here under the Physics tab, we have a Layer
collision metrics. This letter specify what layers can collide with
what other layers. In our case, we want the player hit box layer
to collide with the enemy. So we can cancel everything
else except for the enemy. Okay? And similarly, the enemy hit box should
collide with the player layer. Okay, So let's turn
off everything else using layers like this and making sure that they can only collide with
one other layer. It's also good for making
a game more efficient. If you do this, then
you phone detect any unwanted collisions
and trigger random events. For that, it's
always good to use the collision metrics and reduce the number of collisions and make a
game more efficient. Okay. By the way, one thing I forgot to mention is that for two game
objects to collide, at least one of the game object should have
a rigid body component. Okay? Otherwise collision
won't happen in Unity. What we can do is we can add rigid body component
to our long sword. All right, I'll just make it kinematic so that it won't be affected
by external forces. And I'll also turn off gravity. Okay, so now we have everything
for detecting collisions. So let's actually write the code for
detecting collisions. Now when a trigger like the player Swat enters
the enemies collider, it'll call the on trigger enter function on all the scripts that are
attached to the enemy character. All right, so what we'll do is we'll write the
code for detecting the sword collision and playing the hit reaction and all that in the made
fighter script. Okay, both the player and enemy needs to have
this functionality. Writing it in the Meli
Fighter is the way to go. We also have to make should attach the Meli
Fighter script to the enemy because the enemy should also be able to
fight in our combat system. Right now in the
fighters script, I'll just type on trigger. Here we can see we have a function called
on trigger enter. If a presenter, it'll automatically create the
trigger enter function for us. If this function is called, then that means trigger
entered the characters collider right from here. It'll be good if we verify
if the trigger that entered the characters
collider is actually a weapon. We can do that by checking the
tag of the other collider. Check if other tag equal
to equal to hit box. Before we forget, we have to assign this tag for
all our weapons. Okay, so let me go ahead and assign it
for the long sword. So these are all the predefined tags
that we have in Unity. So we have to create a new one. I'll call this one hit box, and I'll assign that as
the tag of the long sword. Okay, so now back in our script, if the other collider
that entered the characters
collider is a hit box, then we should go ahead and
play the hit animation. But we don't have a hit
reaction animation right now. Then we should go ahead and play the hit reaction animation, But we don't have that
animation right now. Now just for testing, we can just write a
debut log statement. I'll just say something
like character was hit. Okay, so now if we won the game, if we go near the enemy and
play the attack animation, you can see that in the console, it prints the character was hit. Okay, next we have to find
a hit reaction animation. And we have to play
that animation when a sword enters the
character's glider. Okay, again, to find the
hit reaction animation, I'll go to Miximo.com
I'll make sure to select the Erica Cher
character before selecting the animation
because I want all my animations to be rigged by using the
Erica Cher character. You can also use
other characters like the Paladin that
we just downloaded. The reason why I like
using same character for everything is because I
won't have any confusions. I can rig everything
by using the same Aaa. All right, I'll
just switch back to the cache character
under the animations. I'll search for
Swat impact here, we have a few Swat
impact animations. This is the one I want to use. Okay, so go ahead
and download it. I have already done that, so let's go ahead and
import it into Unity. Okay, let me drag and
wrap it into Unity. Let me just make this zoom out so that I can see
the name clearly. This is a new animation. So first we have to
go ahead and rig it. I'll select Humanoid,
and copy from Avada. And select Avada, since that was the one we used while downloading the
animation from Maximo. Okay, next in the
animation step, I'll change everything to original and I'll select
baking to pose for the rotation and transform y just to make sure that we don't have any root
transform in those axis. I don't mind if there's
some root motion in the exit axis because that's fine for an
attack animation. Okay, so let me click the apply button and let me
see how the animation looks. So yeah, that looks fine. So next we need to add
this into our animator. I'll go to the our right layer and I'll add the
animation over here. I'll just rename it to something more shorter,
like sort, impact. Okay, after playing
the impact animation, we have to go back
to the empty state. So let me create a transition
back to the empty state. Next we have to play this animation when the sword
enters a characters crider. So let's go back to the
mele fight descript. From here we have to play
the Swat impact animation. While we play the animation, we also have to make sure that the animation won't be
interrupted by anything else like another attack or
something that we have to use, the same technique that
we used for the attack. We have to set the inaction. We should really set it to false once the animation is over. What I'll do is I'll just
copy the attack function. I'll rename it to Hit Reaction. And I'll change the
animation to Sward Impact. Okay, so this will play the Sward impact animation and
wait until it is complete. Now from here, we can go ahead and call the play hit reaction. By the way, we should
really do this if the player is not already
in another action. Right here, I can add another condition and check if the player is not already
in another action. By the way, I keep
saying player, but when I'm writing
some code in Le Fighter, it applies to both the player
and the enemy characters. All right, let's go to and T and see if the hit reaction
animation is working. All right, so if I go near the enemy character
and try attacking, you can see that the
enemy is playing the hit reaction when we attack. But we'll have a problem here. The enemy will not only play the hit reaction
animation when we attack, but he played whenever the
sword enters his collider. Okay, so in this case you can see that I'm not
attacking the enemy. But he's still playing
the hit animation because the player sword
entered his collider. Okay, so that is one
thing we need to fix and we'll be fixing
that in the next video. Yeah, thanks a lot of watching and I'll see you
in the next video.
12. Attack States: N in the previous video, we made the enemy play
a hit reaction when the players sword enters
the enemy's collider. Now the enemy plays the hit
reaction while the attack, but the problem is
the enemy will play the hit reaction even while
we are not attacking. If we just point the sword
into the enemy's collider, even then the enemy will
play the hit reaction. Right fixes, what we can
do is we can make sure that the player's sword
is only enabled for a short period of time while the attack is being performed. All right, to achieve this, what I'll do is I'll split the attack into
three different states. Wind up, impact, and cool down. Wind up is the preparation
of the attack. Impact is a part where the
player swings a sword. And finally, cool
down is a part where the player brings back the
sword after the attack. Once we split the attack
into three states like this, we can make sure
that the collider of the sword is only enabled
during the impact state. Okay, that will allow
us to solve our issue. Splitting the attack into three different states like this also have lots
of other benefits. It'll allow us to easily
implement things like combos, counterattacks, and all that. Let's start implementing it
in the Madi Fighter script. We need to split
this attack into three parts, right? Do that. What I'll do is I'll define
enum called attack state. I'll define three,
um, states here, wind up impact and cool down. Okay, at the start I'll also add another state called idle which indicates that we are
not performing an attack. All right, then I'll define avatable of
type attack state. Now when we perform the attack, we have to change the state of the attack based on the
time of the animation. Okay, so if we look
at our animation, let me just double look at, okay, let me just
make it a little big. Yeah, if you look at our
animation up to this part, it's actually the wind up
right up to 33 percentage. It's wind up from there to here. It's the impact part. And finally the rest is
the cool down, right? The impact happens between 33 percentage and 54
percentage, right? We can use this time
of the animation to determine which
state we are in. What I'll do is I'll
create two flow variables called impact start
time. Impact Time. The impact start time is
33 percentage, right? I'll set its value as
0.33 because we'll get the normalized time of
the attack 0-1 okay? And impact end time is 55
percentage at 0.55 By the way, I'm just hard coding
this value right now. In the future, we'll create scriptable objects to store
all the data of the attacks. But I'll just hardcode this for now to make the
implementation PC. At the start of the attack, the attack state will
be in wind up, right? Then when the time of the attack reaches
above 33 percentage, then we have to change the
attack state to impact. We have to enable the
collider of the sword. What we can do is what we are waiting for the
animation to complete. We can create a variable
to keep track of the time. If the time goes
above 33 percentage, then we can switch
the state to impact. But the problem is we can't
keep track of the time and switch the state and all that if we are
waiting like this. Okay, if you're waiting
using this function, then we won't be able to
do anything else badly. What I'll do is I'll wait
for the animation to complete one framed time
by using a while loop. Here I'll create a variable called timer, and
I'll set it to zero. By default, the timer is less than equal to the
length of the animation. We'll wait for one frame. Okay? And we also have to
increment the timer each frame. I'll increment it
by time la, time. All right, so what
this will do is until the value of the timer becomes
greater than the length, we'll keep waiting
for one frame. Okay, So this piece of code will wait for the
entire length of animation, just like this function does. But the thing is, if we wait
using a wide loop like this, then we'll also be able to do other things while
we're waiting. All right, I'll remove this line and I'll wait
by using a wide loop. Okay? Now from here if
we are in the wind up state and if our timer becomes greater
than 33 percentage, then we have to switch
from the wind up state to the impact state, right? If the current attack
state is wind up, if the value of the timer is greater than 33 percentage,
how can you check that? We can't simply
check if timer is greater than impact start time tried because this is a percentage of the attack,
not the actual time. We have to find the
normalized time by dividing the timer with the
length of the animation. Okay, here I'll create a
variable called normalized time. It'll be the value of the timer divided by the
length of the animation. Okay, and from here we can check if the normalized time is greater than the
impact start time. I think we can check
greater than equal to because we should start to ching and it's
directory positage. Okay, if this condition is true, then we can change the
attack state to impact here. We can also enable
the sort collider. All right, so we'll
be doing that later. First, let's handle
all the state changes. So next, if you are in the impact state and if the time goes about
the impact end time, then we have to change
from the impact state to the cool down state. Right from here I'll check if the normalized time is greater than equal
to impact end time. In that case I'll change the
attack state to cool down. And from here we should also
disable a collida. Okay? And finally, I'll also write in all safe condition
for our cool down state. Okay? But right now from the cool down state we
don't have to do anything. But in the future we can handle things like combos from
the cool down state. All right, finally, once
we out this wild loop, and we have finished waiting for the
animation to complete, we can change the attack
state back to idol, which means we are not
performing an attack. All right, so now we have split our attack into three
different states. I'm not just doing this for enabling and disabling
the colliders. We could have done that
with much lesser code. But splitting our attacks into different states like this
will be really handy in the future when we implement other features like combos and counterattacks and all that. Okay, next we have to enable the collider when
we start the impact state, and we have to disable it at
the end of the impact state. For that. First I'll get
a reference to the sword. All right, let me also create a variable to store
the collider of the sword. All right, and then from
the start function, if the sword is
actually assigned, then I'll get the collider of the sword by using the
get component function. Okay? And also by default the Swartz Collider
should be disabled, right? So go ahead and disable it. All right, now when we
start the impact state, we can enable a sort collider. Then we can go ahead and disable it at the end
of the impact state. All right, this is all
we have to do now. The sort surely be enabled
during the impact state. Just for seeing how this works, we can go ahead and
make our attack state public so that will be
visible in the inspector. Let's go to your D and tests before we have to assign the
Swat collider to the script. Let me go ahead and do that. Okay, now let's
start testing this. All right, now if you run
into the enemy with a sword, you can see that
the enemy is not playing the hit animation. But if we attack, the enemy is playing
the hit reaction. Because during the impact
state of the attack, the swords collider
will be enabled. All right, we can
actually minimize a game window and look at the attack state in the inspector to see
what's happening. By default, it's
in the idle state. And when we start attacking,
it'll go through, wind up, impact, cool down, and then return back to idle. Okay, we have fixed that issue. We have split our attack into
three states in the code. I'll make the attack
state private again because it's not
good to make it public, because its value can be
changed from other classes. All right, I'll start
the video here. Thanks a lot for watching and I'll see you in the next video.
13. Combos & Architecting Attacks as Scriptable Objects: Hay phone. In this video, we'll architect attacks
using scriptable objects. And we'll also
look at how to add multiple attacks and make
the player perform combos. Let's get started right now. In our code for a
performing attack, everything is hard coded, right? Things like animation,
name of the attack, and the impact start,
time and time. All those things are hard coded. We don't want to do that because slash is not the only attack
we are going to have. We want different
characters to have different set of attacks, right? We want to design
our combat system in such a way that a designer can add and edit the
list of attacks of a character without
touching the code. All right, to achieve that, we'll be using
scriptable object. Unity. Scriptable object is just a data container that can be used to store large
amounts of data. It's really useful
when we want to make our game designer friendly by separating the
data from the code. It also has other benefits like reducing the memory
usage of the project. This is what we're going to
use to architect our attacks. So let's open up our project. First, I'll create a new
script called attack data. Okay, so this is going to be the scriptable object
class that'll store all the data of an
attack for this class. Instead of inheriting
from mono behavior, I'll inherit from
scriptable object. Okay, here we have to define
out the data of the attack, like the animation name, impact, start and
time, et cetera. First, let me create
a citilized field for the animation name. All right, I'll just
call it anim name. Then we'll also
have to expose it by creating a property, right? This is how we usually do it. But to make the
shorter, what we can do is we can create the property in a single line like
this. All right? But the problem is this won't be serialized simply by using the serialized field attribute. Okay? But if we add a field at the
start of the attribute, then it will also
serialize properties. Okay? Defining it like this is much shorter than
creating a private variable and then exposing it using a
property I'll just cuts next. I'll also create properties for the impact start time
and impact gen time. Okay, this will be our
scriptable object class. Next to create instances of the attack data
scriptable object, we have to add an attribute
on top of this class. We have to add this attribute
called create Acid menu. Here we have to pass the
menu name parameter. This will be the
menu that appears when we write click in
the project window. Here I'll give something
like combat system, create a nu attack. But I'll just make
these two properties start with an upper case letter. Because according to the naming conventions of properties, the first letter
should be upper case. All right, now if we go to Unity and click on
our Project window, under the Create menu, we have a new menu
called Combat System. Inside that we have to
create a new attack. Clicking on this will create an instance of our attack
data scriptable object. Okay, here we have fields
like the animation name, impact, start time,
and impact in time. This is how we are going
to create all our attacks. Okay, I like to put
all my attacks in the game folder because
it's something that can be created by
game designers. I'll just leave the
attack that we just created under the Games folder. I'll create another
folder called Attacks. This is where I will
create all our attacks. Okay, so let me go ahead and
create our slash attack, create a new attack and Mate the animation name is also because that's what we gave
in the animated control. All right, let's look up the impact start time
and time in the code. The impact start time is 0.33 and the impact end
time is 0.55 Right, let me enter those values here. All right, now from our
merely Fighters script, we have to use this instance of the scriptable object instead of hard coding the values here. Right, So let me just
remove these two variables. Over here, I'll define
a list of attacks. Okay? So it'll be a list
of the attack data, scriptable object,
and call it attacks. The reason why I'm making
it list over here is because each character
will have a list of attacks that he can
perform as a combo. Right? We'll be implementing
combo soon. All right, here we have to get the animation name of the
attack from our attacks list. Since we haven't
implemented combos yet, I'll just take the first
attack from the list. I'll pass the animation name
to the crossw function. Okay, next we have to do the same for the
impact start time and time. So I'll just take the impact start time property of
the first attack on the list and I'll do the same
for the impact time also. Okay, so now back in Unity in the mainly fighter
script of the player, we'll have a list of attacks. Here we have to add our attack. Now if you play the
game, everything should work as usual. But now we are
taking the data of the attack from
the scripted logic instead of hardcoding
it in the code. Next, let's add more attacks like this and implement combos. I'll go to mix and download
two other animations. The first one I'll download is this outward slash rap search for outward under animations and you'll find this animation. Okay. The next one I want
to use is this Sparta. Again, just search for Sparta under the animations
and you'll find this. Go ahead and download
these two animations. I have already downloaded it. Let me go ahead and import
it into my Unity project. I'll write these two
other new animations outward and kick. So first we have trick them. Let me change the
animation type. Humanoid select, let
me just trick them. Now we should be able to
preview the animation. All right, for this animation, I don't want the
entire animation because the first few frames, in the first few frames, the player doesn't do much. If we take the entire animation, a combo will look really slow. I'll just go ahead and start
this frame, frame number 14. To do that I can just drag this from the start and
place it at frame 14. Okay, so now the animation
is much shorter. Next, I'll change
the based upon to original for all root
transforms and I'll also bake, interpose the rotation and transform y to remove
any root motions. Okay, let me go
ahead and interply. This is what
animation looks like. Okay, so next let's look
at our kick animation. For this one we can use
the entire animation but we have to remove
any rote motion. I'll change the based upon original for all
the rot transforms. I'll back into post
the transformation and trans position. All right, so now we can go ahead and add this
into our animator. Okay, and let me change their names to
something more simple. So for the outward I'll just name it something
like horizontal. I'll name the kick
as Sparta kick. Then we also have to make a transition back
to the empty state, because after playing
these attacks, we have to go back
to the empty state and keep playing
whatever animation we are playing in
the base layer. All right, next let's go ahead and define the attacks
for these under attacks, I'll go ahead and
create a new attack. First I'll create the
horizontal attack, so let me enter its
animation name. And we also have to find the impact start
time and end time. Right? If you look
at the animation, we can start the impact
somewhere around here, around 1920 percentage
of the animation. And then we can end it
around 36 percentage. Okay, back in our attack, impact start time will be
0.20 for 20 percentage, the end time will
be 0.36 All right, next let's create the
Sparta kick attack. The animation name is
Sparta kick itself. Then we have to find the
impact start time and time. Let's look at the
Sparta animation. All right, for this animation, we can start the
impact somewhere around here, 36, 37 percentage. Then we can end it
when the player starts to pull back
his feet, right? So the starting time
as 36 percentage, 0.36 and the ending
time as 0.56 All right. By the way, one thing to note is that in case of the Sparta kick, we should not be
checking for collisions between the players Swat
and the enemy's body. We should be for collision between the foot of the
player and the enemy's body. We'll be setting up colliders on the foot and hands
of the player soon. I just want to let
you know in case if you're wondering
how that would work. Okay, now we have
our three attacks, so we can go ahead and assign them in the attacks
list of our Le fighter. Okay, I'll just make
horizontal our second attack, and Sparta our third attack. All right, and then in
our Le Fightercript, we should write the
code for performing combos using our three attacks. Okay, perform combo. What we can do is
if the tried to attack function is called while the player is
already in an attack, then we can go ahead and call the attack
function again and play the next attack in our
attacks list, right? So the first thing we have
to do is we have to check if tri attack is
called while we are already in an attack, right? I only want to
perform the combo if the current attack
that's being performed is past the impact state. Okay, I don't want to
perform the combo. If try to attack is called when the current attack
is in the wind up state, that would be too soon to
register a combo input. Okay, so what I'll do is
if the tried to attack function is called and if we
are performing an action, and if the attack
state is impacted, cooled down, then we have
to perform the combo. Okay, this should
be double equal to, that's why we
have the error. All right, if the attack
state is impact, cool down, then we should perform
the combo once the current attack reaches
the cool down state. Right, so what I'll do is I'll create
available called combo. I'll also create an
integer variable to keep track of the current
combo count. Here I'll create an integer
variable called combo count. And I'll set it to
zero by default. Then if you try to attack is called while we are in the impact of cool down
state of an attack, then I'll set combo. Okay, Once you reach
the cool down state, if combo variable set to true, that means we have to perform
a combo right from here. First of all, we set the
value of two combo defaults. Then we can go ahead and
increment the combo count. But when we increment, we
also have to make sure that the combo count doesn't go beyond the length of the attack. We have to check if combo count is created equal to the
count of the attacks, then we have to reset it back to zero and cycle through
the attacks again. Right, There's actually a
simple way of writing this. We can just say compo count
is equal to compo count plus one mod I have to
put this in a bracket, and then I have to mark this by the length of our attacks. Okay, so this line is same as
writing these three lines. What it'll do is if the compo count becomes equal to the count
of the attacks, then the result of the
modulus will be zero. It's one of the basic
operators that you learn while learning a
programming language. I'm not going to spend a lot of time explaining how it works. You can just look it up if
you're not sure how it works. This land will increase
the compo count in our attack function. Instead of always
using the zero attack, we have to use the attack at the compo count index, right? So let me also change that for the impact start time
and impact end time. All right, back in here. After incrementing
the compo count, we have to go ahead and call the attack corotin once again. And perform the next attack. From here, I'll go ahead and
call the attack corotine. This will perform the next
attack in our attacks list. Okay, After that, I just want to exit out of
this co routine because I don't want to execute these
two lines where we're setting the attack state title
and inaction defaults because we are still performing
another attack, right? We should not come down
and execute these lines. We want to stop the
corotine from here. To stop a corotine
and exit out of it, we can use B. Okay, so this will
make the exit out of the current corotine
while executing the next attack in
our attacks list. Right, If we don't
perform a combo, then this line won't be executed and we'll reach the
end of that attack, corotine, where all these
variables will be increased. Okay, so from here we also
have to reset our combo count. Because the next time
we start an attack, we want to start
from the beginning. All right, this is all we have
to do to perform a combo. Let's go to Unity and
see if it's working. All right, if I keep
pressing the attack input, you can see that we are
performing a combo. All right. And we can
also try performing the combo in front of our enemy. For the second
attack, the enemy did not play the hit
reaction, right? This is because the second
attack happens really fast, and while the second
attack is being performed, the enemy hasn't fully completed his hit reaction from
the first attack. Okay, if we look at our core, we only play the
hit reaction again if we are not currently
in another action. Right? To improve this, what we can do is we
don't have to wait for the entire length of the
hit reaction animation. The reason is because
if you look at the transition of the
impact animation, you can see that we'll
start transitioning back to the empty state around this point of the
animation, right? If you open the settings,
it's around 0.65, 65 percentage of the
impact animation. We don't have to wait for
the entire impact animation. We can set the inaction back to falls somewhere
around this point. Okay, Since we're going to have different hit
reactions in the future, what I'll do is I'll just
wait for 80 percentage of the length of the
animation instead of waiting for the
entire impact animation. Okay, so wait for 80
percentage length. I'll just start to
multiply the length of 0.8 Now if we go ahead
and test the game again. Should play the reaction
during the second hit. Okay, Now the enemy is also playing reaction
during the second hit. Also, the second
attack is really fast. If you want, you can slow
down a bit from the animator. Here you can set the speed of
the animation if you want. You can slow down a bit
by changing it to 0.9 or 0.8 But I guess
it's not that bad. Since I'm just building
this for a course. I won't spend a lot of
time on polishing stuff, but you can spend a lot
of time on this and find the perfect values to
make your combos look good. Okay, so the next thing
I want to show you is that right now when
we perform the combo, you can see that the
enemy did not play the hit reaction for the
final kick attack, right? The reason for that is because we're checking for the collision between the sword and
the enemy's body, right? But for the kick attack, we should check for
the collision between the player's fruit
and the enemy's body. The interaction will
sometimes work when we're doing it from
the close range. Because in the close range the sword is also passing
through the enemy's body. But if you do it from a
long range like this, you can see that the enemy did
not play the hit reaction. What we have to do is we
have to add hit boxes on the foot and the hands
of the player in case the player has any
attacks like punching. And then we have to enable the colliders accordingly
based on the attack. We'll be doing that
in the next video. Thanks a lot for watching and I'll see you in the next video.
14. Different Attack Hitboxes: He. In this video, we'll add hit boxes to the
hand and feet of the player. Then we'll enable the correct
hit box during the attack, based on the type of the attack. For example, for the
Spartachic attack, we'll enable the hit
box on the right foot. Let's look at how
to implement this. First, let's add collider to the hand and feet of the player. Let me just expand the bones. First I'll add a collider on
the left foot of the player. Okay, so here I'll add a
sphere clyder component. Right now the radius
is pretty big, so we can reduce it to
something smaller like 0.15 Okay, that should be good. Next I'll make the say, trigger because we don't want this collider
to have physics. Then we also have
to set its layer as player hit box because this is a hit
box of the player. All right, we also have
to set its tag as box. This will be the hit box
on the player's foot. Let's also add this
component in the right foot. Copy it from here. Let me
expand and find the right foot. Okay, and I shall and paste
our Spear Glider component. All right, here also we have to set the layer
as player hit box. And we have to set
the tag as hit box. Okay, next let's do the same
for the hands of the player. So I'll just expand spine and first to add it
to the right hand. Okay, so again we can just past the spear glider
that we just copied. All right, here also we
have to set the layer as player hit box and
set the tag as hit box. Okay, let me also do the
same for the left hand. All right, I'll
paste the Spaclider. I'll also change the
layer and the tag. Okay, so one thing to note is that for the
collision to happen, at least one of the
collider should have a fixed component like
rigid body, right? That was the reason why we added a rigid body to our Swat object. But in the case of the
player's hand and feet, I don't want to add rigid
body to all of them. Instead what we can
do is we can add a physics component
to the enemy. The collision will
work if either one of the collider has a
physics component, right? We can add it to the enemy
instead of adding to all the hand and foot
of the player here. For the enemy, we can either add a rigid body or a
character controller, both our physics components and they'll help us to
trigger collisions. I'll just go ahead and add
a character controller. Now since we have a
character controller, we don't need a capsule
collider anymore because the Character Controller
comes with the collider. So I'll just go ahead and
remove the capsule collider. We have to arrange the collider of the character
controller properly. I'll just set the center
to half the height. Okay, we can actually reduce the height to
something like 1.8 Now the center will be 0.9 We
can also reduce the radius. I'll just reduce it to something
like 0.4 for the enemy. It's good to have a big collider because the player should not feel like he attacked and
hit was not registered. Even if the collider
is a bit big, that will work in favor of
us during the game play. All right, so now
we have hit box on the hand and feet
of the player next. For each attack,
we should be able to specify the hit box
that we want to use. Okay, in the case of and slash for a swundle
it'll be the sword, but for the Sparta kick, it'll be the right
foot of the player. Okay, What we can do is in
the attack data script, we can add num called
attack hit box. Okay, so this can be left hand, right hand, left
foot, right foot. Or the sword. Okay,
In your game, if you have more weapons, you can also add
a boxes for them. Next in the attack class, I'll create a property
for the box to use. Okay, Now for each attack, we can specify the hit box to use the slash and
slash horizontal. We'll use the sword, and the Sparta kick will
use the right foot. All right, next from
the minify descript. When we perform the attack, we should not simply
enable the sword collider, but instead we should
enable the right collider based on the
hit box of the attack. First, we need to grab a reference to all the
colliders we have. Let me create ap collider
object for both hand and feet. Okay, so create the left hand collider and then the
right hand collider. And similarly the left foot collider and the
right foot collider. Okay, next from the
start function, we have to grab the
reference to all the glider. We can either make
this a setilized field and assign the
reference from the inspector, but since we are using
a humanoid character, we don't have to do that. There's actually an easier way when we're using a
humanoid character. We can get the bone of that
character from the animator by calling animator
get bone transform. Okay. And here we have to pass the bone
that we want to get. Let's say I want to get
the left hand bone. I just have to pass the
left hand in the parameter. Okay, so this will give us the transform of
the left hand of the player or the enemy based on the character to which this Mealy Fighter
script is attached. Okay, from the left hand bone, we can get the sphere
collider component. All right, we can store this in our left hand
collider object. All right, next we can do
the same for the right hand. In that case, we have to pass the right hand bone and I'll save it into the
right hand collider. Okay, I made a mistake here. I saved it into the left foot collider instead of left
hand. Let me change that. Okay, so now that's correct, and let me add Emmy
Collins at the end. So next I'll do the same and get the collider
for the left foot. So let me change
these two left foot, okay, And finally, let me
get the right foot collider. All right, so I'll change
this to right foot. Okay, so once we get
all the colliders, we have to disable them
by default, right? We should really enable
them when we are attacking. So let me go ahead and
disable all the colliders. Okay, let me just copy and do the same for
the foot colliders. All right, we can put the lines to disable the
colliders in a function because we'll also have to
disable all the colliders after the impact state of
an attack is over, right? I'll just put it in a function called disable all colliders, Okay, I'll just call
that function from here. All right, next, when we
start the impact state, we should not simply enable
the collider of the sword, but instead we should enable the collider based on the
hat box of the attack. Right? For that what I'll do is over here I'll create
another function called a. This function will take a
reference to the attack because we can use it to determine which a box
we have to enable. From here I'll just use a switch statement,
just a crypt tip. We don't have to manually fill in all the cases of
the switch statement. We can just write switch
and hit the tab key twice. Okay, that'll create the syntax. From here we can just specify the variable we want to switch. In our case, we want to check the attack hit box to use enum
right after writing that, if we press Enter key, it'll automatically fill
in all the cases for us. Okay, now based on the value
of the hit box to use, we can go ahead and enable
different colliders. If the attack uses the
left hand hit box, then we can go ahead and
enable the left hand collider. Okay? And then if it
uses the right hand, then we can go ahead and enable
the right hand collider. Similarly, we can
do the same for the left foot and
right foot collider. Okay, And finally,
if you sing a sword, we have to enable
the sword collider. Okay, so now when we go to the
impact state of an attack, we can go ahead and call the enable hit box function and we can pass the attack that
we're performing right now. Okay? And then once impact state is over and we
start the cool down state, we have to disable the
correct collider, right? So in this case we can
just disable all colliders and it'll just make sure to
disable everything, okay? By the way, just rename
this function to disable all boxes just to keep it consistent with enable
hit box function, okay? Ls continuity and tests. When we perform this partakic the collider of the right foot
should be enabled, right? What I'll do is once
I play the game, I'll just minimize
the game window. I'll select the right foot from the inspector so that we can see when the
collider is enabled. Right now it's disabled, but when I perform the combo, you can see that during
the kick attack, the collider of the right
foot is being enabled. Okay, We can also try attacking the enemy and see if the enemy is playing the
hit reaction during the kick. Yeah, enemy is playing the hit reaction. Yeah,
that's working fine. Now since we have a way of
specifying the hit box, we can also create
other types of attacks, like punches and
other kicks and all. If you want to use a different type of attack in your game, then go ahead and do that. Just specify the collider that
you want to use and work. All right, so I'll
stop the video here and I'll see
you the next video.
15. Enemy AI: N. In this video we'll start the implementation
of enemy AI. We'll be using state machine
for implementing AI. And we'll separate
different behaviors of AI into different states. For example, enemy can
have different states like idle chase,
attack, et cetera. Each state will be a different
class that will run on our state machine where using state machine
for managing states. We could also done this
by using enum states, Just like how we
managed the states of the players attack, right? But the problem with
using enum states is that it doesn't scale well
when we have lots of states. If you're planning to
build a complex I, then the code will
become a mess. If you use the Enum approach, using a state machine
is much more scalable and it can handle
more complex cases. We're going to go
with that before we start the implementation. First, let me explain
to you the architecture of state machine
and how it'll work. Each state will be a
separate class like this. Here I'm showing the
Chase state for example. Each state will have
three functions, enter, execute, and exit. As the name suggests,
the enter function will be called when
we enter the state, and the exit function will be called when we exit the state. Then we also have the
execute function. The execute function
will be called every single frame while
the state is active. Okay, If you take the
example of the chase state, we can do things like
play an audio from the enter function to let the player know that
he has been spotted. Then from the exit function, we can play an audio to indicate that the
enemy lost the player. And from the execute function we'll have the right logic for chasing the player most of the times the main logic of the state will be in
the execute function. Now we should have a basic idea of what we're going to build. Let's go ahead and start
the implementation. First, I'll create the
enemy controller script for controlling the enemy
under the scripts folder. I'll create a new folder enemy. In here I'll create a new C sharp script
called Enemy Controller. All right, this script will be responsible for
controlling the enemy. Similar to how the player
controller controls the player. But the main difference is
unlike the player controller, the enemy controller
will not use the player's input to
control the enemy. Instead, it'll use the
state machine to run different states of the enemy based on what's
happening in the game. All right, we need to go ahead and build our state machine that the enemy
controller can use. Let me go back to Unity. Under the assets, I'll create a new folder called Util here, I'll create another folder
called state machine. The reason why I'm
putting this in a folder called utual is because state
machine is something that we can reuse
in multiple games. Usually I'll just build
one state machine and reuse that in all my games. I'd like to consider this as library that I can re
use in multiple games. And that is the reason
why I'm putting it in ut that I can easily move
it into different products. Okay, here I'm going to
create two new scripts. The first one will be called State machine and the second
one will be called State. All right, so let me go ahead
and open these two in which the studio first let's look
at our state machine script. State machine is going
to be a plain class, we don't have to inherit
from the mono behavior. All right, the owner of the state machine will create the instance of it directly. This won't be attached
to any, kay, object. I also make the state
machine class a generic class by
adding this over here. The reason for this is because the state machine can be
used for different purposes, not just for handling
the states of the enemy, for example. It can also be used
to handle a NPC in, again, it can even be used
to handle states of UI. If you have watched my
RPD series on Youtube, then you'll know how
useful state machines are for handling things
like UI states. The point is, since this can be used for handling
multiple things in, again, we want to make
this a generic class. Every time I create an
instance of this class, we'll also specify the
owner of this class. This over here will be the
owner of the state machine. For example, when we create an instance of the
state machine, let's say we want
the state machine to handle the enemy states. Then we can just define it as a state machine of
any controller. Okay? If you want to use it for something else like handling the states of NPC, then you can go
ahead and make it a state machine of
NPC controller. So that is the reason
why we are using generic here and making the state
machine a generic class. We have already used generic
classes in this course, but this is the first
time that we are defining a generic class, okay? For example, when we
create a list like this, this is actually
a generic class. Here, we can create lists
of different types, right? This can be a list of indure, list of string, Yeah, these are generic classes. We have been using them, but this is the first time
that we are creating it. All right, so this will be
our state machine class. Next, let's go to
the state class. The state class is
actually going to be a mono behavior because we want to attach it
to a game object. Then state class is also
going to be a general class because we should be able to specify the owner
of a state class. All right, This is going
to be the base class and all the states in
our gains are going to inherit from the
state class, okay? Inside the state
class. First we'll define the enter function. So let me just create
a public function called the enter function is going to take the
owner of the state, so let me create
parameter for the owner. This function is
not going to have any depition because
this is a base class. When we inherit the
state class from our states like Idle and Chase, then we can overwrite these functions and
the implementation of those functions will be written in those subclasses, okay? Since we want to be
able to override this function from
the subclasses, we have to make it
a virtual function. Let me add a virtual
keyword at the start. Okay? So this will be
the enter function. It'll be called whenever
we enter the state. Next, let me go ahead and
create the execute function. All right, so this will be the function that executes the logic of the state, and this will be called every frame while
a state is active. Okay, finally, let me just copy this and create
the exit function. All right, for the exit
and execute functions, we don't want to take
the O as the parameter because we can just cache it when we get it from
the enter function. All right, these are the functions that we
need in the state class. Now back in a state machine, I'll create a property, the current state of
the state machine. So let me just create
a public property of type state and I'll
call this current state. Okay, I'm just making
the setter private so that we won't be able to change the value of this property
outside this class. All right, next, since the state machine
is a plan C sharp class, we'll be creating an instance
of it using a constructor. It won't have start
wake functions like the monobehavior
class, right? So let's define a
constructor over here. The constructor will take the owner of the state
machine as a parameter. All right, we can just
cast the owner to a private variable since we'll need to use it
from other functions. All right. Next I'll create a function for
changing the state. So let me call
this change state. And this function will take
a state as a parameter. All right, and when this
function is called, we have to switch to the
new state that's passed to this function right from here. First I'll exit the
current state by calling current
state it function. All right, here I'll just add an unconditional operator just because when we call the chain
state for the first time, the current state will
be null and order to throw an exception
in that case, okay? So after exciting
the current state, I'll change the current state to the new state that's passed, and then I'll go ahead
and enter the new state. Okay? And when we enter a state, we also have to pass the owner. So let me go ahead
and pass that out. Right? This function will change the current state for us. Next, I'll create a function called this function will execute the current
state for us. From here I'll just call
current state execute. Okay? Yeah, these are all the functions that we
need in our state machine. So now we can go ahead and add different states
and try testing them. All right, so back in Unity, let me go to the scripts folder, and inside the enemy I'll
create two new scripts, Idle state and Chase state. Okay, so these two are going to be the
states of the enemy. And we have to make it
inherit the state class. Instead of inheriting
from mono behavior, we'll inherit from state. And this is a state
of the enemy, right? So I'll pass the enemy
controller as the owner. Okay, from the state we
have to override the enter, execute and exit functions. And we have to write the behavior for what
should happen in each case. First, let me override
the enter function for this type
override and space. You can see all the
functions that we can overwrite from here. If I set up the function, you can see that
it automatically creates the code for us. Okay? So this
function should have the behavior that should happen when we enter
the idle state. The actual implementation of the state's behavior will
be done in the next video. For now, I'll just write p dot lock statements and make sure that our state
machine is working properly. Here I'll just lock something
like entered idle state. Okay, So next let me go ahead and overwrite
the execute function. I'll just type over write. And if I hit space, we can see the execute
function and then pressing Enter
will automatically create the function for us. All right, from here I'll just print
executing idle state. All right, and finally I'll go ahead and now
write the exit function. From here, I'll just print
exiting idle state, okay? So let's also do the same
thing from the Chase state. First we have to make it inherit the state class, all right? Then we have to define the enter execute
an exit functions, I'll just copy it
from the idle state. And here instead of printing
idle I'll print chase. Okay, so we have to find the
two states of our enemy. Now from the enemy
control script, we have to create
a state machine first and then set the state as idle state and then run
the state machine, okay. First let me create a property
for the state machine. All right, so this will
be a state machine of enemy controller. I'll make it a property
with the private stor. All right, then from the start function I'll go ahead and initialize
the state machine. Okay, so we have to initialize
it using the constructor. While calling the constructor, we also have to pass the owner. The owner will be
this class itself. We can just pass this
keyboard for the owner. Okay, So this will
create an instance of state machine and store it in
our state machine property. Next we have to set the starting state of
the state machine. I'll call state
machine chain state. For the state, we have to pass the reference
of the idle state. Since all these are going to be attached to the
same game object, we can go ahead and use get component to get the reference
of the idle state class. All right, soon we'll
be optimizing this by caching all the states
so that we don't have to call get
component each time. All right, so we have to set
the starting state to idle. Next from the update
function we have to call state machine execute to run the state machine
every single frame. All right, let's go
ahead and test this. First we have to attach all of these scripts to our
enemy game object. First, let me attach the enemy
controller and then I'll attach the idol and J states. All right, so now if
you're on the game, you can see that in the console
first it printed entering idle state and
then it just keeps printing executing
idle state, right? That's because we set the
starting state as idle state. First, enter the idle state and it'll keep executing
this function. Executing idle state. All right, our state
machine is working fine. Next, let's also try switching from the idle
state to the chase state. For that, what all Dos in the execute function
of the idle state? I'll check if the player
pressed a button. If the button is pressed, then I'll switch to
the chase state, okay? I'm doing this just for testing in the actual idle
state behavior, the switch to the chase
state will only happen when we see the player, right? But right now, since we don't have all those
behaviors implemented, I'll just check for test input. And then switch to
the chase state. Okay? I'll check if
the key is pressed. All right, D for test. If that is pressed,
then we can go ahead and change the state to state. To change the state first, we have to get reference to the state machine of
the enemy controller. We have the enemy controller in the function cache it
into a private variable. I just call it enemy. I'll cache it from the function. Okay, so now we can call enemy state machine chain
state to chain state. We can get a reference
to the chain state by calling enemy dot
component chase state. Okay? This will work, but it's not the optimal way
to do it because calling the get component
every time like this is a bad thing
for the performance. We have to cash this somewhere
and use that for that. What I'll do is in
the enemy controller, I'll cash all the states
of the enemy first, create num called enemy states. All right, so right now we only have idol and chase states. We have to catch
the reference of the idol and chase state. To do that, I'm going
to use a dictionary. If you have not used
dictionary before, it's somewhat similar to a list, but the main differences
for the indexes are always an integer value
that starts from zero, right? But in the case of dictionary, we can have any type of index, like string or even an enum. Okay, In the case of dictionary, the indexes are
actually called a key. For our dictionary, the key is going to be the
enemy states enum, the value is going to be
a state class, right? So let me just call the
subject state dict for short. And then from the
start function, first we have to
create an instance of this dictionary. All right? And then we have to cash all our states
into the dictionary. Okay, first let me
cash the idle state. The key of the dictionary is
going to be enemy states. Idle enum, we can get the
value of the dictionary, which is the reference
of the state class by calling the get
component function and getting the idle state. Okay, So next we also have to do the
same for the J state. So let me go ahead and do that. All right, all
states are cash now. Now instead of
calling get competent every time we can
get the value from the dictionary Here I'll
pass of enemy stated. Okay. Now we can also use it
from all the other places instead of calling it competent from here we don't have access
to the state dictionary. What I'll do is instead
of making it public, I'll just keep things
encapsulated by creating another function
called chain state. Okay, This function will
take the enum of the state. All right, from here we can just call state machine chains. State. And for the state we can pass state dictionary
of the enum. Okay, This way we don't have
to expose a dictionary. I think having a
public function like this is much better than
exposing our dictionary. All right, now from here we
can simply call enemy chains. State for the state, we can pass enemy state chase. All right, so the code is really short now and
it looks much cleaner. That's right. So now when you run the game
and press the key, we should actually switch
to the chase state. Okay, so let's go
ahead and test that. All right, so right now we are
executing the ideal state. But if I press the key, okay, I have to be focused
before pressing the key. All right, now when
I press the key, you can see that we exited
from the ideal state and we entered the state and started executing
the chase state. All right, this is how the flow of the
state machine will work. The enemy controller
will always run one of the enemy states based on
what's happening in the game. All right, obviously we don't want to just
print these messages, instead we want to implement
the actual behavior, right? We'll start doing that
in the next video. Yeah, I'll stop the video here and I'll see you
in the next video.
16. Chasing the Player: In the previous
video, we implemented the state machine and we created two states
for testing it. But right now, the
functionality of these two states are
not implemented. We'll be doing that
in this video. The functionality that
we want to implement is when the enemy
spots the player, he should go to the chase state and start chasing the player. All right, first we have to implement the functionality
for detecting the player. For detecting the
player, what I'll do is I'll create a spear
trigger on the enemy. Whenever the player enters
that spear trigger, we'll start tracking him. If the player is also in the
field of view of the enemy, then that means the
enemy can see the player and we can make the enemy
start chasing the player. Okay. First under the enemy, I'll add a new empty game
object called Vision Sensor. Okay? So this will be
the sphere trigger for detecting the
player in here. I'll just click on Add Component
and add a Sphere Glider. All right, right now
it's really small. We want the few range of the
enemy to be much bigger. I'll just increase the radius
to something like six. You can increase it
further if you want your enemy to have a
longer vision range. Okay, next we also have to set the trigger to true
to make a trigger. We'll also create a new
layer for the vision sensor. Let me go ahead and
add a new layer. I'll call this one
Vision Sensor. And then back in
the game object, I'll change its layer
to Vision sensor. All right, next, we only want the vision sensor to detect the collision
with the player. It's not detect collisions
with any other objects. Right, to go the project
settings and under physics, let's find the layer
collision metrics. We only want the vision sensor to collide with the player. Let me remove everything else. All right, now we have
our vision sensor. Next, when the player
enters the vision sensor, we have to start
tracking him. Right? I'll create a script for doing that inside scripts
in the enemy folder. I go ahead and create a new
script called vision sensor. Okay, whenever the player enters the vision
sensor trigger, the on trigger enter function
will be invoked, right? So let's go ahead and define that function. From
this function. We have to start tracking the player track in
the enemy controller. I'll create a list of Me Fighter
called Targets in range. Okay, why am I making this a meal fighter instead of the
player controller? The reason is because in
the future you might also have other allies of the player that the enemy
might want to attack. Saving the targets
as a meal fighter is better than saving it
as a player controller. Okay, I also have a
list here in case there are multiple allies and the player that the
enemy wants to track. All right, let me
also initialize this list from here so that we don't get any error
when we use it. All right, now from
the trigger Er function first we have to get the Mele fighter
component of the collider. So I'll just say other get component and I'll get the
mele fighter component. All right, let me just throw this innovariable
called fighter. If fighter is nautical, no that means the collider that entered the trigger has
a mile fighter component. In that case, we
can go ahead and add that to this list in
the enemy controller. Okay, first let me grab reference to the
enemy controller. So I'll make it a stilizield
variable over here. All right, now we
can go ahead and add the fighter that
entered the trigger to the targets and range list. Okay, this will start tracking the player when he enters
the targets in range list. By the way, it will not
track the enemy even though the enemy has the
Mele fighter script. Because the vision
trigger will only collide with objects in
the player layer. All right, next, when the target moves out
of the vision trigger, we have to stop tracking him. For that, we can use the
on trigger exit function. This will be worked whenever a collider exits the trigger. From here we have to stop
tracking the target. So we can go ahead and call the remote function on the
targets in range list. Okay, Now we are keeping track of the targets that are in
the enemy's vision range. But the thing is the enemy
should only be able to see the target if the target is
also in his field of view. Okay, so what we can do is from the idle state we can loop
through all the targets in the targets and range list
and check if any one of the targets are in the field
of view of the enemy, okay? And if they are in
the field of view, then we can set them as the target that the
enemy must chase. Okay, so let's go ahead and
do that on the idle state. So first of all I'll just remove all the deeper clock statements. We don't need them anymore, we just wrote that for
testing the state. Okay, now from here I'll
use it for each loop I loop through all the
targets in the targets and range list of
the enemy, okay? And then we have
to check if any of these targets are in the
enemy's field of view. Right, in the enemy controller. Let me go ahead and define a
property for the field of. I'll create a float able called it's a shorter
field of few, let me make it a property. And I'll send its value
to 180 by default. Okay. I'll also make it a sized field so that we can
edit it from the inspector. All right, now from here we can check if the target is in the
enemy's field of view. Do that first, I'll
find the vector from the enemy's position
to the target's position. All right, and then we can find the angle between
that vector and the enemy's forward vector to check if the target is in
the enemy's field of view. Okay, so first let me find a vector from the enemy position
to the target position. So we can do that by subtracting the position of the enemy from the position of the target. Okay, let me just store this innovatible called
vector to target. Okay? And then we can
find the angle between the enemies forward vector
and the vector to target. Let me show this inevitable
called angle then. If the angle is
less than O equal to enemy's field
of divided by two, then that means the target is in the enemy's field of view. Okay, By the way, the reason why I'm dividing
the field of view by two is because the angle can be in
the left or right direction. Right. The target can be at 60 degrees to the left or 60
or 70 degrees to the right. In both these cases, he is in the field of
view of the enemy. All right, if the target is
in the enemy's field of view, then we can make the enemy start chasing the target. Right? First for I'll do is in
the enemy controller, I'll define another
property called target. All right, this is the
target that we want to chase From here I'll sit the target to
the target that is in the enemy's field. If you, then we can go to the chase state and start
chasing the target. To chase the target, we can
switch to the chase state, called enemy chain state, and I'll pass the chase state. All right, and finally, let's break out of the
loop so that we won't try to do this for the rest
of the targets in this list. Okay, so let's score it
and try testing this. We have to attach the
vision sensor script. Let me go ahead and do that. And here we also have to
assign the enemy reference. Let's try testing this
now Let me just bring the player down so that it's
not in the field of view. By default picking
us control shift to place the players feet
exactly on the ground. And let's try testing it now. All right, let me
just turn on Gizmos and set up the enemy from the inspector so that we
can see the vision trigger. All right, now when we
enter the vision trigger, so you can see that
the enemy is able to detect and he switched
to the chase state. All right, so a code for
decking the player is working. And next let's also try
entering the vision trigger from an angle that is outside
the enemy's field of view. Okay, so in this case you can see that the enemy is
not able to detect us. But as soon as we enter
the enemy's field of view, the enemy will detect us. And let's switch to
the chase state. All right, next, when the enemy
detects the player and goes to the chase state, we have to make the
enemy chase the player, right for moving enemies or
any other AI characters. We can use Amish and Unity. It's navigation system works by defining a mesh like this. All right, so this blue color is the Amish and that's the area in which the
AI characters can walk. We'll be using this
for moving our enemy. First, we have to take
a Nash for this level. In our level we have two things, a plane and a cube, right? These are the ugly environment
objects that we have. What I'll do is
I'll put these two in another object
called environment. All right, let me
make the cube and play a child of its environment. If you have a lot
more game objects, then you have to put all those in a parent object like this. Then you have to mark the environment object
as navigation static. All right. I also want to change all the children.
So let me just press Yes. The reason why
we're doing this is because when they
are baking abmish, only the objects
that are marked as navigation static
will be considered. Okay, so let's try baking an Amish first the window and
open the navigation window. All right? I'll just
tock it over here. Then under the Bake tab, we can just go ahead and click on Bake with the
default settings. All right, you can see
this blue mesh our scene, so these are the areas on which our air characters
can walk over. Here you can see
that we don't have the blue mesh in the place
where the cube was placed. That is because the
player won't be able to walk in that area. Okay, another thing that
you have noticed that whenever you change the static game objects
in your scene, you'll have to bake
the Nash again. Okay? So just keep that in mind. So now we have Amish en, we can walk on next to
make the enemy move. We also have to add a Nabmish agent component
to our enemy. So let's go ahead and do that. I'll add the naming
component and let's just leave all the settings
at its default value. Now from our chase state, we can make the
enemy move towards his target by using the Amish. All right, so first we have to get the
reference of the Amish. Let's do that from the
enemy controller so that we don't try to get the
reference multiple times. If you do it from the
enter function of the chase state then the issues we'll be calling the chase
state multiple times. And every time we call
it we'll be grabbing reference to put
all those things in the enemy controller. All right, from
here let me catch a reference to the names agent
from the start function. Okay, by the way, to be able to use the
Nama agent class, we have to import the
humanity engine namespace. All right, I'll just name
this Nab Agent shot. Okay, and let me also make the property so that we can
access it outside this class. All right, let me catch a reference to it
from the start function. Okay, and now back to the
chase state function. Let me get rid of all the
deputed dot log statements. And then to be able
to use snapmsh agent, first we had to
cast the reference to the enemy controller. So let me go ahead and do that. Okay, I'll cast the owner
from the enter function. Then from the execute function, we can use the amash agent of the enemy to make the
enemy his target. Right? So we'll make the
Nabs agent motor position. We can use the set
destination function and set a destination
for the Nabs agent. All right, so in this case the destination will
be the position of the target, right? I'll pass the position of
the target as the parameter. And this should make the enemy chase the player and
go to the chase state. Okay, let's go to it
and try testing this. All right, so if I enter
the vision trigger, the enemy is not chasing the character. There's
some issue that we have. That's because we have apply rote motion turned on in
the animator of the enemy. We can just turn it off for
now and we can turn it on when we are performing some
action that has rote motion. Okay, now the enemy should be able to chase the player when we
enter the vision range. Yeah, you can see that the
enemy is chasing the player. But there are two problems. The enemy is not playing any
animations while moving. And the enemy is stopping
on top of the player, right? That looks really weird. We want the enemy to
stop at a distance from which he can
perform attacks, okay? So make the enemy
stop at a distance. We can use the stopping distance property
of the Napa agent. We can also set
it from the code. Setting it from code will be better because depending upon the states we might want to use different
stopping distance. From here we can go ahead and set the stopping distance of the naps agent for the state
I want the enemy to stop at, around 3 meters in
the player. Okay. And I'll just make
it a las field variable so that we can
control it from the inspector. Let me just call it something
like distance to stand. And I'll make it
three by default. And I'll set that
variable here, Okay. Now if we test the game, the enemy should stop at 3 meters and he should not
move to the players position. All right, so you can see
that is working, okay. So the next thing to fix
is we want the enemy to play the running animation
while he's chasing the player. We can easily do that by setting the move amount parameter of
the enemy's animator, right? First we need a reference
to the animator components. Reference to it from
the enemy controller. Okay, let me create
public property here and then I'll go ahead and catch it
from the start function. All right, so now we
can set the amount of the enemies animater based on
the speed of the Nab agent. Okay, So down here I'll call enemy animator set
float function to set the amount parameter. And the value of this
parameter should be set based on the speed in which
the apperent is moving. Right in the abating class, we have a property called speed, but this is actually
the maximum speed in which the navchine can move. It's not the speed at which the Nabachgent is moving right now. To get the current
speed of the navaent, we have to use the
velocity property. Since velocity is
actually a vector, we have to get its
magnitude, okay? This will give us a speed in which the navigant is moving. The value of the move
amount should be 0-1 If you look at the animator, let's open the
Locomotion blend tree. You can see the value of
the move amount should be 0-1 All right, we have to map the
speed of the Napa agent 0-1 by normalizing it. We can easily do that by
dividing the current speed of the Napa agent by the maximum
speed in which it can go. Okay, let me divide this value by the maximum speed
of the Na agent. Okay, so now our enemy
should also play the animation while moving
Tscl, Unity and Test. All right, so yeah, I can see that the enemy is playing animation while moving. All right on E. Last thing I'd like to fix is right now when the enemy turns, you can see that he's
turning really slowly. Even though he's playing
the running animation, I want to increase the
speed at which the enemy turns so that it
looks more realistic. We can do that by changing the angular speed property
of the lab agent. So let's increase it to
a high value like 720. Now the enemy should
turn really fast, okay? So I can see that the enemy
is turning really fast. All right, now our chase
functionality is looking fine. I'll start the video here and in the next video we'll start implementing the
rest of the states. So watching and I'll see
you in the next video.
17. Combat Movement: Hey phone. In the previous
video we made the enemy detect the player and chase her when she's in the field
of view of the enemy. In this video, we're
going to start the implementation
of combat movement. Let me explain what
I mean by that. We're going to
implement a combat system similar to
the one that you see in games like Assassin
Street and Batman. In the system, the enemy won't keep attacking the
player all the time. Instead they'll stand
at the distance from the player and circle
around her like this. And we'll only attack the
player one at a time. Okay, so if you played games
like Assassin Crito Patman, you should be familiar
with this combat system. In our implementation, once the enemies chase the
player and disclose to her, they should either stay idle in a guarded position or
circle around the player. Only one of the enemies should actually attack the
player at a time. All right, in this video we'll start implementing the
part where the enemy stands and circle
around the player. What I'll do is I'll create a state called combat movement. In this state, the enemy
can do three things. The enemy can stand
close to the player while playing a combat
idle animation. Or the enemy can circle
around the player, okay? Finally, if the enemy is
not close to the player, then the enemy will
chase the player, okay? So the state will be called combat movement and it'll handle all the movement
of the enemy once a player or any other target
is detected by the enemy. Okay, so just remember
in the state, the enemy can either stand in a alerted position or circle around the player
or chase the player. Okay, so let's go ahead and start the
implementation of this. First, let's go ahead and change the implementation
of Chase state. Now Chase won't be
a separate state. Instead we'll have a
combat moment state that will handle Chase, combat idol and
circling the player. By the way, if you
want, you can keep them as separate states. But the reason why I'm
combining them into a single state is because these states have
a lot in common. All these three states
handle the movement of the enemy after the player
is detected, tried. It makes sense to combine
them into a single state. We have to create a state
called combat movement. What we can do is
we can actually rename the Chase state to
combat movement state. Instead of creating a new one. As well as right
lick and select. Rename, Rename this to
Combat movement state. Okay, and by the way, in the enemy controller, I'll also rename the name of the Enum so that it doesn't cause any
confusion in the future. Okay, so in a combat
moment state, we have to handle combat, idle chase and
circling the player. Right in here I'll use Aum to differentiate
between those three states. So let me create num
called AI combat states. And this can be idle
chase or circling. Okay, by the way, note that the idol
that we have out here is different
from our idle state. This idol is a combat idol in which the enemy has
spotted the player and is aware of the position of the player right in
this idle state. We'll be playing a
different animation. And we'll have to
do things like make the enemy face the player
and the player, okay. So these two states
are not the same. All right, so we have
three enum states here. Now from the execute function, we should really keep setting the destination of
the nav agent to the player's position if are in the chase state right here. What we can do is
we can write if conditions to check in
which state we are. Okay. By the way, before that we actually have to create a
beatable of state. Let me read it over
here. Okay? And now from the excrete function, we can check in
which state we are. So first I'll check for idle, and then I'll check for chase. And finally I'll
check for circling. Okay, and we surely set the destination of the dam agent to the player's position. If we are in the chase state. Let me cut this and
pace it over here. By the way setting, the
more amount should be done from all the states.
I'll put it outside. If conditions only,
then we'll get a smooth transition
to running and idle. I'll put this outside all
the conditions, okay? Next, if you are in the idle
state or circling state, if the player runs out
of the attacking range, then we have to go back
to the chase state and start chasing
the player again. Right? Regardless of
what state we are in, we have to always go
to the chase state if the player goes far
away from the enemy. Right from here we can
check if the distance between the player
and the enemy is greater than a certain
threshold, okay? So I'll use the vector
three distance function. And I'll check for
the distance between the position of the
player and the enemy. So we can get the player
from enemy target. Okay? And we also have to pass
the position of the enemy. So this will check
for the distance between the players and
the enemy's position. And if that is greater than the distance at which the
enemy is supposed to stand, then we have to go back
to the chase state, right when we are
checking this distance. I'll also add one to
the distance to stand. The reason is because if the enemy goes to
the chase state, while the player
makes small ppens, that will look
pretty unrealistic. For example, if the player is
in the range of the enemy, and then if the player
moves by a small amount, like 0.2 meters, In
that case the enemy should not go to
the chase state and move 0.2 meters, right? That will look, we, we need a threshold above which the
enemy should start chasing. We can actually turn this into a variable in case we want to change it from the inspector. I'll just call this a
just distance threshold. Let me set it to one
by default, okay? And if this condition is true, then we have to go to
the chase state, right? For that, I'll create
a new function over here called start
chase from here, state to chase, okay? In the future, we'll also have to do other things
from this function. That's the reason why I'm
making this a separate function instead of writing
it directly over here. So let me call this
function from here. Now, if the distance between the enemy and the player is greater than a
certain threshold, then we'll go to
the chase state. And chase the player, okay? Next. While we are
in the chase state, if the distance becomes less than equal to the
distance to stand, then you have to go to
the combat idle state. What I'll do is every time
we are in the chase state, I check if the
distance to the player is distance to stand. So let me just copy
this condition. Okay? And in this case we should check if the
distance is less than or equal to the distance is stand by the way to the
distance to stand value. I'll also add a small float
value, like points three. The reason for this is
because distance to stand is actually the stopping distance
of the nav agent, right? The navient will stop at the
distance to stand distance. But when we calculate
distance like this, there is a chance that
it can be a little above than the distance that was
calculated by the nav agent. Just to be safe, we can
add a small value so that this condition will
be true before the nav agent stops, okay? So if this is true, we have to go to the idle state, okay? So from here I'll call a
function called start idle. And then I'll just return because we don't want
to set the destination of the navigant again. Okay, So let's go ahead and create the start
idle function. All right, from the Unction. First of the state
to combat stated. Then while we are in
the combat idle state, we have to play a
different idle animation. Where the character has
his guard up right. In the combat idle state, the character does know
that he is in combat. We do need a different
animation for that. We can simply use our
normal idle animation. So let me go to Miximo and find an animation
for combat idol. All right, so this is the
animation that I want to use. You can find it if you
search for Swat idol. Okay, I have already
downloaded this animation, so let me go ahead and
import this into Unity. So this will be under combat. All right, so we have
to rig this animation. First, let me change
the animation type to human Eid and I'll tell
a copy from definition. And the will be
Erica Archer, Avada. Okay, now we should be able to preview the
animation on the player. All right, this is the title animation
that you want to use while we are in combat. You want this animation
to loop, right? I'll turn on loop
time and loop pose. Then I'll also change all
the based upon to original. I'll check back into
pose for all the root transforms because we have
a loop match for all of it. Okay, so let me go
ahead and hit Apply. Now we can add this animation into our animator controller. Let me take the animator here, I'll rack and drop
the idle state. Let me just call this
combat idle shot. We have to make a transition from locomotion to combat idle. We also have to do the reverse. Let me make one back
to the locomotion. Okay, so we switch to the
combat title animation. Then we set a parameter
from the code here. I'll create a Bolling parameter. I'll just name this something
called combat mode. And if that is true, then we have to go to the
combat title state. Okay, and by the way, we have to make sure
to turn off has exit time because when
the combat mode is true, we don't want to wait for
the locomotion to complete. We want to switch to the
combat title immediately. Okay, next let me set off the transition
back to the locomotion. And here also I'll turn
off the Has exit time. And in this case the condition will be
combat mode equal to fats. Right? When combat
mode is false, we have to go back
to the locomotion. Okay, so we have set up
the combat idle animation, but we need to make a
small improvement to this. Just adding the animation like
this will have a problem. But I'll show you
the problem first and then we'll do
the improvement. All right, back in our code
when we start the idle state, we also have to set
combat mode to true. From here I'll get the
enemy's animator and I'll use the set bull function and set the combat mode
parameter to true. By the way, we also have to set this to
faults when we start chasing because we don't
want the player to be in the combat idle animation
while chasing, right? Okay, so this is
all we got to do. Let's code Unity and
try testing this. All right, so it works, but there was a small problem. The problem is while stopping to the combat
idol animation, the enemy character
is sliding for a small period of time, right? So let me show it to you again. Okay, so you can see
the enemy character was sliding for a
small period of time. This was the problem that
I was talking about. The problem is the transition
from the locomotion to combat idle state doesn't
look good, right? We could fix this by making
an improvement here. What I'll do is I'll
just delete this. Instead, I'll duplicate
the locomotion and I'll rename it
to combat title. Okay, so this is actually
a blunt try, right? Our locomotion was bluntry. But here what we want
to do is we want to replace the first animation with our combat idle animation. Okay? So yeah, we have
to actually dragon drop the clip in
case of bluntries. Okay, So this is pretty similar to our
locomotion plenary. The only difference is
the first animation is the combat idol animation and not our normal idle animation. Okay, so this will help
us to make the transition between the locomotion and combat idol animation smoother. All right, from here we can go ahead and make the same transitions
that we made before. The condition will be
combat mode equal to true, and we have to turn
off the exit time. Then I'll also make a transition
back to the locomotion, and this time the condition will be combat mode
equal to false. Here also we have to
turn off the exit time. Okay, so now the transition
between the locomotion and combat title will
be a lot smoother. The enemy character
should no longer slide. Let's go ahead and test that. Okay, so this is better, but there is actually
a little bit of sliding in between few frames. We could fix that by moving
the code for setting the move amount into the update function of
the enemy controller. This shoes only
have it over here. It won't be executed during the frame in which
the return is called. Okay, so we could move it into the update function of the enemy controller so that
it will always be called. Here we can access directly, You don't have to
call enemy dot. Okay, so let's try
testing this now. All right, now the transition to the combat idol
looks much better. Okay, so now we have implemented combat idol and the chase state in the combat movement
state itself. Next we have to implement
the circling state. I'll be doing that
in the next video. I think we have covered
enough for a single video. Thanks for watching and I'll
see you in the next one.
18. Combat Movement || - Circling around the Player: In the previous
video, we started the implementation of
the combat Mopen state. In this video we'll
continue our work on that and we'll make the enemy circle
around the player randomly when he's in
the combat Mopen state. All right, let's start the
implementation of that. In the previous video we did
define the circling state, but while we are in
the circling state, we are not doing
anything right now. Okay, the way that we won't implement this is
the enemy should randomly circle around the
player while we are in the combat movement
state, right? So implement that. What I'll do is while we are in
the idle state, I'll set a timer, and once
that timer becomes zero, I'll switch to the
circling state. Okay, so that's how we're
going to implement this. First, let me create
a float variable for the timer, all right? We consider it to
zero by default. Then when we start
the idle state, we consider the timer to some value like
three or 4 seconds. If you want to make
it more interesting, we can even set it as a
random value between a range. That is what I'm going
to do over here. I'll create a new serialized
field patable of type two. I'll call this for
an idle time range. Okay, let me just give
it a default value. I'll just set it
to 2.5 By default, the idle time should be a
random value, 2-5 from here. To set the value of the timer, I'll call the random
dot range function. For the main value, I'll
pass idle time range x, and for max value, I'll
pass idle time range y. Our rights and all the
value of the timer will be a random value between
these two ranges. Next, we have to decrease the value of the timer from the execute function. From here I'll check if the
timer is greater than zero. If that's the case, I'll
go ahead and determine the value of the timer
by time delta time, Since the execute function
is called once every frame. What we're doing here is
reducing the value of them by the time that is
past every frame, okay? So this is just a simple way of creating timers ingenuity. Once we set a value for
the timer from here, it'll keep decreasing until
it becomes zero, okay? Now, from the ideal state, if the timer's value becomes
less than or equal to zero, that means the time that we set for the ideal state is over now. And now we can go to the
circling state, okay? So from here to make the
circling state even more random, what I'll do is once the time of the ideal
state is complete, I'll go to the circling state
50 percentage of the time, and in the other 50
percentage of the time, I stay in the ideal
state itself. Okay. When we are here, there is
a 50 percentage of chance. We'll go to the circling
state or the ideal state. To achieve that, what I'll do is I'll use the
random range function to generate an integer
value 0-1 Okay? So the value returned by this function will
either be zero or one. It will not return
decimal values 0-1 It'll either
return zero or one. Okay? That is because we are passing integer
values as the parameter. If you were passing
float values, then this function would return decimal values
between the ranges. But in our case, since
we're going for 50, 50 chance, we just want to generate a value that
is either zero or one. Okay, and here you might be confused by why I am
passing two over here, even though I want an integer
value 0-1 The reason for that is because in the integer version of the
random range function, the max value that we
pass is exclusive. Okay? So here you can see that the min value is inclusive and the max value is exclusive. Okay, But in case of the float version of
the rando dot r function, both the min and max
values are inclusive. All right, that's weird thing about the rando dot
range function, so you have to be
careful while using it. In our case, we're using the integer version and
we want an integer value 0-1 Since you're looking
for 50% of chance, we can just check if the value
returned by this is zero. Since this function
either turn zero or one, this means this condition
will be executed. 50 percentage of time, right? 50 percentage of the time. We'll stay in the
idle state itself. Let me call the start
idle function again. Then in the other 50
percentage of the time, we have to go to the
circling state and start circling around
the player, right? To start the circling state, I'll call start
circling function, but we haven't created that yet. Let's go ahead and create that. Okay, so from this function, first I'll set the state to AI combat state
start circling. And then the circling should be done for a
certain period of time, so we can use our
timer for that, just like we used
in the idle state. Okay, so here I'll define another variable for
the circling time range. Okay, let me define
a vector two and I'll call this
circling time range. The value of this will be, let's say 3.5 or maybe 3.6 just to make
it a little higher. Obviously you can change
these values from the inspector and use
the value that you like. Okay, now from the
start cycling function, we can generate a value between
our circling time range. So let me just copy
this function. I'll change it from idle time range to
circling time range. Okay, so next we have to determine the direction in which we'll be circling. The enemy can either circle
towards the left or right. Okay, so what we can
do is 50% at the time, we can make the enemy
circle to the right and the other 50 we can make
him circle towards left. Okay, so here again I'll use
a random range function. I'll be using the integer
version of this function, and I'll generate
an integer value 0-1 If this is equal to zero, then we can circle towards left, and otherwise we can
circle towards right. Okay, what I'll do is I'll create an
integer variable here. I'll call this
circling direction. Let me set it to
one by the forward, for the left direction. I'll set the value
to positive one. And I'll set the value
to negative one for the right direction, okay? 50% of the times the value of the circling
direction to one and the other 50 begins to
minus one, Okay? So let me show the
value returned by this conditional operator in the circling direction variable. All right, circling direction
will also be random. Next we need to find the animation to play
while we're circling. We can't just use the
normal locking animation, that won't look good. Let's go ahead and find a good animation
for that from Ximo. This is the animation
that I want to use. You can go to Miximo
and search for Sword. You'll find this animation. We need this animation in both
direction, left and right. This one is left, but we also want the one in
the right direction. Okay, this one is right. So go ahead and download
these two animations. I have already done
that. Now let's go ahead and import those two
animations into Unity. So let me just drag
and drop it in here. Okay, these are the animations. So we have to go ahead
and make it humanoid. All right, and now
we should be able to preview the animation
on our player. So this is how the
animation will look like. Okay, while you're here, let's turn on loop
time and loopholes because we want this
animation to loop. For this animation, it's really
important that we change the root transform
rotation to original. Because for this animation, the character is not
moving in the direction in which she's facing right. Instead she's moving towards the side of the direction
in which she's facing. It's really important
to make the original. For most of our animations, we used original as based upon. Tried, but in case
you script this part, it might be okay for
other animations, but it can cause issues
with this animation. Okay, I'll also change the transform position,
exit riginal. I'll check back into
post for rotation and position y because they
are having a loop match. Okay, so let me go ahead and hit Apply and they also have to do the same for
the other animation. So first I'll check loop
time and loopholes. I'll make the based
upon original and then I'll check
back into post for the rotation and position y. Okay, so now let's go ahead and add these animations
to our animator. What I'll do is, since we have two animations that should be played based on the direction
in which we are going, I'll put these two
animations in aplentry. From here I'll create
a new plentary. I'll call this one circling. Okay? In this splintere
we'll have two motions. The parameter that
we're going to use for this splintere is going to
be a circling direction. Okay? In the parameters, I'll add a new integer variable called circling direction, and that is the one you
want to use over here. Okay, So I guess we have to make the parameter a float only then it'll be shown in the parameter drop down
of the blend tree. So let me just let this and add a float parameter called
circling direction. Now it should be shown
in the drop down, okay? Even though we're using
a float parameter here, the value that we
will be setting to it will be an integer, right? That won't be a problem. Okay, Next, the threshold of the circling direction should be negative one
and positive one. We don't want to use the
automated threshold checkbox. If you turn it off,
we'll be able to enter the threshold nally
threshold will be negative 1.1 when the value of the circling
direction is negative one. I want to play the
right animation. We can't copy the X file, We have to copy the
clip while we're using Glentary. Let me
go ahead and do that. When it's negative one,
we'll play animation. When it's positive one,
we'll play left animation. Okay, now we can try
playing the animation. Negative one, the character should walk towards his right. When it's a positive one, the character should
walk towards his left. Okay, that is working. Next, circling, we
have a transition from the combat idle state to the circling state in
the animator, right? I'll create a bulling parameter
for that called circling. And then make a transition from combat idle to circling while the circling
parameter is true. Okay, so here in the condition we can check if the
circling rapid is true and we have to turn off the has exit time because we don't
want to wait for the combat title to complete. Okay, next we have to make a transition back from circling. I'll create a transition
back to the combat title. This will happen when the
circling becomes false. Okay, So this is how our
animator will be set up. Now from our code, we just have to set
these parameters. All right, from the
start cycling function. First I'll set the
circling parameter. So let me call the set bold
function of the animator and set the value of
circling to, okay? And then we also have to
set the circling direction. Since the circling direction
is a float parameter, we have to call the
set float function. We can set the cling to the circling direction
that we found over here. Okay, By the way, when we start the idol
or the chase state, we have to make sure to set the circling parameter
back to falls. So let's go ahead and do that. Okay, so it falls when
we start the chase or idle state when we start circling is setting the primer and we are playing
the correct animations. So now the only thing
that's left to do is actually to move
the enemy character around the player in a circle. Okay, so we have to do that
from the execute function. If the state is circling, right, to rotate the enemy
around the player, we can use the rotate around
function of the transform. In this function,
first we have to pass the position around
which we should rotate. We should rotate around
the player, right? Let me pass the position of the player as the
first parameter. Okay, if we can get that from the target property
of the enemy. All right, as the
second parameter, we have to pass the
axis of rotation. The axis will be the y axis. We can just pass
vector three up. Then we have to pass the angle by which we should
rotate every frame. Okay? We can consider this as the speed in which the
enemy should circle. We can just create a
variable for that. I'll just call that circling speed and also it to
20 by fold, okay? Now from here we can pass the circling speed
as a third parameter, but we also have to multiply it with a circling
direction, right? Only then we'll circle in
different directions based on the value of this
variable, okay? Finally, we also have to
multiply this time delta m to make it frame right
independent, okay? So this will rotate the
enemy around the player. Finally, once the time that we set for the circling
state is over, we have to go back
to the idle state. Right from here I'll check if the timer is less
than O equal to zero. If that's the case,
I'll go ahead and call the idle function to
start the idle state. I'll also return from this
function because we don't want to execute this line, okay? So this is all we have
to do to make our enemy rotate around the
player to score Unity and try testing this, okay? So the enemy should circle
around the player after staying in the idle state
for some time. All right? So you can see that the enemy started circling
around the player. And he goes back to
the idle state again. Again, he starts circling, okay, yeah, that
is working fine. The enemy will stay in
the combat idle state and then circle around the
player at random times. Now we can try adding another enemy to the scene
and see how that looks. Okay, let me just
move it a bit towards the left and now we
can see how it looks. Okay. Yeah, you can see both of them are
circling and by the way, you can see that
when they collide, they'll automatically move out
of the way since they have a Nab meshed agent on them,
but it's not perfect. You could see that
they were stuck for a few seconds before
getting out of the way. Yeah, it's not
perfect, but we'll improve that in the future. For now, I think
this looks okay. Both our enemies are
circling around us, similar to how they do in the combat systems of games
like Assassin Street. All right, so I'll
stop the video here. Thanks a lot for watching and I'll see you in the next video.
19. Improving Circling: In the previous video, we made the enemy circle around
the player like this. But our implementation
has few issues. Right now we'll be fixing those issues and improving our implementation
in this video. First, let me explain
the issues to you. The first issue that we have is the enemies will try to walk
through obstacles like this. Okay? They won't be able to pass through the obstacle because they have character controller, but still they'll keep trying to walk through the obstacle. The reason for this is
because we're using the transform rotate around function for moving
the enemy in a circle. Right? All this function will do is try to move
the enemy in a circle. It won't care if there are
any obstacles in the way. Okay, that is an issue
that we need to fix. This issue will also
happen when there's another enemy in the path
of a circling enemy. All right, in that case, since both enemy
have Nabs agents, they'll try to push each other while an enemy is being pushed. It'll play weird jittery
animations like this. Okay, We need to fix these two problems in the
implementation of circling. The problem with our current
implementation is that we're using transform to trade round function to move
the enemy in a circle. And this function
won't care about the obstacles of other
enemies in our way. Instead of this, we can use the move function
of the Namgent. Yeah, here you can see the Nabhan has a
function called move. If we use this function for
moving the enemy in a circle, then it don't try to move
into obstacles because Nabhan will be aware
of that, right? The bosh will move if
the position to which we are trying to move is
inside the Nash, okay? We can use the move function of the nav agent to fix this issue, but when we use this function, we don't have an easy way to rotate around the
player like this. We'll have to write this
logic on our own. All right? We'll have to find a
vector that we can pass to the move function so that the nav agent
will move in a circle. Okay, let's write
the code for finding the vector that will make
our enemy move in a circle. For achieving this, we can use the same
logic that we used for rotating our camera around the player in our
third person set up. Okay, what we can
do is first we can find a vector between the player's position and
the enemy's position. And then we can
rotate that vector by multiplying it
with a coternion. Okay, so first we need to find the vector from
the player's position to the enemy's position. Okay, so let me call this something like
vector to target. And since you want the vector from the player's position
to the enemy's position, we have to subtract
the player's position from the enemy's position. Okay, so let me go
ahead and do that. All right, so the player will be in the
target of the enemy. So let's get its position. And this will give us the vector from the player's position
to the enemy's position. Okay, now we can rotate this vector by multiplying
it with the quaternion. Let me create a conin by
using the Ton oiler function. We want to rotate this
vector in the y axis, right? In the y parameter
of this function, we can pass the angle by
which you want to rotate. The angle will be circling speed into circling direction
into time delta. Okay, let me copy that
and base it over here. All right, and we can multiply this anion with our
vector to target. This will rotate this
vector by this angle. Okay? So let me show this in another variable called
rotated position. All right, so now we have the position to which
our enemy should move. But we can't pass this position directly to the move
function because the move function accepts
the offset. Okay? You can see here it accepts the offset which is the vector from the enemy's
current position to the rotated position, okay? We can easily get that by subtracting the vector target
from the rotated position. All right, this will make the enemy move in a circle
by using the Nab agent. All right, let me get rid of the transformed rotator around function. We don't
need that anymore. By the way, you might be wondering why I'm using
the move function here. Instead of using the set
destination function of the nave agent right here. We could also use the
set destination function and set the position directly instead of finding
the offset and all that. But the problem with using
the set destination function is that it won't
move the nerve agent if our new position
is very close to our current position, okay? And in our case, the
two positions will be very close because we are
multiplying it by time, Ta, time, every frame, belly move
the character a little bit, Okay, The destination
won't work in that case. By the way, we could
also try to find the destination when we start circling and find a position on the circle to which
the enemy should move. In that case, we don't
have to use time Ta time because it's only done
once, not every frame. But the problem with
that approach is the navigant will just move
the enemy in a straight path. Kate won't take a circular path. That's the reason
why we are using the move function instead
of that destination. All right, this will move
the enemy in a circle. Next, While moving the enemy, we also want the enemy to face towards the
player at all times. Right? That was
already handled by the transformed or rotate around function that we used earlier. But now since we're
using the move function, we'll have to handle
that manually. But it's pretty easy to achieve. We just have to make sure
that the enemy is rotated in the opposite direction of
the rotated position vector. Okay, I'll set the
enemy rotation to the negative of our
rotated position vector. So let me just use the conk rotation function to convert the rotated
position vector into a con. In here we have to pass the negative of the
rotated position. All right, This is
all you have to do to move the enemy in a circle
by using the nave agent. But if it test the
game right now, you'll see that the
enemy is still playing the strafing animation when
he's next to an obstacle. The reason for that is
because we are always playing the circling
animation when we are in the circling
state. That's right. Instead of doing this,
we should only play the circling animation if the
enemy is actually moving. Okay, if the enemy is not moving because
of some obstacle, in that case, we should not
play the circling animation. So we have to make
sure that the enemy is only playing the circling
animation when he's moving. Okay, to achieve that, what we can do is we can play the circling animation based on the velocity of the enemy. If the enemy did not move
in the current frame, the velocity will be zero. When it's zero, we don't have to play the
circling animation, but if the enemy actually
moved in the current frame, the velocity will be a
value greater than zero. And in that case we can play
the circling animation. Okay, we can play the circling animation based on the velocity of the enemy, just like we did for
walking animation. So here we're playing the
walking animation based on the move amount
parameter which we set as the velocity
of the nav agent. Right? We can do something similar for
the circling animation, but one main difference is that the circling animation
should only be played when the enemy is
moving to the side, right. We should not play the circling animation when we
are moving forward. For playing the
circling animation, we should consider the
part of the velocity to the left or right
side of the enemy. We should not consider
the forward component of the velocity, right, even here we're using the entire velocity for controlling the work
animation, right? But this is not really
the right way of doing it because for the
work animation, we should really consider the forward component
of the velocity. The reason why we had that
jittering animation while an agent was being pushed is because we are considering
the entire velocity, not just the forward component
of the velocity, okay? This solution is not enough. Instead Tiara, Tu is split the velocity into its forward
and sideward component. And then use the
forward component for the walking animation and the sideward component for the strafing or
circular animation. We can call it whatever we want. Okay, we'll be using a little bit of math
to achieve this. First, let me explain to you how we are going
to implement this. Let's say this is our enemy and he's facing in
this direction. This is forward vector. Let's say this is the velocity
in which he's moving. Okay, now we have to split this velocity into its forward and
sideward component. So that we can use
the forward component for playing the
walking animation, and the sideward component for playing the strafing animation. Okay, we can split the velocity vector into its forward and sideward
component by using trigonometry. If we say the angle between the forward vector and the
velocity vector is theta, then velocity
multiplied by cos theta will give us the
horizontal component of the velocity vector. The horizontal component is the component in the
forward direction. The vertical component will be velocity multiplied
by sine theta. Okay? The vertical component
is the sideward component. In this case, if a
velocity vector is V, then the forward component of the velocity will be cos theta. The sideward component of the velocity will
be B sine theta. Okay, this is how we're going to find the forward
and sideward component. All right, go ahead and
start implementing this. First we need to get the
velocity of the enemy. We can get it from
navigation do velocity. But it won't work for
the circling animations. Because for circling we're using navigation do
move function, right? When we're moving the
navigation like this, the velocity component
will be empty. This will only have a value when we're moving the naviation agent by using the set
destination function. Okay? So we can't get
the velocity like this. So we'll have to calculate
the velocity mannerly. What is velocity? Velocity is displacement by time, right? The velocity in this
current frame will be the displacement of the enemy in this current frame
divided by the time taken for the
current frame, okay? Velocity will be by dt, dx is the change in displacement and it is the delta time. Find the displacement of the
enemy in the current frame. If that's what I'll
do is I'll store the previous position of the enemy at the
end of the frame. Okay, here let me
create a vector three for saving the previous
position of the enemy. And then at the end of
the update function, I'll go ahead and save the position in the
previous position. Okay, so now we have the previous position and we
can find the displacement by subtracting the
previous position from the current position. Okay? So let me show this in available,
called delta position. Now we can find the velocity by dividing delta position
by delta time. Okay? So this is the velocity of the enemy in
the current frame. Okay, Next we have to split the velocity into their forward
and sideward component. Right? We know that the forward component of the
velocity will be V costa. That's actually an easier way of finding the value of V costa. We don't have to take the angle and find the case and all that. Instead we can do it by
taking the dot product between the velocity and the
forward vector of the my. Okay, So the value of this
will be equal to costa. And if you don't know why
it is, let me explain. The doc product between
two vectors a and B will be a, B, cosceta, right? If we take the dot
product between the velocity and
the forward vector, we will get cos theta. But since we know that
the forward vector is a normalized vector, meaning its length
will be one, right? Since we know it's
length will be one, we can replace by one
and we'll get cost. Okay? The dot product of velocity and the forward
vector will give us V Costa, let me show this invariable, I'll just call this
forward speed, and then we can set the
move amount past the value of forward speed divided by the max speed
of the navigant. Okay, now for the
walking animation, we will only consider the forward component
of the velocity. Now if an enemy gets
pushed toward the site, he won't play the forward
walking animation. Okay. By the way, to reduce the jitter that we get while playing the animation, we can also pass a
damping value while we set the parameter here. I can pass a dam time. As a third parameter, I just pass something like 0.2
When we pass the dam time, we also have to pass the data time to let me
go ahead and pass that. All right, so this will set the move amount more smoothly. Next we need to find the sight word component of the velocity for playing
the strafing animations. Sideward component
will be sine theta. To calculate that, we can
just find the angle between the forward vector and the velocity and then take
sine theta of the anger. Okay, we have to find the angle between the forward
vector and the velocity. For that I'll use vector three. Sine angle function will
give us the sine angle. Which means the value returned by this can be
positive or negative. We want that because based
on the value of this, we should determine
whether we should go to the left or to the right. Okay, So I'll use the signed
angle function and I'll find the angle between the forward vector and
the velocity vector. Okay, When using the
signed angle function, we also have to pass the axis around which we should
take the angle. That should be by axis. Let me pass vector
three up over here. Okay, let me stow this
innovable called angle. Now I can find the
sine of the angle. I'll use math of sine function. And for the angle I'll
pass the angle per. But one thing you have
to notice is that the sine function
takes the angle in radians, not in degrees. Okay, here the sine angle will return the
angle in degrees, which is what we're used to. But for the sine function, we'll have to pass
the angle in radians. We can easily convert the
angle from degrees to radiance by multiplying
it with math, degree to radian to, for short. Okay? This will give us
the value of sine theta. And we know that the
site word component will be velocity multiplied
by sine theta. But in this case, I don't want to multiply
it with the velocity, because Cinta will give
us the value between -1.1 right in the animator.
That is what we want. The circling direction
is a value between -1.1 We can simply go
ahead and use this value. We don't have to multiply
it with the velocity. Let me just throw this
innovaable called strafe speed. Then we can set it into
an animator parameter. I'll use animator
set float function. The parameter that we used for controlling strafing was
called circling direction. I'll just change it to speed because that name
makes more sense. In this case, let me just pass the value that we calculated
for the straight speed, then I'll also pass a dam time. All right, so this will make sure to smoothly
set the straight speed. By the way, let me change this from more amount
to forward speed. We'll change the name of the
parameter in the animator. Also, I just think that forward speed makes
more sense than mount, since we are only considering the forward component
of the velocity. Now we have to go to the
animator and make sure that we'll play the
strafing animation based on the value of
the straight speed. Okay, previously we were playing the safing animation
based on two parameters. A boling parameter
called circling and another flow parameter
called circling direction. Now instead of that, the
safing animation should be played entirely based on
the safe speed parameter. All right, to make
things simpler, what I'll do is I'll create a new blendree with
all the animations. It'll contain idle, walking, running animations, and also the left and right
strafe animations. Okay, so for that we'll need
a two dimensional blende. Let's go ahead and create that. I'll go ahead and add a
new state from Blendtre. I'll just call this
combat movement. All right, this blinry should play different animations
based on two parameters, forward speed and stave speed. Okay, So we'll have
to make this a two dimensional blendry instead
of a one dimensional blendry. If you click on this, we
can change the blend type. I'll change it to D
freeform directional. Now this will ask us to
specify two parameters. First of all, let me just rename the move amount parameter to forward speed because we
changed it from the code. Then let me also create another parameter
called straight speed. Okay, let me just put it right
below the forward speed. Our two rebrand, Trey
will work based on the value of forward
speed and straight speed. Here the first parameter
will be forward speed. And we can change the
second one to stave speed. Okay, now we can add
different motion fields here. The first one will be the
combat idle animation. That should be played when
everything is zero, right? Let me just drag the clip
into the first field. This will be played when both forward speed and
straight speed is zero. All right, next I need to find the walk
and run animations. They are in the
locomotion folder, let me just drag and rub them. Walk will be our
second animation. Walk will be played when the forward speed
is 0.2 or more. Right, actually let me
switch the position of forward and stray
speed because it feels weird to have the
forward speed in the x axis. I want the straight speed in the x axis and forward
speed in the y. So I'll just change the x to straight speed and
y to forward speed. Okay, here the value
of x will be zero. And the value of y should be 0.2 because that's the
threshold for playing the walking animation, right? If you remember, that's how we implemented the
locomotion planter. Okay, so next let's add
the running animation. All right, so in
case of running, the straight speed will still be zero and the forward
speed will be one. Next, let me add two more fields for strafing left and strafing
right animations. Okay, let me go ahead and attach the strafing
animations to those field. First I'll attach the
strafing left animation and then I'll attach the
strafing right animation. Okay, for both strafing animations forward
speed will be zero. Then for the strafing
left animation, the straight speed
will be minus one. For the strafing
right animation, the strap speed will be one. Okay? This is how our blend
tree will be created. Let me show you how it works
by playing the blend tree. Okay, When both these
variables are zero, we are playing the
idle animation. But if I mow it in the
forward direction, you can see that the
player starts walking. All right, and then if we
move towards the left, she'll play the strafing
left animation. And if we move it
towards the right, she'll play the right animation. Okay, by the way, if we have values in both the straight speed
and the forward speed, then it'll play a blend of the strife and the
walk animation. But they don't look really good. We're not going to
use them anyway. In our case the
player will either be strafing or walking forward. The case where we might
get a mixed value is when a character is pushed
by another character. In that case, the
character might go in a diagonal direction.
It might play a blend. But in most normal cases, we'll either play the
strafing animation, the walking animation, or
the running animation. Okay, using a blend
tree like this is much easier than creating two dibles and transitioning
to the circling blind tree. Now we don't need these
two plant trees anymore. We've combined everything
to combat movement, right? We have to make transitions to and from the combat
movement tree. Should go to the
combat movement when combat mode is true. All right. Should go back to the locomotion when the combat mode is false. Okay, This is what
we have to do. And by the way, we can
go ahead and delete the circling and circling
direction parameters because they're
not used anymore. And we can also
remove the lines for setting the circling and
circling direction parameters. Okay, let me go ahead
and remove all those. All right, finally
one thing we have to do is that since we renamed our parameter from
mount to forward speed, we should also make
sure to change that in the player script here, there are two places where we're setting the mount parameter. Since we changed its
name to forward speed, we'll also have to
change it from here. Okay, let me just copy this
and place it over here also. All right, so this is
all you got to do. So let's go ahead
and test the game. Now when an enemy is
being pushed to the side, he's playing the strafing
animation like before. The enemy is not playing the forward walking animation while being pushed to the side. We don't have those
weird structures of animations before. By the way, pushing the enemy like this is not a
desired behavior. We'll improve this in the
future and make the enemy stop circling instead of pushing
other enemies to the side. But when we have lots of
enemies in the scene, there will be little
pushes here and there. It will be good to play the
correct animation based on the direction in which the
enemy is being pushed to, instead of always playing the
forward working animation. All right, next let me test how the enemies circle
around static obstacles. Let me go and stand near this cube and the enemy
should circle around it. Here you can see a beard issue. You can see that
the enemy is not circling past this line. What's happening is when
the enemy passes that line, the pash agent is pulling
the enemy back towards it. Okay, Now the reason
for this is because the destination of
our nappih agent is still set to the
position of our player. When we try to
move our nap agent while the destination is set, then the naps agent
will try to make sure that it's in a position where
it can view the target. Okay, This issue is cost because the
destination of the enemy, staph agent is still set to the players position
while we are circling. You're setting it from chase, but it will remain the
same while we are in the circling state, right? We have to reset the destination when we
start the circling state. To do that from the
start circling state, we can call enemy
navigant reset path. So this will reset the
destination of the enemy. So this should solve issue. So let's go to DM,
tritting this, okay? Now you can see that
the player is able to circle past that point. And we don't have
the startling issue that the Nab Mach
was causing earlier. All right, now the
player is circling fine. When he reaches the obstircle, he'll just move back so that he can complete the circling. Okay, by the way,
before testing this, I increase the circling time so that I can show
you the issue easy, just in case if you're wondering why the enemy is
circling nonstop. That's the reason I just
increased for testing it. Yeah, we have improved
circling of enemies. In this video, we have fixed some edge cases
that we had in circling. I'll drop the video here. Thanks for watching and I'll
see you in the next video.
20. Attacking the Player One by One: In this video will
make the enemy attack the player right. Now the enemy will chase the player and
circle around him. Next we have to make the
enemy attack the player. In three flow combat systems, all the enemies
should not attack the player at the
same time, right? If they all attack
at the same time, then the player won't be able
to fight multiple enemies. Instead of that, the
enemy should only attack the player one at a time, okay? So while implementing attacks, we can't just start attacking from the combat movement state. If we do that, then we won't
be able to make sure that only one enemy is attacking
at a time, right? Instead what we'll do is
we'll create a script called Enemy Manager to manage all the enemies that are
in the player's range. Then we'll pick one of the enemies and make
him attack the player. All right, let's go
ahead and implement this inside scripts
inside the enemy folder. I'll create a new script
called Enemy Manager. The script will be
responsible for managing all the enemies that are
in the players range here. First created list of enemy control and I'll call
this Enemies in range. Okay. Then we need to create functions to add and remove
enemies from this list. First mated public function
called add enemy in range. This function will take the
enemy that we should add. Okay, from the function. I'll go ahead and add the enemy to the
enemy syn range list. All right, just to make sure that we don't
add an enemy twice, I'll make sure that
the enemy range list doesn't already contain the enemy that we're
trying to add. Okay? So contains
function to check that we are only added if
the list doesn't already contain the enemy
that we're trying to add. Okay, so next, a great function for removing an enemy from the enemies in range list. And from the
function we can just go ahead and remove the
enemy from the list. All right, so next
we have to call the add enemy range function when the player is in
the enemy's range. And we have to remove the enemy when the player goes out
of the enemy's range. Okay, so we can do that from
the mission sensor script. So this is a script from
which we are detecting the player right from here. When we detect the player, we can also add the enemy
to the enemy in range list. So let me just add
a brace over here. I add another line for adding the enemy to the
enemy in range list. But we don't have a reference to the enemy manager yet, right? If you want to, can create
a sateliz field variable to get a reference to
the enemy manager class. But since enemy is a manager that will only be
created once in the scene, there's actually a
better way of getting the reference to
the enemy manager. We can use the singleton
design pattern for this. For that, all you
have to do is create a public static instance of the enemy manager and then use that instance
to access it. Okay, I'll create a
public static instance. I'll call this for short. Let me make it a property
with a private stor so that we won't be able to set its value from
outside this class. Then we can initialize it
from the awake function. Okay, from the wake I'll sit. The value of I to this, this war will give
us the reference of the class we are in. Okay. Creating a static
reference like this will only work if this class will only be created once in a scene. All right, so keep in
mind that you can't use this technique for other
classes like enemy controller, for which we need to
create multiple instances. All right, now since we
have a static reference, we can go ahead and use that
from our vision and script. From here we can just type enemy manager to get the
instance of the enemy manager. Now we can go ahead and
call the ad enemy in range function and add this enemy to the
enemies in range list. Okay, Next, we should
also remove the enemy from the enemy in range list when the player
goes out of the enemy's range. All right, let me just
add braces here and I add another line for removing the enemy from the
enemies in range list. Okay, I'll call Remove Enemy in range function and
pass the enemy. That's all I have to do for tracking the enemies in range. For testing this, we can
make this enemies in range list public so that we can see its values
from the inspector. We can change it back to
private once with the testing. Okay, so let's test and
see if it's working. So let me go to Unity. First we have to
attach the enemy manager script to a game object. Here, I'll just create
a new game object called Enemy Manager. All right, let me just
reset its position to zero. And I'll go ahead and add
the enemy manager script. Okay, now when you run
the game by default, there shouldn't
be any enemies in the enemies in range list. But if we go close to an enemy, you can see that
they are added to the enemies in
range list, right? Then if I run far away
from those enemies, they'll be removed from
the enemies in range list. All right, so that's working. Let me just change it
back to rip it since we should not be
able to addit this from outside the
enemy manager class. All right, now we are tracking the enemies that are
in the range of the player. Next, we should set up one of the tracked enemy and make
him attack the player. All right, in our combat system, we want the enemy to attack
the player one by one. After each attack,
I want to wait for a few seconds before
performing the other attack. This will give time for
the player to fight back and it'll make sure not to overwhelm the player
with attacks. Okay, so we need to wait for a few seconds between
attacks to implement this, what I'll do is I'll create a variable called
not attacking timer. All right. This timer
will be updated when none of the enemies
are attacking the player. Once this timer reaches
a threaten threshold, we can select an enemy and make him attack the player. Okay? Once the attack is over, we'll update the timer again. Once it reaches the threshold, we'll select another enemy and make him attack the player. That's how we're
going to implement. This will be done from
the update function. Let me go ahead and overwrite
the update function. And from here we
should update the not attacking timer if none of
the enemies are attacking. Right, So how can check if none of the enemies
are attacking the player? For that, we can use the any function and check if any of the enemies are
attacking the player. And then we can
just negate it to check if none of the enemies
are attacking the player. Okay, let me go ahead and use the NE function on
the enemy's range list. To use the NE function, we have to import
the link namespace. All right, in this
function we have to check any of the enemy in the enemy's and range
list is currently attacking the player, right? To check if the
enemy is attacking, we can just check if they
are in the attack state. Okay, we haven't implemented
the attack state yet. We only have the idle and
combatant state right now. Let's also go ahead and create the attack state in
the scripts folder. I'll go ahead and create
the attack state. And by the way, to keep
the states organized, we can create folder called states inside the enemy folder. We can put all our states in the Okay, now let me open up
the attack state script. All right, so this is going to be a state
of enemy controller, so we'll be doing the implementation of
the attack state later. For now, we just need class to check if the enemy is
currently attacking. Okay, in the enemy controller, we also have to add the state
to our state dictionary. So first let me add it to
the enemy states enum, all right, And then we also have to add it
to the state dictionary. So let me go ahead and do that now from the
enemy manager script, we can check if any of the
enemies in the attack state. By the way, right
now we don't have any function for checking
the current state. So we can get the
current state from state machine, current state. But this is actually
the state class, right? We can't really compare
it with the enemy states. Um, okay, that won't work. To make the simple or we can
do in the enemy controller, we can create a public function
called is in state, okay? So this will take a state for checking if the current state of the state machine
is equal to, equal to the state that is
passed into this function. So we can turn the Enum
into a state class by passing it into
our state dictionary. All right, so in this way we can check if the current state is equal to the anim
state that was passed, and we can just return
the value of this, okay? By the way, this
function should return a buying because it's checking whether or not we are
in the state, okay? If we are in the state that
was passed the function, then this function
will return true, and otherwise it will
return false, okay? So now from here we can
go ahead and use the S in state function and
we can check if the enemy is in the
attack state, okay? So this will not true if any of the enemies are in the
attack state, okay? But what we want to check is if none of the enemies
are in the attack state, that is when we should
update the timer, right? So we can just negate this, and this will be equvalent of checking if none of the enemies
are in the attack state. Okay, so let me put
this in condition, and if none of the enemies
are in the attack state, we should go ahead and
update the attacking timer. Okay, so just go ahead and determine the
not attacking timer by time delta time,
the photoing this. Let me also check if the not attacking timer is greater than zero because I don't want to reduce the value of
this timer past zero. Okay, if none of the enemies
are in the attack state, we'll update the
attacking timer. If the attacking timer reaches
zero or a negative value, then it means it's time for us to select an enemy and
attack the player. Okay, so we should attack
the player from here. For that first we have
to select an enemy from the enemy's range
for attacking the player. Okay, So let's go ahead and
create a function for that. I'll create a function
that returns an enemy. And I'll call this function, select enemy for attack. Okay? And from this function
we can just choose an enemy from the
enemy's range list. If you want, you can just choose a random enemy from this list. But make things
more interesting, what I'll do is I'll choose
the enemy that was in the combat movement state for
the largest amount of time. Okay, if an enemy has been waiting in the combat
movement for a long time, then we'll give priority
to that enemy performing the attack, okay, to people. To do this first we
have to keep track of how long all the enemies have been staying in the
combat movement state. Okay, for this what I'll do is I'll create a new variable in
the enemy controller here. Let me just create
a float variable. I'll just call this
compact movement timer. I'll actually make it a property and let me set its
default value to zero. Okay, And now when we enter
the combat movement state, we can set the combat
movement time to zero, okay? And then from the
execute function of the combat womment state, we can increment
the combat woman. So let me go ahead and do that. Okay, and finally, when
we exit the state, we can go ahead and
send it back to zero. All right, now we
are keeping track of the time for which the enemy is staying in
the combat movement state. We can go ahead and use
that here and find out the enemy that has
been waiting in the combat moment
state for the longest. Okay, for this what
I'll do is first I'll sort the enemies in
range list based on the value of the
combat movement timer. To sort a list, we can
use the auto by function. In this case, we want to sort it in the descending
order, right? The enemy with the highest
combat movement time should be the one
that we select. Okay, I'll go ahead and use the order by
descending function. And we have to order this based on the value of the
combat movement timer. Okay, let me pass that. After sorting the list, we can just return the
first element from it. Okay, so let me just go
ahead and return this. This will give us the enemy with the highest value in the combat
movement time available. Which means it will give us the enemy that has been waiting for the longest amount of
time in the combatant state. All right, by the way, one thing I want
to mention is that this is not the most
efficient way of getting the enemy with the highest compat popen timer
in terms of performance. Okay, simply writing
a for loop and using a variable to keep track of the maximum compat popen timer
would be more efficient. But I'm just going
to use this because this only requires
a single line. It doesn't make a big difference unless you have lots of
enemies in your game. Okay, so if you're like making a game with thousands
of zombies in it, then you probably don't want
to sort the enemy's list. Instead, you should use
a single far loop and find the enemy with the
largest combat moment timer. Okay, so just keep that in mind. Don't use this approach
if you're planning to have thousands of
enemies in your slee. All right, so let's go ahead and call this function from here. And I'll show the returned enemy inavailable called
attacking enemy. Okay. Then we have to make this enemy
attack the player right? For that we can this change
the state of the enemy to attack state. Okay. And finally, we should set
the not attacking time available to some threshold so that we won't attack the
player again immediately. Okay, So we can just set it to two or 3 seconds to
make it seem natural, we can actually use a
random value, Okay? A random value 1-3
will work fine. By the way, let's just make this utilized field
variable so that we can wreak it from the instructor
without changing the code. Here, I'll go ahead and create a vector two variable called
time range between attacks. Okay, let me get a default
value of 1.4 All right, now we can go ahead and use that in the random
range function. All right, now we're picking an enemy and changing state to attack for
attacking the player. But right now we haven't
implemented the attack state, We need to implement that next. But right now we
have another here, it says that we already have another definition
for the attack state. That's because in our
male fighter script we have enum called
attack state. What we can do is
we can rename this to Attack States instead
of attack state. So I'll just use control R R. And I'll rename this
to Attack States. Okay, But that did not
rename any of the instances, because these instances
are still referring to the attack state class
that you created over here. What we can do is we
can change everything. I'll use control and search
for attack state, okay? And then we can replace
all the instances of attack state by attack states. Okay? So we can go
ahead and click on the replace button that will
replace all the occurrences. But one thing to note is that it will also change the attack states that we have over here. Here, we only need
one is all right, now all the others
should be fixed, okay? So now we can start doing
the implementation of our attack state
class from here. First we'll go ahead and
override the enter function. And from the enter function at cashptance to the
enemy controller, just like we do from
all the other states. All right, next, the first thing that the
enemy has to do from the attack state is to
get close to the target so that he can perform
the attack, okay? While the enemy is in the
combat movement state, he'll be standing at
three meter distance from the player, right? But to perform an attack, the enemy needs to come
closer to the player. What we can do is we can change the stopping distance
to something smaller, like 1 meter from
the attack state. Let's go ahead and do that
from the Enter function. I go ahead and change the stopping distance
of the bash agent, and we can create a
stylized field variable for that so that it can be
changed from the inspector. Okay, I'll just call
this one attacks and I'll set its value
to one by deferred. All right, now let's set it
as the stopping distance. After changing the
stopping distance, we can keep setting
the destination of the naps agent from the execute function to make the enemy move
closer to the player. Let me go ahead and write
the execute function. From here I'll go ahead and set the destination
of the Naps agent to the position of the player. We can get the player's position from the target
property of the enemy. All right, I forgot to call
the set destination function. Let me go ahead and
do that. This will make the enemy move closer to the player for
performing the attack. So next, once enemy is
in the attack distance, we can go ahead and
perform the attack, right? We can go ahead and calculate the distance between the player and the enemy by using the vector three
distance function. Just like it did from
the combat mint state. So let me just copy this if condition and paste it
here in the attack state. In this case, we should check if the distance between the
enemy and the player is less than or equal to the
attack distance, okay? They're just adding a small
value over here in case if the distance computed
by the Napa agent is a little bit higher, okay? If the enemy reached
the attacking range, then we should go ahead
and perform the attack. For that, we can go ahead
and create a function. I'll create Tine, because we want to wait
until the attack is over. Let me just call this attack. Okay. While the enemy is
performing the attack, the machine should no longer
move closer to the player. This line should not be
executed while the attack is being performed. Prevent this. What we can do is we can create a Bollin variable
called is attacking. Okay, at the start of the
courting attacking to true. At the end al falls at the
start of the execute function. If it attacking is true, then we'll just return and we won't execute any
of the code below. Okay, This will make sure that the enemy
won't keep chasing the player while
they're attacking Next. From here, we should actually make the enemy
perform the attack, right, since we made
our code modular. And since the code for performing the attack is in
the merely fighter script, which is common for both
player and the enemy, we can go ahead and reuse
this code for the enemy also. Okay, so to perform the attack, all you have to do is call
the tried to attack function. Right now we're already calling it for making player
perform the attack. But we can do the same
for the enemy also. Okay, from the attack state first we need a reference
to the male fighter. We can actually go ahead and cache reference from
the enemy controller, just like we do for the nav
agent animator and all that. Let me just duplicate
this line and create a property for the male
fighter component. I'll just call this
fighter for short. And then I'll go ahead and catch it from the
start function. Okay, now from the attack state, we can access the mere fighter
component of the enemy. From here, we can call
the trite to attack function to make the
enemy perform the attack. Okay, This will make the
enemy perform the attack. We should make the
code wait until the enemy completes
performing the attack. Right after that, we should
set the attacking defaults. How can we know when the
attack may be complete? If you look at the attack
cotine in the male fighter, you can see that once
the attack is complete, set the attack
state back to idle. Okay, we can check the value of the attack state to wait
until the attack is complete. But the at tax state right
now is a private variable. We can go ahead and make
it a public property with a private stor that it won't
be able to change its value, we'll be able to get its value. Okay? And by the way, since it's a standard to make the first letter of
property upper case, I'll just make the
upper case over here. All right, now from here for wait until
the attack is over, we can just say turn new wait until we can keep waiting until the value of
the attack state is idle. Okay, so let me get the attack state from
enemy to fighter. And we can wait until its
value becomes equal to idle. All right, this
will make it wait until the attack
is complete next. Since we have turned off the
root motion of the enemy, by default, we have
to make sure that it's turned on while
we perform the attack. Okay? Because we can have attacks that has
root motions in it. For example, we can
have an attack where the character jumps
and performs a swing. In such cases, we want to
use the root motion of the animation while the
attack is being performed. We have to enable
the root motion. We can go ahead and
do that. From here, I'll set the apply
root motion property of the animator to true
before we start the attack. Once the attack is over, I'll send it back to false. All right, this is all we have to do to
perform the attack. Now we can go ahead and call the attack routine if the
enemy is in the attack range. Let me go ahead and
call it from here. All right, this is all we have to do from
the attack state. Now we can go to Unity and attach the attack
state to our enemies. By the way, it's
time that we make our enemy a prefab so
that we don't have to make changes to all the instances of the
enemy in the game folder, I'll create a new
folder called prefabs, and we'll go ahead and turn
our enemy into a prefab. Okay, so let me just lead the other one and make the
duplicate of the enemy again. So now both the enemies
are a prefab. All right? The good thing about
making them a prefab is that when we want to
make a change to it, we can go inside the
prefab by pressing this button and make
the change from here. In this case, you want
to add the attack state, we can add it from here
inside the prefab. Now if you go back, it should be added to both
the instances of the enemy. All right, so it's really
important to create a prefab before you create multiple instances
of game chicks. Next, we also have to
set up things like attacks and the sword
of the enemy, right? For the enemy, I'll
just give two attacks. The first one will be, the
second one will be horizontal, just like our player by the way, you can go ahead and find different attack
animations for the enemy. But since this is just tutorial, I'm just going to
use the same attacks for player and the enemy. Okay, next we have to create
the sword for the enemy. This model already
comes with the sword. If I collapse of the thing, we have a game object
called Sword over here. But that's not actually,
for some reason, the naming of these
game objects are wrong. This is actually
the swarm object. Okay. But this is actually
a skinned mesh renderer, Which is a mesh that we use for body parts
of the character. So I don't want
to use the sword, instead I'll just use a
model with a normal mesh. So let me just disable
this for the enemy. I'll just use the same sword
that we used for the player. Okay, let me find
the player sword. We can actually go ahead
and click on it from here. Okay, by the way, you can go ahead and find the separate sword
for the enemy. But I don't want to
spend time on things like that because this
is just editorial. So let me just turn the
sword into a prefab, and I'm going to use the
same one for the enemy. So let me go inside
the enemy prefab. Let me find the right
hand of the enemy. Okay, I'll just keep expanding until I find the right hand. Here it is. Let me go ahead and attach a sword
into the enemy's right hand. All right, it is
positioned correctly. One important thing
that we have to do is we have to change
the layer from player hit box to enemy hit box because this is
a hip box of the enemy. Right? Next we also have to assign the sword into the Mealy fighter
script of the enemy. So let me go ahead and do that. All right, so now
we have to set up the Mealy fighter of the enemy so that you
can perform attacks. By the way, if you have
attacks like punches or kicks, then you also have to set
up spear colliders on the hands and feet of the enemy. Just like we did for
the player right here, The player has spear gliders
on all the hands and feet. But I'm just going to script
that step for the enemy because the enemy
has Swat attacks. Okay, so that's
all we have to do. So let's try testing this. Okay, looks like
we have an error, so let me see what's happening. It's in the enemy manager script that's happening
because there are no elements in the
enemies in range list. To prove that error, what we can do is if enemies range count
is equal to zero, then we can just return. And we don't have to execute the code for selecting
the enemy to attack. Okay, While we're here, let's also make sure that
the enemies in range list is initialized by default, okay? If it's not initialized, then we'll get a null reference exception
and we try to use it. So now we can try
testing this again. Okay, so now we have a
missing component exception. It's telling us that
there is no sphere Glider attached to
the left hand object. We haven't attached spear
colliders to the enemy because the enemy doesn't have attacks
like punches or kicks, but we still have
this exception. Let's go to the line that
is causing the error, okay? It's the disabled
hit boxes function here by just
disabling everything, even if that collider
doesn't exist. From here begins to a simpler check to prevent
that from happening, I'll check the left
hand collider is nautical only then I'll
execute this line. Okay? So I can do that
for all the gliders, even for the Swat glider. Okay, so let me also do
it for the other gliders. Okay, so I've added to check for all the gliders and now we
shouldn't get that error. So let me go ahead and
test the game again. And this time we
don't have an errors, so let me go near the enemy. Okay, it looks like we have another reference exception.
Let's see what that is. It looks like we are trying
to perform an attack even before we set a
target for the enemy. Okay, we can debug
this issue by adding a break point and attaching
this to humanity. When you do that, the program will stop executing
at this line. From here, we can check
what's causing the error. In the debug mode,
we can how our any variable and we'll be able to see the value
of that variable. Okay, if you look at the
target of the enemy, you can see that it's null. That means we are going to the attack state even
before setting the target. All right, fix this issue. What we can do is
we can just set a small value for the not
attacking timer by default. Right now, since it'll be zero, we'll start choosing
an enemy for attack as soon as the player
enters the enemy range. Okay, so we don't
want that by default, we can set this to a small
value, like 2 seconds. All right, now let's
try testing this. Okay, so now that error is gone, and you can see that the enemy moved close to the
player and attacked him. Okay, the code for making the enemy attack
is working fine. But right now, the enemy keeps attacking the
player again and again. Right? Instead of this, we want the enemy to move
back after attacking, and we should give a chance for the other enemies to attack. Okay, same enemy should not
keep attacking the player. Instead, all the enemies in range should attack
him one by one. We'll be implementing that in the next video. I'll
hop the video here. I think we have covered
in for a single video. I'll thank watching and I'll
see you in the next on.
21. Retreating after Attack: Here in this video, we'll make the enemy retreat after performing the attack that it will give a chance
for other enemies to come and attack the player. In the previous video, we
implemented the attacks, but right now the enemy
keeps attacking the player. And the other enemies just keep circling without getting
a chance for the attack. Let's make our enemy retreat. After performing
the attack first, we need to find a
walking back animation for making the enemy retreat. This is the animation
that I'm going to use. You can just search
for back backward, walk sword and you'll
find this animation. All right, I've already
downloaded this. Let me go ahead and
import it to Unity. So my animations folder, I'll the backward walk
animation in here. Okay, so how to read this. So let me make it humanoid
copies from oval Erica Archer. Okay, now I should be able
to preview the animation. All right, I want
the salmation loop. I'll turn on loop
time and loopholes. I'll make the based upon original for all the
three root transforms. Since the root
transform rotation and position y is a loop match. I'll go and check bake
into force with them. Yeah, this is how the
animation looks like. Now we can go ahead and
add it to our animator. We could add this to our combat movement
plan tree itself. That way we don't have
to set any parameters, we just have to move
our player back. When we do that, the forward
speed will be negative. We can set up this plant tree in such a way that when the
forward speed is negative, we'll play the backward
walk animation. Okay, so I select the blunt tree and I'll
add a new motion field. Should perform the
backward walk animation when the x is zero and when y is a negative
value, right? The y here is the forward speed. In case you don't remember if the forward speed
is a negative value, then we can play the
backward walk animation. So let me go ahead and
drag this clip in here. Let's try playing this now. If I move the forward speed to -0.2 then you can see that the character
is working backwards. All right, so that
is working fine. Next we need to write the code for making
the enemy retreat. What I'll do is I'll
create a new state for this inside enemy, inside the state's folder. I'll go ahead and create
a new fee shops script. I'll call this retreat
after attack state. Okay, and let me open
it up in visual studio. I'll get rid of
the default code. This class will be a
state of the enemy. I'll make it inherit
state of enemy control. Okay, next I'll override the function of the
state from the function. I'll just catch a reference to the enemy so that we can use
it throughout the class. Okay, so next, still go ahead and override
the execute function. The actual logic of retreating will be
implemented in this function. From here, we just have to
move the enemy back, right? How can we get the
backward direction? Our forward direction is the direction
towards the player. We just have to take
the negative of that, and that'll give us the
backward direction. Okay, first I'll find a
direction towards the player, So we can get that
by subtracting the enemy's position from
the player's position. The player is inside
enemy target. To get the player's
position like this, I'll subtract the enemy's
position from it. Okay, this will give us a
vector towards the player. Let me just throw this
in Avatablerv target. This is the forward direction. We can just negate it to
get the backward direction, and we can move the enemy
in the backward direction. To move the enemy,
I'm going to use the navigant move function, because this will make sure that we won't move the enemy
through an obstacle. Okay, in here we have to pass the offset by
which we want to move. So we can get that by taking the negative of the vector to target and multiplying
this with the speed. Okay, The speed, I'll just create a srilized
field variable over here. Let me just call this
backward walk speed. I'll just add it
to something like 1.5 Pretty fault in here. We can multiply the
direction with the speed. We also have to multiply
it with time, tela time. All right? And by
the way over here, we only want the direction
of the spector, right? So we have to
normalize this next. While the enemy is walking back. I'd also make sure
that the enemy is always facing the
player, all right? Usually if we move
the enemy like this, he'll be facing the player. But in case the enemy got pushed around by
any other enemies, then it'll look weird when
the enemy keeps retreating without facing the
player in this state. I'll just make sure
that the enemy is facing the player
at all times. That we just have to rotate the enemy towards
the target, right? We can get the rotation
for that by calling coton rotation and passing
the vector to the target. Okay? So this is the rotation we want
to set for the enemy, for facing the player. But I don't want to
set it directly. Instead I want to
set it smoothly. What I'll do is I call
cotonion rotate towards. I'll pass the current rotation of the enemy as the rotation. And the two rotation will
be our target rotation. And we can pass a
rotation speed over here. So something like 500 should
be good for the speed. And we have to multiply it
with time delta time, okay? Finally, we have to store
the value of this back in transform rotation, okay? If you want, you could set
the rotation directly, but this will make it a bit
more smoother. All right? And also we have
to make sure that this vector doesn't
have any y component. Okay, just in case if you are in some kind
of slopy terrains, we don't want to rotate
the enemy vertically. The rotation should
really happen in the horizontal plane. I'll set the codon of this
vector to zero, okay? So this will make the
enemy retreat back Next, we should stop retreating
when the enemy reaches a certain distance from
the player, right? So we can retreat
until the enemy reaches the circling distance. Circling distance is 3 meters
from the player, right? So let me just copy this and create a new
variable over here. I'll call this one
distance to retreat. I'll keep this one at 3 meters. Once we are three metres
away from the player, we have to stop the state. And we have to go back to
the combat movement state. Right from here,
we have to check the distance between the
player and the enemy. Let me do that. If that distance is greater than or equal to
the distance to retreat, then we can go ahead and chain the state back
to the combat mid state. I'll call enemy chain state. And I'll pass the
Combat Open State. Okay, I'll also return from the execute function so that we won't execute
any of this again. Okay, we're done with the implementation of
the retreat state. Now we can go to the
enemy controller and we can add that
state over here. Okay, we also have to store
this in our state dictionary. So I'll just duplicate
this and change it to retreat after
attack state. Okay, so now since we
have the retreat state, once we complete the attack, we can switch to the
retreating state. Right? So from here I'll
switch to the retreat state. When we exit the attack state, we have to make sure to reset the destination
of our nav agent, because we're setting the
destination from here, right from the exit function
of the attack state. I go ahead and reset the
destination of the nav agent. So that we can call enemy
navigant reset path. Okay, that's all we have to do. Let's go Unity and attach the retreating
state to our enemy. I'll do it from
inside the prefab. Okay, now the enemy should retreat after
attacking the player. So let me go ahead
and test that. Okay, yeah, after
attacking the player, you can see that the
enemy retreated. And it gave a chance for the other enemy to
come and attack. All right, yeah, the enemy will keep attacking
us one by 11. Thing I don't like here
is after the attack, there's a little
bit of jitter in the animation before going
to the retreat state. This is happening
because we are setting the animator parameters like forward speed and
straight speed and all, even while the enemy
is attacking, Okay? Attack is an action that uses an animation with root motion. In that case, we should not try to modify the
animation like this using our parameters
fixes or break into. If the animation
has root motion, then we can set the
delta position to zero, the velocity and
everything we set to this will be zero
in that case, okay? So from here I'll check if animator apply root
motion is true. If it's true I'll
just return zero for the delta position
and otherwise I'll return the difference of
the transform position and previous position. Okay, so this should fix that jittersue that
we have after the attack. So let's go ahead and test this. Okay, so now it looks much better. The enemy is retreating
after attack and they're taking turns
to hit the player. Okay, I can also try
attacking the enemy. Yeah, it's looking pretty. Okay, so I'll stop
the video here. Thanks for watching, and I'll
see you in the next video.
22. Performing Combos: A phone. In this video, we'll make our enemies perform combo attacks while
attacking the player. Right now, when
the enemy attacks, he's always performing
a single attack that doesn't look very good. So we can make our enemies
more interesting by making him switch between a single attack and different combos randomly. We'll look at how to
do that in this video. So I opened up my attack
state and deplement combos. We just have to call the try to attack function multiple times. Right? What I'll do is
in this attack function, I'll take an inter parameter
called combo count. I'll send it to one by default. After doing the first attack, we should call the tried
to attack function again and do a combo. If the compo count is
greater than one, right? We could do a four
loop from here. Four loop will go from
one to the compo count. It'll be less than compo
count, I is the index. All right, from the loop we can simply call the try to attack function of
the enemy fighter. Okay, But before calling this, we have to make sure that
the previous attack is over. We can do that by waiting until the attack state
reaches the cool down. All right, cool down is the state from which we
want to do the combo. Right, So let me copy this. Wait until coin, I'll
change it to cool down. Okay, so we'll wait until the attack goes
to the cool down state. And then we'll call the try
to attack function again. All right, now all you have to do is pass a combo count to this
attack function. And the enemy will perform different combos based
on the number passed. Okay, from here,
if you pass two, then the enemy will perform
a combo with two attacks. And if you pass three, he will perform a combo
with three attacks. All right, but we have to make sure that the number
of attacks that you pass over here is Leano equal to the number
of attacks that the enemy actually has here. We could try to random
range function and randomly select a combo con between zero and the number of attacks
that the enemy has. Okay, we need access to the
attacks list from here. What I'll do is I'll create
a property to expose that. Let me create a public
property down here. Okay, this has to be
attacked data tell, let me change that now. From here we can choose a
random number between zero and the number of attacks
that the enemy has called. Enemy fighter attacks do count to get the
count of the attack. Note that the second parameter in the random dot range
function is exclusive. We have to do a
plus one over here. All right, now the enemy should perform combo
attacks randomly. So it's core humanity.
And try to distinguish. All right, you can
see that the enemy is performing combos randomly. You could add lots of different
attacks for the enemy. And making perform
different combos. But for the purpose
of this course I'll just use these
two simple animations. Because I want to
make sure that I'm only using free resources
in this course. But you can go ahead and
get better animations and create good combos
for your enemies. You can always create
different combos for different types of
enemies and all that. All right, I'll start
the video here. Thanks a lot for watching and I'll see you the next video.
23. Counter Attacks: N. In this video we will
implement counterattacks. It's going to look like pi.
Let's start implementing it. First we need to find the animations for
the counterattack. I'll find some
animations for that. From Miximo counterattacks,
we need two animations, one for the attacker
and one for the victim. These are the two animations
that I'm going to use. You can just search
for assassination and you'll find these. By the way, these are
not the best animations for counterattacks. There are much
better animations in the asset store that looks
more like a counterattack. But I'm just going
to go with this because in this course, I really want to
use free assets so that you guys can follow along without spending
any extra money. I'm just going to go
with this animation. Go ahead and download
these two animations. I have already downloaded it. Let me go ahead and
import it into Unity. These are the two animations, Brutal Assassination and
Brutal Assassination Victim. So first we have to rig it. All right, so let
me do that now. We should be able to
preview the animation. Okay, in this animation, the player takes a
few steps before actually performing
the attack, right? But we don't want that
for our counterattack. We'll be countering
the enemy's attack while the enemy is
close to the player. We don't want the player
to move forward like this. I'll actually start somewhere
around here. Frame 21. Let me change the start
to 21 and hit Apply. We'll also have to
make that change for the victim's animation. Both these animations are synced and they have the
same number of frames. Let me also change this to 21. Now. Let me go back to the
assassination animation. Yeah, this is how it looks here. I'll chain the based upon of
the rotation to original, transform y to feet, transform exit to original. Actually, the original root
position is not correct. You can see that the root is over here and the
player is over here. We might have to keep this at the center of mass.
That will be better. Okay, here I'll
check baking pose for position because we have a loop match. And
I'll hit Apply. Okay, so next let's look at
the victim's animation here. I'll change root transform
rotation to original y, defeat and exit to original. The original root
transform is correct. In this case both
original and central mass doesn't make much difference. So we can choose
whatever we want. All right, let me hit Apply. We have set up both animations that we need for counterattack. Let's go ahead and add it
to our animator controller. We have to add it to
the our right layer. Let me just copy these two. All right, we have
to make a transition from the brutal assassination of the attacker back
to empty state. Since we want the player to go back to the idle state
after the attack. Here, let me just
change the name of this to something more simple
like counterattack. I'll change this one to
counterattack victim. Okay, in the case of
victims animation, we don't want to go back
to the empty state because once the player does
the counterattack on the victim, the
victim will die. And the victim should stay
in this animation like this. And should not go back to
the idle state, right? We don't have to make
a transition back to the empty state for the
victim's animation. All right, so now we have the animations for
the counterattack. Let's go ahead and
implement it first. Let me explain how we're
going to implement it. While an enemy is
attacking the player. There'll be a short window
in which the player can counter that attack
with M deadlier attack. All right, that's how we're
going to implement it. Let's go to the
compact controller. Stripped from here. When the player presses
the attack button, we have to check if any enemies are currently
attacking the player and if that attack is in a window in which the
player can counter. Right, If that two
conditions are true, then we have to perform the counterattack
instead of performing the normal attack
right from here. First we have to find if any of the enemies are
currently attacking the player. We have list of all the enemies in range
inside the enemy manager. From here, we can
create a function to get the enemy that's currently
attacking the player. I'll call this function
attacking enemy. From here, we can just return an enemy from the enemy range that is in the attacking state. Right? We can just use the first function to return the first enemy that is
in the attacking state. To check the state,
I'll state function of the enemy controller
and I'll check if the enemy is in the
attacking state, okay? So the first
function will return the first enemy that
satisfies this condition. But I don't want to use
the first function here, instead I want to
use first default. The reason is because if there are no enemies that
satisfies this condition, then the first function will
throw an exception, okay? So we don't want that
first default function, we'll just return
null if there are no enemies that satisfies
this condition. This is what you
want to use because we might not have an
attacking enemy at all times. All right, so let's go ahead and return this
from this function. All right, now back in
our combat controller, we can call enemy
manager instance, get attacking enemy function
to get the attacking enemy. I'll just stow this
in a variable. If the enemy is not
equal to order. If there is an attacking enemy, then we also have to
check if that attack is in a window at which
the player can counter. Right, How can you
define this window? There are multiple ways to do that in the attack
scriptable object. You could define a start and end time for the
counterattack window, just like we did for the
impact window. That's one way. But if we do that again,
we'll have to go through all the attacks and find the counterattack window
for each one of them. To keep things
simple, what I'll do is I'll make the wind
up state of the attack. I'll make the wind up state of the attack the window in
which it can be countered. Okay, while the enemy
is attacking us. And if the enemy's attack
is in the wind up state, then we'll be able to
counter that attack. Once the attack is
in the impact state, we should not be
able to counter it. Right? That look really weird. But during the wind up state, we should be able to
counter that attack. All right, this usually
works well for me. If you want, you can
go ahead and specify a counter window for each
attack that will also work. I'm just going to use the wind up state as the counter window. In the male fighter, I'll create a new property
called Is counter. Okay, so slur true if the attack state is in a window, that
can be countered. In our case, that window is the wind up of the
attack from here, we have to check if the attack
state is equal to wind up. All right, and we have
to check one more thing. We also have to check if the
combo count is zero, okay? The reason for this is because since our enemy can
perform combos, we should not be able to counter the second or third attacks
from the combo, right? We should only be able to
counter the first attack. That's the reason
why I'm adding this. All right, now back in
the combat controller, if there is an attacking enemy, then we can also check if that enemy is in a
counterable state. We have to get the
fighter from the enemy and then use the
counterable property. If both these
conditions are true, if the player is not currently performing any other action, then we can go ahead and
perform the counterattack. Right, To check if the player is not currently
in any other action, I'll just use the merely
fighter inaction function. All right, all these
conditions are true. We can perform the
counterattack, otherwise we can just
perform the normal attack. Okay, from here we have to
perform the counterattack. I'll create a function for that. In the male fighter, the function for performing
counterattacks will be similar to the play
it reaction function. We'll have to play an animation and we'll have to wait
for it to complete. And all that what I'll do is I'll just make a copy
of this function. Then I'll just rename it
to perform counterattack. Okay, this function will also take the enemy on which we have to perform
the counter attack. So I'll just call
this the opponent. And from this function
on the player, we have to perform
the counterattack animation on the enemy. We have to perform the
counterattack victim animation. Okay, let me just copy this
function to save time, I'll play the counterattack victim animation on the enemy. All right, after that, the scot will wait
for the animation to complete and at the end we'll
set the inaction to false. There are some more cases
that we have to handle here. While performing
the counterattack, we'll start performing
the counterattack while the enemy is in the
wind up state, right? If we perform the
counterattack then we should not continue the
execution of the sloop. And we should not go
to the states like impact or cool down, right? We have to stop it
from here itself. To achieve that,
what I'll do is over here I'll create a public
property called in counter. Okay. It'll be false by default. And if the encounter is true while we are
in the wind up state, then we should just
break the loop. We should not
continue this attack right from the performed
counter attack function. I'll set the counter property
of the enemy to drew. We can also set that
property for the player. We'll need it in the future while the enemy is deciding
to attack the player. It'll be good to know if the player is already in
a counterattack or not. Yeah, this will also be
useful for the player. Once we are done
playing the animation, we can set both of
these to faults. Okay, so next, once we
perform the counter attack, the enemy should be dead, right? So the enemy should
not be able to perform other behaviors like attack or combat movement once the enemy is dead,
after the counterattack. Okay, so what we can
do is we can create a dead state and we can switch to that state when a
counter attack is performed. Let me go here, inside the enemy, inside
the state's folder. I'll go ahead and create a
new script called Dead State. All right, this will be a
state of enemy control. All right, from here let me
override the enter function. When we are in the dead state, we can go ahead and remove the enemy from the
enemies in range list. If we don't do
that, then we might choose this enemy again
for performing the attack. Let's go ahead and
remove it from the enemies in range list. We can use the remove in range
public function for that. All right, from here
I'll call enemy manager, instance enemy in
range for the enemy. I'll just pass the owner
on the enemy of the state. Right? Apart from removing the enemy from the
enemy manager, we also have to disable the vision sensor of
the enemy, right? If the vision sensor is active, then this will be
triggered again and the enemy will be added
to the enemy manager's list. Once again, we don't want that, we have to disable
the vision sensor. Let's go ahead and do it
from the death state. We don't have a reference to the vision sensor yet
in the NB controller. Let's go ahead and create that. I'll just duplicate this and I'll create a property
for the vision sensor. Okay, I'll make the
setter of this public. I'll be setting the reference
from the vision sensor itself because in
the vision sensor we already have a reference
to the enemy controller. We don't have to get it by using the get competent
in children function. We can just assign
it from here itself. Okay, assign it from
the awake function. Okay, so here I'll say
vision sensor equal to this. Now we should be able to access the vision sensor from here. And we can disable the game object to which the
vision sensor is attached. Okay, remember the vision sensor is not directly
attached to the enemy. It's attached to a child
object of the enemy. We can go ahead and disable
this object entirely. All right, we have
created the death state, now we have to add it to
the state's dictionary. Let me go ahead and create
an enema over here. I'll just duplicate this and
I'll cache the dead state. Okay, so now then we
perform the counter attack. We can also change the
state of the enemy to dead. Let me go ahead and do that. I'll call it changed state
and I'll pass the dead state. Okay, so let's strike. Calling this function from the combat controller to perform the counterattack first we have to make this
function public. Then I'll call it from
the combat controller. Since it's corotine, we'll
have to put it inside start. Corotinei'll call male fighter, perform counterattack
for the enemy. I'll pass the enemy
that's attacking us. Okay, let's go to
you and try testing this to attach the dead
state to our enemy. And now let's run the game. Okay, let me see if I
can counter an attack. Okay, I'm able to
counter the attack, but you can see a lot of
weird stuff happening. We have few issues here. The first issue is, while
performing the counter attack, the enemy and player are facing
in different directions. So we have to rotate
the enemy and player so that they're
facing each other. The second issue is even
after the enemy is dead, he's still circling
around the player. That should not
happen because we did change the state to dead, but something else is
changing that state. Let's look at how
to fix both issues. First, let's rotate
the player and enemy and make them face each other during
the counter attack. To do that, we can just
find the from the p to the enemy and make the player face in the
direction of the vector. First, let the vector from p to the enemy by subtracting the player's position from
the enemy's position. Okay, I'll just
throw this vector inevitable called
displacement vector. Dis vector for shot we want the rotation to be
in the horizontal plane. We don't want any
value in the y axis. Now we can just set
the rotation of the player in the
direction of the vector. I just use cotton
rotation for that, okay? And we can also set
the rotation of the enemy to be the negative
of the displacing vector. Okay, so again, I'll use cotton, do look rotation and I'll pass the negative of displacing
vector for the enemy. Okay, so this should
fix our issue. The next issue is the enemy is still moving around
after he's dead. Here we are, changing the
state to dead, right? The enemy shouldn't be
able to move around. But the problem is, in our
attack state for attack, we actually have
a cotine, right? We're waiting for the
attack to complete and then switching the
state to retreat. The problem is this
code will execute even if the enemy
is dead, right? We don't want that.
We can say the enemy should only retreat if the enemy is not
in the dead state. And if the enemy is still
in the attack state. Okay, if we switched the attack
state to something else, then we don't want to
change the state from here. From here, I'll check
the current state of the enemy attack itself. Only then we should change the state to retreat
after attack. All right, so this
should fix that issue. Now let's test the game and see if those issues are fixed. All right, let me
try to counter. All right, so now we can see that while
performing the counter, the player and enemy
was facing each other. That looks much
better. But there's still one issue that we have. The enemy is falling
to the wrong side. The enemy should fall towards the right and not to the left. We can just fix
that by mirroring the counter attack animation of the victim. Let me
try that again. Okay, let me try to perform
another counterattack. All right, so now the enemy
is falling to the right side. Okay, so that issue is fixed. We have another issue here. The player is going
through the enemy. While performing
the counterattack, the player is behind the enemy. This is surely
happens sometimes, but we need to fix that. The reason why this
issue is happening is because in the initial frames of the counterattack animation, the player is
taking a large step and the player is moving forward by some distance While
the enemy is attacking, the enemy is already
close to the player. When the player
moves the distance, the player will move through
the enemy and go behind him. Okay, but why is this issue
not happening always? The reason for that is
because both the player and enemy has character
controller on them. In most cases, when the player tries to
move forward like that, the character controller
will block it. But in some cases it
might slide through. We can't really depend upon
the character controller. Instead, we'll have to remove this root
motion at the start. We'll have to set
the position of the player manually
so that the player is right in front
of the enemy and it looks good while
performing the counterattack. Okay. From here. When performing
the counter attack, we don't want the player
to have root motion. But the problem is if we set the root
motion to falls from here using the applied root motion property
of the animator, then the player will not also rotate while performing
the counterattack. We don't want that, it'll
look really weird without the rotation. In this case. We want the root motion to control the rotation
of the player, but we don't want
the root motion to change the player's position. Okay. How can we achieve that? We can do that by using the
on animator move function. I'll actually create
that function in the combat controller. If we do it in the
merely fighter, then it will also
affect the enemy. In this case, you only
want to affect the player. I'll create it inside the compact controller
of the player. Here I'll define the on
animator more function. When we define this function, what will happen is
the animator will no longer apply the root
motion to the character. And we'll have to
apply the root motion manually from this function. Okay. So the benefit of applying the throat
motion manually is that we can apply the position and rotation of the throat motion separately. In some cases, we can
apply the rotation of the throat motion and not apply the position of
the throat motion. All right, so we'll have to manually apply the
root motion from here. By the way, if you go
to Unity and select the player here in the applied root motion
property of the animator, you can see that it
is handled by script. Okay? That's because we have to find this animator
move function. Now, the root motion will be completely applied
from this function. From here, we can decide whether to apply the
root motion or not. We can get the root
motion from the animator. Let me catch a reference
to the animator first. All right, I'll catch
it from the function. And now we can get
the root motion from animated delta position
and delta rotation. Okay? And we can apply
them separately. If you want to assign the
position of the root motion, then we just have to add
it to transform position. All right, If you want to apply the rotation
of the root motion, then we have to multiply it with the transformed rotation. I multiply it with
delta rotation, okay? So this is the
position and rotation that the player moved in the
last frame of the animation. Okay, Now we're applying both the position and
rotation of the animation. Now this will be
just like before, where the root
motion is turned on. What we ought to do while the player is
performing the counter, we only want to apply
the root rotation. And we don't want to apply
the root position, right? We want to position the player manually for the counter attack. We don't want the
animation to control it. What we can do is we can use the counter function
of the male fighter. We can say the road
position should only be applied if the player
is not in the counter. Okay, now when the player is
performing a counterattack, we'll apply the position
of the rot motion. We'll apply the rotation
right position. We have to set manually, we can just set the player around 1 meter from the enemy while performing
the counterattack. To get that position, what I'll do is I'll take the
position of the enemy. Then I'll subtract this
displacement vector and multiply it with
the value that I want. Okay, so here I'll just take the normalized
displacement vector. We can add whatever
distance you want. If you want the player
to be two metres away, we can add two over here. But for this attack,
I'll just place the player 1 meter
away from the enemy. This will be the target
position of the player. We can set this directly to the player's position
if you want. But it will be better to set it smoothly while we're playing
the counterattack animation. While this animation
is being played, we have to smoothly
move the player to the target position
to do something else. While the animation
is being played, we can use a wire loop to
wait for the animation to complete instead of
using this function, just like we did while we
were waiting for the attack, right, So let me just copy
these two lines here. While performing
the counterattack. Instead of waiting
for it like this, I'll wait for it
by using a y loop. Okay, From the loop I'll wait for a single frame
by calling Healer Turner. At the end of the loop, I'll make sure to
implement the timer. Okay? Now from here
we can do whatever we want and that will happen barely while the animation
is being played. All right, to set the
position smoothly, we can use vector three.
Move towards function. We want more from the current
position of the player to the target position where the player should be while
performing the counterattack. In the third parameter, we can pass the delta distance by which the player should
move in each frame. I'll just use a value
like five over here. And I'll multiply it
with time delta time. You can always try
changing these values. In future, we might
organize these and place all these data in theteralized fields and use it to control how
counterattacks work. But first, let's complete
the implementation of this. I'll use this to move the player smoothly towards the
target position. Okay, so now we shouldn't get that issue
where the player reaches behind the enemy
while performing the counterattack. Let
me try testing this. All right, so now you can see that we don't
have that issue anymore. One thing I noticed
is that the length of the counterattack animation
is a bit long right now. That's why the enemy
wasn't able to attack the player even after the counterattack
animation was over. Let's look at the
attack animation again. And here you can see that
after the attack is over, we still have a few frames at the end, so we can
get rid of that. I'll shorten this to
around here, frame 118. So let me just type that
as the end frame. Okay? So now it should work better. Okay? You can adjust the distance from 1 meter if
you want. That's up to you. And by the way, next we
have to fix another issue. If I go and attack from here, you can see that they'll
just stand up because they'll try to play
the hit reaction. The colliders of the enemy are still enabled even
after they're dead. Okay, so in this case we just have to disable
the Character controller. And we can also go ahead and
disable the nave agent so that it won't block the
path of other enemies. We can go ahead and do
that from the death state. First I'll disable
the nav agent. All right, and then we can disable the
character controller. We don't have a reference to the character controller yet, let me go ahead and make that
in the enemy controller. I'll just duplicate this and create a property for the
character controller. I'll just assign it from
the start function. Okay, so now from here we can go ahead and disable the
character control also. So now we shouldn't
have that issue. All right, so let me
test the counterattack. All right, so now
both enemies at it. And now if I tried to
attack the collider, you can see that we don't
have that issue anymore. Next, there's another issue
I want to fix right now. After performing
the counter attack, the player is going back to the previous rotation in
which she was standing right. Let me show it again.
Okay, so after the attack, the player will rotate back.
And we don't want that. We can just fix
that pretty easily. This issue is
happening because in player controller we
have target rotation. After the attack is over, we will execute the
update function again. We'll go back to our
target rotation, right? We can fix this issue by
setting the target rotation to the player's current rotation whenever we are in action, okay? If inaction is true, I'll set the target rotation
to players current rotation. And now we shouldn't
have that issue anymore, so let me try testing it again. Oops, I missed it. Okay. Now, after
the counter attack, the player is not changing
the direction in which she's facing. That
issue is fixed. Yeah, now we have
a basic version of counterattack working. It's not perfect right now. There are a few improvements
that we need to make. For example, the player
should not be able to counter when the enemy is
attacking from the behind. If we allow that, then the combat will be really
easy for the player. We'll be making such improvements
in the coming videos. I'll stop the video here. Thanks a lot for watching and I'll see you in the next video.
24. Targeting System: Hey prone. In this video, we'll build a targeting
system with which the player can target
different enemies like this. Here you can see that if
I'm looking at this enemy, then that enemy
will be targeted. But if I change my
camera direction and look at the other enemy, then that enemy
will be targeted. I'll be implementing that in this video before we start implementing
the targeting system. First, let me fix
an issue that we currently have, the issue. If we approach the
enemy from behind, then we'll get an exception. Let me actually place my player behind the enemy and then
I can show you the issue. Okay, if I approach
the enemy from behind. Yeah, you can see we have
a non reference exception. And this is the line that is causing the
nonference exception. It's in the attack state. Why are we even going
to the attack state? The enemy haven't
spotted us yet, right? That's happening from
the enemy manager. If there are any
enemies in range, then we'll select one
of those enemies and we'll make them attack
the player, right? We'll do this even if the
enemy hasn't spotted us. All right, while selecting
the enemy for attack, we can just add a
condition and make sure that the enemy
spotted the player. Okay, we can check that
by checking if there's a target in the enemy
controller from here. By selecting the enemy, I'll select the enemy
that has a target here. I'll check if the target
is not equal to null. This on the first enemy
that spotted the player. Okay, since we added
this condition, the attacking enemy can be null. Here I'll just write
another condition. I'll check attacking enemy
is not equal to null. Then I'll try to
attack the player. All right, so now that
should fix our issue, so let me try to sing it again. Okay, so now we don't
have any issues. So now let's start implementing
our targeting system. So let me open up my
combat controller here. I'll create a vaiable for
storing the target enemy. All right, I'll just make
this a public variable for now so that we can see the target enemy
in the inspector. In future, I'll change
this to a property. For the target enemy, we have to choose an enemy in the direction in which
the player is looking. Okay, that direction will be the direction from the
camera to the player. Let's create a function for
getting that direction here. I'll create a public function called Get Targeting Direction. From here, we just have
to return the vector between the camera and
the player For that. First we need a
reference to the camera. I'll just catch the reference
from the awake function. We can get the camera from
camera main property. From it, we can get
the camera controller. I'm just getting the
camera controller here since we might need it for
something else in the future. We can get things like plan,
rotation and all that. So now we can use that to find the direction from the
camera to the player. And we can get that by subtracting the
position of the player, actually the position
of the camera from the position of the player. Okay, let me just show this in avaitble
called from camera. In this vector, I'll
set the y component to zero because you only want the direction in the exit axis. We don't care about
the component. All right, we can just
return this vector in its normalized form because you only one direction as well return vector from
camera normalized. Okay, so now we have
a function to get the direction in which
the player is looking. Next we have to find an
enemy in that direction. We'll be doing that
from the enemy manager. Here we have a list
of enemies in range. From here, we could loop through all the enemies in this
list and we could find an enemy that is closest to the direction in which
we are pointing, okay. How can we find
the enemy that is closest to the direction
in which we are pointing? Let's figure it out
by using a diagram. Let's say this is the player
and these are the enemies. Let's say this is
the direction in which the player is looking. Okay, from here we want
to find the enemy. That is, to this direction we can just extend the
direction vector of the player. Then we have to find
this distance between the enemy and that
vector, right? How can we find that distance? We can use a little bit of
trigonometry to achieve it. First, let's draw a vector
from the player to the enemy. And let's say the angle between that vector and our
direction vector is theta. Then if we split the vector to the enemy into its horizontal
and vertical components, biosynta, then the horizontal
component will be costa. The vertical component
will be sine theta. Okay? In this case, the vertical component
will give us the distance to the
direction vector, right? These two vectors are same. Vertical component will
give us the distance. We can find it by
computing sine theta. So let's go ahead and
implement this from the code in the enemy manager. I'll create a public
function for finding the closest enemy in the direction in which
the player is looking. All right, I'll call this Get
enemy to player direction. Okay, so compute this
firstly needed reference to the player from the script. Let me create a zeralized
field reference for that. We can actually get
the compact controller because that's the script
attached to the player. If you use it, we'll be able to set the target enemy easily. Let's get that with the player. From here I'll call Player Get Targeting
Direction function to get the direction in
which the player is looking. Let me store this innovatble
called targeting direction. Now we have to loop through all the enemies
in the enemies in range list and find
the enemy that is closest to our targeting
direction, right? Let me use a foraged loop to loop through all the enemies. Then to find distance to our
targeting direction line, we just have to
compute v sine theta. Where is the vector from
the player to the enemy. First, let me find the vector from the player to the enemy. We can easily get that by subtracting the
player's position from the enemy's position. Okay, let me just throw this innovatible called
vector to enemy. Here. We don't need the
y component to zero. Now we have to compute the
sine theta distance to the targeting direction
line will be V. Get that. First we have to find theta, which is the angle between the direction vector and
the vector to the enemy. I'll use the angle function
to find that angle. Okay, let me just stare
this innovatible now. We can find the distance to the targeting direction
by computing sine theta. V is the magnitude of
the vector to the enemy. Then let me multiply
it with set. I'll use math of sine function
and I'll pass the angle. One thing you have to
remember is we have to pass the angle as radiance
to the sine function. So we have to multiply
the angle with Matt. Did Dan dicto? Okay, that will give us the distance between the enemy and the targeting direction. Now we have to loop through all the enemies and
find the enemy with the least distance to the
tagting direction, right? To find that, begin to
create two variables here to keep track of the
distance the closest. So I'll create in distance and I set it to a
really high value, like infinite deep. By default. Then I'll create a
bailable to store the closest enemy as
it to not by default. Now from here, if the distance that we computed
is less than mind distance, then the new mind
distance is going to be the distance and
the closest enemy is going to be this enemy. Okay? Now by the
end of the F loop, we will have the closest
enemy in this variable. So we can go ahead and return
that from this function. Okay, now from the
update function, we can call get closest enemy to play a action
and get the closest enemy, and we can save it into the
target enemy of the player. Okay, By the way, calling this function every
single frame might be a bit expensive because we are calculating the
magnitude and all that. To optimize it a bit, we can just make sure that
we're only calling it every 0.1 seconds or something that I'll just create
a timer over here. I said it is zero by default. I'll keep updating it at the
end of the update function. And then if the timer
is greater than 0.1 then we can
execute this line. And we also have to reset the
timer to zero in that case. Okay, I'll just make this
greater than equal to, it doesn't really matter, but I just like things
to be perfect. Now, this slowly be called. Every 0.1 seconds, it'll find the closest enemy to the direction in which
the player is looking. Go to Unity and
try testing this. First we have to
assign the reference of the player to
the enemy manager. We will also make
the player a single. Both ways will work. Let
me try testing this now. All right, so I'll
have to minimize this and select the player to see which enemy is selected. Right now, none of the enemies are selected as a target enemy. But if I move closer and
if I look at this enemy, you can see that an
enemy is selected here. But if I change my
looking direction and look at the other enemy, you can see that the
enemy also changes. Okay? So the enemy is changing based on the direction
in which we are looking. Our logic is working fine, but we need to provide
feedback to the player to understand which
enemy he's looking. The player can't look at
the inspector, right. To do that, we could
just highlight the enemy that the player is looking by just giving a
little bit of glow like this. It's a common thing
that you'll see in games like Assassin Three. Let's go ahead and
implement that. To make our enemy glow. We could just create
a simple shader for those who don't
know what a shader is. A shader is a program that determines how things should
be rendered on the scene. If a thing that you see
in the scene right now, they have a shader and the shader is deciding how
it should be rendered. It's really useful
if you want to add a visual effect on like a glow or
transparency or anything. Let's go ahead and make
a shader for making our enemies glow the acts. I'll create a new
folder called shaders. Okay, here I'll
create a new shader. Let me go to Create Shader
Universal Under Pipe Plan, because that's the pipe plan you're using for this project. From here I'll
select shade graph. Okay, I'll call this
highlight shader. We'll be creating our shader by using the stool shader graph. I'm not going to go and explain the basics of shader
graph and all that. That's not the focus
of this course. There are many courses
in free tutorial is that are dedicated
to shaders. You can go ahead
and check them out. In this video, I'll
be just creating this simple shader and I'll also make the
completed shader available for download in case you don't want
to create it on your own and just want to import
and use it in your project. Let me go ahead and
create this shader. First, we want to add a texture because our
enemies have a texture. Right, this is a texture
used by the enemy model. Before implementing
the glow and all that, first let's add the texture. Click on this plus button and I'll add a new
texture to the, I'll just call this texture. We can just plug it into the
base color of our shader. We can't plug it
directly because the base color has
to be an RGB value. We just have to pass it through sample texture node to
convert it into RGB. By the way, what
I'm doing here is I'm clicking on the
sport of this node. I'm dragging, this will give us a list of nodes to which this
texture can be connected. Okay, from here
we can search for sample textuuy it'll give
us a sample text node. If you click on
that, it'll create that node and create a
connection to that node. Okay, let me just minimize this and the RGBA value
into our base color. All right, now we have created a basic shader that will
just show the texture, but in the shader we also
want to add a little bit of glow for highlighting
the enemy, right. To do that we can use the emission property
of the shader to them. We can just pass, I'm pressing Space button here and it'll keep
me the list of nodes. From here I can search
for feral effect. Okay. This is an effect that is commonly used
for glow in the center, it'll be black and towards
the edges it'll be white. We can also change it by
playing with the power. All right, we'll connect this to the emission property here. We can also create
a variable so that we can control it from the meal. It's just like exposing
available in the inspector here. I'll create a float
variable and I'll call this glow power. Let me just drag and drop
the variable over here. I'll attach it to the power. Everything has became white. So that's because
the default value of this variable is zero. So we can change it to something like one or two by default. Okay, here we have our chat. Now let me go ahead
and save this by clicking on the Save
Asset function. And I'll use Shift space
to go out of full stream. Okay, so here we have a shader. So now we can use the shader
to create a material that we can attach to our enemy that I'll right
click on the shader. Okay, I'm not right
clicking here, I'm clicking on the shader. And then I'll go ahead and
create a new material. Okay, I'll just call
this highlight material. Put the texture,
I'll just search for paladin texture and
this will give us the textures of our enemy here, The diffuse is the one
that we want to use because that's the
one with the color. Okay, let me put this material
in our Materials folder, Now we can try attaching
it to our enemy. Let me go inside the prefab and let me find the
object with the mesh. Actually, for this enemy,
we have two objects, one for the body and
one for the head. But for some reason
the naming is given by the mix is wrong, it
doesn't really matter. What matters is we have to apply the highlighting material
for these two game objects. If you look at the skin measure under Property of
the object here, you can see this is the material that it's currently
using right now, so we could replace it with
our highlighting material. You can see that we have a white color at the edges which makes it seems
like the enemy is glowing. Okay, We also have to do it
for the helmet of the enemy. Let me also change
that, and by the way, I'm just trying this out. We don't want the enemy
to have this material. By default, we'll have to dynamically switch the
material from the script. Okay, so this is how
the enemy will look. Now let me switch back the
materials to the old material, which I believe
is pain material. We have to switch the material dynamically when an
enemy is being targeted. Right, so let's go ahead and implement that in
our Util folder. I'll create a script for changing the material
of a skinned mesh. So I'll call this skinned
mesh highlighter. All right, in the script. Silky debatable for storing the meshes that we
need to highlight. All right, so this
will be a list of skinned meshrenderka.
This is Vicus. The enemy is rendered by
using a skinned meshnder. Right? If you were trying to highlight a simple object
like a cube or spear, then we can't use this
because it will be a mestrendera and not
a skinned metrederka. So I'll just call this
one meshes to highlight. Okay, next I'll create
two variables for storing the original material and the highlighted
material for the mesh. So fast I'll create
a material called original material and then I'll create one for the
highlighted material. Okay. I'll create
a public function here called highlight mesh. And still take a bulling
so that we can specify to highlight or unhighlight
the mesh. Okay? And from this
function we have to look through all the
meshes in the list. All right? And for each
mesh we have to change the material according to the value of the
highlight pulling. If highlight is true, then we have to use the
highlighted material. Otherwise you can just use
the original material. Okay, this is all going
to do from the script. Now let's go Unity and
attach it to our enemy. I'll go inside the prefab and I'll attach the skin
mesh highlighter. Next we have to assign
all these fields, these two other Me objects
that we need to highlight. Right, let me draw and drop them to our meshes
to highlight list. All right, then
we have to assign the original and
highlighted material to the original material is met. The highlighted
material is called highlighted material.
Let me also assign that. Yeah, now we have a script with which we can switch
the material of the enemy and highlight
the enemy first, let me create a reference to the highlight in the
enemy controller. So I'll just duplicate this and I'll create an instance
of the higher. Okay, we have to catch it
from the start function. Now we can use it to highlight the enemy from the
enemy manager. Whenever we change, whenever
we set the target enemy, we also have to
highlight that enemy. Right? What I'll do is I'll just throw this in
another table called enemy. We only have to do
the highlighting. The closest enemy and our current target
enemy are different. So fail if the enemy
is not equal to all, if the closest
enemy is not equal to the enemy that we're
currently targeting. Okay, if those two
conditions are true, that means our
target has changed, so we have to change it in the
player target enemy, okay? And we also have to
highlight the target enemy. To do that, we can call
player dot target, enemy. Match highlighter.
Highlight match function. Okay, And here I'll pass through because we
want to highlight it. Along with this, we also have to unhighlight the enemy that
was previously highlighted. Right before we change
the target enemy, Let me show the
previously highlighted enemy in another variable. All right, now from here we can just un
highlight that enemy. I'll just duplicate
this line to save time. Okay, So this will highlight
the previous enemy. Just to be safe, I'll just add an unconditional
operator everywhere. Let me also add it over
here, because just in case, if a highlighter is not
attached to an enemy, the whole thing
should not break. Right Now, let's go Unity and test if everything
is working okay. Yeah, if I go close to an enemy, you can see that that
enemy is highlighted. And let me look at
the other enemy. Yeah, now you can see that the other enemy is highlighted. All right, that looks fine. We are highlighting
the enemy that is closest to the direction
in which we are looking. Okay, by the way, if you want, we can also change the power of the globe if you want the highlighting to
be more prominent. Let me just materials and find
the highlighting material. We can reduce this
to something like 1.5 That should
increase the glow. Reducing this value will
be increasing the glow. You can see it
happening. I'll sit it to something like 1.5
Okay, let's maximize this. Say this looks fine to me. We have built a system for
targeting different enemies. This will also work while
we are in the combat. All right, I'll stop
the video here. In the coming videos, we'll
be looking at how to make the player attack the enemy that's currently being targeted. And we'll also make the player face the targeted
enemy while in combat. I'll be stopping the video here. Thanks a lot for watching and I'll see you in the next video.
25. Lock On Combat Mode: One. In this video, we'll implement the lock on mode or the combat
mode. For the player. The player will
be able to target an enemy and go to
the lock on mode. And once the player is
in the lock on mode, she'll always be facing the target enemy
while she's moving. Okay, if we move the player towards the site
while she's in lock on, then she won't walk normally. Instead she'll while looking
at the target enemy. Other than that, we'll also be fixing a couple of
bucks in this video. Let's get started. First,
before we implement lock on, I want to fix two issues that we have in our
game right now. Let me show you the issue first. The first issue is if the enemy attacks
while they're chasing, then you can see
that they are not playing the animation while
retreating after the attack. Okay, this only happens when
they chase us and attack. We need to fix that issue. It's happening in the
combat open state when the enemy starts, we're setting the
combat mode as false. Okay. But we don't have
to do this because now since we're using a Blentry
for the combat movement, we do have running
animation in here, right? While we can use this
running animation, we don't have to go out of the combat mode and back to
locomotion while chasing. We don't have to
set the combat mode to falls when we start chase. Let me remove this line, begin to set it to true when we start the combat movement
state itself. I'll move this line to the Enter function of
the combat movement state. Okay, and then we can Sid. It falls from the idle state because once we go
to the idle state, we don't want the enemy
to be in the combat move. All right, so this
should fix issue, so let's try testing it. Okay, so now we don't
have that issue. The enemy is playing
the walk back animation correctly while retreating. That issue is fixed. There is another issue that
we have, the issue. If we kill the enemy that is
being currently targeted, then you can see that that enemy is still
being highlighted. Okay, once an enemy dies, that enemy should no longer be the target enemy of
the player right? Now if you look
at the inspector, you can see that that enemy is still the target enemy right. Now when we implement lock on, the player will
still keep locking onto the enemy even
after he's dead. We have to fix that if
a target enemy is dead, then we have to reset
the target enemy. Let's do that. We can actually do this whenever we remove an enemy from
the enemies in range. Okay, so we might remove the
enemy when the enemy dies, or we might even
remove the enemy when the enemy is far
away from the player. Right? In both
cases we don't want the enemy to be the
target enemy from here. After removing the enemy from
the enemies in range list, we can also highlight
the enemy from here. I'll call my highlighter
to highlight mesh and pass falls to
highlight the enemy. We really need to do
this if the enemy that's being removed is
the target enemy. Let me check that here. Okay, so if the enemy that's being removed
is the target enemy, then first we'll
unhighlight the enemy. And then we also have to
change the target enemy. Right, I'll the target enemy as the closest enemy in
the player's direction. Let me call get closest enemy to player direction function. Okay, so this will
reset the target enemy. If there are no other enemies in the enemy's in range list, then the target enemy
will be set to nor. In both cases, we'll change the target enemy from the
enemy that's being removed. Okay? After changing
the target enemy, we can go ahead
and highlight it. Okay? So here you have to use
the unconditional operator because in some cases there might not be any other
enemies close to the player, and in that case, the
target enemy will be null. If you don't use
unconditional operator, then you'll get an
exception here, okay? So let me go ahead and highlight the new target enemy. Okay? So now once an enemy is dead, he should no longer be targeted. So let me try testing it and let me kill these two
enemies really quick. Okay, All right, so now we can see that
once the enemy is, we are no longer
targeting that enemy. All right, that issue is fixed. Now we can go ahead
and implement the lock on mode or the
combat mode for the player. First, I want to improve the walking and
running animation that you're playing while
we're in the combat mode. If you go to the
combat move, bluntree, you can see that for the walking and running
animation we're playing, playing, walking and
running animation. Right? It would
be better to have an animation in which the player is walking and
running with a sword. Okay, just like our walk
back animation here, the player is actually holding a sword and that
looks much better. All right, let's find
some animations for that. I'll go to Miximo. In the animations, I'll search
for sword and shield walk. Okay, so here we have an animation where the
player walks with the sword. So I have to check in
place for this because we don't want the root
motion of the animation. Okay, so go ahead and
download this animation. I have already downloaded it. Next I'll search for sword and shield run to
find the run animation. Okay, so this is the animation I'm going to use for the run. Again, you have to check the in place check box before
downloading it. All right, so go ahead and
download these two animations. I have already downloaded it. Let me mote them into
my animation folder. These are the two animations. I'll dragon wrap them into
the combat animations folder. First we have to rig
these two animations. Okay, so let me
make it humanoid. I'll choose the Erica Ava. All right, Now I should be
able to review the animation. We want this animation loop. So I'll turn on loop
time and loopholes. I'll chain the root transform
rotation to original, root transform position to feet transform position,
exit to original. Then I'll check
bake into pose for all the three root transforms because they have loop match. Okay, So this is how the
animation will look like next. Let me do the same for
the run animation. First Elton on loop
time and loopholes. Okay? And then I'll
set the based upon for all three transforms and
then I'll check bake, interpose for all the
three transforms. Okay? This is how the Ron
animation will look like. Let's replace the old walk and run animation with
these two animations. Okay? This is the
walk animation. I'll replace it
with the new sword and shield walk animation. And this is the run animation. So I'll replace it with
sword, shield, run. All right, so now the player should walk and run
while holding a sword. Okay, that look much better than the previous walk in run animations when we
are in the combat mode. Okay, Next we should implement the code for making the
player go to the combat mode. So the player can go to the
combat mode in two ways. The first pay, the player
can press the lock on input to lock onto an enemy
and go to the combat mode. And second, when the
player attacks an enemy, we can lock onto the enemy
and go to the combat mode. Okay. So the player
should be able to do it manually by pressing
the lock on input. And we should also
automatically go to the combat more when the player
attacks an enemy, Right? First, let me define an
input for the lock on. I'll go to the input
manager in here. I'll just replicate
this jump input and create a new
input for the lock. Let me name lock
on for the button. I'll just give the
key on the keypoart. We also have to define this
input for the joystick, but I'll be doing that
later because for the joystick I want to
use the right trigger. Using the trigger as a button
is a bit more complex. I'll be covering that later. First, let's actually implement the lock on for the player. I'll go to the combat
controller script from here. When the player presses
the lock on input, we have to go to
the combat mode. First I'll create a property
for the combat mode. All right, I'll create a
Bolling property called combat. I want to implement
this in such a way that when we set the value of
the combat mode property, we also have to
set that value in the combat mode parameter
of our animator. All right, when we
set this property, we also have to set that value to the animators parameter. We can do that
from the sector of the property when we're
setting the property. We can also set the
animator parameter. All right, to be
able to do that, we can't just define
the property like this. This is actually a short
way of defining a property. It is called auto implemented
properties in C Sharp. Okay, You can check
the documentation. We can also define the
properties like this, in which we can implement the getter and sector
of the property. When we implement the
getter and sector, we'll also be able to
do other things when the value of the
property is set, okay? So we can't define the
property like this. This is a short form for
defining the property, so we'll have to
define the property in its long form itself for that. First I'll create a private pooling variable
called Combat mode. All right, here the
C is lower case. Then I'll create a public
property Combat Mode with a upper case. Okay. And in this property I'll implement the getter and setter. In the getter we
just have to return the combat mode
private Viper. Okay. There are different
ways to write the get. You could write using
a curly brasis like this and then just return
the combat mode Viper. All right, but since this
getter only has a single line, we could simply use
a lambda like this. We don't have to write
braces or anything. Okay, next let's
define the setter. Again. For the
setter, we can define it like this using
a lambda function. In the case of the
setter, we have to set the value to the
combat mode variable. The value will be the
value that is set onto this property, okay? So we could define the
setter like this if we only had a single line. But in our case, we
also want to set the animator parameter when we are setting the
property's value, right? So I'll use a curly base because we'll have multiple
lines in the setter, okay? And from here first I'll set the combat mode variable to the value that is
set to the property. This is same as this, okay? This is the actual
way of defining a property with the
getter and setter. And this is just a short
way of writing this. When we write the property in the short way behind the scenes, C sharp will define a
private variable like this and define getters
and setters for it. Okay? In this case we can't
use the short definition. We'll have to use the
actual definition of the property from
here, from the set, whenever we're setting the
value of the combat mode, we should also set the
animator parameter. Right from here I'll call
animated dot sit bool. And I'll sit the value
of the combat more to the value that was currently
set to the property, okay? So now, whenever we set the
value of this property, the property will
automatically set that same value to the
animator parameter, okay? And from here we could
also add more checks. So for example, when we
don't have a target enemy, we should not be able to
go to the combat mode. Right from here, we could just check if the target
enemy is null. If the target enemy is null, we should not be able to
go to the combat mode. So we can just set the
combat mode as false. All right, so yeah, this is the power of
using properties. We can add any number
of statements in the getter and setter and we can execute additional logic when we get set the value
of a property. Okay, so now we have the
property for the combat more. Next we should
change the value of this property when the player
presses the lock on input. Right from here I'll check if the lock on
input is pressed. All right, and if the
lock on input is pressed, then we have to reverse the
value of the combat mode, right, when they are
not in the combat mode. And when we press
the lock on input, we have to go to
the combat more. And then when we are
in the combat mode and if the lock
input is pressed, then we have to go out
of the combat mode. Okay, from here we just have to reverse the value
of the combat mode. Like this, it will
automatically do checks like this to make sure
that we won't go to the combat mode without
a target enemy. And it will also set the
same value in the animator. All right, using a property like this will allow us to make our code a lot
cleaner and shorter. We don't have to execute those additional logic from here, from wherever we
set the property. Next, when the player
performs an attack, we have to go to
the combat mode. Right from here I'll just
set the combat mode to true. All right, again, even though we're setting
it to true from here, it'll only be set if
there's a target enemy. All right, next, when we
are in the combat mode, the target enemy of the
player can become right. For example, when
the target enemy dies or when the target
enemy goes out of the range, we'll see the
target enemy as nu. In that case, we should change the combat
mode back to false. Otherwise, the player
will be locking on to the dead enemy when we set
the target enemy to Nor, we have to set combat
mode to false. Again, for doing that, we can use the setter
of the property. First, let me turn
this into a property. I'll rename this to
have a upper case, because the properties
should start with an upper case, right? And then we can define
the getter and setter. We also have to define
a private variable for the target enemy, right? And from the getter we can return that target
enemy Come from the setter, we can set the value of the
target enemy, private viber. Then if the target enemy is, then perhaps set the
combat mode to false. Right, we can't be in the combat mode if there
is no target enemy. Okay. So we have implemented
the logic for setting and resetting
the combat mode, right Next we should implement the logic for how the player should behave when
he's in the combat mode. Okay, so we'll be doing that
from the player controller. From here in the player
controller, right. Now we're just
rotating the player to the direction in which
she's moving, right? But when we are in the combat,
we should not do that. The player should
always rotate towards the target enemy, right? Also when we are in
the combat more, we should split
our velocity into forward and sideboard component and set it to both
forward, speed and speed. In the combat mode,
the player will always be facing
the target enemy. The player will be strafing
around the target enemy. We have to change this
implementation from here. I'll check if we are
in the combat mode. And by the way, to access
the combat mode property, we need a reference
to the combat control from the player controller. Let me go ahead and
cache a reference to the combat controller
from the awake function. Okay, now from here we can check if we are
in the combat mode. If you are in the combat mode, we have to use a different logic for updating and
animating the player. Otherwise, if you're
not in the combat mode, we can use our old logic. So I'll just copy these lines
and paste it over here. All right, so if you
are in the combat mode, I don't want the player
to be able to run. Okay, I'm designing
this combat system similar to the combat system from the assassin street games. While you are in
the combat mode, we won't be able to run. We'll only be able to walk. And if you won't run, then we have to plus the
lock on input again to go out to the combat mode and then we'll be able to
run away from the enemy. Okay, that's how I'm going to implement
this combat system. We won't be able to run while
we are in the combat mode. While we are in the combat mode, what I'll do is I'll divide
the velocity by four, okay? This will reduce
the speed in which we can move while we
are in the combat mode. All right, then while we
are in the combat mode, the player should always rotate and face towards
the target enemy. Right, then for
setting the animation, we should split
the velocity into its forward and
sideward component. And we should set it into the forward speed
and stray speed. Right, the player
should also be able to Sra when we are
in the combat mode. Okay, we have to do these two things from
the combat mode. And by the way, I'll move the character
controller function to the end because we are modifying the velocity
from here, right? I'll also move the line
where we're setting the speed for the gravity here. When we are modifying
the velocity, we should not modify the speed because this is purely
controlled by the gravity. It's not controlled by the
player's input, right? The gravity should not
change when we are in the combat modest it from here. All right, next we have to rotate the player
towards the target enemy. For that first I'll find the vector from the player
to the target enemy. We can find that by subtracting the position of the player from the position
of the target enemy. Okay, let me just toll this innovable called
target vector. I'll set the y value of
the target vector to zero because we only want to rotate the player in the
horizontal plane. All right, then from
the target vector, I'll find the target rotation by using the cotton
rotation function, just like we're doing here. Let me just copy this line. Okay, by the way,
I really do this, if the player is moving right, visually change the rotation
when the player is moving. And after finding
the target rotation, we can just rotate
the player towards the target rotation by using
the rotate towards function, just like we're
doing it from here. So I'll just copy this
line and base it here. All right here. We could use a different
rotation speed if you want while we
are in the combat part. But I'll just keep it
as the same for now. And I'll see how it looks. Okay. This will
make sure to rotate the player towards the target enemy when we are in the combat. Next we have to
split the velocity and set it into the
forward and speed. We have already done
this for the enemy. If you look at the
update function of the enemy controller here, you're splitting
the velocity into forward and the
sideward component. And we're setting it to the forward speed and
straight speed. Right, we can do the same thing from the player controller while the player is in
the combat mode. I'll just copy these lines and I'll paste it in
the player controller. Okay, so here we have here, our animator is not a
property, it's available. So I'll change the first
letter to lower case. Then here for the speed we
have to use the most speed. All right, now all the
errors are fixed and we are splitting the velocity
and setting it to the forward speed
and the straight speed. All right, by the way, I just noticed that
I forgot to change the mode direction to target vector here by copying
and pasting this line. Okay, let me also change that. When the player is
in the combat mode, we are handling the rotation
and animation differently. Let's go to it and
try testing this. Let me go near an enemy to
take the enemy and I'll press the key to lock onto the enemy
and go to the target mode. Here you can see that
the player is playing the combat idle animation. The player is in the
combat mode from here. If we try moving the player, you can see that the player
is always facing the enemy. The player is T. Then we move
towards the site, right? The player is not
just walking forward and rotating towards a more
direction like before. Okay, one thing to note here is that the animations might not look good when we
move in some directions. The forward, the backward walk, the walk towards
the left side and the walk towards the right
side will look fine. Okay, But when we try to
walk in diagonal directions, it won't look that good. By the way, when I move out of the range will go out
of the lock on mode. If I try to move in
directions like this, you can see that the animation doesn't look good. All right. The reason for that is because we don't have a
dedicated animation for walking in diagonal
directions, right? We're just playing a blend of the strafing and walking forward
and backward animations. The blend will not look good in some cases it's always best to have diagonal
walking animations, but unfortunately,
I haven't found any free diagonal
walking animations where the player is
holding a sword. I think we don't
even have any normal diagonal walking
animations in Miximo. But if you buy some
professional animation pack, you should have diagonal
walking animations in it. Since this is, I won't be using any
paid animations in it. But I just want to let
you know that if you want better diagonal
walking animations, you can find them in animation packs like
this one by bald. You can add those
animations to the bluntre. I can show you an
example in which I used a pad animation pack called
Swadanimstp by Ubold. In this pack, all the animations
are made by using Mocap. And we have animations for diagonal box while
holding a sword. This is a project where I
use the paid animation pack. Here you can see that the
diagonal locking animations look a lot more realistic. There is no foot
sliding Synthia using dedicated animations and not a blend between
two animations. These are the animations are used for creating
this controller. Here you can see that we have a 45 degree animation for the
left and right direction. We also have a strafe 135 degree animation
for left and right. We have diagonal blocks
in all the directions. This is how the Plintree set up. We have all the diagonal walking animations
in the Plentree. We can also try
testing the Blinreef. I try to play diagonal
walking animations from here, you can see that the
animation looks really clean. Yeah, I just showed you
this in case you wanted to set up your controller with
paid animations like this. But I'll only be using free
animations in this course. It's going to be okay
for the most part, but we might have some
food threading issue here and there during
diagonal walks. Yeah, just keep that in mind. Now let's get back to
testing the combat mode. All right, we can try testing it while
we are in the combat. Yeah, it looks pretty fine. We can move around the enemy
like we are in the battle. This looks much
better than walking normally while we are in combat. I'll stop the video here. The next video, we'll
look at how to make the player rotate towards the enemy while
performing an attack. Thanks watching, and I'll
see you in the next video.
26. Directional Attacks & Rotating while attacking: One. In this video, we'll make some improvements
in our compact. First, we'll make our characters rotate towards the attacker
while taking a hit. Then we'll also allow our player to attack in
different directions. For example, if an enemy
is standing behind me, I can just point in that
direction and press attack. And then the player
will turn and attack. Yeah, let's look
at how men this. First I want to make
the player face the enemy while the player is taking a hit from the enemy. All right, the enemy
should also do the same. They should praise the
player while they are taking an attack
from the player. So let's go ahead
and implement that. I'll go to my male
fight descript. This is where we have the code for playing the hit reaction. While we play the hit reaction, we want to rotate
the character and face the attacker
for that first, we need a reference
to the attacker. Right? I'll add a parameter in this function
for the attacker. Then before playing
the hit reaction, we can make the character
face the attacker that first I'll find the vector from the
character to the attacker. We can get that by subtracting the character's position
from the attackers position. I'll just set the Y of
the displacing vector to zero because you only want to rotate it in
the horizontal plane. Then to rotate the character, I'll use cotonrotation to get the rotation from
the displacing vector. Okay, same thing that we did over here for
the counterattack. We're just rotating a character
to face the attacker. All right, now from here
when we call this function, we also have to
pass the attacker. Here we only have the collider that attacked this character. This collider will be the sword. Right From this, we have
to get component in parent and get the male fighter to find the character
that attacked the player. Okay, this is not
really efficient. We'll be improving
this in the future. Being the status
script to our weapons and cash reference to the
mainly fighter in that script. That way people won't
have to use get component in parent and
it'll be more efficient. We'll be doing that
in the future. This is all you
have to do to make the character face the
attacker during hit reaction. Next one thing I want to do is I want to change
how the targeting works while we are
in the combat mode. Okay, if you're not
in the combat mode, we should target like this by using the direction
of the camera. But while you are
in the combat mode, I don't want to use the camera
for targeting the enemies. Instead I'll just use the forward direction
of the player. Okay, so this works really well because when an
enemy attacks the player, we will rotate towards
our enemy, right? If we set the
targeting direction to our forward direction, then we'll automatically target that enemy who attacked us, which is what we
want in the combat. All right, from here, if we are not in
the combat mode, then we can find the targeting
direction like before. Otherwise we can just return the forward direction
of the player. Okay, Using the forward
direction will be better because a player will rotate the lot while
we're in combat. And it's always good to target the enemy that is
in front of the player. Let's go to T and
try to distinguish. Okay, let me just go
to the lock on mode. Yeah, now you can see that
when an enemy attacks, we are facing that enemy while
playing the hit reaction. Okay, so this can
be a bit snappy, but lots of assassin
screen games use these techniques
where the player will just instantly rotate towards the attacker before
taking the hit. Next, I want to
be able to attack enemies based on our
input direction. Here I was intending to
attack the enemy behind me, but the player will
always attack in the direction in
which she's facing. Right? That's a problem. We won't be able to attack
the enemy behind us. Instead of this, we
should be able to specify a direction and attack the
enemy in that direction. While we are attacking, we want to rotate
towards that enemy. Let's go ahead and implement that here in the
combat controller. When we perform an attack, we also have to pass the direction in which
we want to attack. Okay, So we can get
the input direction by multiplying the horizontal
and vertical input with the cameras rotation
like we do over here, right here we are. Using it to find the direction
in which we should move. But this is basically
the input direction that the player
is giving, right? So we can do the same to find the direction in which
you should attack, okay? So we can actually store this
in a public property here. Here, I'll create
a public property called Intent Direction. I'll call it Input Direction
to keep things simple, okay? And I'll just set it from here. Now we should be able to
get that direction from here and use it for the
direction of our attack. But from here first we
need a reference to the player controller. Right? What we can do is
we can just make the player controller
a singleton, because we have to access
it from multiple scripts. Here, let me create a public static instance of the
player controller. Then I'll just initialize
it from the awake. Now we can access it from
the combat controller. From here I'll say player
controller instance input direction to get the direction in which
we want to attack. Now we're passing the direction to our t to attack function. But this function doesn't take
a parameter at the moment. Let's define a parameter here. So I'll call this
attack direction. And what I'll do is I'll make this nullable vector and set
its default value to null. Okay? The reason
for this is because this function is also
used by the enemy, right? In case of the enemy, we
won't be able to pass the targeting direction or anything. That's
only for the player. So this has to be a
default parameter and the attacking direction
should be ignored by default. Okay, let me also define this parameter in
our attack function, and I'll pass it while we're
calling the attack function. Okay, so now when we are
performing an attack, we should also rotate to
the attacking direction. All right, from here first I'll check if the
attacking direction is not null. If that's true, then
we can use con, rotate towards, we can rotate our character towards
the attacking direction. For the from rotation I'll pass the current rotation
of the character. We can get the two
rotation by using corotation and passing
the attack direction. Finally, full speed we can
pass something like 500, but I'll just make it a
Liz field variable over here instead of just hard
coding the rotation speed. All right, so I'll
define it over here and I'll set its value
to 500 by default. Okay, let me pass it over here. And we also have to multiply
it with time delta time. We have an error over here. That's because the attacking direction is nullable vector. But the look, rotation takes a vector, not inullable vector. We can just pass the value of the attacking direction and
that issue should be fixed. Okay, now we should rotate towards the
attacking direction. We forgot to do one thing. We have to set this rotation back to the
rotation of the player. We forgot to do that. Yeah, the rotate the player towards
the attacking direction. Third score, unity
and protesting this. All right, let me go to the combat mode and let me
try attacking this enemy. Yeah, you can see
that the player turned and attacked the enemy. Next, we can do another thing to improve our attacks
even more from here. Instead of passing the players input directly to
the attack function, we can check for the enemy that is closest to
this direction. Then we can pass the
direction to that enemy. Okay, if we do that, the player will rotate towards the closest enemy and
attack the enemy. That will be a bit more
accurate than passing the input direction
directly simplian that. So in our enemy manager script, we already have a
function to get the closest enemy to
a direction, right? We can use this function to get the enemy to the
input direction. But right now, this function uses the targeting direction, which is not what we want. What we can do is
instead of always using the targeting direction
from this function, we can take the direction
as a parameter. All right, let me just
remove this line. Now in here, instead of
targeting direction, you have to use direction. I'll also rename
this function to get closest enemy to direction. Okay, So now when we
call this function, we also have to
pass the direction. The direction in this case is the targeting direction
of the player. So we can just pass that
while calling this function. Okay? It looks like
the function's name was not changed because we
already had an error here. Just copy paste the name. All right, so now the
other should be gone. And I'll also change
the name over here. Here we also have to pass the
direction as a parameter. Again, I'll use player targeting direction
to get the direction. All right, so now
we can also re use this function for any
direction that you want. From the combat controller, we can call enemy
manager instance, get closest enemy to direction for the
direction I'll pass, the input direction
of the player. Okay, I'll show the result. Innovatible enemy to attack. Then I'll create
a nullable vector called direction to attack. This will be noted by default. If we have an enemy to attack, then we can set the direction
to a vector to that enemy. Okay? If the enemy
to attack is not, then direction to attack will be a vector from the player
to the enemy to attack. We can get that by
subtracting the position of the player from the position
of the enemy to attack. Okay, so we can pass this as the direction
for our attack. This will be a bit
more accurate. The player will
exactly rotate towards closest enemy in
our input direction while performing an attack. Okay, let's try testing this. All right, let me go
to the combat mode. Yeah, you can see that the
player is rotating perfectly. Okay. Now we can
perform attacks in different directions and we can target enemies
standing around us. All right, so I'll
stop the video here. Thanks a lot for watching and I'll see you in the next video.
27. Controller Input Setup for Combat: In this video, we'll set up controller input for our attack. Right now, we don't
have a way to attack the enemy or go to the lock
on mode from the controller, we'll be implementing
that in this video. Adding a controller input for attack will be
pretty straightforward. We can just use the
joystick button two, which is the X button
on the joystick. All right, we already have an input for that
called Fire three. We just have to rename
that to attack. And now we should be
able to attack by using the X button
on the joystick. Okay, next I want to be able to do the lock on from
the controller. For the lock on, we'll be
using the left trigger. That is the input commonly used in games like Assassin Screen. Using the left trigger will
be a bit more complex because the left and right
triggers of the joystick are axis, they're not buttons. Let's look at how to set it up. Since the left triggers an axis, I'll actually duplicate
my vertical axis and create a new one
called Lock on Trigger. This is going to be
a joystick axis. An axis, I'll choose
the ninth axis. Okay, nine taxis is the left trigger and the right
trigger is the ten taxis. All right, we have defined the
input. Now the challenges. How can we know if the
left trigger is down? We can't use the get button down function because the left
trigger is not a button. The only thing that we
can get is the value, the axis value of
the left trigger. All right, if I do this I
can get the access value. But the problem is we should
not keep changing this if the player holds down
the left rigger, right? Instead, we should
really change this if the left rigger was pressed
in the current frame, right? If we just check
something like the value of left trigger is greater
than 0.1 or something, then the problem is
when we hold it, we'll keep changing the combat
mode, We can't do that. Instead we need a
function, get button down. Which will only be true if that button was pressed
in the current frame. Okay? Unfortunately we don't
have a function like that. For axis, we only have button. Let's create that
function on our own. I'll create a new helper
script for this in the Util. I'll go ahead and create a
script called Joystick helper. Okay, here we can create a bulling function called
get left trigger down. All right, from here
we just to check if the left trigger was pressed in this
current frame, right? That means it was not pressed
previously but now it is pressed to check if it's
currently pressed or not. What we can do is we can
check its axis value. Okay, Lucent put, get access
draw to get its axis value. The name of the axis
is lock on trigger, we can check if its
value is greater than 0.1 I also want to take
the absolute value of this because we don't
care about the sign of this axis, okay? So if the axis
value of a lock on trigger is greater than 0.1 that means it is
currently pressed. Next, we have to check if
it was previously pressed. So we can use a boren
variable to store that. I'll create a variable
called previously pressed. It'll be false by default
and at the end of our function we'll set it to the value of
currently pressed. Okay. Now if the
left trigger was not previously pressed and
if it is currently pressed, then that means the trigger
was pressed in this frame. Okay, so we can return
true in that case, and otherwise we can just
return false. All right. This is all we have to
do. This will check if the left trigger was pressed
in the current frame. But I want to generalize
this so that I can reuse this function
for all our axis, like right trigger, the
right joystick and all that. Okay, To do that, what we can do is here, I'll just change the name of the function to get axis down. Instead of get
left trigger down, I'll take the axis
name as a parameter. Okay, here I'll
use the axis name. Then we can't just use a single boling variable to check if it's
previously pressed. Because now this axis can
be any axis in Ojostic, we have to store the value of all those axises instead
of this Boolean variable. We can create a dictionary with axis name as a key and
boling as the value. I'll just call this axis states. All right, now from here to check it's
previously pressed, we can just get the value of
axis states of axis name. This will help us understand if this axis was pressed
in the previous frame, we also have to update
its value to the new, to the currently pressed value. Okay? We don't need
the line anymore. We are doing that from here. When we get the axis like this, we will get narrower because the first time this
function is called, the axis state will
not have that axis. Here. We can just check if the axis state
contains that axis. If it doesn't contain,
then we can just add it. I'll call axis states to
add for the key L pass, the axis name and for the value L pass falls because we're
adding it for the first time. All right, so now we have
a generic function with which we can check if any of the axis on
our joystick is down. Let me just make this class
a singleton so that we can easily access it from
any script that we want. So create a public
static instant of the scal power and then from the awake function
I'll initialize it. Okay, now from our combat
controller we can just use joystick helper instance do get access down and
for the access we can just pass lock on trigger. Now this slowly return if the lock on trigger was
pressed in the current frame. Okay, let's go to Unity. First we have to attach the
script to a game object. So create a game object over
here called joystick helper. Let me reset its position and I'll add the joystick
helper script to it. Now if we play the game, we should be able to lock onto an enemy by pressing the left
trigger on the joystick. We should also be able to
attack an enemy by pressing the X button on the joystick.
Yeah, that is working fine. We have added controller
input for the compact. I'll chop the video here. Thanks for watching, and I'll
see you in the next video.
28. Stunned After Taking Hit: Here in this video, we'll be making some improvements
to our combat system. So the first improvement that
I want to make is I want the enemy to be
stunned for some time after taking an attack
from the player. Okay, so let me show
you why we need that. Just to make it easier test, I'll just remove
the second enemy. And if I go ahead and attack
the enemy while he's moving, you can see that right
after we attacked, the enemy just kept
moving back like nothing had happened, right? The problem with this is we
won't be able to perform combos and stuff if the enemy keeps moving
after the first hit, right? We don't want that. Instead we want the
enemy to be stunned for some time before going
back to the normal states. Okay, to implement that, we'll have to make getting hit functionality a
separate state, right? Right now getting
hit and playing the hit reaction is handled
from the male fighter, from this function, right? It can happen from
any of these states. If that happens, we
won't be able to control how the hit
reaction affects the enemy. What we can do is when we
play the hit reaction, we can change the
state of the enemy to a separate state called
getting hit or something. All right, first let
me create a state for that in here inside the
state's folder of the enemy. I'll go ahead and
create a new script and I'll call this
Getting Hit State. Okay, so this will be a state
of the enemy controller. And from the state still, go ahead and our right,
the enter function. From the enter function, I'll
just cache a reference to the enemy just like we
do from all our states. Okay, so we have to switch to the state when the enemy gets
hit by the players attack. Right from here we
can't just switch to the getting it state by calling enemy controller
getting chain state. The reason is because male
fighter is a script that is used for both the enemy
and the player, right? We did that because we want to reuse the code as
much as possible. From here we can't
write any line of code that is specific
to the enemy. Okay, if you do that, then it won't make sense when this function is
executed for the player. From here we can reference
the enemy controller. We have to figure out a way to notify the enemy
controller that the enemy got hit without referencing it from the
male fight descript. Okay, how can we do that? We can do that by using
a delegate over here, we can create an
action delegate. I'll explain what this is in a moment. First,
let me write it. To use the action delegate, we have to import the
system namespace. Yeah, over here we can
create an action delegate. And I'll just call this on hit. Okay, so what is this action class that
you see over here? So you can think of this as a special data type that can
store functions in it, okay? In this variable we can
store a list of functions. When we call this action, all the functions that are stored in it will
also be called. Okay? When we define
an action like this, we can reference it from the enemy controller and we
can store functions in it. Okay, let me show you
how we can do that. From here, we can access
the on got hit action, we can attach a function to it. We can do that by using the
plus equal to operator. Over here, I'll just
to find a function called react to it. Then I can store that
function to the on got hit action simply by using
this plus equal to operator. Okay? Now, this function will be stored in the got hit action. Now, if we call this action, let's call it from the play
hit reaction function. If I call the action like this, then what will happen is
all the functions that are attached to this action
will also be called, okay? So since this
function is attached, this function will be called. Then the on got hit,
action is called. Okay. So basically what we just did over here is we
found a way to notify the enemy controller when
the Malay fighter takes a hit without referencing the enemy controller
in the Malay fighter. Okay, so actions are a way to inverse the
dependency, right? Right now we don't have to make the male fighter depend
upon the enemy controller, but we are doing the opposite. Right? You're making
the enemy controller depend upon the male
fighter, which is okay. But making the male
fighter depend upon the enemy controller
is not okay, because this is something that
we re use for the player. All right, in such cases we can use the action delegate
to inverse dependency. Okay, when we define
an action like this, it is a good practice
to make it an event. The reason is because events add an extra layer of security
to the delegates. So let me explain what I mean. If this was not an event, then we would be able
to completely replace all the functions in this action by using
the equal to operator. If I do this, everything in
this action will be replaced. Allowing that can lead to mistakes because we
might accidentally replace it from some other place and the function that I attached
over here will be gone. Okay? It's always good
to use plus equal to attach the function to the existing list instead
of completely replacing it. The thing is, if we
make this an event, then we won't be able
to replace this, will get an error in
that case to the events. We can only add a
function like this. We can't replace it entirely. Okay. So yeah, it's always a good practice to make our
action delegates at event. Another thing is when we
call the action like this, we might get an
error if on got hit. Action doesn't contain
any function, okay? So just to be safe, what we can do is we can use an unconditional
operator and then call the invoke function, okay? So this is the same as
calling the action directly, but let's make sure that
we won't get narrower. If on got hit, action is not okay. Now, whenever an enemy
gets hit with an attack, we will call this
react to hit function. From this function, we
can change the state to the getting hit state, right? Yeah, from here we can just call the chain
state function and we can pass the getting
hit state. All right? We haven't defined
that state over here, so let me define it in the enum, and I'll also add it to
our state dictionary. Okay, Now I should be able to change the
state to getting hit. All right, this will work fine. But there's actually an
easier way to write this. We don't have to
define a new function and attach it like this. Instead we can just use a
lambda function like this. Okay, lambda function is just an easier way
to define function. Using this, we can define
functions in a single line. We don't have to define
it and add it separately. Okay, You can use the other
method if you prefer that, but this is just a
cool way to do it. Okay, Now whenever
the enemy gets hit, we'll go to the
getting hit state. And from the state once the
hit reaction is complete, we want to make the enemy
stunt for some time and then go back to the
combat movement state. Okay, from the
getting hit state, we need a way to know if the hit reaction is
complete or not. If you want to, can use the inaction property of the male fighter from
the execute function. But there is actually a more
simple way of doing it. We could again use an event and listen to that event
from the getting hit state. Let's actually do
that over here. I'll just replicate
this and create another event called
on hit reaction. Well, I'll just call it horn hit complete to keep it short. Okay, from here, once the hit reaction
animation is completed, I'll call horn hit complete
and I'll invoke it. Okay. Now from the
enter function of the getting it stated, we can just listen to the on hit complete event that's inside the male
fighter of the enemy. I'll call enemy fight
On hit complete. I'll just attach a
lambda function to it. From the function, I'll
call another corotine. Okay, I'll name the corotine. Go to combat movement
and runs routine. Firstile weight for a
small time just to give the impression that the enemy is stunned after the attack. While you seal return new
weight for seconds function. Let me actually define a
salalized field variable for the seconds to wait. I'll just call this
something like stern time. I'll sell it something
like 0.5 default. Okay. Now we can
use that over here. After that we can go ahead and change the state to
the combat movement. I'll call enemy chain state. I'll pass the combat movement, um, as the parameter. Okay. Now from here from the lambda function picking
is called chart corotinell, the combat moment, corotine. All right? Yeah, this is
all you have to do now. When the enemy gets hit, we'll go to the
getting hit state. Once the hit reaction
is complete, we'll wait for a small time and then go back
to the combat put. Okay, so this will make
the enemy stunned. Let's go it and test this. First we have to attach the getting hit straight
to our enemy prefab. All go inside the prefab
and attach it like this. Oops, I got the wrong script, so let me attach it again. All right, now let's
start testing. Now if I attack the
enemy while he's moving, you can see that the enemy
stops retreating back and stays in the stunt
state for some time. Okay, you can adjust the value of the stunt tim if
you want the enemy to be stunned for a longer time. Another thing I want
to change is in the enemy manager script when we select an
enemy for attacking. I'll also add an
additional condition over here to make sure that the enemy is in the
combat movement state. Okay, I'll use in
state function. I'll check if the enemy is
in the combat movement. Adding this condition,
we'll make sure that we won't select enemies
that are in other states, like getting hit fight. That will look really weird
if an enemy who is getting hit suddenly starts
attacking us, okay? Next, After implementing
the getting hit state, we'll have an edge case
that happens when the enemy gets hit before actually
seeing the player. Let me show you the issue first. So let me run the game. And if I hit the enemy before
the enemy actually sees us, then you can see that we have this null reference exception that is happening
from this line. And the reason why
it's happening is because the enemy got
hit before seeing us. After waiting for
the stunt time, we'll switch to the
combat moment state. Right, when we are in
the combat moment state, the enemy hasn't seen us yet. And the target will be null when we are
executing this line, which causes the null
reference exception. Okay, so currently
we are setting the target when we see an enemy from the
idle state, right? But this time we did not switch to the combat
moment from the idle state. Instead we switched from
the getting hit state. Okay? So to keep things simple, what we can do is in
the execute function, if enemy target the null, then we can go ahead and find the target and store
it in this property. Okay, so we have already
written the code for finding the target from the targets in range
in the ideal state. So we can also reuse
this code from here. Okay, what I'll do is I'll
actually move this code into a separate function and use that function from
all the places from where we need
to find the target. I'll actually move it into
the enemy controller Here I'll create a public
function called find target. This function will
return a male fighter. Okay? And to this function, I'll copy paste our forage
loop for finding the target. Okay, since we are in the
enemy control itself, we don't have to use enemy dot. We can access all these
properties directly from here. We don't have to set the target and change the state to
combat moment or anything. We just have to
return the target if the target is in the
enemy's field of view. Okay, so from here I'll
just return the target. And if we don't have any target, then I'll just return null
at the end of the function. Okay, so now from the idle state we can use
the fine target function. I'll call enemy fine target. And I'll store the result in
the enemy target property. Then if enemy target is not, then we can go ahead
and change the state to combat movement
like we do over here. Let me do that. This is all we have to do so we can
get rid of the old. All right, next we
can do the same from the combat open state
for finding a target. If the target is not,
let me call the enemy, do find target function and store the result
in the target. This will find the target if the target is currently not set. Okay, one more thing
I'll do from here is if enemy target is null even after calling
the fine target function, that means there is
some other issue and we don't have
any targets nearby. Right, So in that case
we can simply switch back to the idle state because we can't be in
the combat popen state. If there isn't any target, I'll call enemy state. And I'll change the
state to the idle state. All right, and I'd
also just turn from this function because we don't want to execute
any of the code below. All right, I'm just
trying this for safety. In most cases, the target won't be null after calling the
fine target function. All right, by the way, I made a mistake over here. This should be equal
to equal to null. Instead of not equal to null. I will set the target
if the target is null. Let me go to it and test to
see if the issue is fixed. All right, so now we attack
the enemy from behind. You can see that we don't
have that issue anymore. Yeah, we have made
an improve video. If you attack the enemy
while he's moving, he'll just stand there and
stay stunned for some time. We'll be able to perform
combos on the enemy. I'll stop the video here and I'll see you
in the next video.
29. Long Range Attacks: Ron. In this video we'll add long range attacks
to our combat system. If the player is standing at
a distance from the enemy, then the player will perform
the long range attack, and otherwise the player will
perform the normal attacks. Okay, so let's look at
how to implement this. First we have to
find an animation for a long range attack. I'll go to Miximo. This is the animation
that I'll use. Okay? The player covers some
distance in this attack. It's the E to be perfect for
a long range attack search. And you'll find this animation. I have already downloaded it. Under the Animations folder. Inside combat, I add the
spin slash animation. That's what I named
the animation. Okay, first I have to
rig this animation. So I'll make a Temanod
and use the pad of the Eric Archer now should be able to
preview the animation. All right, we don't want the last few frames
of this animation. All right. Right now the
rotation is not a look match. If we make this
animation a bit shorter, somewhere around here, the
loop match will become orange. I'll just change the end
to something like 24. Okay, so I want to make this loop match because
I don't want to use the root motion of animation
for rotating the player. The initial rotation and the final rotation
is almost same. So we could check baking interpose instead of
using the root rotation. And then I'll change the
based upon to original. Okay, then for the y
root transform position, I'll change the based upon feet. I'll also use bak interpose. Okay, so let me go ahead
and apply it next. Let's add this animation to
our animator controller. So let me just drag and drop the sprint slash
animation over here, and I'll make a
transition back to the empty state for the name. Let me just remove
the space between the spin slash because we haven't used spaces for
any of our animations. Okay, next let's strike the code for performing this
long range attack. I'll open up my male
fighter script in here, I'll create a new
list of attacks, and I'll call this
long range attacks. Then from the attack function, we have to check the
distance between the player and the
target of the attack. All right, if the distance is
above a certain threshold, then we have to perform
the long range attack instead of performing
the normal attacks. Okay, In this function. First, in reference to
the target of the attack, so that we can find
the distance between the player and the
target of the attack. What I'll do is in the
parameter of this function. Instead of taking the
attack direction, I'll take the target
of the attack. The target will be
a male fighter. I'll also make it a
default parameter because we should be able to try out the attacks even
without a target. Okay, and since we
have the target, we can find the attack
direction from it, right? We can subtract the position of the target and the player to
find the attack direction. Over here, I'll define the
attack direction by default. I'll just send it to the
forward vector of the player. But if the target is natcoonal, then we can find the
attack direction by computing the
vector to the target. We can get the vector
to the target by subtracting the position of the player from the
position of the target. All right, here I'll just
set the component to zero, because we only
want the direction in the horizontal plane. Then we can find the
attack direction by normalizing the
vector to target. Okay, here we have another. Because the attack direction
is not an ullible vector, it's just a normal vector. We can remove the value
property from here. Now when we call this function, we have to pass the target. Instead of the attack direction in the t to attack function. Also we have to change
the parameter to target. Okay, I'll pass that while
calling the attack routine. Now when we call the trite to
attack function from here, we just have to pass
the enemy to attack, the enemy to attack is the
reference of enemy controller. I'll call fighter to
get the male fighter. Okay, now we don't need these
lines where we calculate the direction because
we're already doing it from inside
the attack function. Okay, this should work
just like before. But now we also have a
reference to the target. And now we can find the distance between the player
and the target. And then decide what attack
to perform based on that. Okay, since we have already calculated
the vector to target, we can easily find the distance
by taking its magnitude. Right? Distance
will be vector to target magnitude. All right. Magnitude will be the
distance of the vector. And when we normalize it, we will get the direction
without the distance, okay? So next, if the distance to the target is greater
than a certain threshold, then you have to perform
a long range attack instead of performing
the normal attacks. Okay? So what I'll do is over here I'll create another
setlized field variable. And I'll just call
this something like long range attack
threshold, okay? And I'll set it to something
like 1.5 by default, okay? And from here, if
the distance is greater than the long
range attack threshold, then we have to perform the
long range attack, right? Right now we're referencing the attack directly
from the attacks list by using the Bo count
as the index, right? When this condition is
true, we can't use this. Instead we have to use the
long range attack, right? To achieve that, what
I'll do is I'll create a variable called
attack By default, I'll set it to this value. Attacks of bo count. Okay, I'll use
that variable over here instead of referencing
it directly from the array, From the list, not array. So let me change all the places where we are using
attacks of combo cook. And I'll replace it with
the attack variable. Okay, let me also change
it over here. Over here. Okay. I hope I miss any place. We can just sell
the strip it and do a search just to
confirm it. Okay. Yeah. We have changed
everything now. What we can do is if distance is greater than the long
range attack threshold, then we can set the attack as long range attacks
of zero, okay? So since you only have a
single attack in this list, I'm just taking
the first element to make this more interesting, what you can do is you can add
more attacks to this list. And you can just take a random long range attack so that you'll have some
variety in the combat. But I'm just going to take
the first element because we have a single long
range attack right now. Okay? All right,
so this is all you have to do to perform
the long range attack. So let's go to the
T. And first we have to attach a long range
attack to the meli, fight descript of the player. Right, so we have to create the attack
scriptable object first. I'll just duplicate
our slash attack, and I'll rename this to Spin. Okay, I'll also rename the
animation name from here. We also need to find the impact, start time, and the end time. Let's check the
animation to find that. Okay, so we can start the
impact somewhere around here, around 54 percentage. We can end it
somewhere around here, around 84, 85 percentage. Okay, let me go ahead and input those values into the
impact Start, time and time. Start time will be 0.54
and the end time will be 0.84 All right, now let's attach this attack to our long range attack list. All right, now when
we test the game, if you attack the
enemy from a distance, you can see that the player
is using a long range attack, but he's not
attacking correctly. Right? Let me just
run it again to show you if I attack from here. He's not moving in
the right direction. Okay, That is because the player is using the root motion
of the animation to move. But for long range
attacks like this, we don't want to depend
upon the root motion. Instead we want to make
sure that the player is moving in the direction
towards the enemy. Okay, so we'll have to move the player manually
from the code, instead of depending upon the
root motion of the player. What I'll do is for each attack, I add in optional property
called move to Target. And if that is turned on,
then we'll move the player towards the target of the attack while the
attack is being performed. Okay, we need this because we can't depend upon the root
motion for every attack. Yeah, let's go ahead
and implement this. Open up the attack
data script in here. Let me just duplicate this, and I'll create another
property called Move to target. Okay, so this will be a
bulling property that will indicate if we should move towards a target
while attacking. Okay, for this to work, we also need some
additional parameters. So we'll need a float called
distance from target. Okay, so let me
explain what this is. We don't want to move to the exact position of the
target that look really weird. The player and the target will overlap at the same
position if you do that. Instead of that, we
need to stand at a distance from the
target while attacking. Right? It'll be around 1 meter or 0.78 meters or something. Okay, So I'll just set
it to one by default. Then we also need
another float variable, and I'll call this one
max move distance. This will be the maximum
distance the player can move while performing
the attack, okay? So this is just to make
sure that the player won't be able to move
really long distances. If the enemy is far away, that will make the attack
look very unrealistic, right? The default value, I'll
give something like three. Now when we are
performing the attack, we have to move
the player towards the target If the motor
target bullying is true. Okay, What I'll do is over here first I'll create a vector three called start position. This will be the starting
position of the player. All right, and then I'll create another vector three for
the target position. This is the position that
the player must reach. Initially, I'll just set it to zero and we'll
find it from here. Okay, the target position
will be the position of the target minus the distance from target offset, right? We don't want to reach the
exact position of the target. We surely reach at this distance
from the target, right? The target position will be
the position of the target. Minus attack direction, multiplied by the
distance, target offset. Okay? We can calculate the
target position like this, by the way, by calculating
the distance here. Also, we can subtract the
distance from target. Because our player won't be
moving this whole distance. The player will be standing
at this distance, right? Yeah, we can calculate the
target position like this, but one thing that
we are missing here is we're not considering the maximum distance
if he just uses. The problem is if the enemy
is far away from the player, the player will move
long distances, which will make it
look unrealistic. We also have to check
if the distance is less than the
maximum distance, right, then we can calculate the target
position like this. I'll just change this to
less than or equal to. And otherwise, the target position should be calculated based on
the maximum distance. All right, so the
target position will be the starting position plus the maximum distance in
the attack direction. Okay, let me do that. The target position to
starting position plus attack direction multiplied by the maximum distance, okay? And by the way, we really
need to do all this if motor target
pulling is true, okay? As I'll check the value
of motor target pulling and I really calculate the target position if
motor target is true. All right, now we're calculating the target position Next, while performing the attack, we have to move the player
towards the target position. Right from here I'll check
if the target is not et attack Mott is true. If both these
conditions are true, then we can move our player
towards the Taggert position. For that we can just
use a simple lope. Let me call vector three lob. I'll pass the starting position
as the first parameter, the tag position as
a second parameter. For the time, I'll just
pass the normalized time. Okay, let me set this position back to the
position of the player. This will move the player
or let me just call this attacker
because male fighter is common for both the
player and the enemy. So this will move
the attacker towards the target while
performing the attack. Okay, so now we can
go to humanity, and once the script
reload is complete, we should have the new
parameters over here. By the way, let me just
add a header to segregate these over here in
the attack data. I'll go ahead and add
a header attribute. I'll just call this
move to target. Okay, this is throwing an error because for colon
syrilized field attribute, we also have to use
field header attribute. Okay, that should fix zero. And now this looks much better for the
sprint slash attack. We can turn on the
motor target checkbox. And this should move
the player towards the target while performing the attack. Let me
try to distinguish. Yeah, you can see that
performing the attack, the player is moving in
the right direction. Okay. But I think the distance
from target is a bit high. That's why the player is
not reaching the target. Let me just reduce
it to something like 0.5 and try it again. Now we can see that while performing the
long range attack, the player is moving in the right direction and the player is also
hitting the target. Okay, So the long in attacks
are not perfect, right? Now If the enemy charges towards us while
performing the attack, then we'll go through the enemy. Because we calculated
the target position before the attack, right? We calculated this
before the attack. But the enemy moved
from this position while the attack was
being performed. Okay, we'll fix that
issue in the next video. So, yeah, I'll drop
the video here. Thanks for watching and I'll
see you in the next video.
30. Moving To Target Improvements: Here in the previous
video we implemented long range attacks and
we created a system for moving the attacker towards the target while
performing the attack. Right, in this video we'll
be making some improvements to the system and we'll also fix some issues
that we currently have. Okay, right now we have a system to move the
attacker towards the target. But one limitation of this is
the movement to the target will always happen throughout the entire duration
of the attack. Okay, That works fine for
the spin slash attack, but it might not work
for other attacks, okay? For example, we could also use this system to move
our character a little while performing
our normal slash attack, this one, right? But for this attack,
we can't move the character throughout the entire duration
of the attack. Because after this part, the character is pulling back the sword and going to
the ideal position. Right? During this part we
should not move the character. But yeah, in the first
part of the attack, we could add a little bit of movement so that the player
will reach the target and the chance of hitting
the target is more okay. So to be able to implement that, what we need is we need
to be able to specify a time range of the attack in which the
movement should happen. Okay, if you specify that, then we can make sure
that the character slowly move in that time range. Let's go ahead and
implement that. Let me open up the
attack data script here. I'll just duplicate this. And I'll create another
property called start time. This will be zero by default, and then I'll duplicate that
and I'll create move time. And this will be one
by default. Okay? These properties will
take normalized time. Zero means start of the attack, and one means end of the attack. Okay? By default
the movement will happen throughout the
entire attack animation. But if you want,
we can change this right now in the
maley fight descript, when we are moving the
character towards the target, we can make sure that the
movement only happens between the start and end time that
we specified over here. Okay, in the L, we can't simply pass
the normalized time, because if we do that,
then the movement will happen throughout the
entire attack animation. Instead of that, we have to find a percentage time from our normalized time
based on the move, start and end time
of the attack. Okay, we can easily find a percentage by using
this formula that is normalized time minus start time divided by end time
minus start time. Okay? So this will give
us the percentage of the normalized time in between the start time
and the end time. All right, I'll just call
this percentage time and I'll pass it as the
time parameter of the lobe. Okay, now we can test this for the slash attack. I'll turn on move to target. And here we have to reduce
the maximum distance, because this is not
a long range attack. I'll reduce it to something like one or something even smaller, like 0.8 Okay, distance
from target will be 0.5 just like a spin attack. Then we have to
specify the move, start time, and end time. The default values are
not correct over here. I guess I saved it before
actually setting the value. Not a problem. We can
just select everything and change the
default value to 0.1 Okay, but we don't want to move the character
throughout the entire attack. Let's find out the
start and end time by looking at the animation,
the slash animation. We want the movement to
happen only until this point. Somewhere around here, 46, 48. That's right after that the player is pulling
back the sword, so we don't want to move
forward during that part. Okay, so I'll go back to the
attack scriptable object, and for the end time I'll
give 0.46 for 46 percentage. Okay, so now we can try
testing this and okay, looks like I made a
mistake in the formula, so this should be
attack start time. All right, so now let's
start testing it again. Go close to the enemy
and perform the attack. You can see that
our character is moving forward while
performing the attack. The chance of fitting
the enemy is okay. Next I want to fix some issues
that we currently have. To show you the
issue, what I'll do is I'll just
duplicate this enemy. And I'll create another enemy
right next to it like this. Let me actually keep
them a bit closer. Now if I go ahead and
attack from here, you can see that both enemies are playing the hit animations. I don't really like that.
With our normal attacks, we should only be able to
attack a single enemy, right? It is just my preference. If you want, you can
keep it as it is and let multiple enemies take
hit from a single attack. But I don't really
like this. Let me go ahead and prevent
that from happening. To do that, what I'll
do is I'll create a property called Current
Target over here. Actually we just need a
private paper because we'd only be accessing it
from the male fight descript. I'll set the value
of this from here. At the start, I'll set
the current target to the target that was passed
to the attack function. At the end, I'll set
it back to null. Okay? So this variable will store the target that we
are currently attacking. And by the way, since
we're storing it, we also have to make
sure that we're passing the target whenever we
are calling the attack, we're also calling
it from here to perform the combo
right from here. Also we have to pass the target. Then we also have to
pass it even when you're calling the try to attack function to perform
the enemy's attack. Right now we're only
doing it for the player, but we can also easily
do that for the enemy. When calling tried to attack, we can just pass enemy target as the target of the attack. Okay, So now we
are always passing the target and the target will be stored in the current
target variable. Okay, so now when we detect a collision between a
weapon and the character, what we can do is we can
check if the current target of the attacker is this
character who took the hit. Okay, so this is a attacker, so let me actually store it in variable because we'll
have to reuse it. All right, from here we can
just say attacker transform. Then we have to check if
the current target of the attacker is same as the
character that took the hit. Okay, if it's then
we can just return. If it's not equal to
this, then I'll return. And I won't play
the hit reaction. Okay, we'll only play
the hit reaction. The hit box hits the
intended target. All right, now when
we perform a hit, both of these characters
should not play the hit reaction, right? Only one will play
the hit reaction. That is much better. Next, there's another
issue I want to show you. If I keep chasing this character while
that character is in the retreating state, then that character will keep
moving backwards like this. All right? We can fix that issue pretty easily in the
retreat after state. Right now we are
stopping that state and going back to combat
movement only after the distance between the
player and the enemy becomes greater than
this threshold, right? So the problem with this is if we keep following the enemy, the enemy will always
move backwards, right? So it fixes from here. Instead of checking the distance between the enemy and the
current position of the player, we can check for the
distance between the enemy and the position of the player at the start
of the retreating state. Okay, over here I'll just create a vector three table
called target position. And I'll store the initial
position of the player. In this we can get that from enemy target transform position. We can pass that into our function for
calculating distance, okay? Now that issue should be gone. Okay? Let me also
hit the other enemy. Now if I chase the enemy, you can see that the enemy will stop after a certain distance. Yeah, that issue is fixed. So I'll stop the video here and I'll see you
in the next video.
31. Combat Improvements & Edge Case Fixes: In this video, we'll make some improvements and fix
edge cases in combat. Right now in the combat system, the player can
just win the fight by spamming the attack button. We'll pin such things and we'll make it so
that the player will have to fight strategically by using counterattacks
and all that. Let's start the video first. Let me clean up the
code a bit so that we can avoid issues and edge cases. So the first thing I
want to do is over here, while we're passing the enemy to the try to attack function, I want to use a null
conditional operator. All right, if we don't do this, then we'll get an exception
when we try to make the player attack when
there is no target enemy. Okay, we need to add this null
conditional operator over here in the attack function of the male fighter over here. I want to add another
condition and make sure that there is at least
one long range attack. I'll check long range attacks do count is greater than zero, then I'll set the attack
as the long range attack. Okay, If you don't do that, then we'll get an
exception if no long range attacks we
added to this list. Okay, So the next issue that I want to fix is in
the getting hit state. If we enter the
getting hit state again before this core
routine is complete, then the problem is we'll go to the combat pit state from the first time the getting
hit state was called. Okay, This issue is not noticeable because our
stunt time is really small. But if your stunt
time was like three, 4 seconds, then you'll
really notice this issue. All right. It fixes. What we have to do is while entering the getting hit state, we have to stop any cotines
that were previously running. Okay, so to do that we can just stop all cotines function. This will stop all the cotines cell running
in the script. Okay, we have done
some clean up. Next I want to make the
player move towards the enemy at for the horizontal and
the Spartachic attack also. Okay, right now we're
only doing it for the slash attack and
the spin slash attack. But we can also add
a little bit of movement for these two attacks. First, let me do it for
the horizontal attack. I'll turn on move to target. For the distance from target, I'll set something like 0.9 I'll reduce the max
more distance to something like 0.8 I believe that was
the value that we used for the slash attack. I'll use that same
value over here. Then we need to
find the move start time and the move end time. Let's look at the
animation for that. This is the horizontal
slash animation. We can start the movement from the starting
of the animation. We can end it somewhere around here, Around 36 percentage. Okay, We don't want any
movement after that, because the player is
pulling back the sword. After that point, we
want the movement to happen 0-36 percentage. Okay? In a horizontal attack, the move start time will be
zero and the move and time will be 0.36 Okay. Next, let me also add
movement to the kick attack. I'll turn on more to target, and again we'll change
the distance to 0.9 I have already played
with these values. I know that 0.9 is
like a good value. You might be using different
attack animations. You can play with
these values and find the best value that
suits your animation. Okay, next, tell, change
the maximum distance to 0.8 then we have to find the start time
and end time again. Let me look at the
animation. Okay. We can start the bopen at
the start of the animation. We can end it
somewhere around here. Once the kick is complete, that is 44 percentage. Let me go back to the attack
scriptable object here. The start time will be
zero and the end time will be 0.44 All right. Oops, I just change it for
the spin slash attack. I have changed for the
Sparta kick attack instead. Okay, yeah. Both these attacks should have similar distance from target
and maximum distance. Because they are
like combo attacks that should be performed
from the same distance. The maximum distance
is the same, but distance from target is 0.5 We can actually
make it 0.9 itself. It's good to have the
distance from target of our combo attacks has
the same value, right? Because those attacks will be performed from the same spot. Yeah, let's try testing this. Okay, let me bring
him over here. Yeah, you can see that
the player is moving towards the enemy while
performing each attack. All right, so that
is working fine. The next thing I want to improve is right now the enemy won't be able to hit the player
while the player is attacking. Okay, The problem with
this is the player can just spam attacks continuously and the enemies won't be
able to hit the player. Okay, So let me just show
it to you if I go over here and if I spam attacks
on an enemy like this, then the other enemy
won't be able to hit me. Okay? I can just spam attacks and I'll
eventually win the fight. That should not be possible. We want the player to fight more strategically by using
counterattacks and all that, instead of just spamming
attacks at the enemy. Okay, the reason why this is happening is because right now, when a hit is registered, we really consider it if
the character that got hit is not in any
other action, okay? If the player was in an action
like performing an attack, then that hit won't
be considered. We can't use this because
this will allow the player to spam attacks and not get hit. Instead of this, what I'll do is I'll create another
weighty pull over here called is getting
hit or is taking it. Okay. I'll set it to false, where default and then on the play hit
reaction function while setting in action, I'll also set is taking
it to true and at the end I'll set is
taking it to false. Okay? Now from here, instead of if the player
is not in action, we can check if the
player is not taking hit. All right, so now the character won't take a hit while he
is already taking a hit. But he'll take hit during other actions like
performing attacks. By the way, I'll also add
another condition here. I'll check if the player
is not in counter, we don't want the
player to take hits while performing
counter attacks. Okay, by the way, since we have this
is taking it fab, we can also check that while we're attacking
from this ile loop, if it becomes true, then we can just break
out of the while loop and stop that attack. Okay, so let's just this now. All right, let me go ahead and try spamming attacks
on this enemy. Yeah, now we can see that
while I'm attacking, I'm also getting hit, right? But another problem
that I can see here is the player can attack
immediately after taking a hit. Right? Unlike the enemy, we're not preventing
the player from attack while the player
is taking a hit. Okay, so we have to
prevent that from here. From here we can add another condition and check if the player is not taking hit. Okay, the variable that we created over here
is a private variable. We can just make it
a public property so that we can access
it outside this class. All right, let me add
and a private setter. And I'd also make the first letter upper case so that we are following
the naming convention. All right, from here I'll check
if male fighter is taking hit is false and we truly be able to attack
if we're not taking hit. Okay, so now it's
St testing this. Okay, so now we can
see that we are not immediately attacking
after we got it right. But one more thing
I would like to improve is I would like to change the target of the
player once we get hit. Right, right. Now if I'm currently attacking this enemy and if the other
enemy comes and attacks me, then after taking the hit, the player is turned
back to the old enemy and attacking him, right. Once the player gets
hit by an enemy, the player's target
change to that enemy. Let's also change that from
the combat controller. Whenever the player gets hit, we have to change
the target enemy of the player right from here. How can we know when
the player is hit? We can use the on got hit event. For that, we have
been using this from the enemy controller to be notified when
the enemy gets hit. We can do the same thing from the combat controller to know
when the player gets hit. Okay, I'll actually do it
from the start function. So let me find the
start function. I usually like subscribing to the events from the
start function. Instead of the age function, I usually only use
a wake function for initializing the references. Okay, from the start function, I'll subscribe to the on hit
event of the male fighter. All right, so this function will be triggered whenever
the player gets hit. And when that happens
we can check if the target enemy of the player and the attacker who hit the player is different. Okay, and if they are different, then we can change
the target enemy to the attacker who
hit the player. Right, from this function, we need to know the
attacker who attacked the player in the
on got hit event. We can actually add a
parameter for the attacker. To add a parameter
to the action, we just have to use its
generic form and specify the type that we want to pass as the parameter like this, okay? And once we do that, we
will have an error over here because this event
requires a parameter. But we are not passing
the parameter that's right from here when
in working the event. We can also pass the
attacker as a parameter. But the attacker that we get
over here is the transform. The parameter that we defined over here
is a male fighter. We can actually get the male fighter in
this function also. Instead of passing
attacker dot transform, we can just pass the attacker itself which is
the male fighter. I'd also change it over here. Now we have to use attacker
transform dot position. Okay, now our on hit event has a parameter
for the attacker. Now when we're attaching
a function to it, that function should also have
a male fighter parameter. Okay, we're also attaching functions to it from
the enemy controller. I'll just write and find
all its reference here. Also attaching a function from
the enemy controller here. Also we have to define a male
parameter for the attacker. All right, now back in
our combat controller, when the player gets hit, we can check if the attacker and the target enemy of
the player are different. Right, if they're different, then we can change
the target enemy. From here I'll check attacker is not equal to
the target enemy. The target enemy
is a reference of enemy controller what we
want to male fighter. So I'll target enemy fighter. Okay, if they're different, I'll change the target enemy to the attacker who
attacked the player. Okay, again, since the
attacker is a male fighter, we'll have an Ab and we try to assign it to the
enemy controller. From here we have to use
Et component and get the enemy controller okay. By the way, we only
have to do this if we are in the combat mode. So I also had a condition
for that over here. Now when an enemy
hits the player, we'll change the target enemy of the player to that enemy
who hit the player. Okay, let's go to Unity
and testing this. Okay, that did not work. As I expected, the player
should have attacked the enemy who attacked her
last. But that did not happen. I posted the video and found out the issue here in the
combat controller. When we are choosing
the enemy to attack, we are taking the input
direction, right? The problem with
the input action is that if we do not give
any directional input, then the input
direction will be zero. All right, when we
pass zero over here, the enemy to attack will not be selected correctly, right? If we can just prevent that
by adding a check over here. So if the input action
is not equal to zero only then we should use
the input action, right? Otherwise, we should just
use the forward vector of the player for the direction
to find the enemy, right? So we can actually put this in a separate function
to make our cleaner. Let me copy this whole thing. In the Player
Controller, I'll create a new public function called
Get Intent direction. Okay, and here are
the spaces here. We don't have to use
Player controller I because they are already in
the Player controller script. All right, so we just
have to return this. Now from here we can just use the get intent
direction function. And it'll give us
the direction in which we should th up
the enemy to attack. Okay, I'm just adding an extra condition to make sure that the
direction won't be zero. All right, so now
let's try distinguis. All right, so now we can see
that when an enemy attacks, we are changing the target of the player to that enemy
who just attacked. That looks much better
than the player continuing to attack the enemy she was previously attacking.
I'm happy with this. Yeah, we have done lots of improvements to the combat
system in this video. I'll stop the video here and I'll see you in the next video.
32. Health, Taking Damages & Dying: Hayborn. In this video,
we'll implement health, taking damages and dying for both the player
and the enemy. First, let's start
implementing a health system. I'll open up the
male fight descript. And over here I'll add a
property for the health. The good thing is if we add the health in
the male fighter, it'll be added for both
the player and the enemy. We won't have to add it separately for a
player and the enemy. All right, so I'll
add it over here. I'll create a float
variable called health. I'll actually make
this a property just so that we can access the health from other scripts in case if you want to check if the player is dead or not. Okay, I'll make it
a property with the private setter and I'll set its default value to
something like 25. Okay. We can change this later. Let me make this Cz
field so that we can set the starting health
from the inspector. Okay. Since it's the property, we have to specify it as
field colonized field. All right, so this will be
the health of the character. We have to reduce the health whenever the character
takes a hit. Right? Whenever the
character takes a hit, we also have to
reduce the health. I'll actually create a separate function for
reducing the health, and I'll call this damage. This will take a
float variable for the damage from this function. We just have to reduce the
damage from the health. Okay? And we can also clamp it to make sure that the health doesn't
go below zero. It's not necessary,
but I don't like my health br reaching
negative values. So I'll just make sure
to clamp it at zero. All right, now whenever
we take a hit, we can also reduce the health by calling the take damage
function for the damage. I'll just give
something like five for now later we can make it so that the damage will
change based on the attacker and the attack
that he's performing. Okay, but for now
I'll just a five. All right, this will reduce the health of the character when
he takes an attack. Next, if the health
becomes zero, then that character
should die, right? The way I want this to
work is surely play the hit reaction
if the health is greater than zero after
taking the damage. Okay, if the health
is greater than zero, then we have to play
the hit reaction. Otherwise we have to
play a death animation. All right, I'll create a separate function
for that. Over here. I'll just call this play Death. I'll take the attacker
as a parameter just in case you want to play different animation
based on the attacker. For now I'll just keep
it simper and play a single death
animation every time. We can just call
that function from here if the health is
not greater than zero. Okay, so now from this
function we have to play the death animation for that first we have to find
a death animation and add it to our
animated controller. Right? So I'll just go to Maximo and I'll
find a death animation. So this is the animation
I want to use. You can search for die
and you'll find it. All right, so yeah, go ahead and download
this animation. I have already done that.
I'll go back to Unity and I'll import this animation
to our combat folder. Okay, so this is the
new death animation. So first let me it. All right, let me choose the Erica Archer and
rig this animation. Now I should be
able to preview it. Okay, so in the
animation settings, I'll just change the
based upon defeat. And I'll just keep everything
else as it is now. Let me add this into our animator controller and I'll just call this something
like far back death. Okay. Since it's a
death animation, we don't have to create
a transition back to the empty state because the player will be dead after
playing this animation. Right from the play death
animation function, we can just call
animator cross fade and we can play the back
death animation. Okay, so now when a
character takes a hit, he'll play the hit reaction or the death animation based
on the value of the health. Okay, if the character died, then we'll also have to
do a few other things. For example, if the character
is a enemy character, then we have to go to
the dead state, right? If you don't do that, then the enemy character
will still try to attack the player after
he's dead, right? We don't want that. We have
to go to the dead state, just like we do when the enemy
dies from a counterattack. Okay, here we don't know if this character is a player character or
the enemy character. The male fight descript
is common for both. Right, so we can't just
change the state from here. We were able to do it
for the counterattack because counterattacks
could only be performed on enemies. We knew that this
character would always be an enemy, right? But taking damage is common for both the player and the enemy. From here, we won't know if this character is
player or the enemy. We can't change the
state of the enemy to the dead state from here. Instead what we can do is
from the enemy controller, we are already listening
to the on got hit event of the male
fighter, right? We can handle that from here if the health of the
character is zero. Okay, so the got hit event is invoked from the play
hit reaction function. We can just move it outside the play hit reaction
function and put it somewhere over here so that it will always be
called when we get a hit. We also want this event to be invoked when the
health becomes zero. And when the player dies, right, I'll just
move it outside. And now from the
enemy controller we can just add brass
to this function. From here we can also check
if the character died. All right, so I'll check if the, if the fighter health
is greater than zero, then we can just
go to the getting hit state just like we're
doing right now. Okay. Otherwise we have to
go to the dead state. Okay, I'll just call the chain state function and
I'll pass the dead state. All right, so when we do this, there will be an edge case
that we'll have to solve the edge cases from
the getting hit state. We'll actually wait for some
time before going back to the combat movement right here, I'll just have to add a
single check and make sure that once the enemy
is in the dead state, we should never go back to
the combat movement state. Okay, so we should really go to the combat movement if the
enemy is not in the red state. If you don't do this, then
we'll have an edge case, where the enemy will go to the combat movement
even after he is dead. Okay, so we can just add
this check to prevent that. Yeah, this is all we have to do. So score Unity and test this. By default, the enemies and the player will
have a health of 25. You can adjust and use
a value that you like. The player's health is
zero for some reason. I'll just make it 25. Okay, I'm just
giving a low value here because it'll be
easy for me to test. I won't have to perform like lots of attacks to
kill the enemy. Yeah, now let's
start testing this. So if I go attack one of the enemies after
like five attack, you can see that
the enemy is dead. Okay, the same will
be the case for us. After getting fewer
attacks, we will also die. So let me show you that, yeah, if we take few attacks
from the enemy, our health will become
zero and we'll also die. Okay, Yeah, you can see
that it is working, but we have a few issues here. Even after we're dead, the enemies are
still attacking us. We don't have to worry
about this much, because once the player is dead, we'll go to the
game of our state. We'll go to some menu where
we can restart the game. But still, we don't
want the enemy to attack right
after we're dead. We want our enemies to
be smarter than that. Another issue right now we
can move the player violated. We should also prevent that to stop the player from
moving. Once she's dead. What we can do is here in
the player controller, while you're checking
for the inaction, we can also check
if the health of the male fighter is less
than equal to zero. If that's the case, then
we'll just return from here and we won't execute the code for moving
the player and all that. Okay, if you add this, then the player won't be able
to move after he's dead. Next, we should stop the enemies from attacking the player once the
player is dead. Right, That what we can do is from the update function
of the enemy controller, we can check if the target
of the enemy is dead. From here, I'll check if target health is less
than O eicozero, which means the target is dead. If the target is
dead, we can remove it from the targets
in range list. Okay? And then we can
also remove this enemy from the enemy's in range
list of the enemy manager. So that the enemy manager will not choose this
enemy for attack. Okay? And then we have to change the state of this
enemy to idle state, okay? But I don't want to do
it from here itself. The reason is because
it'll look weird if the enemy just suddenly goes to the idle state when
the player is dead. Instead, I want some time to pass and the enemy to retreat back before going back
to the idle state, okay? So I want to go to the
idle state from here. Instead, I'll do it from
the combat movement state. From here, I'll check if enemy
target health psamiclzero. If that's the case,
then I'll change the state to the idle state. Okay? I'll also reset
the target to null. And I'll just return from here so that we won't execute
any of the code below. Okay, so changing the state to idle from here
will be much better. Otherwise it'll look weird
if the enemy just changes to idle right after
performing an attack. Okay, so let's try testing this just to make things easier. At rest, I'll change
the health of the player to something
smaller, like ten. So that the player will
die in one or two hits. Okay, so let me test this. All right, so the players
did and the enemy should go back and go to the
idle state, okay. Now we can also move the player, since we're preventing that
from the player controller. Okay, from here you can do other things like go to the game off
screen and all that. That is up to you. We're not building an entire
game in this series. We're building the
combat system. You can build upon
this and do the rest. We implemented health, taking damage and dying in this video. I'll have the video here.
Thanks a lot for watching. And I'll see you
in the next video.
33. Alerting nearby enemies when a target is spotted: Hey everyone. In this video
we look at how to make our enemies at other enemies that are close to them
when they spot a target. All right, right now the issue is I can just go
ahead and attack this enemy, and the other enemy won't be
alerted about this attack. Okay, that seems like a really dumb behavior
for the enemy. Instead of this, when an
enemy detects the player, that enemy should also alert
all the nearby enemies. All right, let's implement that. Whenever an enemy
detects a target, that enemy should also
alert the nearby enemies. And set the same target
for the nearby enemies. Okay, let's create a function for that inside
the enemy control. Over here I'll create
a public function called alert nearby enemies. Okay? And in this function, first we need to get all
the enemies that are close to the current
enemy, right? So we can detect the
nearby enemies by using a overlap box, right? So what this will do
is this will turn all the O that overlaps with
a box that we create, okay? We can specify the
dimensions of the box, and this function will turn all the objects inside that box. Okay? So here we are to specify the position and the
dimensions of the box. The position, I'll just
pass the position of the enemy for the dimension. Let me just pass a
vector with ten in the x and z axis and one
in the y axis. Okay? So the height of the box
will be 2 meters total, because this is half
extents, right? You can see that the second parameter
is the half extends, which means the height will
be the double of this. The height will be 2 meters and its length and width will
be 20 meters, right? So we can actually make
this a variable so that we can change it
from the inspector. So here I'll create a property called alert
nearby enemies range. Okay, let me set it to 20 by default and I'll also
make it a citrilized field. All right. Now, begin this past that as the
dimension of our, our lap box. The next parameter
we have to pass rotation of the overlap box
that we want to create. We don't want to
have any rotation so I'll just pass cotton identity. For the final parameter, we have to pass the layer in which we should
look for collisions. This is an optional
parameter but it's good to pass the
layer because we know that all our enemies will be
in the enemy layer, right? So there's no need to check
for other game chicks, we just have to check
for the enemies. So we can do that by
passing the enemy layer, so we don't have a reference to the enemy layer from here. I guess we can add reference
to it from somewhere like the enemy manager here, I'll create a zerlized field property for
the enemy layer. Okay, and we can pass that
as the fourth parameter. Okay, so let me go to the enemy manager script and assign the enemy
layer before I forget. Okay, so now back in our code, this function filter, turn all
the enemies in this range. Basically, it'll get
all the nearby enemies. And by the way, let me
just make this name a bit more shorter, like alert range so that
the code is not that long. That's just my
personal preference. All right, so this function
will return a list of colliders that overlaps
with this box. So let me just show that in
available, called colliders. And then we can use a
for each loop to loop through all the colliders, okay? And if the returned collider
is actually an enemy, then we have to alert
that enemy by setting its target as the target
of this enemy, right? First thing we can do is
this function will also return the current enemy on which we're running
the school, right? We don't want to alert
the current enemy because it already
spotted the player. We can skip the current enemy. If the game object of the collider is equal to
the current game object, then we can just
skip that enemy. I'll do that by calling
the continued function. Okay, Then from the collider, I'll get the enemy
controller component right. So the colliders can
be of any game object, but we only warn
the game objects that are enemies, right? By getting the enemy
controller component, we can confirm that the
collider is an enemy. So let me stow this in
available, called nearby enemy. And then if nearby enemy is, if the nearby enemy does
not already have a target, then we can go ahead and alert that enemy
and set its target. Okay, so also check if that enemy doesn't have
any target right now, we have to check if the
target is equal to, equal to n. If that's the case, then we can alert that enemy
by setting its target. Set its target to the target
of the current enemy, okay? And then after
setting its target, we also have to change its state to combat movement state, right? Once an enemy has a target, we have to go to the
combat movement state. This function will alert
all the nearby enemies. Now we have to
call this function whenever an enemy
spots a target, right one places
from the idle state. If the target is not null, that means the
enemy actually spot the player right from here. Along with changing the
state to combat movement, we can also call enemy
alert nearby enemies. And alert all the enemies
that are close to this enemy. Okay, so when we spot the player and
when we set the target, we also have to
call the function for alerting the nearby enemies. Okay, another place
from which we are detecting the target and setting it is from the
combat moment state. The reason why we have
this code over here is because there's a
chance that we might reach the combat movement state
before the enemy even saw the player right cases. If the player hits the
enemy from behind, the enemy will go to
the ten kit state. And then the enemy will go to the combat movement when the enemy reaches the
combat moment state. If the target is not, then
we are finding the target by calling the find target
and setting it right. Since we're setting
the target from here, we also have to call the alert nearby enemies
function from here. But I think there's a
better way of setting the target now since we have the on got hit event in the male fighter and since we are getting
the attacker from here, we can actually set the
target from here itself. We don't have to wait to reach
the combat permit state. And we don't have to
find the target again. The target is already passed as an attacker in the
on got hit event. Right? Instead of
doing it from here, we can do it from the
on got hit event. So I'll actually
remove this code. Okay, from here. If the health is
greater than zero, that means the enemy took
a hit and is not dead. In that case, we can also set the target and alert
the nearby enemies. I only want to set the target if the target is currently null. All right, if the target is not, we can set the target
after the attacker. This is much more efficient. We don't have to call the fine
target function from here and loop through all
the possible targets and all that, right? Then after setting the target, we also have to call the
alert nearby enemies. Okay, yeah, this is all you have to do to
alert the neighbor enemies. So let me go to Unity. And first I'll increase
the health of the player. 200. We can test for some time without
the player being dead. Yeah, now let me
try testing this. All right, so now if
I attack one enemy, the other enemy will
also be alerted. Okay, we can also try placing the other enemy a bit away from the current enemy. If it's within the
ten metre distance, then the other enemy
will be alerted. So let me place it
somewhere over here, within a ten meter distance. Now if I try
attacking this enemy, you can see that the other
enemy is also alerted. Okay, so we can also try placing the other enemy at
some other location like this. And if it's within ten meter, then that enemy
should be alerted. So yeah, you can see
that it is working fine. Okay, we can even try adding
more enemies in the scene. And when we attack
one of the enemies, all the other enemies
should be alerted. Okay, yeah, that
is working fine. When we attack one enemy, all the other enemies in the
scene are being alerted. I'll drop the video here. Thanks for watching and L
see you in the next video.