using UnityEngine; namespace Movement { [RequireComponent(typeof(CharacterController))] public class PlayerController : MonoBehaviour { [System.Serializable] public class MovementSettings { public float MaxSpeed; public float Acceleration; public float Deceleration; public MovementSettings(float maxSpeed, float accel, float decel) { MaxSpeed = maxSpeed; Acceleration = accel; Deceleration = decel; } } [Header("Aiming")] [SerializeField] private Camera m_Camera; [SerializeField] private MouseLook m_MouseLook = new MouseLook(); [Header("Movement")] [SerializeField] private float m_Friction = 6; [SerializeField] private float m_Gravity = 20; [SerializeField] private float m_JumpForce = 8; [Tooltip("How precise air control is")] [SerializeField] private float m_AirControl = 0.3f; [SerializeField] private MovementSettings m_GroundSettings = new MovementSettings(7, 14, 10); [SerializeField] private MovementSettings m_AirSettings = new MovementSettings(7, 2, 2); [SerializeField] private MovementSettings m_StrafeSettings = new MovementSettings(1, 50, 50); [SerializeField] private float m_SprintMultiplier = 1.5f; [SerializeField] private float m_BhopMultiplier = 1.2f; public float Speed { get { return m_Character.velocity.magnitude; } } private CharacterController m_Character; private Vector3 m_MoveDirectionNorm = Vector3.zero; private Vector3 m_PlayerVelocity = Vector3.zero; private bool m_IsCrouching = false; private bool m_JumpQueued = false; private Vector3 m_MoveInput; private Transform m_Tran; private Transform m_CamTran; private void Start() { m_Tran = transform; m_Character = GetComponent(); if (!m_Camera) m_Camera = Camera.main; m_CamTran = m_Camera.transform; m_MouseLook.Init(m_Tran, m_CamTran); } private void Update() { m_MoveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")); m_MouseLook.UpdateCursorLock(); QueueJump(); Crouch(); if (m_Character.isGrounded) { GroundMove(); } else { AirMove(); } m_MouseLook.LookRotation(m_Tran, m_CamTran); m_Character.Move(m_PlayerVelocity * Time.deltaTime); } private void QueueJump() { m_JumpQueued = Input.GetButton("Jump"); } private void AirMove() { float accel; var wishdir = new Vector3(m_MoveInput.x, 0, m_MoveInput.z); wishdir = m_Tran.TransformDirection(wishdir); float wishspeed = wishdir.magnitude; wishspeed *= m_AirSettings.MaxSpeed; wishdir.Normalize(); m_MoveDirectionNorm = wishdir; float wishspeed2 = wishspeed; if (Vector3.Dot(m_PlayerVelocity, wishdir) < 0) { accel = m_AirSettings.Deceleration; } else { accel = m_AirSettings.Acceleration; } if (m_MoveInput.z == 0 && m_MoveInput.x != 0) { if (wishspeed > m_StrafeSettings.MaxSpeed) { wishspeed = m_StrafeSettings.MaxSpeed; } accel = m_StrafeSettings.Acceleration; } Accelerate(wishdir, wishspeed, accel); if (m_AirControl > 0) { AirControl(wishdir, wishspeed2); } m_PlayerVelocity.y -= m_Gravity * Time.deltaTime; } private void AirControl(Vector3 targetDir, float targetSpeed) { if (Mathf.Abs(m_MoveInput.z) < 0.001 || Mathf.Abs(targetSpeed) < 0.001) { return; } float zSpeed = m_PlayerVelocity.y; m_PlayerVelocity.y = 0; float speed = m_PlayerVelocity.magnitude; m_PlayerVelocity.Normalize(); float dot = Vector3.Dot(m_PlayerVelocity, targetDir); float k = 32; k *= m_AirControl * dot * dot * Time.deltaTime; if (dot > 0) { m_PlayerVelocity.x *= speed + targetDir.x * k; m_PlayerVelocity.y *= speed + targetDir.y * k; m_PlayerVelocity.z *= speed + targetDir.z * k; m_PlayerVelocity.Normalize(); m_MoveDirectionNorm = m_PlayerVelocity; } m_PlayerVelocity.x *= speed; m_PlayerVelocity.y = zSpeed; m_PlayerVelocity.z *= speed; } private void GroundMove() { if (!m_JumpQueued) { ApplyFriction(1.0f); } else { ApplyFriction(0); } var wishdir = new Vector3(m_MoveInput.x, 0, m_MoveInput.z); wishdir = m_Tran.TransformDirection(wishdir); wishdir.Normalize(); m_MoveDirectionNorm = wishdir; var wishspeed = wishdir.magnitude; wishspeed *= m_GroundSettings.MaxSpeed; if (m_JumpQueued) { wishspeed *= m_BhopMultiplier; } if (Input.GetKey(KeyCode.LeftShift)) { wishspeed *= m_SprintMultiplier; } if (m_IsCrouching) { wishspeed *= 0.5f; } Accelerate(wishdir, wishspeed, m_GroundSettings.Acceleration); m_PlayerVelocity.y = -m_Gravity * Time.deltaTime; if (m_JumpQueued) { m_PlayerVelocity.y = m_JumpForce; m_JumpQueued = false; } } private void ApplyFriction(float t) { Vector3 vec = m_PlayerVelocity; vec.y = 0; float speed = vec.magnitude; float drop = 0; if (m_Character.isGrounded) { float control = speed < m_GroundSettings.Deceleration ? m_GroundSettings.Deceleration : speed; drop = control * m_Friction * Time.deltaTime * t; } float newSpeed = speed - drop; if (newSpeed < 0) { newSpeed = 0; } if (speed > 0) { newSpeed /= speed; } m_PlayerVelocity.x *= newSpeed; m_PlayerVelocity.z *= newSpeed; } private void Accelerate(Vector3 targetDir, float targetSpeed, float accel) { float currentspeed = Vector3.Dot(m_PlayerVelocity, targetDir); float addspeed = targetSpeed - currentspeed; if (addspeed <= 0) { return; } float accelspeed = accel * Time.deltaTime * targetSpeed; if (accelspeed > addspeed) { accelspeed = addspeed; } m_PlayerVelocity.x += accelspeed * targetDir.x; m_PlayerVelocity.z += accelspeed * targetDir.z; } private void Crouch() { if (Input.GetButton("Crouch")) { if (!m_IsCrouching) { m_IsCrouching = true; m_Character.height = 0.5f; // Adjust the crouch height as needed } } else { if (m_IsCrouching && m_Character.isGrounded) { m_IsCrouching = false; m_Character.height = 2f; // Adjust the normal height as needed } } } } }