using UnityEngine; namespace GPUInstancer { // Note: This is a stripped down version of the FirstPersonController script from the Unity Standard Assets package. // It's dependencies have been removed/modified and it has been taken into the GPUInstancer namespace to avoid conflicts. // This class is only used in the demo scenes that showcase the GPU Instancer capabilities. [RequireComponent(typeof (CharacterController))] public class FPController : MonoBehaviour { [SerializeField] public float m_WalkSpeed; [SerializeField] public float m_RunSpeed; [SerializeField] public float m_JumpSpeed; private bool m_IsWalking; private MouseLook m_MouseLook; private Camera m_Camera; private bool m_Jump; private float m_YRotation; private Vector2 m_Input; private Vector3 m_MoveDir = Vector3.zero; private CharacterController m_CharacterController; private CollisionFlags m_CollisionFlags; private bool m_PreviouslyGrounded; private bool m_Jumping; private float m_StickToGroundForce = 10f; private float m_GravityMultiplier = 2f; private void Start() { m_CharacterController = GetComponent(); m_Camera = Camera.main; m_Jumping = false; m_MouseLook = new MouseLook(); m_MouseLook.Init(transform , m_Camera.transform); } private void Update() { RotateView(); // the jump state needs to read here to make sure it is not missed if (!m_Jump && Cursor.lockState == CursorLockMode.Locked) { m_Jump = Input.GetButtonDown("Jump"); } if (!m_PreviouslyGrounded && m_CharacterController.isGrounded) { m_MoveDir.y = 0f; m_Jumping = false; } if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded) { m_MoveDir.y = 0f; } m_PreviouslyGrounded = m_CharacterController.isGrounded; } private void FixedUpdate() { float speed; GetInput(out speed); // always move along the camera forward as it is the direction that it being aimed at Vector3 desiredMove = transform.forward*m_Input.y + transform.right*m_Input.x; // get a normal for the surface that is being touched to move along it RaycastHit hitInfo; Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo, m_CharacterController.height/2f, Physics.AllLayers, QueryTriggerInteraction.Ignore); desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized; m_MoveDir.x = desiredMove.x*speed; m_MoveDir.z = desiredMove.z*speed; if (m_CharacterController.isGrounded) { m_MoveDir.y = -m_StickToGroundForce; if (m_Jump) { m_MoveDir.y = m_JumpSpeed; m_Jump = false; m_Jumping = true; } } else { m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime; } m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime); m_MouseLook.UpdateCursorLock(); } private void GetInput(out float speed) { // Read input float horizontal = Input.GetAxis("Horizontal"); float vertical = Input.GetAxis("Vertical"); // On standalone builds, walk/run speed is modified by a key press. // keep track of whether or not the character is walking or running m_IsWalking = !Input.GetKey(KeyCode.LeftShift); // set the desired speed to be walking or running speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed; m_Input = new Vector2(horizontal, vertical); // normalize input if it exceeds 1 in combined length: if (m_Input.sqrMagnitude > 1) { m_Input.Normalize(); } } private void RotateView() { m_MouseLook.LookRotation (transform, m_Camera.transform); } private void OnControllerColliderHit(ControllerColliderHit hit) { Rigidbody body = hit.collider.attachedRigidbody; //dont move the rigidbody if the character is on top of it if (m_CollisionFlags == CollisionFlags.Below) { return; } if (body == null || body.isKinematic) { return; } body.AddForceAtPosition(m_CharacterController.velocity*0.1f, hit.point, ForceMode.Impulse); } } public class MouseLook { public float XSensitivity = 2f; public float YSensitivity = 2f; public bool clampVerticalRotation = true; public float MinimumX = -90F; public float MaximumX = 90F; public bool smooth; public float smoothTime = 5f; public bool lockCursor = true; private Quaternion m_CharacterTargetRot; private Quaternion m_CameraTargetRot; private bool m_cursorIsLocked = true; public void Init(Transform character, Transform camera) { m_CharacterTargetRot = character.localRotation; m_CameraTargetRot = camera.localRotation; } public void LookRotation(Transform character, Transform camera) { if (m_cursorIsLocked) { float yRot = Input.GetAxis("Mouse X") * XSensitivity; float xRot = Input.GetAxis("Mouse Y") * YSensitivity; m_CharacterTargetRot *= Quaternion.Euler(0f, yRot, 0f); m_CameraTargetRot *= Quaternion.Euler(-xRot, 0f, 0f); if (clampVerticalRotation) m_CameraTargetRot = ClampRotationAroundXAxis(m_CameraTargetRot); if (smooth) { character.localRotation = Quaternion.Slerp(character.localRotation, m_CharacterTargetRot, smoothTime * Time.deltaTime); camera.localRotation = Quaternion.Slerp(camera.localRotation, m_CameraTargetRot, smoothTime * Time.deltaTime); } else { character.localRotation = m_CharacterTargetRot; camera.localRotation = m_CameraTargetRot; } } UpdateCursorLock(); } public void SetCursorLock(bool value) { lockCursor = value; if (!lockCursor) {//we force unlock the cursor if the user disable the cursor locking helper Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } } public void UpdateCursorLock() { //if the user set "lockCursor" we check & properly lock the cursos if (lockCursor) InternalLockUpdate(); } private void InternalLockUpdate() { if (Input.GetKeyUp(KeyCode.Escape)) { m_cursorIsLocked = false; } else if (Input.GetMouseButtonUp(0)) { m_cursorIsLocked = true; } if (m_cursorIsLocked) { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } else if (!m_cursorIsLocked) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } } 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; } } }