Ledge hanging not "grabbing" the ledge

I have my Ledge hanging state mostly completed, it working, I transition from the falling state to my ledge Hanging State, everything animates correct, but the Player does not “Grab” the ledge, it just goes into the hanging state, but continues to the ground while playing the hanging animation… I can see this because the Players collider goes back to the terrain.

My Player Hanging State
Thanks

public class PlayerHangingState : PlayerBaseState
{
    private readonly int LedgeHangingBlendTreeHash = Animator.StringToHash("LedgeBlendTree");
    protected readonly int LedgeMovementDirectionHash = Animator.StringToHash("LedgeMovementDirection");
    private const float CrossFadeDuration = 0.1f;
    private Vector3 closestPoint;
    private Vector3 ledgeForward;
    public PlayerHangingState(PlayerStateMachine stateMachine, Vector3 closestPoint, Vector3 ledgeForward) : base(stateMachine)
    {
        this.closestPoint = closestPoint;
        this.ledgeForward = ledgeForward;
    }

    public override void Enter()
    {
        stateMachine.transform.rotation = Quaternion.LookRotation(ledgeForward, Vector3.up);

        stateMachine.InputReader.LedgeClimbEvent += InputReader_LedgeClimbEvent;
        stateMachine.InputReader.LedgeDropEvent += InputReader_LedgeDropEvent;

        stateMachine.Animator.CrossFadeInFixedTime(LedgeHangingBlendTreeHash, CrossFadeDuration);
        stateMachine.Animator.SetFloat(LedgeMovementDirectionHash, 0);
    }

   
    public override void Exit()
    {
        stateMachine.InputReader.LedgeClimbEvent -= InputReader_LedgeClimbEvent;
        stateMachine.InputReader.LedgeDropEvent -= InputReader_LedgeDropEvent;
    }

    public override void Tick(float deltaTime)
    {
        Vector3 movement = new Vector3();

        movement += stateMachine.transform.right * stateMachine.InputReader.MovementValue.x;

        Move(movement, 1, deltaTime);
        stateMachine.Animator.SetFloat(LedgeMovementDirectionHash, stateMachine.InputReader.MovementValue.x);
    }

    private void InputReader_LedgeDropEvent()
    {
        Debug.Log("Hanging - ledge drop event");
        stateMachine.SwitchState(new PlayerFallState(stateMachine, 1));
    }

    private void InputReader_LedgeClimbEvent()
    {
        //Climb Ledge
        Debug.Log("TO DO: Ledge Climbing!");
    }


}

My code is slightly different, but I don’t think the changes I made are the issue… (or are they?), I think it has to do something with the Animation, I made sure they are set to no Motion, but I have an off set on the Y position, because otherwise the animation was playing halfway through the ground…

So, I tried a number of different settings with the animations, and I fiddled with trying to set the Players Y position when in the HangingState… but nothing fixed the issue, yet, not matter what I’ve tried my Players collider goes back to the terrain, and leaves my character hanging, but on the ground.
I came up with a Work around though, until I can actually solve the issue.
By creating a Ledge.cs script that contains a collider that is set up at the distance from the bottom of the players collider. It is disabled on awake, but when collides with a LedgeDetector, it enables preventing the player from falling… I dont know if its the best solution, but it works. And, it allows me to have ledges where if the player goes too far left or right they fall off.
Here is my Ledge.cs

public class Ledge : MonoBehaviour
{
    [Title("Ledge", "Ledge Collider", TitleAlignments.Centered), SerializeField]
    Collider ledgeCollider;
    [BoxGroup("Input Reader"), SerializeField]
    InputReader inputReader;

    LedgeDetector ledgeDetector;

    private void Awake()
    {
        DisableLedgeCollider();
    }
    private void OnEnable()
    {
        inputReader.LedgeDropEvent += DisableLedgeCollider;
    }
    private void OnTriggerEnter(Collider other)
    {
        if(other.TryGetComponent<LedgeDetector>(out ledgeDetector))
        {
            EnableLedgeCollider();
        }
    }
    private void OnTriggerExit(Collider other)
    {
       
        if(other.TryGetComponent<LedgeDetector>(out ledgeDetector))
        {
            DisableLedgeCollider();
        }
    }

    public void EnableLedgeCollider()
    {
        ledgeCollider.enabled = true;
    }
    public void DisableLedgeCollider()
    {
        ledgeCollider.enabled = false;
    }

I think that the issue is in your PlayerHangingState.Tick() method…
The base Move() method will always include the force receiver’s input… in this case, gravity, so while the game thinks you’re still hanging on, gravity is pulling the character downward in the Move() component.

Compare this to Nathan’s Tick()

    public override void Tick(float deltaTime)
    {
        if (stateMachine.InputReader.MovementValue.y > 0f)
        {
            stateMachine.SwitchState(new PlayerPullUpState(stateMachine));
        }
        else if (stateMachine.InputReader.MovementValue.y < 0f)
        {
            stateMachine.Controller.Move(Vector3.zero);
            stateMachine.ForceReceiver.Reset();
            stateMachine.SwitchState(new PlayerFallingState(stateMachine));
        }
    }

You’ll note that there is no Move() at all unless the player tries to move down, and at that point we switch states.

Thanks Brian… I put the move() method in, so that while hanging, the player could move left or right on the ledge… I’ll look into how to alter my implementation tonight! Thanks for the reply!

I think you’ll need a more specialized Move for that, tied to that specific state, that allows left and right movement, but switches state for up and down movement… and when you move left or right, do not use the ForceReciever’s input or you’ll drop to the ground .

I actually did experiment by disabling the force receiver, when in that state, but it did not help. Still at work, but excited to go home and try new solutions, lol.

Disabling it won’t work if the Move method is still polling ForceReceiver, as it will just return the last force value before the force receiver was disabled without updating it frame to frame (which is… worse!).

So, I don’t have much time to experiment tonight, but unfortunately, I complete replaced the Tick Method, with the above, basically testing if the the move method was the thing effecting the gravity… but my player character still falls. So, I still have some experimenting to do… However, earlier, I have come up with a work around that I still have some tweaking to work on to get everything lined up, but it accomplishes the goal for the time being.
I basically placed a landing pad on the ledge that tracks where, on the ledge the player is. When the player climbs up the ledge, at the end of the animation (a timer set by the Animation Length), I disable the player controller, move the players transform to the landing pads location and reenable the character controller. Its simpler than it sounds. The only issue I currently Have is the on the final frame of the animation the player is up in the air, which causes it to snap back to the platform in a weird way, but I have an idea to correct that issue as well. Here is an example of it in action.

In case your wondering, my idea to correct the issue is to move the characters transform on the first frame of the animation, while also resetting the location of the climbing animations offset. So that, when the animation starts, it will be correctly positioned on the platform.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms