using UnityEngine; public enum MoveState { Idle, Walking, Running, Jumping, Falling}; [RequireComponent(typeof(CharacterController))] public class FPSController : MonoBehaviour { [Header("General")] [SerializeField] private MoveState m_moveState = MoveState.Idle; public bool playerControl = true; [Header("Movement")] [SerializeField] [Range(0f, 1f)] private float m_moveBackwardFactor = .8f; [SerializeField] [Range(0f, 1f)] private float m_moveSideFactor = .8f; [SerializeField] private float m_antiBumpFactor = .75f; private CharacterController m_characterController; private bool m_grounded = true; private float m_speed; private Vector2 m_input = Vector2.zero; private Vector3 m_moveDirection = Vector3.zero; private Vector3 m_moveVelocity = Vector3.zero; private CollisionFlags m_collisionFlags; [Header("Walking")] [SerializeField] private float m_walkSpeed = 5f; [Header("Running")] [SerializeField] private float m_runSpeed = 8f; [Header("Jumping")] [SerializeField] private float m_jumpVerticalSpeed = 7f; [SerializeField] private float m_jumpHorizontalSpeed = 2f; [SerializeField] private float m_framesGroundedBetweenJumps = 1; private float m_jumpFrameCounter; [Header("Falling")] private bool m_falling = false; [Header("Camera")] [SerializeField] private FPSMouseLook m_mouseLook; public Camera m_fpsCamera; [Header("Physics")] [SerializeField] private float m_gravity = 20f; // ---------- Properties ---------- public float Speed { get { return m_speed; } set { m_speed = Mathf.Clamp(value, 0, m_runSpeed); } } public MoveState CurrentMoveState { get { return m_moveState; } } private void Awake() { m_characterController = GetComponent(); m_fpsCamera = Camera.main; } private void Start () { m_mouseLook.Init(transform, m_fpsCamera.transform); m_speed = m_walkSpeed; m_jumpFrameCounter = m_framesGroundedBetweenJumps; } private void Update () { if (!playerControl) return; m_mouseLook.LookRotation(); if (m_grounded) { if (m_falling) { m_falling = false; } if (Input.GetKey(KeyCode.LeftShift)) { m_speed = m_runSpeed; } else { m_speed = m_walkSpeed; } } else { if (!m_falling) { m_falling = true; } } MovePosition(); UpdateMoveState(); } private void MovePosition() { m_input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); if (m_input.y < 0) m_input.y *= m_moveBackwardFactor; if (m_input.x != 0) m_input.x *= m_moveSideFactor; if (m_input.sqrMagnitude > 1) m_input.Normalize(); if (m_grounded) { m_moveDirection = transform.forward * m_input.y + transform.right * m_input.x; RaycastHit hitInfo; Physics.SphereCast(transform.position + m_characterController.center, m_characterController.radius, Vector3.down, out hitInfo, m_characterController.height / 2f, Physics.AllLayers, QueryTriggerInteraction.Ignore); m_moveDirection = Vector3.ProjectOnPlane(m_moveDirection, hitInfo.normal).normalized; m_moveDirection.y = -m_antiBumpFactor; m_moveVelocity = m_moveDirection * m_speed; if (!Input.GetButton("Jump")) m_jumpFrameCounter++; else if (m_jumpFrameCounter >= m_framesGroundedBetweenJumps) { m_moveVelocity.y = m_jumpVerticalSpeed; m_moveVelocity += m_moveDirection * m_jumpHorizontalSpeed; m_jumpFrameCounter = 0; } } m_moveVelocity.y -= m_gravity * Time.deltaTime; m_collisionFlags = m_characterController.Move(m_moveVelocity * Time.deltaTime); m_grounded = (m_collisionFlags & CollisionFlags.Below) != 0; } // ---------- Move State ---------- private void UpdateMoveState() { if (!m_grounded) { if (m_characterController.velocity.y < 0) m_moveState = MoveState.Falling; else m_moveState = MoveState.Jumping; return; } Vector2 moveInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); if (moveInput.sqrMagnitude == 0) { m_moveState = MoveState.Idle; return; } if (m_speed == m_runSpeed) m_moveState = MoveState.Running; else if (m_speed == m_walkSpeed) m_moveState = MoveState.Walking; } }