Jump Glitch?

Why is my character landing at just a little over 1.0f on the Y axis ONLY when jumping?

Here’s my character script

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Rendering;

namespace Fragsurf.Movement {

   /// <summary>
   /// Easily add a surfable character to the scene
   /// </summary>
    [AddComponentMenu ("Fragsurf/Surf Character")]
   public class SurfCharacter : MonoBehaviour, ISurfControllable {

       public enum ColliderType {
           Capsule,
           Box
       }

       ///// Fields /////

        [Header("Physics Settings")]
       public Vector3 colliderSize = new Vector3 (1f, 2f, 1f);
        [HideInInspector] public ColliderType collisionType { get { return ColliderType.Box; } } // Capsule doesn't work anymore; I'll have to figure out why some other time, sorry.
       public float weight = 75f;
       public float rigidbodyPushForce = 2f;
       public bool solidCollider = false;

        [Header("View Settings")]
       public Transform viewTransform;
       public Transform playerRotationTransform;

        [Header ("Crouching setup")]
       public float crouchingHeightMultiplier = 0.5f;
       public float crouchingSpeed = 10f;
       float defaultHeight;
       bool allowCrouch = true; // This is separate because you shouldn't be able to toggle crouching on and off during gameplay for various reasons

        [Header ("Features")]
       public bool crouchingEnabled = true;
       public bool slidingEnabled = false;
       public bool laddersEnabled = true;
       public bool supportAngledLadders = true;

        [Header ("Step offset (can be buggy, enable at your own risk)")]
       public bool useStepOffset = false;
       public float stepOffset = 0.35f;

        [Header ("Movement Config")]
        [SerializeField]
       public MovementConfig movementConfig;
       
       private GameObject _groundObject;
       private Vector3 _baseVelocity;
       private Collider _collider;
       private Vector3 _angles;
       private Vector3 _startPosition;
       private GameObject _colliderObject;
       private GameObject _cameraWaterCheckObject;
       private CameraWaterCheck _cameraWaterCheck;

       private MoveData _moveData = new MoveData ();
       private SurfController _controller = new SurfController ();

       private Rigidbody rb;

       private List<Collider> triggers = new List<Collider> ();
       private int numberOfTriggers = 0;

       private bool underwater = false;

       private float _wishJumpScroll;

       ///// Properties /////

       public MoveType moveType { get { return MoveType.Walk; } }
       public MovementConfig moveConfig { get { return movementConfig; } }
       public MoveData moveData { get { return _moveData; } }
       public new Collider collider { get { return _collider; } }

       public GameObject groundObject {

           get { return _groundObject; }
           set { _groundObject = value; }

       }

       public Vector3 baseVelocity { get { return _baseVelocity; } }

       public Vector3 forward { get { return viewTransform.forward; } }
       public Vector3 right { get { return viewTransform.right; } }
       public Vector3 up { get { return viewTransform.up; } }

       Vector3 prevPosition;

       ///// Methods /////

       private void OnDrawGizmos()
       {
           Gizmos.color = Color.blue;
           Gizmos.DrawWireCube( transform.position, colliderSize );
       }

       private void Awake () {
           
           _controller.playerTransform = playerRotationTransform;
           
           if (viewTransform != null) {

               _controller.camera = viewTransform;
               _controller.cameraYPos = viewTransform.localPosition.y;

           }

       }

       private void Start () {
           
           _colliderObject = new GameObject ("PlayerCollider");
           _colliderObject.layer = gameObject.layer;
           _colliderObject.transform.SetParent (transform);
           _colliderObject.transform.rotation = Quaternion.identity;
           _colliderObject.transform.localPosition = Vector3.zero;
           _colliderObject.transform.SetSiblingIndex (0);

           // Water check
           _cameraWaterCheckObject = new GameObject ("Camera water check");
           _cameraWaterCheckObject.layer = gameObject.layer;
           _cameraWaterCheckObject.transform.position = viewTransform.position;

           SphereCollider _cameraWaterCheckSphere = _cameraWaterCheckObject.AddComponent<SphereCollider> ();
           _cameraWaterCheckSphere.radius = 0.1f;
           _cameraWaterCheckSphere.isTrigger = true;

           Rigidbody _cameraWaterCheckRb = _cameraWaterCheckObject.AddComponent<Rigidbody> ();
           _cameraWaterCheckRb.useGravity = false;
           _cameraWaterCheckRb.isKinematic = true;

           _cameraWaterCheck = _cameraWaterCheckObject.AddComponent<CameraWaterCheck> ();

           prevPosition = transform.position;

           if (viewTransform == null)
               viewTransform = Camera.main.transform;

           if (playerRotationTransform == null && transform.childCount > 0)
               playerRotationTransform = transform.GetChild (0);

           _collider = gameObject.GetComponent<Collider> ();

           if (_collider != null)
               GameObject.Destroy (_collider);

           // rigidbody is required to collide with triggers
           rb = gameObject.GetComponent<Rigidbody> ();
           if (rb == null)
               rb = gameObject.AddComponent<Rigidbody> ();

           allowCrouch = crouchingEnabled;

           rb.isKinematic = true;
           rb.useGravity = false;
           rb.angularDrag = 0f;
           rb.drag = 0f;
           rb.mass = weight;


           switch (collisionType) {

               // Box collider
               case ColliderType.Box:

               _collider = _colliderObject.AddComponent<BoxCollider> ();

               var boxc = (BoxCollider)_collider;
               boxc.size = colliderSize;
               Debug.Log ( $"boxc.size : { boxc.size }" );

               defaultHeight = boxc.size.y;

               break;

               // Capsule collider
               case ColliderType.Capsule:

               _collider = _colliderObject.AddComponent<CapsuleCollider> ();

               var capc = (CapsuleCollider)_collider;
               capc.height = colliderSize.y;
               capc.radius = colliderSize.x / 2f;
               Debug.Log ( $"capc.radius : { capc.radius }" );

               defaultHeight = capc.height;

               break;

           }

           _moveData.slopeLimit = movementConfig.slopeLimit;

           _moveData.rigidbodyPushForce = rigidbodyPushForce;

           _moveData.slidingEnabled = slidingEnabled;
           _moveData.laddersEnabled = laddersEnabled;
           _moveData.angledLaddersEnabled = supportAngledLadders;

           _moveData.playerTransform = transform;
           _moveData.viewTransform = viewTransform;
           _moveData.viewTransformDefaultLocalPos = viewTransform.localPosition;

           _moveData.defaultHeight = defaultHeight;
           _moveData.crouchingHeight = crouchingHeightMultiplier;
           _moveData.crouchingSpeed = crouchingSpeed;
           
           _collider.isTrigger = !solidCollider;
           _moveData.origin = transform.position;
           _startPosition = transform.position;

           _moveData.useStepOffset = useStepOffset;
           _moveData.stepOffset = stepOffset;

       }

       private void Update () {

           _colliderObject.transform.rotation = Quaternion.identity;


           //UpdateTestBinds ();
           UpdateMoveData ();
           
           // Previous movement code
           Vector3 positionalMovement = transform.position - prevPosition;
           transform.position = prevPosition;
           moveData.origin += positionalMovement;

           // Triggers
           if (numberOfTriggers != triggers.Count) {
               numberOfTriggers = triggers.Count;

               underwater = false;
               triggers.RemoveAll (item => item == null);
               foreach (Collider trigger in triggers) {

                   if (trigger == null)
                       continue;

                   if (trigger.GetComponentInParent<Water> ())
                       underwater = true;

               }

           }

           _moveData.cameraUnderwater = _cameraWaterCheck.IsUnderwater ();
           _cameraWaterCheckObject.transform.position = viewTransform.position;
           moveData.underwater = underwater;
           
           if (allowCrouch)
               _controller.Crouch (this, movementConfig, Time.deltaTime);

           _controller.ProcessMovement (this, movementConfig, Time.deltaTime);

           transform.position = moveData.origin;
           prevPosition = transform.position;

           _colliderObject.transform.rotation = Quaternion.identity;

       }
       
       private void UpdateTestBinds () {

           if (Input.GetKeyDown (KeyCode.Backspace))
               ResetPosition ();

       }

       private void ResetPosition () {
           
           moveData.velocity = Vector3.zero;
           moveData.origin = _startPosition;

       }

       private void UpdateMoveData () {
           
           _moveData.verticalAxis = Input.GetAxisRaw ("Vertical");
           _moveData.horizontalAxis = Input.GetAxisRaw ("Horizontal");

           _moveData.sprinting = Input.GetButton ("Sprint");
           
           if (Input.GetButtonDown ("Crouch"))
               _moveData.crouching = true;

           if (!Input.GetButton ("Crouch"))
               _moveData.crouching = false;
           
           bool moveLeft = _moveData.horizontalAxis < 0f;
           bool moveRight = _moveData.horizontalAxis > 0f;
           bool moveFwd = _moveData.verticalAxis > 0f;
           bool moveBack = _moveData.verticalAxis < 0f;
           bool jump = Input.GetButton ("Jump");

           if (!moveLeft && !moveRight)
               _moveData.sideMove = 0f;
           else if (moveLeft)
               _moveData.sideMove = -moveConfig.acceleration;
           else if (moveRight)
               _moveData.sideMove = moveConfig.acceleration;

           if (!moveFwd && !moveBack)
               _moveData.forwardMove = 0f;
           else if (moveFwd)
               _moveData.forwardMove = moveConfig.acceleration;
           else if (moveBack)
               _moveData.forwardMove = -moveConfig.acceleration;

           if(_wishJumpScroll >= 0f)
               _wishJumpScroll -= Time.deltaTime*0.5f;

           float mouseWheelAxis = Mathf.Abs(Input.GetAxisRaw("Mouse ScrollWheel"));
           _wishJumpScroll = Mathf.Clamp(Mathf.Lerp(0, _wishJumpScroll+mouseWheelAxis*20f, 0.5f), 0f, 2f);

           if (Input.GetButtonDown ("Jump") || _wishJumpScroll >= 0.04f || mouseWheelAxis >= 0.05f)
               _moveData.wishJump = true;

           else if (!Input.GetButton ("Jump"))
               _moveData.wishJump = false;
           
           _moveData.viewAngles = _angles;

       }

       private void DisableInput () {

           _moveData.verticalAxis = 0f;
           _moveData.horizontalAxis = 0f;
           _moveData.sideMove = 0f;
           _moveData.forwardMove = 0f;
           _moveData.wishJump = false;

       }

       /// <summary>
       ///
       /// </summary>
       /// <param name="angle"></param>
       /// <param name="from"></param>
       /// <param name="to"></param>
       /// <returns></returns>
       public static float ClampAngle (float angle, float from, float to) {

           if (angle < 0f)
               angle = 360 + angle;

           if (angle > 180f)
               return Mathf.Max (angle, 360 + from);

           return Mathf.Min (angle, to);

       }

       private void OnTriggerEnter (Collider other) {
           
           if (!triggers.Contains (other))
               triggers.Add (other);

       }

       private void OnTriggerExit (Collider other) {
           
           if (triggers.Contains (other))
               triggers.Remove (other);

       }

       private void OnCollisionStay (Collision collision) {

           if (collision.rigidbody == null)
               return;

           Vector3 relativeVelocity = collision.relativeVelocity * collision.rigidbody.mass / 50f;
           Vector3 impactVelocity = new Vector3 (relativeVelocity.x * 0.0025f, relativeVelocity.y * 0.00025f, relativeVelocity.z * 0.0025f);

           float maxYVel = Mathf.Max (moveData.velocity.y, 10f);
           Vector3 newVelocity = new Vector3 (moveData.velocity.x + impactVelocity.x, Mathf.Clamp (moveData.velocity.y + Mathf.Clamp (impactVelocity.y, -0.5f, 0.5f), -maxYVel, maxYVel), moveData.velocity.z + impactVelocity.z);

           newVelocity = Vector3.ClampMagnitude (newVelocity, Mathf.Max (moveData.velocity.magnitude, 30f));
           moveData.velocity = newVelocity;

       }

   }

Here’s my character’s controller

using UnityEngine;
using Fragsurf.TraceUtil;

namespace Fragsurf.Movement {
   public class SurfController {

       ///// Fields /////

        [HideInInspector] public Transform playerTransform;
       private ISurfControllable _surfer;
       private MovementConfig _config;
       private float _deltaTime;

       public bool jumping = false;
       public bool crouching = false;
       public float speed = 0f;
       
       public Transform camera;
       public float cameraYPos = 0f;

       private float slideSpeedCurrent = 0f;
       private Vector3 slideDirection = Vector3.forward;

       private bool sliding = false;
       private bool wasSliding = false;
       private float slideDelay = 0f;
       
       private bool uncrouchDown = false;
       private float crouchLerp = 0f;

       private float frictionMult = 1f;

       ///// Methods /////

       Vector3 groundNormal = Vector3.up;

       /// <summary>
       ///
       /// </summary>
       public void ProcessMovement (ISurfControllable surfer, MovementConfig config, float deltaTime) {
           
           // cache instead of passing around parameters
           _surfer = surfer;
           _config = config;
           _deltaTime = deltaTime;
           
           if (_surfer.moveData.laddersEnabled && !_surfer.moveData.climbingLadder) {

               // Look for ladders
               LadderCheck (new Vector3(1f, 0.95f, 1f), _surfer.moveData.velocity * Mathf.Clamp (Time.deltaTime * 2f, 0.025f, 0.25f));

           }
           
           if (_surfer.moveData.laddersEnabled && _surfer.moveData.climbingLadder) {
               
               LadderPhysics ();
               
           } else if (!_surfer.moveData.underwater) {

               if (_surfer.moveData.velocity.y <= 0f)
                   jumping = false;

               // apply gravity
               if (_surfer.groundObject == null) {

                   _surfer.moveData.velocity.y -= (_surfer.moveData.gravityFactor * _config.gravity * _deltaTime);
                   _surfer.moveData.velocity.y += _surfer.baseVelocity.y * _deltaTime;

               }
               
               // input velocity, check for ground
               CheckGrounded ();
               CalculateMovementVelocity ();
               
           } else {

               // Do underwater logic
               UnderwaterPhysics ();

           }

           float yVel = _surfer.moveData.velocity.y;
           _surfer.moveData.velocity.y = 0f;
           _surfer.moveData.velocity = Vector3.ClampMagnitude (_surfer.moveData.velocity, _config.maxVelocity);
           speed =  _surfer.moveData.velocity.magnitude;
           _surfer.moveData.velocity.y = yVel;
           
           if (_surfer.moveData.velocity.sqrMagnitude == 0f) {

               // Do collisions while standing still
               SurfPhysics.ResolveCollisions (_surfer.collider, ref _surfer.moveData.origin, ref _surfer.moveData.velocity, _surfer.moveData.rigidbodyPushForce, 1f, _surfer.moveData.stepOffset, _surfer);

           } else {

               float maxDistPerFrame = 0.2f;
               Vector3 velocityThisFrame = _surfer.moveData.velocity * _deltaTime;
               float velocityDistLeft = velocityThisFrame.magnitude;
               float initialVel = velocityDistLeft;
               while (velocityDistLeft > 0f) {

                   float amountThisLoop = Mathf.Min (maxDistPerFrame, velocityDistLeft);
                   velocityDistLeft -= amountThisLoop;

                   // increment origin
                   Vector3 velThisLoop = velocityThisFrame * (amountThisLoop / initialVel);
                   _surfer.moveData.origin += velThisLoop;

                   // don't penetrate walls
                   SurfPhysics.ResolveCollisions (_surfer.collider, ref _surfer.moveData.origin, ref _surfer.moveData.velocity, _surfer.moveData.rigidbodyPushForce, amountThisLoop / initialVel, _surfer.moveData.stepOffset, _surfer);

               }

           }

           _surfer.moveData.groundedTemp = _surfer.moveData.grounded;

           _surfer = null;
           
       }

