Files
XRLib/Assets/Scripts/Simulator/FirstPersonCamera.cs
2025-09-25 18:08:55 +09:00

291 lines
9.8 KiB
C#

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<Camera>();
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<Camera>();
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;
}
}
}
}