Day 91 — Climb up from Ledge Grab

Connor Fullarton
5 min readJun 14, 2021

Hey and welcome!

Now that we can grab the ledge fine it’s time to implement a way to climb onto the ledge from our hanging idle position.

Start things off by searching for a climbing animation you like in Mixamo and import it into your project. If you used the Hanging Idle animation that I used you’ll be able to find a climb animation specifically for that called Climbing.

Once that’s imported, drag it into your animator as usual and make a transition from your Ledge Grab state to this new Climbing state and then create a ClimbUp trigger parameter that you can use as the condition for this transition.

If you test out your animation however you’ll notice something off when you play it out:

You’ll notice that our character snaps back down to the ledge and that’s because the character controller you see as the green capsule here isn’t moving at all so we need to add in code to make that happen.

With the code though we’re going to be doing something a little different to our usual MonoBehaviour C# scripts. If you click on your climbing state in the animator you’ll notice an Add Behaviour option in the inspector. Click on that and create a behaviour script called ClimbUpBehaviour or similar and then open that script up to see this:

This time our class is inheriting StateMachineBehaviour and you’ll notice a bunch of predefined methods commented out, the one that matters to us currently is OnStateExit which as the name implies will run code when the animator exits the Climbing state.

We’ll come back to this but just know that we’ll be needing this to recognise what to do when we exit the climbing state.

Let’s start thing off here by adding in the following code to our Ledge script:

private Vector3 _standPos;if (other.tag == "Ledge_Grabber")
{
Player playerController = other.transform.parent.GetComponent<Player>();
if (playerController != null)
{
playerController.GrabLedge(_handPos, this);
}
else
{
Debug.LogError("Can't find Player script");
}
}
}
public Vector3 GetStandPos()
{
return _standPos;
}

The _standPos value here is going to be the position that our character stands to when they climb up. To get an accurate value for this go ahead and duplicate the player object and disable the character controller on the copy. Now run the game and get your movable character to hand from the ledge and click on the trigger for it in the animator so that the player stops on top of the ledge crouched.

You can then move your duplicate player while the game is running and line up the feet of both objects until you’re happy with the positions. The x, y, z values of the duplicates position is what you’ll be adding as the _standPos.

This part is mostly trial and error and up to you to get something that you like, you may need to change up the values in the root transform for the climbing animation to get things to line up the way you want to.

Next up add in this new code into your Player script:

private bool _onLedge = false;
private Ledge _activeLedge;
private void Update()
{
if (_onLedge == true)
{
if (Input.GetKeyDown(KeyCode.E))
{
_anim.SetTrigger("ClimbUp");
}
}

}
public void GrabLedge(Vector3 handPos, Ledge currentLedge)
{
_controller.enabled = false;
_anim.SetBool("LedgeGrab", true);
_anim.SetFloat("Speed", 0.0f);
_anim.SetBool("Jumping", false);
_onLedge = true;
_activeLedge = currentLedge;
transform.position = handPos;
}
public void ClimbUpComplete()
{
transform.position = _activeLedge.GetStandPos();
_anim.SetBool("LedgeGrab", false);
_onLedge = false;
_controller.enabled = true;
}

We’re adding in a new method and some new code into our GrabLedge method here. First up let’s look at our _activeLedge variable which has a data type of Ledge, as in our Ledge script. If you noticed in the ledge script we were passing in this into the GrabLedge method, that just means we’re adding in the particular instance of the Ledge that the current script is running on.

So if you have two ledges named Ledge1 and Ledge2 and the player is currently hanging from Ledge2 then it’s that one that’s getting passed into the code here.

This ledge component that gets passed in is then assigned to _activeLedge which we then use in our ClimbUpComplete() method in order to get the value of _standPos from the Ledge script.

Everything else in the code is some cleanup making sure that our animations are playing in order and that we’re only moving or initiating the ledge grab when we want to.

Lastly let’s head over to the ClimbUpBehaviour script and add in this code:

override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
var player = animator.gameObject.transform.parent.GetComponent<Player>();
if (player != null)
{
player.ClimbUpComplete();
}
}

All that’s happening here is that we’re running the ClimbUpComplete() method from our Player script when the Climbing animation has finished.

With all that in place you should have something like the above working for you now!

--

--

Connor Fullarton

Hey and welcome! My name is Connor and my goal here is to put out a daily post for a full year about my game development journey.