       /// <summary>
       ///
       /// </summary>
       private void CalculateMovementVelocity () {
           switch (_surfer.moveType) {

               case MoveType.Walk:

               if (_surfer.groundObject == null) {

                   /*
                    // AIR MOVEMENT
                    */

                   wasSliding = false;

                   // apply movement from input
                   _surfer.moveData.velocity += AirInputMovement ();

                   // let the magic happen
                   SurfPhysics.Reflect (ref _surfer.moveData.velocity, _surfer.collider, _surfer.moveData.origin, _deltaTime);

               } else {

                   /*
                    //  GROUND MOVEMENT
                    */

                   // Sliding
                   if (!wasSliding) {

                       slideDirection = new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z).normalized;
                       slideSpeedCurrent = Mathf.Max (_config.maximumSlideSpeed, new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z).magnitude);

                   }

                   sliding = false;
                   if (_surfer.moveData.velocity.magnitude > _config.minimumSlideSpeed && _surfer.moveData.slidingEnabled && _surfer.moveData.crouching && slideDelay <= 0f) {

                       if (!wasSliding)
                           slideSpeedCurrent = Mathf.Clamp (slideSpeedCurrent * _config.slideSpeedMultiplier, _config.minimumSlideSpeed, _config.maximumSlideSpeed);

                       sliding = true;
                       wasSliding = true;
                       SlideMovement ();
                       return;

                   } else {

                       if (slideDelay > 0f)
                           slideDelay -= _deltaTime;

                       if (wasSliding)
                           slideDelay = _config.slideDelay;

                       wasSliding = false;

                   }
                   
                   float fric = crouching ? _config.crouchFriction : _config.friction;
                   float accel = crouching ? _config.crouchAcceleration : _config.acceleration;
                   float decel = crouching ? _config.crouchDeceleration : _config.deceleration;
                   
                   // Get movement directions
                   Vector3 forward = Vector3.Cross (groundNormal, -playerTransform.right);
                   Vector3 right = Vector3.Cross (groundNormal, forward);

                   float speed = _surfer.moveData.sprinting ? _config.sprintSpeed : _config.walkSpeed;
                   if (crouching)
                       speed = _config.crouchSpeed;

                   Vector3 _wishDir;

                   // Jump and friction
                   if (_surfer.moveData.wishJump) {

                       ApplyFriction (0.0f, true, true);
                       Jump ();
                       return;

                   } else {

                       ApplyFriction (1.0f * frictionMult, true, true);

                   }

                   float forwardMove = _surfer.moveData.verticalAxis;
                   float rightMove = _surfer.moveData.horizontalAxis;

                   _wishDir = forwardMove * forward + rightMove * right;
                   _wishDir.Normalize ();
                   Vector3 moveDirNorm = _wishDir;

                   Vector3 forwardVelocity = Vector3.Cross (groundNormal, Quaternion.AngleAxis (-90, Vector3.up) * new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z));

                   // Set the target speed of the player
                   float _wishSpeed = _wishDir.magnitude;
                   _wishSpeed *= speed;

                   // Accelerate
                   float yVel = _surfer.moveData.velocity.y;
                   Accelerate (_wishDir, _wishSpeed, accel * Mathf.Min (frictionMult, 1f), false);

                   float maxVelocityMagnitude = _config.maxVelocity;
                   _surfer.moveData.velocity = Vector3.ClampMagnitude (new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z), maxVelocityMagnitude);
                   _surfer.moveData.velocity.y = yVel;

                   // Calculate how much slopes should affect movement
                   float yVelocityNew = forwardVelocity.normalized.y * new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z).magnitude;

                   // Apply the Y-movement from slopes
                   _surfer.moveData.velocity.y = yVelocityNew * (_wishDir.y < 0f ? 1.2f : 1.0f);
                   float removableYVelocity = _surfer.moveData.velocity.y - yVelocityNew;

               }

               break;

           } // END OF SWITCH STATEMENT
       }

       private void UnderwaterPhysics () {

           _surfer.moveData.velocity = Vector3.Lerp (_surfer.moveData.velocity, Vector3.zero, _config.underwaterVelocityDampening * _deltaTime);

           // Gravity
           if (!CheckGrounded ())
               _surfer.moveData.velocity.y -= _config.underwaterGravity * _deltaTime;

           // Swimming upwards
           if (Input.GetButton ("Jump"))
               _surfer.moveData.velocity.y += _config.swimUpSpeed * _deltaTime;

           float fric = _config.underwaterFriction;
           float accel = _config.underwaterAcceleration;
           float decel = _config.underwaterDeceleration;

           ApplyFriction (1f, true, false);

           // Get movement directions
           Vector3 forward = Vector3.Cross (groundNormal, -playerTransform.right);
           Vector3 right = Vector3.Cross (groundNormal, forward);

           float speed = _config.underwaterSwimSpeed;

           Vector3 _wishDir;

           float forwardMove = _surfer.moveData.verticalAxis;
           float rightMove = _surfer.moveData.horizontalAxis;

           _wishDir = forwardMove * forward + rightMove * right;
           _wishDir.Normalize ();
           Vector3 moveDirNorm = _wishDir;

           Vector3 forwardVelocity = Vector3.Cross (groundNormal, Quaternion.AngleAxis (-90, Vector3.up) * new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z));

           // Set the target speed of the player
           float _wishSpeed = _wishDir.magnitude;
           _wishSpeed *= speed;

           // Accelerate
           float yVel = _surfer.moveData.velocity.y;
           Accelerate (_wishDir, _wishSpeed, accel, false);

           float maxVelocityMagnitude = _config.maxVelocity;
           _surfer.moveData.velocity = Vector3.ClampMagnitude (new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z), maxVelocityMagnitude);
           _surfer.moveData.velocity.y = yVel;

           float yVelStored = _surfer.moveData.velocity.y;
           _surfer.moveData.velocity.y = 0f;

           // Calculate how much slopes should affect movement
           float yVelocityNew = forwardVelocity.normalized.y * new Vector3 (_surfer.moveData.velocity.x, 0f, _surfer.moveData.velocity.z).magnitude;

           // Apply the Y-movement from slopes
           _surfer.moveData.velocity.y = Mathf.Min (Mathf.Max (0f, yVelocityNew) + yVelStored, speed);

           // Jumping out of water
           bool movingForwards = playerTransform.InverseTransformVector (_surfer.moveData.velocity).z > 0f;
           Trace waterJumpTrace = TraceBounds (playerTransform.position, playerTransform.position + playerTransform.forward * 0.1f, SurfPhysics.groundLayerMask);
           if (waterJumpTrace.hitCollider != null && Vector3.Angle (Vector3.up, waterJumpTrace.planeNormal) >= _config.slopeLimit && Input.GetButton ("Jump") && !_surfer.moveData.cameraUnderwater && movingForwards)
               _surfer.moveData.velocity.y = Mathf.Max (_surfer.moveData.velocity.y, _config.jumpForce);

       }
       
       private void LadderCheck (Vector3 colliderScale, Vector3 direction) {

           if (_surfer.moveData.velocity.sqrMagnitude <= 0f)
               return;
           
           bool foundLadder = false;

           RaycastHit [] hits = Physics.BoxCastAll (_surfer.moveData.origin, Vector3.Scale (_surfer.collider.bounds.size * 0.5f, colliderScale), Vector3.Scale (direction, new Vector3 (1f, 0f, 1f)), Quaternion.identity, direction.magnitude, SurfPhysics.groundLayerMask, QueryTriggerInteraction.Collide);
           foreach (RaycastHit hit in hits) {

               Ladder ladder = hit.transform.GetComponentInParent<Ladder> ();
               if (ladder != null) {

                   bool allowClimb = true;
                   float ladderAngle = Vector3.Angle (Vector3.up, hit.normal);
                   if (_surfer.moveData.angledLaddersEnabled) {

                       if (hit.normal.y < 0f)
                           allowClimb = false;
                       else {
                           
                           if (ladderAngle <= _surfer.moveData.slopeLimit)
                               allowClimb = false;

                       }

                   } else if (hit.normal.y != 0f)
                       allowClimb = false;

                   if (allowClimb) {
                       foundLadder = true;
                       if (_surfer.moveData.climbingLadder == false) {

                           _surfer.moveData.climbingLadder = true;
                           _surfer.moveData.ladderNormal = hit.normal;
                           _surfer.moveData.ladderDirection = -hit.normal * direction.magnitude * 2f;

                           if (_surfer.moveData.angledLaddersEnabled) {

                               Vector3 sideDir = hit.normal;
                               sideDir.y = 0f;
                               sideDir = Quaternion.AngleAxis (-90f, Vector3.up) * sideDir;

                               _surfer.moveData.ladderClimbDir = Quaternion.AngleAxis (90f, sideDir) * hit.normal;
                               _surfer.moveData.ladderClimbDir *= 1f/ _surfer.moveData.ladderClimbDir.y; // Make sure Y is always 1

                           } else
                               _surfer.moveData.ladderClimbDir = Vector3.up;
                           
                       }
                       
                   }

               }

           }

           if (!foundLadder) {
               
               _surfer.moveData.ladderNormal = Vector3.zero;
               _surfer.moveData.ladderVelocity = Vector3.zero;
               _surfer.moveData.climbingLadder = false;
               _surfer.moveData.ladderClimbDir = Vector3.up;

           }

       }

       private void LadderPhysics () {
           
           _surfer.moveData.ladderVelocity = _surfer.moveData.ladderClimbDir * _surfer.moveData.verticalAxis * 6f;

           _surfer.moveData.velocity = Vector3.Lerp (_surfer.moveData.velocity, _surfer.moveData.ladderVelocity, Time.deltaTime * 10f);

           LadderCheck (Vector3.one, _surfer.moveData.ladderDirection);
           
           Trace floorTrace = TraceToFloor ();
           if (_surfer.moveData.verticalAxis < 0f && floorTrace.hitCollider != null && Vector3.Angle (Vector3.up, floorTrace.planeNormal) <= _surfer.moveData.slopeLimit) {

               _surfer.moveData.velocity = _surfer.moveData.ladderNormal * 0.5f;
               _surfer.moveData.ladderVelocity = Vector3.zero;
               _surfer.moveData.climbingLadder = false;

           }

           if (_surfer.moveData.wishJump) {

               _surfer.moveData.velocity = _surfer.moveData.ladderNormal * 4f;
               _surfer.moveData.ladderVelocity = Vector3.zero;
               _surfer.moveData.climbingLadder = false;
               
           }
           
       }
       
       private void Accelerate (Vector3 wishDir, float wishSpeed, float acceleration, bool yMovement) {

           // Initialise variables
           float _addSpeed;
           float _accelerationSpeed;
           float _currentSpeed;
           
           // again, no idea
           _currentSpeed = Vector3.Dot (_surfer.moveData.velocity, wishDir);
           _addSpeed = wishSpeed - _currentSpeed;

           // If you're not actually increasing your speed, stop here.
           if (_addSpeed <= 0)
               return;

           // won't bother trying to understand any of this, really
           _accelerationSpeed = Mathf.Min (acceleration * _deltaTime * wishSpeed, _addSpeed);

           // Add the velocity.
           _surfer.moveData.velocity.x += _accelerationSpeed * wishDir.x;
           if (yMovement) { _surfer.moveData.velocity.y += _accelerationSpeed * wishDir.y; }
           _surfer.moveData.velocity.z += _accelerationSpeed * wishDir.z;

       }

       private void ApplyFriction (float t, bool yAffected, bool grounded) {

           // Initialise variables
           Vector3 _vel = _surfer.moveData.velocity;
           float _speed;
           float _newSpeed;
           float _control;
           float _drop;

           // Set Y to 0, speed to the magnitude of movement and drop to 0. I think drop is the amount of speed that is lost, but I just stole this from the internet, idk.
           _vel.y = 0.0f;
           _speed = _vel.magnitude;
           _drop = 0.0f;

           float fric = crouching ? _config.crouchFriction : _config.friction;
           float accel = crouching ? _config.crouchAcceleration : _config.acceleration;
           float decel = crouching ? _config.crouchDeceleration : _config.deceleration;

           // Only apply friction if the player is grounded
           if (grounded) {
               
               // i honestly have no idea what this does tbh
               _vel.y = _surfer.moveData.velocity.y;
               _control = _speed < decel ? decel : _speed;
               _drop = _control * fric * _deltaTime * t;

           }

           // again, no idea, but comments look cool
           _newSpeed = Mathf.Max (_speed - _drop, 0f);
           if (_speed > 0.0f)
               _newSpeed /= _speed;

           // Set the end-velocity
           _surfer.moveData.velocity.x *= _newSpeed;
           if (yAffected == true) { _surfer.moveData.velocity.y *= _newSpeed; }
           _surfer.moveData.velocity.z *= _newSpeed;

       }

       /// <summary>
       ///
       /// </summary>
       /// <returns></returns>
       private Vector3 AirInputMovement () {

           Vector3 wishVel, wishDir;
           float wishSpeed;

           GetWishValues (out wishVel, out wishDir, out wishSpeed);

           if (_config.clampAirSpeed && (wishSpeed != 0f && (wishSpeed > _config.maxSpeed))) {

               wishVel = wishVel * (_config.maxSpeed / wishSpeed);
               wishSpeed = _config.maxSpeed;

           }

           return SurfPhysics.AirAccelerate (_surfer.moveData.velocity, wishDir, wishSpeed, _config.airAcceleration, _config.airCap, _deltaTime);

       }

       /// <summary>
       ///
       /// </summary>
       /// <param name="wishVel"></param>
       /// <param name="wishDir"></param>
       /// <param name="wishSpeed"></param>
       private void GetWishValues (out Vector3 wishVel, out Vector3 wishDir, out float wishSpeed) {

           wishVel = Vector3.zero;
           wishDir = Vector3.zero;
           wishSpeed = 0f;

           Vector3 forward = _surfer.forward,
               right = _surfer.right;

           forward [1] = 0;
           right [1] = 0;
           forward.Normalize ();
           right.Normalize ();

           for (int i = 0; i < 3; i++)
               wishVel [i] = forward [i] * _surfer.moveData.forwardMove + right [i] * _surfer.moveData.sideMove;
           wishVel [1] = 0;

           wishSpeed = wishVel.magnitude;
           wishDir = wishVel.normalized;

       }

       /// <summary>
       ///
       /// </summary>
       /// <param name="velocity"></param>
       /// <param name="jumpPower"></param>
       private void Jump () {

           if (!_config.autoBhop)
               _surfer.moveData.wishJump = false;
           
           _surfer.moveData.velocity.y += _config.jumpForce;
           jumping = true;

       }

       /// <summary>
       ///
       /// </summary>
       private bool CheckGrounded () {

           _surfer.moveData.surfaceFriction = 1f;
           var movingUp = _surfer.moveData.velocity.y > 0f;
           var trace = TraceToFloor ();

           float groundSteepness = Vector3.Angle (Vector3.up, trace.planeNormal);

           if (trace.hitCollider == null || groundSteepness > _config.slopeLimit || (jumping && _surfer.moveData.velocity.y > 0f)) {

               SetGround (null);

               if (movingUp && _surfer.moveType != MoveType.Noclip)
                   _surfer.moveData.surfaceFriction = _config.airFriction;
               
               return false;

           } else {

               groundNormal = trace.planeNormal;
               SetGround (trace.hitCollider.gameObject);
               return true;

           }

       }

       /// <summary>
       ///
       /// </summary>
       /// <param name="obj"></param>
       private void SetGround (GameObject obj) {

           if (obj != null) {

               _surfer.groundObject = obj;
               _surfer.moveData.velocity.y = 0;

           } else
               _surfer.groundObject = null;

       }

       /// <summary>
       ///
       /// </summary>
       /// <param name="start"></param>
       /// <param name="end"></param>
       /// <param name="layerMask"></param>
       /// <returns></returns>
       private Trace TraceBounds (Vector3 start, Vector3 end, int layerMask) {

           return Tracer.TraceCollider (_surfer.collider, start, end, layerMask);

       }

       /// <summary>
       ///
       /// </summary>
       /// <returns></returns>
       private Trace TraceToFloor () {

           var down = _surfer.moveData.origin;
           down.y -= 0.15f;

           return Tracer.TraceCollider (_surfer.collider, _surfer.moveData.origin, down, SurfPhysics.groundLayerMask);

       }

       public void Crouch (ISurfControllable surfer, MovementConfig config, float deltaTime) {

           _surfer = surfer;
           _config = config;
           _deltaTime = deltaTime;

           if (_surfer == null)
               return;

           if (_surfer.collider == null)
               return;

           bool grounded = _surfer.groundObject != null;
           bool wantsToCrouch = _surfer.moveData.crouching;

           float crouchingHeight = Mathf.Clamp (_surfer.moveData.crouchingHeight, 0.01f, 1f);
           float heightDifference = _surfer.moveData.defaultHeight - _surfer.moveData.defaultHeight * crouchingHeight;

           if (grounded)
               uncrouchDown = false;

           // Crouching input
           if (grounded)
               crouchLerp = Mathf.Lerp (crouchLerp, wantsToCrouch ? 1f : 0f, _deltaTime * _surfer.moveData.crouchingSpeed);
           else if (!grounded && !wantsToCrouch && crouchLerp < 0.95f)
               crouchLerp = 0f;
           else if (!grounded && wantsToCrouch)
               crouchLerp = 1f;

           // Collider and position changing
           if (crouchLerp > 0.9f && !crouching) {
               
               // Begin crouching
               crouching = true;
               if (_surfer.collider.GetType () == typeof (BoxCollider)) {

                   // Box collider
                   BoxCollider boxCollider = (BoxCollider)_surfer.collider;
                   boxCollider.size = new Vector3 (boxCollider.size.x, _surfer.moveData.defaultHeight * crouchingHeight, boxCollider.size.z);

               } else if (_surfer.collider.GetType () == typeof (CapsuleCollider)) {

                   // Capsule collider
                   CapsuleCollider capsuleCollider = (CapsuleCollider)_surfer.collider;
                   capsuleCollider.height = _surfer.moveData.defaultHeight * crouchingHeight;

               }

               // Move position and stuff
               _surfer.moveData.origin += heightDifference / 2 * (grounded ? Vector3.down : Vector3.up);
               foreach (Transform child in playerTransform) {

                   if (child == _surfer.moveData.viewTransform)
                       continue;

                   child.localPosition = new Vector3 (child.localPosition.x, child.localPosition.y * crouchingHeight, child.localPosition.z);

               }

               uncrouchDown = !grounded;

           } else if (crouching) {

               // Check if the player can uncrouch
               bool canUncrouch = true;
               if (_surfer.collider.GetType () == typeof (BoxCollider)) {

                   // Box collider
                   BoxCollider boxCollider = (BoxCollider)_surfer.collider;
                   Vector3 halfExtents = boxCollider.size * 0.5f;
                   Vector3 startPos = boxCollider.transform.position;
                   Vector3 endPos = boxCollider.transform.position + (uncrouchDown ? Vector3.down : Vector3.up) * heightDifference;

                   Trace trace = Tracer.TraceBox (startPos, endPos, halfExtents, boxCollider.contactOffset, SurfPhysics.groundLayerMask);

                   if (trace.hitCollider != null)
                       canUncrouch = false;

               } else if (_surfer.collider.GetType () == typeof (CapsuleCollider)) {

                   // Capsule collider
                   CapsuleCollider capsuleCollider = (CapsuleCollider)_surfer.collider;
                   Vector3 point1 = capsuleCollider.center + Vector3.up * capsuleCollider.height * 0.5f;
                   Vector3 point2 = capsuleCollider.center + Vector3.down * capsuleCollider.height * 0.5f;
                   Vector3 startPos = capsuleCollider.transform.position;
                   Vector3 endPos = capsuleCollider.transform.position + (uncrouchDown ? Vector3.down : Vector3.up) * heightDifference;

                   Trace trace = Tracer.TraceCapsule (point1, point2, capsuleCollider.radius, startPos, endPos, capsuleCollider.contactOffset, SurfPhysics.groundLayerMask);

                   if (trace.hitCollider != null)
                       canUncrouch = false;

               }

               // Uncrouch
               if (canUncrouch && crouchLerp <= 0.9f) {

                   crouching = false;
                   if (_surfer.collider.GetType () == typeof (BoxCollider)) {

                       // Box collider
                       BoxCollider boxCollider = (BoxCollider)_surfer.collider;
                       boxCollider.size = new Vector3 (boxCollider.size.x, _surfer.moveData.defaultHeight, boxCollider.size.z);

                   } else if (_surfer.collider.GetType () == typeof (CapsuleCollider)) {

                       // Capsule collider
                       CapsuleCollider capsuleCollider = (CapsuleCollider)_surfer.collider;
                       capsuleCollider.height = _surfer.moveData.defaultHeight;

                   }

                   // Move position and stuff
                   _surfer.moveData.origin += heightDifference / 2 * (uncrouchDown ? Vector3.down : Vector3.up);
                   foreach (Transform child in playerTransform) {

                       child.localPosition = new Vector3 (child.localPosition.x, child.localPosition.y / crouchingHeight, child.localPosition.z);

                   }

               }

               if (!canUncrouch)
                   crouchLerp = 1f;

           }

           // Changing camera position
           if (!crouching)
               _surfer.moveData.viewTransform.localPosition = Vector3.Lerp (_surfer.moveData.viewTransformDefaultLocalPos, _surfer.moveData.viewTransformDefaultLocalPos * crouchingHeight + Vector3.down * heightDifference * 0.5f, crouchLerp);
           else
               _surfer.moveData.viewTransform.localPosition = Vector3.Lerp (_surfer.moveData.viewTransformDefaultLocalPos - Vector3.down * heightDifference * 0.5f, _surfer.moveData.viewTransformDefaultLocalPos * crouchingHeight, crouchLerp);

       }

       void SlideMovement () {
           
           // Gradually change direction
           slideDirection += new Vector3 (groundNormal.x, 0f, groundNormal.z) * slideSpeedCurrent * _deltaTime;
           slideDirection = slideDirection.normalized;

           // Set direction
           Vector3 slideForward = Vector3.Cross (groundNormal, Quaternion.AngleAxis (-90, Vector3.up) * slideDirection);
           
           // Set the velocity
           slideSpeedCurrent -= _config.slideFriction * _deltaTime;
           slideSpeedCurrent = Mathf.Clamp (slideSpeedCurrent, 0f, _config.maximumSlideSpeed);
           slideSpeedCurrent -= (slideForward * slideSpeedCurrent).y * _deltaTime * _config.downhillSlideSpeedMultiplier; // Accelerate downhill (-y = downward, - * - = +)

           _surfer.moveData.velocity = slideForward * slideSpeedCurrent;
           
           // Jump
           if (_surfer.moveData.wishJump && slideSpeedCurrent < _config.minimumSlideSpeed * _config.slideSpeedMultiplier) {

               Jump ();
               return;

           }

       }

   }
}

Here’s what it’s doing

qkqqr8

I suggest reasoning through standard dynamics formula for computation of initial launch velocity to arrive at a given height of jump.

After that you can decide if it is a miscalculated value, or if something else is going wrong in your code.

This controller code contains the formula for launch velocity to reach a height:

Right around line 78

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

Every time I’m here & have a question that needs help, you always tell me to go away & do it myself. Truth be told, I need actual help with this. I actually admit that I cannot do this alone. Now, with all due respect, you always give me this default answer & I’m done with it. Honestly, this is why I hardly ever come on here anymore. Can I please get someone who actually CAN help to help me?

You described your issue with 18 words and posted 1K lines of code. Not sure what you think is going to happen?

Kurt’s advice to break down your problem is excellent advice, as usual.

I’m trying to guide you to investigate things like this, plus I don’t have your setup.

I fired up the CC package I linked above and the capsule does indeed lands at the capsule height PLUS the skin width. This puts the character not at 1.0 but rather 1.08f. Googling (see below) certainly seems to confirm this.

Looking at the skin width of 0.08, if I increase skin width to 0.16, the height follows, and if I reduce the capsule height from 2.0 to 1.84 (to allow top and bottom skin of 0.08f), it settles almost precisely at 1.0.

Reading here, the skin width is critical to system stability, so it cannot be set to zero:

