using UnityEngine; [RequireComponent(typeof(CharacterController))] public class FirstPersonController : MonoBehaviour { [Header("Movement")] public float walkSpeed = 4f; public float runSpeed = 7f; public float gravity = -12f; [Header("Look")] public float mouseSensitivity = 2f; public float touchSensitivity = 0.15f; public float maxPitch = 75f; [Header("References")] public Camera playerCamera; CharacterController _cc; float _pitch, _yaw, _vy; bool _locked; int _touchId = -1; Vector2 _lastTouch; void Start() { _cc = GetComponent(); // Spawn at exact Spatial spawn point: (-0.36, 0, -2.34) facing south (~180°) // Y=0 → CharacterController places feet at floor; centre offset = height/2 transform.position = new Vector3(-0.36f, 0f, -2.34f); _yaw = 180f; // facing south (into gallery), matching Spatial spawn rotation } void Update() { HandleLook(); HandleMove(); } void HandleLook() { #if UNITY_WEBGL && !UNITY_EDITOR if (_locked) Apply(Input.GetAxis("Mouse X") * mouseSensitivity, Input.GetAxis("Mouse Y") * mouseSensitivity); else HandleTouch(); #else if (Input.GetMouseButton(1)) Apply(Input.GetAxis("Mouse X") * mouseSensitivity, Input.GetAxis("Mouse Y") * mouseSensitivity); #endif } void Apply(float dx, float dy) { _yaw += dx; _pitch = Mathf.Clamp(_pitch - dy, -maxPitch, maxPitch); transform.rotation = Quaternion.Euler(0, _yaw, 0); if (playerCamera) playerCamera.transform.localRotation = Quaternion.Euler(_pitch, 0, 0); } void HandleTouch() { for (int i = 0; i < Input.touchCount; i++) { var t = Input.GetTouch(i); if (t.phase == TouchPhase.Began && _touchId == -1) { _touchId = t.fingerId; _lastTouch = t.position; } else if (t.fingerId == _touchId) { if (t.phase == TouchPhase.Moved) { var d = t.position - _lastTouch; Apply(d.x * touchSensitivity, d.y * touchSensitivity); _lastTouch = t.position; } else if (t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled) _touchId = -1; } } } void HandleMove() { float spd = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed; var dir = (transform.right * Input.GetAxisRaw("Horizontal") + transform.forward * Input.GetAxisRaw("Vertical")).normalized; if (_cc.isGrounded) _vy = -0.5f; else _vy += gravity * Time.deltaTime; _cc.Move((dir * spd + Vector3.up * _vy) * Time.deltaTime); } void OnApplicationFocus(bool f) { if (f) _locked = true; } public void OnPointerLockLost() => _locked = false; }