Clamp fall speed best practice?

I have a custom AirTime ability (akin to Fall) and I want to clamp the fall speed. With all the settings UCC offers, I feel like there is a dedicated approach to enable such a limitation. What is the best way to clamp the fall speed?
 
I've tried the following, but it works poorly as the counter force is visually jumpy and the character can easily fall through surfaces that it otherwise doesn't fall through.

C#:
public override void Update()
{
    // Clamp the fall speed
    UpdateMaxFallVelocity();
}

private void UpdateMaxFallVelocity()
{
    if (m_CharacterLocomotion.Velocity.y < maxFallSpeed)
    {
        Scheduler.ScheduleFixed(0, ClampFallVelocity);
    }
}

private void ClampFallVelocity()
{
    m_CharacterLocomotion.AddRelativeForce(Vector3.up * -maxFallSpeed, 0);
}
 
You could use the state system to adjust the character's gravity magnitude, but this wouldn't clamp their fall speed if it already exceeds the limit when activated.

You can actually clamp the character's velocity directly like so: (in FixedUpdate)

m_CharacterLocomotion.Velocity = new Vector3(velocity.x, minFallSpeed, velocity.z);
 
@Andrew I tried what you suggest, but it is not actually limited as expected as can be seen from the Console screenshot. Any ideas? (Console.Log is just a Debug.Log utility that allows multiple args)

C#:
    void FixedUpdate()
    {
        if (!m_CharacterLocomotion.Grounded)
        {
            Vector3 velocity = m_CharacterLocomotion.Velocity;
            float limit = -15f;
            if (velocity.y < limit)
            {
                Console.Log("was", velocity.y, "now", Mathf.Max(velocity.y, limit));
                m_CharacterLocomotion.Velocity = new Vector3(velocity.x, Mathf.Max(velocity.y, limit), velocity.z);
            }
        }
    }

1595287736667.png
 
I'm not sure what the issue is here? The velocity y value is being clamped to -15 as expected. Log m_CharacterLocomotion.Velocity.y after the clamping has been applied.

Here's my full script:


C#:
using UnityEngine;
using Opsive.UltimateCharacterController.Character;

public class ClampFallSpeed : MonoBehaviour
{
    UltimateCharacterLocomotion m_CharacterLocomotion;
    public float minFallSpeed;
    public float velocityY;

    void Start() {
        m_CharacterLocomotion = GetComponent<UltimateCharacterLocomotion>();
    }

    void FixedUpdate() {
        if (!m_CharacterLocomotion.Grounded) {
            Vector3 velocity = m_CharacterLocomotion.Velocity;

            if (velocity.y < minFallSpeed) {
                m_CharacterLocomotion.Velocity = new Vector3(velocity.x, minFallSpeed, velocity.z);
            }
        }

        velocityY = m_CharacterLocomotion.Velocity.y;
        Debug.Log(velocityY);
    }
}

Using velocityY to see the character's Velocity.y value in real-time in the inspector, I can see that it remains clamped at the value of minFallSpeed when falling. The log also shows this.
 
Another more reliable method would actually be to override ApplyPosition and restrict MoveDirection. The RestrictPosition ability does something similar to this:

C#:
        public override void ApplyPosition()
        {
            var targetPosition = m_Transform.position + m_CharacterLocomotion.MoveDirection;
            if (RestrictedPosition(ref targetPosition)) {
                m_CharacterLocomotion.MoveDirection = targetPosition - m_Transform.position;
            }
        }

So you could override ApplyPosition and clamp the y value of MoveDirection.
 
I'm not sure what the issue is here? The velocity y value is being clamped to -15 as expected. Log m_CharacterLocomotion.Velocity.y after the clamping has been applied.

Here's my full script:


C#:
using UnityEngine;
using Opsive.UltimateCharacterController.Character;

public class ClampFallSpeed : MonoBehaviour
{
    UltimateCharacterLocomotion m_CharacterLocomotion;
    public float minFallSpeed;
    public float velocityY;

    void Start() {
        m_CharacterLocomotion = GetComponent<UltimateCharacterLocomotion>();
    }

    void FixedUpdate() {
        if (!m_CharacterLocomotion.Grounded) {
            Vector3 velocity = m_CharacterLocomotion.Velocity;

            if (velocity.y < minFallSpeed) {
                m_CharacterLocomotion.Velocity = new Vector3(velocity.x, minFallSpeed, velocity.z);
            }
        }

        velocityY = m_CharacterLocomotion.Velocity.y;
        Debug.Log(velocityY);
    }
}

Using velocityY to see the character's Velocity.y value in real-time in the inspector, I can see that it remains clamped at the value of minFallSpeed when falling. The log also shows this.

Yes, but when you read the "was" portion of the Console screenshot, the velocity.y keeps climbing. As a result the desired restriction isn't actually occurring.

Thank you for the ApplyPosition suggestion, I'll explore that option.
 
Last edited:
I'll likely post the solution after work, but in digging deeper I learned that clamping/tweaking m_CharacterLocomotion.GravityForce is also required.
 
For those interested, I got the result I was after via:

C#:
public override void UpdatePosition()
{
    base.UpdatePosition();

    if (!m_CharacterLocomotion.Grounded)
    {
        Vector3 velocity = m_CharacterLocomotion.Velocity;
        if (velocity.y < limitFallSpeed)
        {
            // Clamp velocity
            m_CharacterLocomotion.Velocity = Vector3.ClampMagnitude(new Vector3(velocity.x, Mathf.Max(velocity.y, limitFallSpeed), velocity.z), limitFallSpeed);

            // Clamp external gravity
            if (m_CharacterLocomotion.GravityForce < limitExternalGravity)
            {
                m_CharacterLocomotion.GravityForce = limitExternalGravity;
            }
        }
    }
}
 
Top