Attacking the player - Complex Enemy Behavior - 2D Platformer - Part 16 [unity]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what is up guys welcome to Barden my name is Heinrich today we're gonna take a look at how we can make our enemy attack our player so where we last left off we gave our enemy the ability to detect our player so if he turns and he sees us he'll detect us and then he'll charge at us like that now I want to make it so that when the enemy gets close to the player he will try to attack the player so let's take another look at the state machine diagram that I made I've made some updates to it so again here we can see all the transitions we need between the different states we currently have our idle state moving state player detected state charge state and our look for players state so before we move on with the attack players state I think we should just go back through all these states and make sure we have all these transitions set up so let's go to unity and let's go to our enemies folder States no enemies specific folder enemy 1 and let's just make sure all of these are open ok so we have our e 1 player detected state charge state look for players state idle state move state ok we have all of our state's and start with the move state so in the move state we have a transition to our idle state and to our player detected state so here we have player detected and idle ok so that's done in our idle state move state player detected state ok we're good there then from our player detected state we have a transition to our charge players state or we have a transition to our look for players state or attack player state so from player detected we currently have a transition to our charge state and our look for player state so let's just while we're here just to make sure we don't forget to add this transition later put in a little to do reminder for ourselves like that ok and that should be it for the player detective state now in the charge player stay we have a transition back to player detected state or we have a transition to look for player state or we have a transition to a tagged player state so in the charge state we currently have a transition to look for players state or we have a transition to our player detected state boom boom so we're good there now I just need one to go to our attack state so let's just add a comment for that as well like that and then in our look for player state we have a transition either to our player detected state or our move state so look for player and we have both of those okay so all our transitions are good now so we are ready to work on our attack state so how we're going to handle attacking is going to be very similar to how we're handling it on the player so far so in our player combat controller let's go take a look at that when we attack we have a function called check attack hitbox that gets called by the attack animation that sets off a physics 2d overlap circle check to see if there's any enemies in a radius at a certain position and then we store all those enemies and then we damage all those enemies so we're going to be doing the same thing for the enemy attacks so what we'll have is we'll have an attack state that inherits from state but then we're going to think ahead and think okay what kind of attacks do we want well for now we want a melee attack that the enemy has but we know that later on we're going to add a ranged enemy that shoots a bow so maybe we also want a ranged attack so instead of attack being our main attack state we will make two more classes that both inherit from attack that'll be our melee attack and range attack states that will take care of doing the different things that are different between melee attacks and range attacks so for instance a melee attack is going to set off a check to see if there's any enemies in a certain radius whereas a ranged attack is going to take care of spawning a projectile instead another thing we need to do is seeing as our attacks get triggered by the animation but our animation is not on the same game object as our script we're going to have to make another script that will allow us to interact with our states from our animations so let's just start off by creating the attack script so let's go back to our enemies folder and then into our state's folder and I'll create a new C sharp script that would just call a taxed 8 like that we can then open it up in Visual Studio and then we can start off by just deleting all of this pre generated code and getting rid of mana behavior and making it inherit from state instead so like I just said a tagged state is going to be our base attack class where our different attacks will inherit from this so let's think about what every single attack no matter what needs to have well every attack is going to need to have some sort of position be it melee or ranged with a melee attack we need to know where the center of the damage is so we have a position and we're going to check a certain radius around that position for any enemies but a range attack also needs a position but in this case it's going to be a spawn position where we want to spawn the projectile so seeing as that is something that both melee and range combat has we can declare it in our attack state so I'll start off by saying protected and we'll just save it as a transform and I'm just going to call it attack position so unlike our other classes we're not going to be having a reference to a data class in this class instead we'll have separate ones for our melee and range attack classes so for now we can just take care of setting up the constructor so we can click on attack States a control full-stop generate constructor and now seeing as our attack position is going to be a child game object of our alive game object we probably want to pass it through with our constructor so let's add it to the constructor transform Attack Position and then inside the constructor we say this stopped attack position equals attack position like that and now next we can just generate our overrides we can click on attack state and if control full-stop is not working for you I think oh I think pressing alt enter also does the same thing this is all going to be different based on what editor you're using but this is how you do it in Visual Studio 2019 okay so let's just generate the overrides and again we don't want equals get hash or to string like that okay before we head on with the different attack States let's work on getting the enemy to know when to attack so if you'll remember in the last episode I talked about having a long range action and a short range action so for most enemies the melee attack is going to be their short-range action so let's head to our entity script and we're just going to write another check function like these check player in min aggro range functions and this time we're just going to write a function the checks to see if the player is in range of our close range action so we'll come and we'll say public virtual bool and we'll call it check player in close range action like that and now in the function we're just going to return a physics 2d ray cast again but first we need to go back to our entity data class and specify the distance we want to check for so let's go to our D underscore entity and let's define a close range action distance so public float close range action distance and we'll to set it equal to 1 by default back in entity we can now say return physics 2d dot ray cast and again we'll fire the Ray from our player check dot position in the alive game object stop transform right direction and we're going to use entity data close-range action distance as our distance and entity data dot what is player as our layer mask cool so now we have a function that we can use to check if our player is close enough to our enemy to get attacked so if we go to our state diagram we can now see we have two states where we can attack our player from either our player detected state or our charge player state so let's start off with our player detected state so let's go to our bass player detected state class and when I say bass I mean so not the enemy specific one and so now in this state we can do another check to see if the player is close enough to get attacked so as you can see we already have a perform long range action so let's just use this convention and say perform short range action so protected bull perform short range action like that and now we can just add this to our ducek's function we'll just come and say perform short range action equals entity dot two check player close range action so it would appear I have messed up my naming convention again let's just fix that okay so check rating close range action and here I'm saying short range let's change this to close range instead so just click on it and press ctrl R twice and we'll just change it to close range like that okay so now in our player detected state you will know when the player is close enough to be attacked so now we just need to go to our a one player detected state to make the transition to the attack state but we have not yet made the attack state so we'll leave that for later now let's go to our charge player state and we can do the exact same thing so we'll just find our charge state and then again we can create another protective tool that will call perform close-range action like that and then just add it to our ducek's function so perform close-range action equals entity dot check player in close range action like that cool so now in both of our charge stage and our protected state we should be able to go and transition to our attack state once we've created it okay so let's start looking at the attacks again so like I said earlier all of our tax usually get triggered by an animation at some point so like when we have a melee attack we only trigger the damage at a certain part so when the character swings the sword only once he swings a sword we actually check to see if there is any enemies that should take damage so another thing that all of our attack States should have is a trigger attack and finish attack functions now these functions are specifically there to get cold by the animations so let's just come to the bottom of our state and let's create a public virtual void that will called trigger attack and we're not actually going to put anything in this function here we just want to know that all attack states have this function and I'll tell you why we put it here in just a second and then next we want another public virtual void and this function we're going to call finish attack like that so the trigger attack function is going to get cold at the part of the animation where we're supposed to do damage or where we're supposed to like fire the projectile and finish attack gets called at the end of the animation and that's how our attack State is going to know that the attack is done and then we can transition to another state so let's just come back up to our variables and let's create a protected boolean and we'll just call it is animation finished and then in our enter function we'll just set that to false like that and then in our finish attack function we'll set that to true so is animation finished equals true like that so now our next problem is how do we call these functions from our animations when we did it on our player if we come to our animation window and then go to our attack one animation you can see we have these different animation events in our animation so this one as you can see calls our check attack hitbox function and the reason we can do this is because our animator is on the same game object as our script so we have our player here are our scripts here's our animators so they can directly communicate with each other but in the case of our enemy our script is sitting on our enemy one game object but our animator is sitting on our live game object so they cannot interact with each other if we add an animation event to any of our animations we cannot call any of the functions from enemy one so instead what we're going to do is we're going to create another script that we can put on our a live game object that it is going to relay messages from our animations to our States so let's come back to our project view and then go to our scripts folder and I'm not quite sure where the script should go yet so we'll just leave it in our bass scripts folder so we'll say create new c-sharp script and I'm just gonna call it animation to state machine like that as we're going to use it to take messages from our animation to our state machine so let's go ahead and open this up and this script is going to be very simple you can go ahead and delete this code and this time it needs to inherit from on a behavior as we want to put this script on our game object so the script is going to need two functions the same two functions as in our state's our trigger attack and finish attack functions so we'll save private void trigger attack like that and then private void finish attack like that noe okay so in order for these functions to be able to call the functions from our attack state we need to have a reference to our attack state so let's create a public attack state variable that will just call attack state like that and then what we're going to do is in our trigger attack function will just say attack state dot trigger attack look at that and then in our finish attack state will just say attack state dot finish attack just like that now what makes is so awesome is we can have lots of different types of attacks with different code in the trigger attack and finish attack but we only need one of these scripts attached to the game object and it'll be able to call these functions from any type of attack be it melee or range maybe magic in the future because our attack state base class already has these two functions so now the last thing we need to do is we need to set this attack state reference what we're gonna do is when we enter an attack state we set this reference to that attack so first we need to get a reference to this animation to state machine script somewhere and seeing as all enemies are going to have we might as well do this in our entity class as well so it's going to be the same as these components that we have here so let's declare a public animation to state machine variable and we'll just call it a TSM and then we'll create the public getter and the private setter like that and now in our start function we can get the reference to that component so we'll say a TSM equals a live game object dot get component and the type of the component is animation to state machine like that so now that entity has a reference to this script we can come back to our attack State and then in the enter function we can say entity dot 80s M seeing as we have a public getter for it then we say dot attack state equals this so from now on whenever we enter an attack state and the attack animation plays when it calls the trigger attack and finished attack functions it'll know exactly which state to come to you now before we move on to our melee attack state class let's just set our velocity to zero in the enter function so entity dot set velocity zero F let's head back to unity and let's come back to our enemies and States folder and let's create a new C sub script that will call melee attack state and let's open it up in visual studio so we can start off by getting rid of this code again and then we can inherit from a tag state instead of state now this time we're going to have data for this state so we should actually come back to unity and go to our data folder and then create a new C sharp script that's going to be P underscore melee attack like that you can go ahead and open it up as well in our melee attack State class we can then create our protected the underscore melee attack variable that we'll call stage data and then we can generate the constructor like that and then we can add the data to our constructor so comma b underscore melee attack called state data and then inside the constructor we save this dot state theta equals state data like that and now we can go ahead and create the function overrides second call full store generate overrides untick the first three click OK and now we should have everything we need so now what we need to do is come to our trigger attack function and write the code that's actually going to detect if the player can get damaged so we'll come and we'll have an array of collider two DS like that we'll call it detected objects and we'll set this equal to physics 2d dot overlap circle all and the position is going to be our attack position dot position and the radius of attack we need to go and declare in our data class so let's come and get rid of this code turn it into a scriptable object so have a public float attack radius by default let's set it equal to 0.5 don't forget your F and back here we can come and say state data dot attack radius and then our layer mask we should also come and set in our data class so we'll have a public layer mask and we'll just call it what is player so you can come and add that as our layer mask state data of what is player like that so now when our animation triggers our attack we're going to create that circle and see if our player is inside of that circle now to damage the player we need to loop through everything that gets detected so we'll say for each and you can just hit tab to autocomplete and so we'll say for each Collider 2d hold Collider in detected objects pass the message that it has been damaged so collider dot form a send message and the function we're calling is damage and then we need to pass it our attack details which we have not yet created so attack details is going to be something that I'm going to change once again because I've been doing it in a really stupid way so if we come look at our player combat controller script and we look at our attack details you can see a tag detail is currently an array of floats that is of size to where the zeroth element represents our damage or our position I can't remember and then the other element represents the other one so this is a bad way of doing it because now if we want to make it change to attack details we have to go change it everywhere so instead of doing it this way we're going to use a struct we're going to create our own data type that is going to hold all the information for an attack so let's come back to unity and in our scripts folder let's create a another folder that will just call struts open that up and then inside of it create a new C sharp script that will call attack details like that if we open that up we can get rid of all this pre generated code and get rid of the inheritance and now instead of it being a public class we're gonna change class to struct and that is how we create a new struct now inside of here we can declare everything we need so now instead of storing our exposition as a float we can just store the position of the thing doing the damage as a vector2 so we can save public vector2 and we'll just call it position like that and then we can have our damage amount so public float damage like that so now if we come to our melee attack state let's create our attack details so at the top we can just declare a nother protected and then we can just call the class name like that so attack details and we'll just call it attack details like that so now we just need to set these different variables in our attack details so we need to know what our damage is this will just come back to our melee attack data class and declare the damage of the attack so public float attack damage and we'll just set it to 10 by default and then in our enter function on our melee attack state you will say attack details dot damage amount equals state data adopt attack damage like that we can also set the position so attack details dot position equals entity dot a live game object transform o'clock position perfect so now we just need to change it on our pair combat controller as well so we have our private float attack details let's just change this to attack details like that we can get rid of this and now where we set the attack details down here we can just say attack details dot damage amount equals attack 1 damage and attack details dot physician equals transform dot position like that so of course this is now going to break it for our dummy again we'll fix that in just a second but now I want you to make it so that when our enemy gets damaged it'll use this format so let's come to our damage function so now instead of taking in an array of floats feel so on to take in an attack details datatype and we're going to decrease our health not by attack details 0 but instead attack details dot damage amount and then we can just change this to attack details dot position X instead that should work and it's basically almost everything we need now we just need to create the enemy specific melee attack state class and set up the transitions and of course the animations as well so let's go back to unity and let's go to our enemies folder enemies specific enemy 1 and let's create a new C sharp script called e 1 underscore melee attack state like that and open it up we can get rid of this code make it inherit from melee attack state this time generate the constructor and we want to get a reference to our enemy 1 so we'll say private enemy 1 call it enemy and let's add it to our constructor so enemy one called enemy add it to the constructor this dot enemy equals enemy like that and let's get the function overrides you don't want the first three click ok cool so now basically all we need to do is create the transitions out of our attack State so we can come to our logic update function and we can say if is animation finished that means we know the attack is done let's transition somewhere else so let's go take a look at our state machine diagram and you can see when the attack is finished we're either going to transition to look for player if we can no longer detect the player or we're gonna transition to player detected if the player is still in the aggro range so we need to come back to our scripts and actually now that I think about it in our attack state we should already check to see if the player is within the min Agra range so let's create another protected boolean cold is player in min aggro range like that add it to our checks so is player in min Agra range equals entity dr. check player in min Agra range like that and now in our a one melee attack state we can say if is player in min Agra range then state machine dot change state to enemy dot player detected state like that else you want to go to our look for player states so state machine dot change state to enemy dot look for players state like that cool that should be everything set up in this enemy specific class now we just need to transition to this class so let's look at our to do window and we have these two transitions over here so let's just click on this one and actually we can't do this yet because we still need to initialize the attack on our enemy script so let's come to our enemy one class and let's create a public ye1 underscore melee attack state and what is call it melee attack state like that then create the complicate ER and the private setter and then let's get the data so serialize feel private d underscore melee attack I'll just call it a melee attack state data like that and now let's call the constructor so melee attack state equals a new e one underscore melee attacks taped will pass this as the entity state machine as the state machine the animation boolean name is just going to be melee attack like that now we need our attack position transform so this we're going to declare on our enemy so we can create another serialize field that is going to be a private transform and we'll just call it melee attack position like that we'll pass that through with the constructor next we need our state data so melee attack state data and then find me we'll pass this through as our enemy 1 now if we come to our to-do list and we go to the first one we can add this transition so in here we want to say if perform close-range action then state machine dot change state to enemy dot melee attack state like that and then we just change this to an else there now I can go to our other to do and basically do the exact same thing so let's just add it at the start as well so we'll say if perform post range action then state machine dot change state to our enemy dot melee attack state change this to else if and then get rid of the to do and that should be it for the code we'll see now let's head back to unity let's start off by creating the attack animation so let's pull up our animation window and let's click on our a live game object and then go create new clip navigate to our animations folder and we'll just call it enemy one underscore melee attack like that let's go to our sprites folder and our enemies folder so frames 12 2 frames 17 yep 12 to 17 is our attack animation so we can play that a sample rate of 60 is way too fast let's change that to 15 and see what it looks like cool that looks quite good let's see if we can make it a little bit better seeing as it's an enemy it doesn't have to attack as quickly so we can maybe have it hold this position a little bit longer maybe three frames like that that looks quite good and then let's make it so it holds to this position for an extra frame like that that looks quite good okay so we have our melee attack animation now we can get rid of the animation window let's go to our animator and here is our melee attack animation state let's start off by creating the bool parameter which is melee attack like that and we want to transition to the melee attack from our play detected and our charge state so let's create those transitions first from player detected to melee attack click on the transition and make sure has exit time is unpicked change the transition duration to 0 and then add the two conditions the first condition is player detected is false the second one is melee attack is true next we have our transition from charge to melee attack make sure has exit time is not ticked set the transition duration to zero add the two conditions the first one this time is charge is false and media tag is true so now once we finish the meet attack we either want to transition to look for player or player detected so make a transition back to player detected on tick has eggs a time transition duration is 0 add the two conditions the first one is melee attack is false the second one is player detected is true and then we make a transition from here to look for player move this a little bit click on the transition has exit time is false transition duration is 0 add the two conditions first condition melee attack is false and second one is look for player is true and that should be everything okay next let's come to our a live game object and create a new empty child and we'll call it melee attack position like that click on enemy 1 and let's just drag this into the slot before we forget let's just move it into a better position we can't see the radius so we should probably go and create a gizmo for that let's head back to our code let's come to our enemy 1 script and let's create the drug is most function at the bottom so public override void on draw gizmos like that and I just go to our entity class and make sure that our on draw gizmos is a virtual void perfect ok and now in here we can just say gizmos dot draw wire sphere and we want to draw it on melee attack position that position and the radius is melee attack state data dot attack radius like that and then go to our d underscore melee attack class and we just need to make it so that we can create the asset so let's go to another state data class and copy this come back here paste it in and the file name is going to be a new melee attack state data and we'll just call it melee attack state like that save that and then when we go back to unity we can now go to our enemy 1 data folder and say create data state data melee attack state and we'll just call it e1 underscore melee attack data like that well we're clicking on it let's change what is player 2 player and then let's click on enemy 1 and drag this in like that so now we can see where our attack is so I'm just going to move it around a little bit more I put it around here we can actually click on our animation window and then go to our melee attack and run through it and here we can see what the attack looks like so now we can try and line this up a bit better I'm just gonna increase the radius of the attack a little bit so let's say 0.75 and then move it around here like that perfect okay now I just need to make sure that we actually call the trigger and finish attack functions from our state so let's click on our a live game object and let's add a component and this is the animation to state machine script now if we go to our melee attack animation we can add our animation events so we want to call the trigger attack at this position of the animation so we can just click on add event and the function when a call is trigger attack and then at the very end we want to add another event and this time we want to call finish attack so everything should work now let's test it out fingers crossed so when our enemy detects us you gonna run at us and he's gonna get stuck okay so let's debug and find out what's happening so when we run this we can see that our enemy is working fine he detects us and now when he gets close enough he's not transitioning to the attack state let's take a look at the animator or soul in the charged state our melee attack is set to true as our conditions here wrong ahh so let's see if this was the issue let's go to our animator on this transition this has to be melee attack not look for player if we run the game now let's hope it works boom there you go so the enemy attacks us sometimes so as you can see it basically works we just need to fine-tune the variables and like the check distances and things a little bit so let's make it so we can see what our close range and long-range action distances are so let's go to our scripts and let's go to entity and in our on draw gizmos function let's start off by saying gizmos draw wire sphere and we're going to draw it on our player check dot position plus then we'll say vector three cuz we to cast it as a vector three and we're adding our check distance to this so in here we'll say alive geo transform dot dot write multiplied with our entity data dot close range action distance like that and I should set the radius to something like zero point two two we don't need this here okay like that so we're saying is we're going to look at the player check position and then we just gonna add to our right of the of the enemy the the close range action distance let's do the same for our Agra distances so just copy this and paste it twice and so instead of close range action distance we're going to use our min Agra distance and our Max Agra distance like that I hope that makes sense I don't feel like I explained that very well but basically we're just going to our player track position and then going to the right or left depending on what direction the enemy is facing these distances and drawing all those so if we go back to unity we're gonna be able to see that until you run the game as you can see if you just pause this we have these little spheres that represent the different distances now the nice thing about using those scriptable objects for these distances is we can change these during runtime and they will save so when we stop playing the game these will stay at the same position so if we come to our enemy one game object and then go to our enemy one data class let's increase our min now let's increase our Max Agra distance to like 8 and the min Aggra distance to seven like that just so we have little buffer zone and then our close range action distance is a bit too close I think I want to make it over here so that we will attack just at the tip so let's try to that's too much 1.5 1.75 that looks good so now if we run the game he's gonna charge at us again hmm he ran past us so it seems like we still have a couple of issues what is this so we're getting this error because we're trying to use a live game object in our on drug is Mo's but we don't have a reference to it yet because we only set the reference in start so instead of this let's use vector 2 dot right like that that should get rid of that air-cool so we don't have any other errors so now let's try and figure out why our enemy is running past us so we charge for a little bit and stops like he's supposed to because we did change the time to a number that's quite low okay and now he attacks us why is he running past us okay so there's something wrong in our charge state let's go look at our charge state so it doesn't look like anything's wrong in our charge state itself let's go to our one charge state ha okay so here's the issue this is my bed so currently we can only transition to our melee attack state if the charge time is over which is not correct so we can take this if statement cut it out and we'll put it at the very front and they'll say else if we're detecting a Ledge or a wall and then else if the charge time is over now in here we're saying when the charge time is over if the player is in the men are going to go to the player detected state so we can get rid of this else like that and then afterwards we can say else so if the player is not in the managua range so we're not detecting the player state machine adopt change state to enemy dot look for player like that okay let's see if that fixed it so when the enemy sees us cool I don't think he's going to run past us this time awesome and as you can see your enemy can still kill us so this is giving a lot more life to our enemy now what you need to get it to feel a little bit better by messing around with our transitions between states and maybe adding some different checks but for now I think we'll call it good so we cannot yet damage our enemy we're going to leave that for the next video we can also not damage our dummy anymore because we changed our attack details to use the struct so I'm not gonna fix that I'll leave that as a challenge to you guys and I'll fix it in the next video but for now I hope you guys are enjoying this series as much as I am it's really fun seeing this enemy come to life and as we keep going his behavior will become better and better and he won't do things like this where he stands on a ledge just looking at us so next time we're gonna make it so that we can actually damage the enemy and we'll take a look at this stun State so if we damage the enemy a certain amount of times you will get stunned and you can't do anything else for a certain amount of time and then eventually we can kill him and that should be the end for our first enemy then we can move on to our second enemy the archer and I'm so excited to show you guys how easy it is to make the second enemy but for now I would just like to thank all my supporters and wonderful people on patreon and a huge special thanks to Lois for your support on patreon I hope you guys all have a wonderful day
Info
Channel: Bardent
Views: 6,162
Rating: undefined out of 5
Keywords: Unity., tutorial, player, 2D, platformer, walljumping, wall sliding, jumping, Unity, Animation, ground, check, physics2d, castcirlce, variable, jump, height, Wall, Jumping, Movement, improvement, user, friendly, code, 2019, 2019.2.0f1, Ledge, climb, dead, cells, system, easy, beginner, animation, Dash, Ghosting, After, Image, After Image, Basic 2D Combat, Unity combat, Comabt, Combat, Melee, Melee Combat, Basic, Enen, Enemy, Patrol, State, Machine, respawn, hit, finite state maching, state machine, enemy, behavior, Charge
Id: Qp38X9TEVqI
Channel Id: undefined
Length: 46min 15sec (2775 seconds)
Published: Sat Apr 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.