//Apply friction to both vertical and horizontal momentum based on 'friction' and 'gravity'; //Handle sliding down steep slopes; private void HandleMomentum() { //If local momentum is used, transform momentum into world coordinates first; if (useLocalMomentum) { momentum = tr.localToWorldMatrix * momentum; } Vector3 _verticalMomentum = Vector3.zero; Vector3 _horizontalMomentum = Vector3.zero; //Split momentum into vertical and horizontal components; if (momentum != Vector3.zero) { _verticalMomentum = VectorMath.ExtractDotVector(momentum, tr.up); _horizontalMomentum = momentum - _verticalMomentum; } //Add gravity to vertical momentum; _verticalMomentum -= tr.up * gravity * Time.deltaTime; //Remove any downward force if the controller is grounded; if (currentControllerState == ControllerState.Grounded) { _verticalMomentum = Vector3.zero; } //Apply friction to horizontal momentum based on whether the controller is grounded; if (currentControllerState == ControllerState.Grounded) { _horizontalMomentum = VectorMath.IncrementVectorTowardTargetVector(_horizontalMomentum, groundFriction, Time.deltaTime, Vector3.zero); } else { _horizontalMomentum = VectorMath.IncrementVectorTowardTargetVector(_horizontalMomentum, airFriction, Time.deltaTime, Vector3.zero); } //Add horizontal and vertical momentum back together; momentum = _horizontalMomentum + _verticalMomentum; //Project the current momentum onto the current ground normal if the controller is sliding down a slope; if (currentControllerState == ControllerState.Sliding) { momentum = Vector3.ProjectOnPlane(momentum, mover.GetGroundNormal()); } //Apply slide gravity along ground normal, if controller is sliding; if (currentControllerState == ControllerState.Sliding) { Vector3 _slideDirection = Vector3.ProjectOnPlane(-tr.up, mover.GetGroundNormal()).normalized; momentum += _slideDirection * slideGravity * Time.deltaTime; } //If controller is jumping, override vertical velocity with jumpSpeed; if (currentControllerState == ControllerState.Jumping) { momentum = VectorMath.RemoveDotVector(momentum, tr.up); momentum += tr.up * jumpSpeed; } if (useLocalMomentum) { momentum = tr.worldToLocalMatrix * momentum; } }
//Determine current controller state based on current momentum and whether the controller is grounded (or not); //Handle state transitions; private ControllerState DetermineControllerState() { //Check if vertical momentum is pointing upwards; bool _isRising = IsRisingOrFalling() && (VectorMath.GetDotProduct(GetMomentum(), tr.up) > 0f); //Check if controller is sliding; bool _isSliding = mover.IsGrounded() && IsGroundTooSteep(); //Grounded; if (currentControllerState == ControllerState.Grounded) { if (_isRising) { OnGroundContactLost(); return(ControllerState.Rising); } if (!mover.IsGrounded()) { OnGroundContactLost(); return(ControllerState.Falling); } if (_isSliding) { return(ControllerState.Sliding); } return(ControllerState.Grounded); } //Falling; if (currentControllerState == ControllerState.Falling) { if (_isRising) { return(ControllerState.Rising); } if (mover.IsGrounded() && !_isSliding) { OnGroundContactRegained(momentum); return(ControllerState.Grounded); } if (_isSliding) { OnGroundContactRegained(momentum); return(ControllerState.Sliding); } return(ControllerState.Falling); } //Sliding; if (currentControllerState == ControllerState.Sliding) { if (_isRising) { OnGroundContactLost(); return(ControllerState.Rising); } if (!mover.IsGrounded()) { return(ControllerState.Falling); } if (mover.IsGrounded() && !_isSliding) { OnGroundContactRegained(momentum); return(ControllerState.Grounded); } return(ControllerState.Sliding); } //Rising; if (currentControllerState == ControllerState.Rising) { if (!_isRising) { if (mover.IsGrounded() && !_isSliding) { OnGroundContactRegained(momentum); return(ControllerState.Grounded); } if (_isSliding) { return(ControllerState.Sliding); } if (!mover.IsGrounded()) { return(ControllerState.Falling); } } //If a ceiling detector has been attached to this gameobject, check for ceiling hits; if (ceilingDetector != null) { if (ceilingDetector.HitCeiling()) { OnCeilingContact(); return(ControllerState.Falling); } } return(ControllerState.Rising); } //Jumping; if (currentControllerState == ControllerState.Jumping) { //Check for jump timeout; if ((Time.time - currentJumpStartTime) > jumpDuration) { return(ControllerState.Rising); } //Check if jump key was let go; if (jumpKeyWasLetGo) { return(ControllerState.Rising); } //If a ceiling detector has been attached to this gameobject, check for ceiling hits; if (ceilingDetector != null) { if (ceilingDetector.HitCeiling()) { OnCeilingContact(); return(ControllerState.Falling); } } return(ControllerState.Jumping); } return(ControllerState.Falling); }