Help with Fall Damage

I have a problem with my Health script. My intension was to when the players falls from a certain height to the ground the player decrease in health… But with this script I’m using it’s not quite working and I can’t figure out why.

If I’m on a platform higher than the heightThreshold and jump down to another platform, or jump UP to another one for that matter, I lose health…

My platform is 6 units above the ground, the platform below is 5 units above the ground - so the difference is 1 unit = No FallDamage should be applied… but when I jump to the lower platform I lose health.

And the same if I jump up to another platform higher than the one at 6 units up…

using UnityEngine;

/// <summary>
/// Class responsible for controlling player's health
/// </summary>
public class HealthController : MonoBehaviour {

    [HideInInspector] public float CurrentHP;
    public float maxHP = 100f;

    public bool regenerate;
    public float regenerateSpeed = 5f;

    public float delayToRegenerate = 5f;
    private float nextRegenerationTime = 0f;

    public CameraAnimations cameraAnim;

    [Space()]
    public AudioClip heartBeat;

    [Header("Fall Damage")]
    public float heightThreshold = 5f;
    public float damageMultiplier = 2.2f;

    public AudioManager audioManager;

    private float higherHeight = 0f;

    private FPSCE_Controller mController;
    private CapsuleCollider capsuleCol;
    private PlayerUI ui;

    private void Start()
    {
        mController = GetComponent<FPSCE_Controller>();
        capsuleCol = GetComponent<CapsuleCollider>();
        ui = GetComponent<PlayerUI>();

        CurrentHP = maxHP;
    }

    private void Update()
    {
        CheckFalling();

        CurrentHP = Mathf.Clamp(CurrentHP, 0, maxHP);
        audioManager.isDying = CurrentHP < maxHP * .35f;

        if (CurrentHP < maxHP * .3f)
            audioManager.PlayHeartbeatSound(heartBeat, CurrentHP, maxHP * .3f);

        if (regenerate && CurrentHP < maxHP && CurrentHP > 0 && nextRegenerationTime < Time.time)
            CurrentHP += Time.deltaTime * regenerateSpeed;
    }

    public void FallDamage(float damage)
    {
        ApplyDamage(damage);
        cameraAnim.FallShake();
    }

    private void ApplyDamage(float damage)
    {
        if(CurrentHP > 0 && damage > 0)
        {
            CurrentHP -= damage;
        }

        nextRegenerationTime = Time.time + delayToRegenerate;
    }

    private void CheckFalling()
    {
        if(mController.m_Controller.isGrounded && higherHeight > 0f)
        {
            if(higherHeight > heightThreshold)
            {
                FallDamage(Mathf.Round(damageMultiplier * -Physics.gravity.y * (higherHeight - heightThreshold)));
            }

            higherHeight = 0f;
        } else if(!mController.m_Controller.isGrounded)
        {
            if (CheckDistanceBelow() > higherHeight)
                higherHeight = CheckDistanceBelow();
        }
    }

    public float CheckDistanceBelow()
    {
        RaycastHit hit = new RaycastHit();
        if (Physics.SphereCast(transform.position, capsuleCol.radius, Vector3.down, out hit, heightThreshold * 10))
            return hit.distance;
        else
            return heightThreshold * 10;
    }

}

firstly I’d recommend this be split into two scripts, fall damage and health are different things which don’t really belong in the same script. The fall damage just needs a reference to the health script so it can call “healthscript.applydamage(somevalue)”.

I also think you’re over working it a little with the raycast and trying to calculate the height at the start of the fall. I’d approach it the other way around; In update just check the isgrounded and store the “last grounded” position and a “was grounded” bool (i.e. isGrounded gets cached for the next check), you can then check if you were falling last frame and aren’t falling this frame (i.e. “!wasGrounded && isGrounded”). Then the “fall distance” is just the difference in the y attributes of the current position and the last grounded position (clamped to stop negative “falling up”).

I’ve seen that solution before and know about it, but I think there’s an issue with that solution and therefore I don’t want to use it :slight_smile: If I walk off the edge or jumped off it it wouldn’t make any difference at all when applying damage.

I think I’ve never seen your solution (which seems to be the most common solution in the Unity community) in a game before… It makes more sense to lose more health when jumping off a edge than when walking off of it…

I’m not aiming for realism, but I want it to be believable.

@LeftyRighty

your sphere cast is kinda throwing me off. Any reason you’re not just casting a ray straight down to check distance?

Track the highest point in a given jump, and compare to that instead. If grounded, set highestPoint to the current hieght; if not grounded, set highestPoint to Mathf.Max(current height, highestPoint). Then compare when landing.

2 Likes

Thanks… I will try that. It seems logical :slight_smile: