using UnityEngine; using System.Collections; namespace UVC.Simulator { enum CamState { fly, topView } public class FirstPersonCamera : MonoBehaviour { [Header("Fly")] [SerializeField] private float move3DSpeed = 10f; [SerializeField] private float rotate3DSpeed = 100f; [SerializeField] private float zoom3DSpeed = 500f; [SerializeField] private float camera3DXMin = -50f; [SerializeField] private float camera3DXMax = 50f; [SerializeField] private float camera3DYMin = 2f; [SerializeField] private float camera3DYMax = 40f; [SerializeField] private float camera3DZMin = -50f; [SerializeField] private float camera3DZMax = 50f; [SerializeField] private float camera3DZoomMin = 35f; [SerializeField] private float camera3DZoomMax = 90f; [SerializeField] private float camera3DRotateMin = -90f; [SerializeField] private float camera3DRotateMax = 90f; [Header("Top View")] [SerializeField] private float topViewPanSpeed = 10f; [SerializeField] private float topViewQEVerticalSpeed = 5f; [SerializeField] private float topViewOrthoSize = 20f; [SerializeField] private KeyCode toggleTopViewKey = KeyCode.Tab; private Vector3 initial3DRotation; private Vector3 initial3DPosition; private float initialFOV = 60f; private Coroutine cameraMoveCoroutine; private float timeCameraChange = 1f; private Vector3 topViewPosition = new Vector3(-4f, 16f, 1f); private readonly Vector3 topViewRotation = new Vector3(90f, 0f, 0f); private CamState CamState = CamState.fly; float CameraSpeedAlpha = 0f; Vector3 offSet = new Vector3(0f, 0f, 0f); private GameObject target; public GameObject Target => target; private Camera cam; private Vector3 lastMousePos; private void Start() { cam = Camera.main; cam.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, (camera3DZoomMax + camera3DZoomMin) / 2, 0.05f); initial3DPosition = transform.position; initial3DRotation = transform.eulerAngles; } void Update() { Zoom3DCamera(); if (CamState == CamState.fly) { Rotate3DCamera(); MoveKeyBoard(); } else { PanningCamera(); } } private void MoveKeyBoard() { float x = Input.GetAxisRaw("Horizontal"); float z = Input.GetAxisRaw("Vertical"); Move3DCamera(x, z); } private void Move3DCamera(float x, float z) { Vector3 moveVector = new Vector3(x, 0, z); if (Input.GetKey(KeyCode.Q)) { moveVector += Vector3.down; } else if (Input.GetKey(KeyCode.E)) { moveVector += Vector3.up; } transform.Translate(moveVector * move3DSpeed * Time.deltaTime); Vector3 p = transform.position; p.x = Mathf.Clamp(p.x, camera3DXMin, camera3DXMax); p.y = Mathf.Clamp(p.y, camera3DYMin, camera3DYMax); p.z = Mathf.Clamp(p.z, camera3DZMin, camera3DZMax); transform.position = p; } void Rotate3DCamera() { if (Input.GetMouseButton(2)) { float xRotationInput = -Input.GetAxis("Mouse Y"); float yRotationInput = Input.GetAxis("Mouse X"); Vector3 newRotation = transform.localEulerAngles + new Vector3(xRotationInput, yRotationInput, 0f) * rotate3DSpeed * Time.deltaTime; float clampedXRotation = ClampAngle(newRotation.x, camera3DRotateMin, camera3DRotateMax); newRotation.x = clampedXRotation; newRotation.z = 0f; transform.localEulerAngles = newRotation; } } public void EnterTopView() { CamState = CamState.topView; if (cam == null) cam = Camera.main != null ? Camera.main : GetComponent(); if (cam != null) { // Projection 전환 cam.orthographic = true; cam.orthographicSize = topViewOrthoSize; } // 회전/위치(진입 시 각도 고정, 위치는 유지. 필요 시 고정 좌표로 이동하려면 주석 해제) transform.rotation = Quaternion.Euler(topViewRotation); // transform.position = new Vector3(transform.position.x, Mathf.Max(transform.position.y, camera3DYMin), transform.position.z); } public void ExitTopView() { CamState = CamState.fly; if (cam == null) cam = Camera.main != null ? Camera.main : GetComponent(); if (cam != null) { cam.orthographic = false; //cam.fieldOfView = Mathf.Clamp(initialFOV, camera3DZoomMin, camera3DZoomMax); } } public void ToggleTopView() { if (CamState == CamState.topView) ExitTopView(); else EnterTopView(); } void PanningCamera() { float x = Input.GetAxisRaw("Horizontal"); float z = Input.GetAxisRaw("Vertical"); if (Mathf.Abs(x) > 0.001f || Mathf.Abs(z) > 0.001f) { Vector3 pan = (transform.right * x + transform.up * z) * topViewPanSpeed * Time.deltaTime; transform.position += new Vector3(pan.x, 0f, pan.z); } if (Input.GetMouseButtonDown(2)) lastMousePos = Input.mousePosition; if (Input.GetMouseButton(2)) { Vector3 delta = Input.mousePosition - lastMousePos; lastMousePos = Input.mousePosition; Vector3 dragPan = (-transform.right * delta.x - transform.up * delta.y) * (topViewPanSpeed / 200f); transform.position += new Vector3(dragPan.x, 0f, dragPan.z); } transform.rotation = Quaternion.Euler(topViewRotation); } private float ClampAngle(float angle, float min, float max) { angle = NormalizeAngle(angle); if (angle > 180f) angle -= 360f; return Mathf.Clamp(angle, min, max); } private float NormalizeAngle(float angle) { while (angle > 360f) angle -= 360f; while (angle < 0f) angle += 360f; return angle; } private void Zoom3DCamera() { if (cam == null) return; float z = -Input.GetAxis("Mouse ScrollWheel") * zoom3DSpeed * Time.deltaTime; cam.fieldOfView = Mathf.Clamp(cam.fieldOfView + z, camera3DZoomMin, camera3DZoomMax); } #region movetotarget private void ResetToPosition(int mode) { transform.parent = null; offSet = Vector3.zero; if (cameraMoveCoroutine != null) StopCoroutine(cameraMoveCoroutine); if (mode == 0) { cameraMoveCoroutine = StartCoroutine(MoveToTarget(null, initial3DPosition, Quaternion.Euler(initial3DRotation))); ResetZoom(); } else if (mode == 1) { cameraMoveCoroutine = StartCoroutine(MoveToTarget(null, topViewPosition, Quaternion.Euler(topViewRotation))); ResetZoom(); } } private void MoveCameraToObject(GameObject target) { ResetZoom(); if (cameraMoveCoroutine != null) StopCoroutine(cameraMoveCoroutine); this.target = target; if (target != null) { cameraMoveCoroutine = StartCoroutine(MoveToTarget(target, target.transform.position, target.transform.rotation)); } } #endregion private IEnumerator MoveToTarget(GameObject target, Vector3 pos, Quaternion rot) { if (cam == null) yield break; Vector3 targetPosition = pos + offSet; Vector3 startPosition = cam.transform.position; Quaternion startRotation = cam.transform.rotation; Quaternion targetRotation = (target != null) ? Quaternion.LookRotation(pos - targetPosition + new Vector3(0f, 1f, 0f)) : rot; float elapsedTime = 0f; CameraSpeedAlpha = 0f; while (elapsedTime < timeCameraChange) { cam.transform.position = Vector3.Lerp(startPosition, targetPosition, elapsedTime / timeCameraChange); cam.transform.rotation = Quaternion.Lerp(startRotation, targetRotation, elapsedTime / timeCameraChange); CameraSpeedAlpha += 0.001f; elapsedTime += Time.deltaTime * CameraSpeedAlpha; yield return null; } cam.transform.position = targetPosition; cam.transform.rotation = targetRotation; cameraMoveCoroutine = null; } public void ResetZoom() { StartCoroutine(ZoomOut()); } private IEnumerator ZoomOut() { if (cam == null) yield break; float startZoom = cam.fieldOfView; float targetZoom = (camera3DZoomMax + camera3DZoomMin) / 2f; float elapsedTime = 0f; CameraSpeedAlpha = 0f; while (elapsedTime < timeCameraChange) { cam.fieldOfView = Mathf.Lerp(startZoom, targetZoom, elapsedTime / timeCameraChange); CameraSpeedAlpha += 0.01f; elapsedTime += Time.deltaTime * CameraSpeedAlpha; yield return null; } } } }