Ledge Climb/Drop - 2D Platformer Player Controller - Part 25

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what is up guys welcome to barton my name is heinrich and today we're going to take a look at adding our ledge climb mechanic back into our player controller the ledge climb essentially works the exact same way it did before but with one major improvement and that is that it no longer requires our platforms to line up with the grid now before we get started with our ledge climb we quickly have to address a small bug from the previous episode so let me quickly demonstrate if we click on our player and we take a look at our player data we currently have our amount of jumps set to one but if i run the game and then run up to a wall and wall jump whoops and spam jump i get a second jump now the reason for this is a small bug with our wall jump coyote time so let's go ahead and open up our player in air state so we come to sub states player and air state so our problem lies within our do checks function over here we start our wall jump cloudy time if we are no longer touching a wall but we did just touch a wall so when we leave our player in air state we don't reset any of these variables back to false meaning when we come back into our player and air state we immediately call the do checks function from our enter function and then when it runs through this it sees that our old is touching wall is still true and so it starts our coyote time so the way to fix this is in our exit function we need to set our oldest touching wall and all this touching well back to false and the same with our is touching wall and is touching while back so just bold is touching wall equals false bold is touching world back equals false and then is touching ball equals false is touching wall back equals false so if we save that and run the game again this should no longer be an issue as you can see i can wall jump and spam jump as much as i want and we won't get a double jump perfect okay so now we're ready to get started with our actual ledge climb to begin with let's take a look at our state machine diagram as you can see our ledge climb state is a standalone state just like our player and air state so we're going to create this in our sub states folder we have two transitions coming to our ledge climb state one is from our touching wall state and another is from our player in air state both of these transitions are based on the exact same conditions and that is just if we detect a ledge we're going to transition to our ledge climb state we then have one transition coming from our ledge climb state to our idle state because when we finish our ledge climb we're standing on top of the ledge and our idle state is our entry point for our player grounded state i'm actually missing a transition here because we have the option to drop from our ledge climb so there's another transition going back to our player in air state okay so with that out of the way let's quickly talk about how the ledge climb actually works so let's picture this our player currently has a raycast from the center so from this point here being shot out towards our facing direction so to the right we use this to detect a wall we're going to add another raycast at the same x position but a higher y position around here that's going to shoot the same distance to the right or to the facing direction this is going to be our ledge check so what happens is say our player is in a position like this our wall check raycast is going to detect this platform over here but our ledge check is going to detect nothing this is how we know that there is currently a ledge that we can climb what we're then going to do is save the position that the player was in when we detected the ledge and then we're going to determine the x distance from our wall check position to the ledge and then we're going to determine the y distance from our ledge check position downwards we're going to use this information to determine what this corner position is once we know what this corner position is we're going to have a starting position and an end position we're going to hold the character in our starting position while our ledge climb animation runs and when it finishes we'll move the character to the ending position just like that i hope that makes sense if it does not it should once we start programming it and of course if you don't understand something you can always hop in the discord and ask for some help okay let's get started so the first thing we need to do is come to our substates folder and then create a new c-sharp script and we'll just call it player ledge climb state let's go ahead and open that up and seeing as our player last time state isn't actually a substate of any other state we're just going to inherit straight from our player state we can also go ahead and get rid of this pre-generated code and then let's go ahead and create our constructor so we can right-click on the name quick actions and refactorings generate constructor there we go the next thing we need to do is go set up the basics in our player script so let's open that up that is in our player folder player finite save machine player we can come up to our state variables region and let's go ahead and create the variables so public player ledge climb state and we'll just call it ledge climb state and then don't forget to give it the public getter and the private setter and i see in our wall jump state i accidentally said protected instead of private go ahead and fix that and then next we can come down to our unity callback functions and then in our awake function we can initialize our ledge climb state so ledge climb state equals a new player ledge climb state with this as our player state machine as our state machine player data as our player data and then finally our animation boolean name is going to be ledge climb and this time instead of leaving it just as a ledge climb we're going to add state at the end and you'll see why once we start dealing with our animator and the state machine should be with a capital s and that should be all that we need to do in our callback functions so next we can work on our transition to our player ledge climb state so the first thing we need to do for that is create our actual ledge check so let's go back to unity and let's create another empty game object for the player so create empty and just call it ledge check as you can see this is currently overlapping with our ground check so we can just go ahead and move that up some around here so let's just say 0.5 that should work next we can come back to our player script and then underneath our check transforms region let's go ahead and create a variable for that so we have a serialized field that holds a private transform and we'll just call it let's check go ahead and close down that now in our check functions we essentially have the exact same function as for check if touching wall so let's just declare that underneath that one we'll say public pool check if touching ledge and so this time we'll say return physics 2d dot raycast and for our origin this time we're going to use ledgecheck. our direction is vector2.right multiplied with our facing direction and then our distance is also playerdata.wallcheckdistance and then finally our layer mask is playerdata.what is ground cool so now we can check for our ledge so now we can come to our player and airstate then let's go ahead and declare a boolean to store our ledge check so we have private bull is touching ledge and then we can go ahead and add that to our do checks so after we check for is touching wall and is touching all back we'll say is touching ledge equals player dot check if touching ledge like that so like i said if our wall check returns true but our ledge check returns false we know there's a ledge that we can grab now an important thing that we need to do is as soon as we detect a ledge we need to save the position of the player so that we can determine the corner position if we don't save it immediately and we do it somewhere else there's a chance that our physics update might happen and move our character slightly so our math isn't going to quite work out so what we're going to do is come to our player ledge climb state and we're just going to declare a private variable of type vector 2 and we'll just call it detected position like that we'll then come and create a public void function that we'll call set detected position and this function is going to take in a vector 2 as a parameter so vector 2 and we'll just call it position and all this function does is set our detected position equal to the position that we pass the function so now back in our player in air state after we've done the check for our ledge and wall we will say if is touching wall and not is touching ledge then we want to set that position so we'll say player dot ledge climb state dot set detected position and we'll just set it to player dot transform dot position so this is going to save the exact x and y coordinates of the player when we detected the ledge and this is going to be very useful now the last thing we need to do in our player and air state is set up the transition to our player ledge climb state so let's come down to our logic update function and just after our first if we'll create another else if and the conditions are the same so is touching wall and not is touching ledge then we want to say state machine dot change state to our player dot ledge climb state just like that so we're basically done with our player and air state let's take a look at our player ledge climb state let's go ahead and generate our function overrides so control full stop generate overrides we can deselect all and the only functions we want is our enter function our exit function our logic update function and our two animation trigger functions go ahead and click ok and there is everything that we need so now that we know we're in our ledge climb state we need to stop our character's velocity and freeze his position so let's go to our player script and in our set functions let's create another set velocity function and this is just going to be set velocity zero i probably should have made this function a while back already but it's just going to be public void set velocity zero and it's not going to take any input parameters and all this function does is set our x and y velocity to zero so we say rb dot velocity equals vector 2 dot 0 and then we also just have to update our current velocity so current velocity equals vector 2.0 now in our player ledge clamp state in the enter function we can start off by calling that function so player dot set velocity zero and then next we set the position of our player so player dot transform dot position equals detected position so now we need to determine the corner of our ledge so let's create another private vector too and we'll call it corner position like that next we're going to write a function that determines our corner position and we're going to do that in our player script so let's go ahead and come to our other functions so our other functions region and let's create a public function that is going to return a vector 2 and this vector 2 is going to be the corner position and we'll just call the function determine corner position this function does not have any input parameters so the first thing we need to do is determine our x distance to do this we're going to declare a raycast 2d a raycast hit 2d and we'll just call it x hit we'll then set this equal to physics 2d dot raycast we're essentially just doing another wall check so wall check dot position as our origin vector 2 dot right multiplied with our facing direction as our direction our player data dot wall check distance as our distance and then finally playerdata.what is ground as our layer mask so this raycast is again going to check for the wall and we're going to have a raycast hit 2d object that we can now work with from this object we can then get our distance so we'll say float x distance equals x hit dot distance and this is going to return the distance from our raycast origin to the object that we detected in this case our ledge in this case our platform next we need to determine the y distance so to do this we're going to fire a ray downwards from our ledge check height but move to the right our x distance let me actually quickly explain that a little bit better back in unity so let's just move our character back up to the ledge over here so our raycast ledge is being fired out like this and here is our x distance what we're going to do is fire another ray from up here but we're going to use this x distance to determine how far we should fire the ray down from that means that this ray is also going to detect this ledge and then we can determine this y distance and like i said we can then use this information to determine this corner position okay so back in our script we will say workspace dot set and our x we're going to set to x distance multiplied with our facing direction so this way we're either going to move left or right depending on the direction we're facing and the y we're just going to set to zero we don't need any y offset next we can declare another raycast hit 2d and we'll call this one y hit we can then set this equal to physics2d dot raycast and this time our position is going to be check dot position plus vector 3 multiplied with our workspace so we're essentially just adding the x distance to our ledge checked out position but we have to cast it as a vector 3 seeing as this is a vector 3. our direction is then going to be vector 2 dot down and then as our distance we're going to use the distance between our wall check and our ledge check so we'll say ledge check dot position dot y minus wall check dot position dot y and then finally our layer mask is going to be player playerdata.what is ground now from this raycast head2d we can determine our y distance so we'll just say float y distance equals y hit dot distance now finally we can use this to determine the corner position and then return that as our vector 2. so we'll say workspace.set and our x position is going to be wall check dot position dot x plus our x distance that we measured multiplied with our facing direction and then as our y we're just going to say ledgecheck.position.y minus y distance and that should give us the exact coordinate of the corner of this ledge we can then go ahead and return our workspace perfect so let's go ahead and come back to our player ledge climb state script and in our enter function let's set our corner position equal to player dot determine corner position now all we need to do is determine our start and stop positions for our ledge climb before we can do that in our player alleged climb state we need to create two variables to hold offsets for our start and stop positions so let's go ahead and open up our player data script so in our data folder player data let's go ahead and create a header for our ledge climb state so square bracket header and we'll just call it ledge climb state and we're going to have two public vector 2 variables so public vector 2 the first one is going to be our start offset and the second public vector 2 is our stop offset okay so we can come back to our player ledge climb state and now we need to declare two more vector 2 variables this is going to be for our start and stop positions so private vector 2 the first one is going to be our start position and then we have another private vector 2 stop position back in our enter function we can say start position dot set and for x we're going to take our ledge corner position dot x and subtract our start offset and for our x component we're going to use our corner position.x and either subtract or add our start offset depending on our facing direction so we can just say corner position dot x minus and then we can open up some brackets and say player dot facing direction multiplied with our player data dot start offset dot x and so the reason we have minus over here is because if we're facing right so facing direction is 1 we want to subtract from our corner position and if we're facing left we want to add to our corner position for our y component we're going to say corner position dot y minus our player data dot start offset dot y in this case it's always going to be minus next we can determine our stop position so stop position dot set and again we're going to use corner position dot x and this time instead of minus it's plus because if we're facing right we want to move to the right if we're facing left we want to move to the left and then we can open up some brackets again and say player dot facing direction multiplied with our player data dot stop offset dot x and then for our y component it's again going to be corner position dot y and this time it's plus player data dot stop offset dot y just like that after we've done this we can go ahead and set our player's position to our start position so player dot transform dot position equals start position like that and we can test this in just a second all we need to do is come to our logic update function and again set our velocity to zero so player dot set velocity zero and then set our position so player dot transform dot position equals start position go ahead and save that and let's go to unity and run the game and see if it works oops okay so we forgot to assign our ledge check variable so let's go ahead and drag that in and let's give it another try let's just maximize this awesome so as you can see the center of our player is being held right on that corner position and we don't currently have any offset set so it's just going to stay right there now before we move on with our player ledge climb state let's go ahead and work on the animations so let's go ahead and pull up our animation window and then let's navigate to our player animation folder so animations player and let's go ahead and create a new clip for our ledge climb we're going to have three different animations we're going to have the grab component the hold component and the climb component so let's just say for the first one let's grab then let's create another clip this time we have ledge hole and then finally we have our ledge climb we can then navigate to our sprites folder so sprites player and here we have our ledge climb sprites so we first want to work on our ledge grab animation we can go ahead and change our sample rate to 10 and then drag in the first two sprites obviously this isn't the smoothest animation but it's the best that i could come up with and of course it also won't be repeating like this it'll only happen once so this is the grab portion of our ledge climb next we have the ledge hold animation which is just the second frame by itself awesome and then finally we have our ledge climb animation so this is going to be sprite three four and five we won't be using these last two sprites we can also go ahead and change the sample rate to 10 and that's basically what it's going to look like of course it looks really silly when it repeats like this but oh well so that's it for the animations we can go ahead and close down our animation window so let's take a look at our animator if we click on our player we can see our three new animations but we can just go ahead and delete all three of them because instead of having all three animations here we're going to use a substate machine for those three animations so you can go ahead and right click say create sub state machine click on it and just rename it to ledge climb let's just go ahead and reposition that and now we need to set up some transitions so we have to come and create a new boolean parameter and this time it's going to be called ledge climb state remember this is what we passed to our constructor let's go ahead and drag that to the top and then make a transition from entry to ledge climb click on it and set the condition to ledge climb state is true we can then make a transition from our ledge climb to exit click on that and set our condition to ledge climb state is false perfect now if we double click on ledge climb it'll come to our substate machine we can now create three new animation states for our grab hold and climb so say right click create state empty click on it and just call it grab let's create another empty state machine or animation state and call it hold and then create one more and we'll just call it climb okay let's just go ahead and position these things a little bit better so when we enter our ledge climb substate machine the first animation we want to play is our grab animation so this initial transition is okay from our grab animation we're then going to go to our hold animation and then finally we can come to our climb animation but we can also choose to not climb the ledge and instead drop down so from hold we can either transition to climb or directly to exit so before we set up any of the transitions we need to go ahead and add the animations for these states so let's come to our player animations folder let's click on our grab state and get our ledge grab animation drag that up here then we can click on hold drag in our ledge hold animation and then finally our ledge climb animation so our first transition is going to be from our grab state to our hold state we can go ahead and click on the transition and open up our settings and set our exit time to 1. we can then also change our transition duration to 0. so no matter what the grab animation is going to play once and then we're going to transition to our hold animation so we're going to stay in our hold state until our player gives our character input telling him to either climb or drop from the ledge so we need to come and create another boolean parameter and this one is going to be called climb ledge so this is the reason why the first parameter we named ledge climb state just so that we didn't confuse these two so we can go ahead and drag that to the top and then we'll make a transition from our hold state to our climb state click on it untick has exit time set the transition duration to zero and then under conditions just say climb ledge is true we can then make another transition from our hold state to our exit state click on that make sure to untick has exit time set the transition duration to zero and under conditions instead of climb ledge we're going to use our ledge climb state parameter and the condition is that that is false we'll then make the same transition from our climb state to our exit state click on that untick has exit time the transition duration is zero and the condition is that ledge climb state is false so while we're busy hanging on the ledge if we press down we're immediately going to drop if we climb we're going to climb and then we're going to go to our idle state so that is all set up we can go ahead and test this out let's just go ahead and move the character down here again let's go ahead and run it and so now if we grab a ledge as you can see we're busy in our hold animation again our offsets are not set yet but now that we have this animation let's go ahead and mess with our offsets until we get what we want so let's go ahead and open up our player data and now these offsets are all going to be positive seeing as we accounted for left or right in the math itself so to start off with let's set our start offset to 0.5 and 0.5 let's see what that looks like okay so it's not so our x offset seems about right but our y offset is not quite right so let's try one for our y offset let's see what that looks like okay so our y offset is just about right but our x is a little bit too much i'm going to change the x to 0.45 and the y i'm going to change to 0.9 that's about right i think we can probably change the x to 0.4 but you get the idea this is how you're going to calibrate your offsets we cannot test the stop offset yet we'll get to that in just a second but as you can see everything is working fine okay let's move on so now once our character is hanging on the ledge we want to give the player the option to climb or drop from the ledge to do this we first need to know when is the character actually hanging for this we're going to use our animation trigger so over here we're going to let the state know that okay we're now currently hanging so we first need to come and create a private boolean that we're going to call is hanging we can then come to our animation trigger and just set that equal to true we then need to come back to unity and in our animation window let's click on our player open up our ledge grab animation and add an animation event at the end and the function recall from this animation is our animation trigger function so here animation trigger now if this information will know okay the character is hanging you are free to read in the input to know if we should climb or drop so let's come back to our script and now we need to read in our x and y input so let's create variables for those as well we have a private int x input and then privateint y input in our logic update function let's go ahead and get those values so x input equals player dot input handler dot normalized input x and then y input equals player dot input handler dot normalized input y so now like i said based on this input we're either going to climb or drop from the ledge so we need to create another boolean that's going to keep track of if we're climbing the ledge or not so private pool and we'll just call it is climbing and then we can come back down to our logic update function and we'll just say if our x input equals equals our player dot facing direction so if we're hanging on a ledge and we give x input away from the ledge we don't want to start climbing the ledge only if we're giving x input towards the ledge we'll then say and is hanging so we can't immediately start climbing if we're not yet hanging on the ledge and we are not already climbing the ledge so not is climbing if all of this is true we're going to set is climbing equal to true and then we'll let our animator know to play the climbing animation so player dot anim dot set bool and the boolean we want to set is our climb ledge boolean and we just want to set it to true now we need to account for our drop input so we'll say else if y input equals equals negative one so only if our y input is down and is hanging and again we are not climbing the ledge so not is climbing if all of these conditions are met we'll say state machine dot change state to our player dot in air state because we're dropping from the ledge okay let's go ahead and give this a try again we're not yet done with this script but we're just going to test everything so far so if we're hanging on the ledge and we press s we drop just like that perfect now if we're hanging on the ledge and we press d you can see the climb animation starts playing i see we can mess around for offsets a little bit more we'll do that in just a bit okay but now we're just stuck in this climbing loop we're not yet telling the script or telling the state that we are at the top of the ledge for this we're going to use our animation finish trigger so back in our script in our animation finish trigger function we can just go ahead and say player dot anim dot set bool and the boolean we want to set is our climb ledge bool again and this time we're just setting it to false now this is just to make sure that next time we start the ledge climb it doesn't automatically start climbing the ledge this is just cleanup now to actually finish the ledge climb it's we're going to come back to our logic update function and we're going to say if is animation finished so this boolean automatically gets set to true whenever this function gets called or sorry this function gets called we know that we're at the top of the ledge so if this is true we're just going to change to our idle state so we'll say state machine dot change state to player dot idle state after this we're then just going to say else and we're going to put everything else in there like that so now we just need something to call our animation finish trigger so let's go back to unity click on our player so we can look at the animations and then come to our ledge climb animation we can go ahead and add an animation event drag it to the end and this time the function we're going to select is our animation finish trigger it's a shame that it's so far down so here animation finish trigger awesome so now our animation is going to let our state know that it's finished the last thing we need to do is come to our exit function and we'll say is hanging equals false so let's just clean up for the next time we enter the state and then we're going to say if is climbing so in this case we're exiting the state after we climb the ledge we need to change our player's position to our stop position so player.transform.position equals stop position and then we'll say is climbing equals false so we need this if statement because we might exit the state when we drop from the ledge in which case we don't want to change our position to our stop position let's go ahead and test this out awesome so as you can see we're leaving our ledge climb state but because our stop offset is zero we're just getting dropped on the ledge again meaning we start the ledge climb again so let's go ahead and mess with our stop offsets so click on player open up the player data and let's just start with one one as our x and y offsets awesome so as you can see once we finish climbing the ledge we end up up here but obviously this is these offsets are a bit too big so let's go ahead and dial them down a little bit let's try 0.4 for the x and 0.75 for the y that y might actually be a little bit too small we will see actually not i think that looks pretty good there we go so as you can see we can climb our ledge we can drop grab this ledge climb it awesome now let me show you something else with the new method of detecting the corner or determining what the corner position is if we were to for some reason just click on our grid and move our platforms around so let's move it half down half to the right so now this corner position no longer correlates with a like integer position it's uh going to be something like 4.5 or whatever okay so as you can see it works perfectly now um the reason i give some issues is i've noticed whenever you change your tile map it's a good idea to remove your tile map collider and your composite collider and just add them back on for some reason it it messes with with uh with the physics sometimes i don't know why in my testing this worked fine and as you can see now it works fine again there's nothing wrong with it cool so our new way of determining the corner position works a lot better than it did before let's go ahead and just reset our tile map save that and we're basically done the last thing we need to do is just set up our transition from our is touching wall state so let's go ahead and open that up come to our player scripts um super state folder player is touching wall or player touching wall and all we need to do is create another protected boolean and we'll say is touching ledge we can then come to our do checks function and add that so is touching ledge equals player dot check for ledge or check if touching ledge and let me just move this up before i forget okay and then in our do checks function after we check for the ledge we're going to say if is touching wall and not is touching ledge then player dot ledge climb state dot set detection position to our player dot transform dot position and then we just have to set up the transition itself so in our logic update function at the end we'll just say else if is touching wall and not is touching ledge state machine dot change state to player dot ledge climb state so if we save this and try it out we have our normal transition from our player and air state if we drop down hold control so we start climbing the wall and then climb up to the ledge you can see we grab it as expected and there we go that's our new and improved ledge climb mechanic i hope you guys enjoyed that so that's going to do it for today's episode thank you guys so much for waiting for this uh after i dealt with sickness and exams and before i go i would just like to give a huge thank you to all of my supporters and wonderful people on patreon and a huge special thank you to binary chef essay cody lee pyro says and miguel gray for your support on patreon it means a lot and you guys are awesome and i hope you all have a wonderful day
Info
Channel: Bardent
Views: 5,597
Rating: undefined out of 5
Keywords: Unity., tutorial, player, 2D, platformer, walljumping, wall sliding, jumping, Unity, Animation, ground, check, physics2d, 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, Unity combat, Comabt, Melee, Melee Combat, Basic, Enemy, Patrol, State, Machine, respawn, hit, finite state maching, state machine, enemy, behavior, Archer, FSM, New, Input, drop
Id: 0OE-jSDRIok
Channel Id: undefined
Length: 39min 2sec (2342 seconds)
Published: Sat Sep 12 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.