| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 | using UnityEngine;namespace HQFPSWeapons{    public class PlayerMovement : PlayerComponent    {        public bool IsGrounded { get => m_Controller.isGrounded; }        public Vector3 Velocity { get => m_Controller.velocity; }        public Vector3 SurfaceNormal { get; private set; }        public float SlopeLimit { get => m_Controller.slopeLimit; }        public float DefaultHeight { get; private set; }        [SerializeField]        private CharacterController m_Controller = null;        [SerializeField]        private LayerMask m_ObstacleCheckMask = ~0;        [BHeader("Core Movement...")]        [SerializeField]        [Range(0f, 20f)]        private float m_Acceleration = 5f;        [SerializeField]        [Range(0f, 20f)]        private float m_Damping = 8f;        [SerializeField]        [Range(0f, 1f)]        private float m_AirborneControl = 0.15f;        [SerializeField]        [Range(0f, 3f)]        private float m_StepLength = 1.2f;        [SerializeField]        [Range(0f, 10f)]        private float m_ForwardSpeed = 2.5f;        [SerializeField]        [Range(0f, 10f)]        private float m_BackSpeed = 2.5f;        [SerializeField]        [Range(0f, 10f)]        private float m_SideSpeed = 2.5f;        [SerializeField]        private AnimationCurve m_SlopeSpeedMult = new AnimationCurve(new Keyframe(0f, 1f), new Keyframe(1f, 1f));        [SerializeField]        private float m_AntiBumpFactor = 1f;        [BHeader("Running...")]        [SerializeField]        private bool m_EnableRunning = true;        [SerializeField]        [Range(1f, 10f)]        private float m_RunSpeed = 4.5f;        [SerializeField]        [Range(0f, 3f)]        private float m_RunStepLength = 1.9f;        [BHeader("Jumping...")]        [SerializeField]        private bool m_EnableJumping = true;        [SerializeField]        [Range(0f, 3f)]        private float m_JumpHeight = 1f;        [SerializeField]        [Range(0f, 1.5f)]        private float m_JumpTimer = 0.3f;        [BHeader("Crouching...")]        [SerializeField]        private bool m_EnableCrouching = false;        [SerializeField]        [Range(0f, 1f)]        private float m_CrouchSpeedMult = 0.7f;        [SerializeField]        [Range(0f, 3f)]        private float m_CrouchStepLength = 0.8f;        [SerializeField]        [Range(0f, 2f)]        private float m_CrouchHeight = 1f;        [SerializeField]        [Range(0f, 1f)]        private float m_CrouchDuration = 0.3f;        [BHeader("Sliding...")]        [SerializeField]        private bool m_EnableSliding = false;        [SerializeField]        [Range(20f, 90f)]        private float m_SlideTreeshold = 32f;        [SerializeField]        [Range(0f, 50f)]        private float m_SlideSpeed = 15f;        [BHeader("Misc...")]        [SerializeField]        [Range(0f, 100f)]        private float m_Gravity = 20f;        private float m_UncrouchedHeight = 0f;        private Vector3 m_DesiredVelocityLocal;        private Vector3 m_SlideVelocity;        private CollisionFlags m_CollisionFlags;        private bool m_PreviouslyGrounded;        private float m_LastLandTime;        private float m_NextTimeCanCrouch;        private float m_DistMovedSinceLastCycleEnded;        private float m_CurrentStepLength;        private void Awake()        {            DefaultHeight = m_Controller.height;            RaycastHit hitInfo;            if(Physics.Raycast(transform.position + transform.up, -transform.up, out hitInfo, 3f, ~0, QueryTriggerInteraction.Ignore))                transform.position = hitInfo.point + Vector3.up * 0.08f;        }        private void Start()        {            Player.IsGrounded.AddChangeListener(OnGroundingStateChanged);            Player.Run.AddStartTryer(TryRun);            Player.Jump.AddStartTryer(TryJump);            Player.Crouch.AddStartTryer(TryCrouch);            Player.Crouch.AddStopTryer(TryUncrouch);            Player.Death.AddListener(OnDeath);        }        private void Update()        {            float deltaTime = Time.deltaTime;            Vector3 translation = Vector3.zero;            if (IsGrounded)            {                translation = transform.TransformVector(m_DesiredVelocityLocal) * deltaTime;                if (!Player.Jump.Active)                    translation.y = -m_AntiBumpFactor;            }            else                translation = transform.TransformVector(m_DesiredVelocityLocal * deltaTime);            m_CollisionFlags = m_Controller.Move(translation);            if ((m_CollisionFlags & CollisionFlags.Below) == CollisionFlags.Below && !m_PreviouslyGrounded)            {                bool wasJumping = Player.Jump.Active;                if (Player.Jump.Active)                    Player.Jump.ForceStop();                Player.FallImpact.Send(Mathf.Abs(m_DesiredVelocityLocal.y));                m_LastLandTime = Time.time;                if (wasJumping)                    m_DesiredVelocityLocal = Vector3.ClampMagnitude(m_DesiredVelocityLocal, 1f);            }            Vector3 targetVelocity = CalcTargetVelocity(Player.MoveInput.Get());            if (!IsGrounded)                UpdateAirborneMovement(deltaTime, targetVelocity, ref m_DesiredVelocityLocal);            else if (!Player.Jump.Active)                UpdateGroundedMovement(deltaTime, targetVelocity, ref m_DesiredVelocityLocal);            Player.IsGrounded.Set(IsGrounded);            Player.Velocity.Set(Velocity);            m_PreviouslyGrounded = IsGrounded;        }        private void UpdateGroundedMovement(float deltaTime, Vector3 targetVelocity, ref Vector3 velocity)        {            // Make sure to lower the speed when moving on steep surfaces.            float surfaceAngle = Vector3.Angle(Vector3.up, SurfaceNormal);            targetVelocity *= m_SlopeSpeedMult.Evaluate(surfaceAngle / SlopeLimit);            // Calculate the rate at which the current speed should increase / decrease.             // If the player doesn't press any movement button, use the "m_Damping" value, otherwise use "m_Acceleration".            float targetAccel = targetVelocity.sqrMagnitude > 0f ? m_Acceleration : m_Damping;            velocity = Vector3.Lerp(velocity, targetVelocity, targetAccel * deltaTime);            // If we're moving and not running, start the "Walk" activity.            if (!Player.Walk.Active && targetVelocity.sqrMagnitude > 0.05f && !Player.Run.Active && !Player.Crouch.Active)                Player.Walk.ForceStart();            // If we're running, or not moving, stop the "Walk" activity.            else if (Player.Walk.Active && (targetVelocity.sqrMagnitude < 0.05f || Player.Run.Active || Player.Crouch.Active))                Player.Walk.ForceStop();            if (Player.Run.Active)            {                bool wantsToMoveBackwards = Player.MoveInput.Get().y < 0f;                bool runShouldStop = wantsToMoveBackwards || targetVelocity.sqrMagnitude == 0f || Player.Stamina.Is(0f);                if (runShouldStop)                    Player.Run.ForceStop();            }            if (m_EnableSliding)            {                // Sliding...                if (surfaceAngle > m_SlideTreeshold && Player.MoveInput.Get().sqrMagnitude == 0f)                {                    Vector3 slideDirection = (SurfaceNormal + Vector3.down);                    m_SlideVelocity += slideDirection * m_SlideSpeed * deltaTime;                }                else                    m_SlideVelocity = Vector3.Lerp(m_SlideVelocity, Vector3.zero, deltaTime * 10f);                velocity += transform.InverseTransformVector(m_SlideVelocity);            }            // Advance step            m_DistMovedSinceLastCycleEnded += m_DesiredVelocityLocal.magnitude * deltaTime;            // Which step length should be used?            float targetStepLength = m_StepLength;            if (Player.Crouch.Active)                targetStepLength = m_CrouchStepLength;            else if (Player.Run.Active)                targetStepLength = m_RunStepLength;            m_CurrentStepLength = Mathf.MoveTowards(m_CurrentStepLength, targetStepLength, deltaTime * 0.6f);            // If the step cycle is complete, reset it, and send a notification.            if (m_DistMovedSinceLastCycleEnded > m_CurrentStepLength)            {                m_DistMovedSinceLastCycleEnded -= m_CurrentStepLength;                Player.MoveCycleEnded.Send();            }            Player.MoveCycle.Set(m_DistMovedSinceLastCycleEnded / m_CurrentStepLength);        }        private void UpdateAirborneMovement(float deltaTime, Vector3 targetVelocity, ref Vector3 velocity)        {            if (m_PreviouslyGrounded && !Player.Jump.Active)                velocity.y = 0f;            // Modify the current velocity by taking into account how well we can change direction when not grounded (see "m_AirControl" tooltip).            velocity += targetVelocity * m_Acceleration * m_AirborneControl * deltaTime;            // Apply gravity.            velocity.y -= m_Gravity * deltaTime;        }        private bool TryRun()        {            if (!m_EnableRunning || Player.Stamina.Get() < 15f)                return false;            bool wantsToMoveBack = Player.MoveInput.Get().y < 0f;            return Player.IsGrounded.Get() && !wantsToMoveBack && !Player.Crouch.Active && !Player.Aim.Active;        }        private bool TryJump()        {            // If crouched, stop crouching first            if (Player.Crouch.Active)            {                Player.Crouch.TryStop();                return false;            }            bool canJump = m_EnableJumping &&                IsGrounded &&                !Player.Crouch.Active &&                Time.time > m_LastLandTime + m_JumpTimer;            if (!canJump)                return false;            float jumpSpeed = Mathf.Sqrt(2 * m_Gravity * m_JumpHeight);            m_DesiredVelocityLocal = new Vector3(m_DesiredVelocityLocal.x, jumpSpeed, m_DesiredVelocityLocal.z);            return true;        }        private bool TryCrouch()        {            bool canCrouch =                m_EnableCrouching &&                (Time.time > m_NextTimeCanCrouch || m_NextTimeCanCrouch == 0f) &&                Player.IsGrounded.Get() &&                !Player.Run.Active;            if (canCrouch)            {                SetHeight(m_CrouchHeight);                m_NextTimeCanCrouch = Time.time + m_CrouchDuration;            }            return canCrouch;        }        private bool TryUncrouch()        {            bool obstacleAbove = DoCollisionCheck(true, Mathf.Abs(m_CrouchHeight - m_UncrouchedHeight));            bool canStopCrouching = Time.time > m_NextTimeCanCrouch && !obstacleAbove;            if (canStopCrouching)            {                SetHeight(DefaultHeight);                m_NextTimeCanCrouch = Time.time;            }            return canStopCrouching;        }        private void OnGroundingStateChanged(bool isGrounded)        {            if (!isGrounded)            {                Player.Walk.ForceStop();                Player.Run.ForceStop();            }        }        private Vector3 CalcTargetVelocity(Vector2 moveInput)        {            moveInput = Vector2.ClampMagnitude(moveInput, 1f);            bool wantsToMove = moveInput.sqrMagnitude > 0f;            // Calculate the direction (relative to the us), in which the player wants to move.            Vector3 targetDirection = (wantsToMove ? new Vector3(moveInput.x, 0f, moveInput.y) : m_DesiredVelocityLocal.normalized);            float desiredSpeed = 0f;            if (wantsToMove)            {                // Set the default speed.                desiredSpeed = m_ForwardSpeed;                // If the player wants to move sideways...                if (Mathf.Abs(moveInput.x) > 0f)                    desiredSpeed = m_SideSpeed;                // If the player wants to move backwards...                if (moveInput.y < 0f)                    desiredSpeed = m_BackSpeed;                // If we're currently running...                if (Player.Run.Active)                {                    // If the player wants to move forward or sideways, apply the run speed multiplier.                    if (desiredSpeed == m_ForwardSpeed || desiredSpeed == m_SideSpeed)                        desiredSpeed = m_RunSpeed;                }                // If we're crouching...                if (Player.Crouch.Active)                    desiredSpeed *= m_CrouchSpeedMult;            }            return targetDirection * (desiredSpeed * Player.MovementSpeedFactor.Val);        }        private bool DoCollisionCheck(bool checkAbove, float maxDistance, out RaycastHit hitInfo)        {            Vector3 rayOrigin = transform.position + (checkAbove ? Vector3.up * m_Controller.height : Vector3.zero);            Vector3 rayDirection = checkAbove ? Vector3.up : Vector3.down;            return Physics.SphereCast(new Ray(rayOrigin, rayDirection), m_Controller.radius, out hitInfo, maxDistance, m_ObstacleCheckMask, QueryTriggerInteraction.Ignore);        }        private bool DoCollisionCheck(bool checkAbove, float maxDistance)        {            Vector3 rayOrigin = transform.position + (checkAbove ? Vector3.up * m_Controller.height : Vector3.zero);            Vector3 rayDirection = checkAbove ? Vector3.up : Vector3.down;            return Physics.SphereCast(new Ray(rayOrigin, rayDirection), m_Controller.radius, maxDistance, m_ObstacleCheckMask, QueryTriggerInteraction.Ignore);        }        private void SetHeight(float height)        {            m_Controller.height = height;            m_Controller.center = Vector3.up * height * 0.5f;        }        private void OnControllerColliderHit(ControllerColliderHit hit)        {            SurfaceNormal = hit.normal;        }        private void OnDeath()        {            m_DesiredVelocityLocal = Vector3.zero;        }    }}
 |