From your video, it appears this MIGHT be part of your complaint, but it might be something more as well, making it variable on each jump, which I suspect might be your actual complaint.

After spending a few minutes tracing your code visually, there are a LOT of places that set and adjust and clamp velocities. Generally you should keep that simpler. Every line of code is a place for an error to hang out.

Those extra adjustments to velocity (particularly the .y field) would be the first place I would look for errors. Using source control you can massively change / comment out blocks of code so you can fearlessly try stuff, then revert. Things you should try are to turn off (comment out) certain of the velocity components, and also use the Debug.Log() steps above to find out what each one is doing to your velocity, when it is stopping the Y velocity, etc.

I can’t do that for you. All I can suggest is to simplify things, and at LEAST identify the data flows involved in standing and jumping, ignoring or disabling all the other crouch and other stuff. Turn it off at least, because then if it starts working… hey, it might be the crouch code!

But in any case, you’re not alone:

It’s actually a Rigidbody.

I read that and parsed it as CharacterController, and in any case, I focused entirely on studying where you modify vertical velocity.

So traditionally we know Rigidbody-driven colliders meet neatly at the collider boundaries when left at rest.

What happens if you disable your script above, physically lift the object up in the inspector and drop it. Does it still hang midair?

If so we have a collider issue.

If not we have an issue somewhere in your manipulation of the velocities above.

It is not possible for me to answer this question. All I can do is guide you to answer it for yourself. You have the project, you have the code, you have it all set up, and you can type Debug.Log() as easily as anyone.

Focus on the velocity and/or collider and find out why it is stopping, because we know that’s not how Rigidbodies work. They do come to rest in immediate proximity.

@Kurt-Dekker : So I removed the “Surf Character” script & put a rigidbody on the object then enabled gravity & pulled it up & it was always 1, so it is definitely the script doing it.

Now you’re getting somewhere! Start carefully disconnecting portions of the script until you hone in on the part (or parts) that might be causing the issue.

Once you identify that block of code, you can begin to reason further:

  • this code has a problem
  • this code has no problem, but it assumes X and X is wrong
  • this code must be tweaked but only during jumping.

etc.