Creating a modular ledge climb system in Unity using the new input system.
In this article I will go over how to make a modular ledge grab and climb system in Unity utilizing the new input system.
In my base scene I have a human character I downloaded from GameDevHQ’s Filebase. I wont go over how to set up the basic character controller as I already have done this in previous articles, however here is a link to a git repository with a character controller using the input system set up. You will need to provide your own humanoid asset.
The first step is to download and install your packages as the script wont work without them. I am using Cinemachine and the new input system, both of which can be downloaded using the package manager and searching the Unity registry tab.
You will get a warning pop up about disabling the UnityEngine APIs. Select Yes. This will restart Unity.
Next in our set up is download our hang and climb animations. I used Maximo for these. Make sure you download as FBX for Unity.
We can now set up the animations. In the animator window we are going to set up the following structure.
Specifically for the ledge system we will transition from any state to the hanging idle when grab ledge is triggered. From here the hanging idle animation will loop until climb is triggered. Climb will transition to idle after the animation is complete.
Ledge grab and climb
The basic idea behind the ledge grab system is to create an object attached to our player that when it collides with a ledge will trigger our hanging animation. While hanging we have the ability to click our jump button which will cause the player to climb up the ledge.
First I will set up the ledge checker, while we are setting it up we can keep the mesh renderer but it needs to be removed when the set up is done.
To finish setting up the checker add a Box Collider as a trigger, Rigidbody with no gravity and set the tag to “LedgeCollider”.
Next step is to create an object to ledge checker to collide with. I will call this ledge indicator. The ledge indicator needs to have 2 child objects, one will be called hand position and the other stand position.
Next we create the functionality. I will create a script called LedgeCollisionChecker. On this script we will create an action that takes in 2 Vector3s. These values will come from the child objects. Next we check for any collisions with objects tagged as LedgeCollider” and if found we invoke our event.
Over on the player controller script we want to read these values so we first subscribe to the event.
When the event is fired we trigger a function that moves the player to the hand position from the event and store the stand position for later use. I also set the rotation slightly as during my testing of the positions there was some slight clipping. We also disable our character controller to prevent gravity (so we can hang indefinitely) and so the player can’t move. We also have a bool to determine if we are hanging which we will set to true.
Now in the jump method we can add some logic that will trigger our climb. We will call a climb method and return so we don’t trigger our jump.
Our climb method will just trigger our animator.
Over on our update method we will prevent any errors due to the character controller being disabled by returning while hanging is set to true.
On our Player Animation Controller Script we will also subscribe to the event. We will call a function that sets a “interacting with ledge” bool to trigger the grab ledge animation when the event is fired.
The interacting with ledge bool will just prevent the player from rotating the player model.
Now we check current functionality. What we will notice is that the snapping position is off.
We can fix the hand position by dragging the player to the correct position in play mode and copying the transform information. We can then move the hands position game object out of the ledge indicator and paste the component values, this ensure there is no local/world space issue. Now just slide the hands position object back into the parent object and test again.
As you can see the player ends up in the right spot but there is a massive snap in their movement. All that can be done is to adjust the position of the ledge indictor to most reduce the snapping effect. While testing it’s a good idea to remove the hand position object to prevent moving it from its current position.
After 10 minutes of testing I ended up with the following.
The climb animation currently can be triggered as well. This however only moves the player model and not the player game object as below.
We can get around this by calling an event on an animation behavior script when the climb animation is complete and moving our player to the “standing position” which we stored earlier. The standing position can be setup, tested and configured the same way we found the correct hand positions.
To set up the new behavior script on the animation, open the animator window with the player selected and select the climbing state. Next in the inspector click “Add Behavior”. This allows you to create a behavior script. I will call mine ClimbUpAnimation. O
n this script we call an event our player can subscribe to when the animation is complete (OnStateExit).
On the player we can subscribe to this event and create a function called climb complete.
In this function we can move the player to the standing position, re-enable the character controller and set the hanging bool to false.
On the player animation controller we will subscribe to the event and call a function that will set the interacting with ledge bool to false.
Next we can see our animation in action.
That’s all for now.