Skip to main content

Examples

This section showcases working setups using the Ledge System.

Use these examples as a reference when integrating the system into your own project.


Sample Character

The SampleCharacter prefab demonstrates a complete working implementation.

It includes:

  • Ledge detection
  • Movement along ledges
  • Climbing up
  • Dropping
  • Jumping away (Braced Only)
  • Hopping up (Braced Only)

Braced vs Free

  • A braced ledge position is when the player model can brace its feet on the wall of the ledge
  • Braced Ledge Position
  • A free ledge position is when the player model is fully hanging and is holding itself up only by its hands
  • Free Ledge Position

Demo Scene Overview

The demo scenes is set up to test different ledge scenarios.

It includes:

  • Straight ledges
  • Inner corners
  • Outer corners
  • Varying heights

Basic Setup Flow

To quickly test the system:

  1. Add the SampleCharacter to your scene
  2. Ensure ledge surfaces are on the correct layer
  3. Press Play
  4. Approach a ledge while airborne

You should see the character snap to a valid ledge.


Movement Modules

Detect Ledge

The player should be airborne when detecting ledges.

We use a two ray system to detect potential ledges.

one miss check and one hit check. (in the video below, the red line is our miss check and the blue line is the hit check)

Detect Ledge Move Details
  • DetectLedgeMove is a BaseLedgeMovement component.
  • When the miss check misses and hit check hits, it asks LedgeHandler to give a ledge position.
  • Information of the ledge is sent to ledge agent by callingLedgeAgent.EnableLedgeGrab
  • LedgeAgent sets the ledge information and restricts movement so the movement is handled by the ledge movements.

If you want to customize this behaviour, we recommend starting with LedgeAgent.EnableLedgeGrab.

You can then modify the DetectLedgeMove if you want it to be working in a different way. The two-ray hit/miss approach has proven reliable across a wide range of geometry configurations. Remember all you need to provide for the LedgeHandler to calculate a CharatcerLedge is an initial hit point close to the ledge edge and it's normal.


Move Along Ledge

The player can move sideways across connected ledges. It handles curves, inclines and declines.

Move Along Ledge Details
  • OnLedgeMove is a BaseLedgeMovement component that listens to player input.
  • When input is received, it asks LedgeHandler to give updated ledge position based on the movement direction
  • Information of old and new ledge positions are sent to LedgeAgent
  • LedgeAgent creates MoveRequests and passes them to PlayerLedgeMoveRequestProcessor
  • PlayerLedgeMoveRequestProcessor handles movements and provides updated positions via event callbacks
  • The example player controller and animation system use the positions provided by PlayerLedgeMoveRequestProcessor to handle IK and animations.

If you want to customize this behaviour, we recommend starting with how LedgeAgent.MoveOnLedge creates the MoveRequests and how PlayerLedgeMoveRequestProcessor processes them.


Climb Up

When a ledge is climbable, the player can move up over it.

Climb Up Details
  • JumpClimbLedge is a BaseLedgeMovement component that listens to player input.
  • If the ledge is climbable, it asks LedgeAgent to move the player up the ledge and run the associated animations.
  • Some movements are controlled by animation root motion. Climb is one of them.
  • AnimationRootMotionProxy detects the climb animation applies root motion (via LedgeAnimationData)
  • When the animation is done, the player object is pushed the target climb position (TargetLocation)
  • Ledge info is flushed and the character is back in the normal state where it can detect new ledges.

If you want to customize this behaviour, we recommend starting with how the LedgeAgent.EnableAutoLedgeClimb and AnimationRootMotionProxy are handling movement and creating the TargetLocation.


Drop From Ledge

The player can release and fall from the ledge. If there is any potential ledge below, the agent tries to grab it.

Drop From Ledge Details
  • DropFromLedge is a BaseLedgeMovement component that listens to player input.
  • DropFromLedge checks if there is any potential ledge bellow the player capsule.
  • If there are any potential ledges, then the potential initial hit point and a potential hit normal is calculated
  • If not, then nothing is calculated.
  • LedgeAgent.DisableLedgeGrabAndDrop is called.
  • if potential ledge data is provied, LedgeHandler.LandOnLedge is called for the player to snap back onto the ledge.
  • if not, after a short delay ledge info is flushed and the character is back in the normal state where it can detect new ledges.

If you want to customize this behaviour, we recommend starting with the LedgeAgent.DisableLedgeGrabAndDrop.


Jump Away (Braced Only)

The player can jump away from the ledge and rotate 180 degrees. If a potential ledge is behind the player object, the agent tries to grab it.

Jump Away Details
  • JumpClimbLedge is a BaseLedgeMovement component that listens to player input.
  • If the input is pointing at the back of the agent and jump is requested.
  • JumpClimbLedge checks if there is any potential ledge behind the player capsule.
  • If there are any potential ledges, then the potential initial hit point and a potential hit normal is calculated
  • If not, then nothing is calculated.
  • LedgeAgent.DisableLedgeGrabAndJumpAway is called
  • if potential ledge data is provied, LedgeHandler.LandOnLedge is called for the player to snap back onto the ledge when the player character has done a full rotation to face the ledge.
  • if not, we wait until the player character has done a full rotation to face the ledge, then ledge info is flushed and the character is back in the normal state where it can detect new ledges.

If you want to customize this behaviour, we recommend starting with the LedgeAgent.DisableLedgeGrabAndJumpAway.


Hop Up (Braced Only)

The player can jump up from one ledge to either a jumping up aniamtion. If a potential ledge is above the player object, the agent tries to grab it.

Hop Up Details
  • JumpClimbLedge is a BaseLedgeMovement component that listens to player input.
  • if the ledge is not climbale we process a hop up instead of a climb
  • JumpClimbLedge checks if there is any potential ledge above the player capsule.
  • If there are any potential ledges, then the potential initial hit point and a potential hit normal is calculated.
  • If not, then nothing is calculated.
  • LedgeAgent.DisableLedgeGrabAndJumpUp is called
  • if potential ledge data is provied, LedgeHandler.LandOnLedge is called for the player to snap back onto the ledge.
  • if not, after a short delay ledge info is flushed and the character is back in the normal state where it can detect new ledges.

If you want to customize this behaviour, we recommend starting with the LedgeAgent.MoveUpOnLedge and LedgeAgent.DisableLedgeGrabAndJumpUp.


Animations

LedgeAnimationData

This is an ScriptableObject that refereces an animmation state in the animation state machine.

  • It specifies if this animation should apply root motion.
  • It specifies if this animation should start or stop IK.
  • Some timing variables.

AnimationRootMotionProxy

This is a MonoBehaviour that has a reference to a list of LedgeAnimationData and checks:

  • If the currently running animation is part of the defined list of LedgeAnimationData
  • If so, then it checks if the LedgeAnimationData applies root motion, and if so, takes control of position update from the IAnimationRootMotionConsumer (in this case our PlayerController)
  • It checks if the IK should be anbled/disabled and if so makes sure the IK is enabled/disabled at the right time.

Customizing the Examples

The example setup is fully modular.

You can:

  • Replace movement components
  • Modify detection settings
  • Integrate your own character controller
  • Swap animation / IK systems

  • Review Runtime Components to understand how the example is built
  • Explore Core System to understand the underlying logic
  • Start replacing parts with your own implementation