diff --git a/FPSController.cs b/FPSController.cs new file mode 100644 index 0000000..cd5d1e8 --- /dev/null +++ b/FPSController.cs @@ -0,0 +1,168 @@ +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; + } +} diff --git a/FPSMouseLook.cs b/FPSMouseLook.cs new file mode 100644 index 0000000..0611282 --- /dev/null +++ b/FPSMouseLook.cs @@ -0,0 +1,93 @@ +using System.Collections; +using System.Collections.Generic; + +using UnityEngine; + +[System.Serializable] +public class FPSMouseLook { + + public float xSensitivity = 2f; + public float ySensitivity = 2f; + public bool clampVerticalRotation = true; + public bool clampHorizontalRotation = true; + public float minimumX = -90F; + public float minimumY = -90F; + public float maximumX = 90F; + public float maximumY = 90F; + public bool smooth = false; + public float smoothTime = 2f; + public bool lockCursor = true; + public GameObject player; + + private Transform m_camera; + private Transform m_character; + private Quaternion m_characterRot; + private Quaternion m_cameraRot; + private bool m_cursorIsLocked = true; + + public void Init(Transform character, Transform camera) + { + m_character = character; + m_camera = camera; + m_characterRot = character.localRotation; + m_cameraRot = camera.localRotation; + + UpdateCursorLock(); + } + + public void LookRotation() + { + float yRot = Input.GetAxis("Mouse X") * xSensitivity; + float xRot = Input.GetAxis("Mouse Y") * ySensitivity; + + m_characterRot *= Quaternion.Euler(0f, yRot, 0f); + m_cameraRot *= Quaternion.Euler(-xRot, 0f, 0f); + if (clampVerticalRotation) + m_cameraRot = ClampRotationAroundXAxis(m_cameraRot); + + + if (smooth) + { + m_character.localRotation = Quaternion.Lerp(m_character.localRotation, m_characterRot, + smoothTime * Time.deltaTime); + m_camera.localRotation = Quaternion.Lerp(m_camera.localRotation, m_cameraRot, + smoothTime * Time.deltaTime); + } + else + { + m_character.localRotation = m_characterRot; + //m_camera.localrotation = m_characterRot; + m_camera.localRotation = m_cameraRot; + } + } + + public void UpdateCursorLock() + { + if (m_cursorIsLocked) + { + Cursor.lockState = CursorLockMode.Locked; + Cursor.visible = false; + } + else if (!m_cursorIsLocked) + { + Cursor.lockState = CursorLockMode.None; + Cursor.visible = true; + } + } + + private Quaternion ClampRotationAroundXAxis(Quaternion q) + { + q.x /= q.w; + q.y /= q.w; + q.z /= q.w; + q.w = 1.0f; + + float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan(q.x); + + angleX = Mathf.Clamp(angleX, minimumX, maximumX); + + q.x = Mathf.Tan(0.5f * Mathf.Deg2Rad * angleX); + + return q; + } +} \ No newline at end of file