Ledge Up

We need a way for us to get on top of the ledge once we get close enough to a flat (walkable) surface while climbing.

BOTW ledge up example

Root Motion Montage

One way to accomplish this is by playing the animation, moving the character, and blocking user input till it finishes. However this create a dependency on timing the animation correctly, and if either side changes it requires the other to change as well. Instead we'll use an animation with Root Motion.

Root Motion animations move the physical character with the animation (as opposed to in place animations)

red line showing the root bone of the animation physically moving

Similar to those games, we'll automatically ledge up whenever we're close to a ledge. .h

Let's add our call to check for TryClimbUpLedge() after we move (since we might have come into range of a ledge this frame)

.cpp

Before filling out TryClimbUpLedge() we need a way to access the Root Motion animation to play.

We're going to use a Montage for this which thankfully supports replicating root motion in network games (not that we are one but just good to know).

Create a new animation montage and add the proper Root Motion animation to the default slot track

(green) animation, (yellow) optional values you might need to tweak to get the animation looking right speed wise

You might find that you have to play around with some of the settings like End Time, Play Scale, and Blend In/Out to get it looking right

Calling Montage from C++

As long as we have a reference to the montage via an AnimationInstance we can run it from c++.

There are 3 prerequisites needed before we can actually play the ledge climb.

  • We're moving upwards

  • We're near a ledge (i.e. there is a gap in the wall with a stand-able surface above)

  • We can actually fit on the ledge (it isn't too small, or obstacles in the way)

.h

First lets store the animation montage in BeginPlay()

.cpp

Upwards Movment check

First we check if we're moving up by comparing the angle of movement (Velocity) and what we know to be the Component's up

Need to use the Component's Up versus global Up because the wall might be at an angle like an over or underhang

Then we reset the rotation and play the montage (which will move us the rest of the way).

Reached Ledge check

The purpose of HasReachedLedge() is to make sure we're within range of a ledge, of which there are two parts

  • Check at some height above the character to see if we don't collide with a wall

  • The length of the check should be at least equal to the shape sweep

The reason for the length check is to protect against false positives like this, where there technically is a ledge but not a traversable one

We can use the existing EyeHeightTrace() for the height to fire the ray from, but need to remember that when climbing we shrunk the capsul collider, so now instead of using just the Component's BaseEyeHeight, we need to add back on the height proportional to the capsul collider we shrunk

.cpp

then add the new check to HasReachedLedge()

.cpp

Ledge Traversability check

Lastly, we need to account for situations where the ledge above is large enough to stand on, but either the ledge isn't flat enough, or there isn't enough space on the ledge (i.e. obstacles exist blocking)

the raycast doesn't hit anything indicating we have enough ledge space, but obstacles prevent us from ledging up

To do this we'll sweep at the expected ledge up with a shape similar to our character's capsul collider.

If it hits anything we know there is something in the way, otherwise if not then we know there is at least enough space for our character!

(left) enough space for the capsule collider, (right) not enough space for the capsule collider

.cpp

Last thing to do is make sure we don't change the rotation while in root motion which happens during our physics check GetSmoothClimbingRotation.

.cpp

Now we will ledge up when reaching the surface!

Last updated