Creating a Ledge Grab in Unity (2.5D Platformer) (1) — Detecting a Ledge and Suspending the Player
This ledge grab mechanic seemed a little daunting to me at first, but breaking it down into a series of smaller tasks made it feel a lot more manageable:
- We need to detect when the player should grab a ledge (most likely using OnTriggerEnter and some colliders).
- We need to ‘snap’ the player to a desired position and suspend them in mid-air as they seem to hang from the ledge. This should also disable their movement.
- We should play a ‘ledge hanging’ animation on loop.
- We need to enable the player to trigger a climbing animation when they press a desired key (for instance, the ‘E’ key).
- We finally need to ensure the player avatar is positioned correctly after the climbing animation has finished and that control is returned to the player.
For this article, lets just focus on these first three steps 😉
1. To detect whether the player should automatically grab a ledge, we can make use of the OnTriggerEnter method, some BoxColliders, a RigidBody, and a tag. Create a cube for both a ‘Ledge Checker’ and a ‘Ledge Grab Checker’ GameObject. Disable or remove the MeshRenderer component and set ‘isTrigger’ to true on their colliders.
Next, ensure our Ledge Grab Checker has a dedicated tag like ‘Ledge_Grab_Checker’, and that it also has a RigidBody with gravity disabled (and you may also want to tick to freeze it on all axes). Ensure this GrabChecker is positioned and scaled appropriately over the player avatar (this should generally be positioned where the hands of our avatar will be when they’re in a ‘hanging from ledge’ animation). We’ll also need to ensure this object is parented to either the Player GameObject or the model itself.
For how I’ve positioned and set these objects up:
2. As you might have noticed in one of the screenshots, I’ve created and attached a custom ‘Ledge Checker’ script. This is where I’m storing the code to detect the collision and trigger a LedgeGrab method on the player. Since the Player class will be storing and handling the bulk of the logic needed for this mechanic, the Ledge Checker script is kept pretty short and sweet:
3. In the Player class, the LedgeGrab method needs to disable player movement (which I’m doing by disabling the CharacterController component), set a “LedgeGrab” boolean to true (and clear/reset some of the other animation parameters), set the player’s transform.position to the Vector3 grab position (passed into the method from the LedgeChecker), and most likely set a ‘ledgeGrabbing’ boolean to true. Not all of the following is probably strictly necessary, but it seems to work quite nicely for my purposes:
4. Just as we’ve done previously, you’ll need to search Mixamo for an appropriate ‘ledge hang’ animation, then download and import the .fbx for Unity file. As with the other animations, you’ll need to set the Rig to Humanoid, duplicate the animation clip, and implement it in your Animator Controller. In particular, we’re going to need to be able to transition from our jump and falling animation states to this one using custom ‘LedgeGrab’ bool parameter.
I’ll admit, my animator controller’s starting to look a bit messy, so perhaps using a transition from ‘Any State’ would be a better option for this. Either way, here’s how mine’s currently looking:
Don’t worry about the ‘Climb Up’ state just yet; the main thing is that we have these working transitions to our hanging state based on that LedgeGrab parameter (the one we’re setting to true in the LedgeGrab method itself).
5. Finally, we need to set up our ‘GrabPosition’. We could set this particular Vector3 purely through code, or we could instead create an empty GameObject, assign its transform to a variable, and then pass its transform.position into our method. I’ve opted for the latter, purely because I prefer being able to see and adjust this position in the Scene view.
For creating and positioning this GrabPosition object, I’d recommend heading into Play mode, manually triggering the player’s ‘Hanging from Ledge’ animation (by selecting the model and ticking the bool in the Animator window), pausing the game, and then positioning the player till their hands appear to be grabbing onto the ledge (without the model clipping through the ledge itself). Once you’ve got this just right, we can duplicate the player, remove any child GameObjects and components, and then copy this object. We should then be able to return to Edit mode, paste this object back in, and have the position we need.
All that should be left is for us to drag this object from the Hierarchy over into _ledgeGrabTransform variable on the LedgeGrab script in the Inspector.
With a little bit of tidying up and a bit of testing and tweaking, our player should be transitioning into our ‘hanging from ledge’ animation and position on cue 😉