코드 정리 및 DataValidator 추가, HttpPipeLine 쓰레드에서 실행되도록 수정
This commit is contained in:
244
Assets/Scripts/UVC/Util/CameraController.cs
Normal file
244
Assets/Scripts/UVC/Util/CameraController.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace UVC.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// 마우스 입력에 따라 카메라를 이동, 회전, 줌하는 컨트롤러입니다.
|
||||
/// - 마우스 가운데 버튼 드래그: 카메라 평행 이동 (Pan)
|
||||
/// - 마우스 오른쪽 버튼 드래그: 카메라 회전 (Orbit)
|
||||
/// - 마우스 휠 스크롤: 카메라 줌 (Zoom)
|
||||
/// </summary>
|
||||
public class CameraController : SingletonScene<CameraController>
|
||||
{
|
||||
[Tooltip("카메라 평행 이동 속도")]
|
||||
public float panSpeed = 20f;
|
||||
|
||||
[Tooltip("카메라 회전 속도")]
|
||||
public float rotationSpeed = 300f;
|
||||
|
||||
[Tooltip("카메라 줌 속도")]
|
||||
public float zoomSpeed = 10f;
|
||||
|
||||
[Header("Movement Smoothing")]
|
||||
[Tooltip("패닝 시 마우스 이동량의 최대값을 제한하여, 프레임 드랍 시 카메라가 급격하게 튀는 현상을 방지합니다.")]
|
||||
public float maxPanDelta = 50f;
|
||||
|
||||
private Vector3 lastPanPosition;
|
||||
private Vector3 rotationPivot;
|
||||
private bool isRotating = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
// 스크립트 시작 시, 회전의 기준이 되는 중심점을 카메라 앞쪽으로 초기화합니다.
|
||||
rotationPivot = transform.position + transform.forward * 10f;
|
||||
}
|
||||
|
||||
// Update 대신 LateUpdate를 사용하여 카메라 움직임이 다른 모든 업데이트 이후에 처리되도록 합니다.
|
||||
// 이를 통해 카메라의 떨림이나 끊김 현상을 줄일 수 있습니다.
|
||||
void LateUpdate()
|
||||
{
|
||||
HandlePanning();
|
||||
HandleRotation();
|
||||
HandleZoom();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 가운데 버튼으로 카메라를 평행 이동시킵니다.
|
||||
/// 프레임 지연으로 인한 급격한 이동을 방지하기 위해 이동량을 제한합니다.
|
||||
/// </summary>
|
||||
private void HandlePanning()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(2))
|
||||
{
|
||||
lastPanPosition = Input.mousePosition;
|
||||
}
|
||||
|
||||
if (Input.GetMouseButton(2))
|
||||
{
|
||||
Vector3 delta = Input.mousePosition - lastPanPosition;
|
||||
|
||||
// 프레임 드랍 시 델타 값이 너무 커져서 카메라가 튀는 것을 방지하기 위해 최대값을 제한합니다.
|
||||
if (delta.magnitude > maxPanDelta)
|
||||
{
|
||||
delta = delta.normalized * maxPanDelta;
|
||||
}
|
||||
|
||||
// 카메라의 로컬 좌표계를 기준으로 이동량을 변환하여 월드 좌표계에서 이동시킵니다.
|
||||
transform.Translate(transform.right * -delta.x * panSpeed * Time.deltaTime, Space.World);
|
||||
transform.Translate(transform.up * -delta.y * panSpeed * Time.deltaTime, Space.World);
|
||||
|
||||
lastPanPosition = Input.mousePosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 오른쪽 버튼으로 카메라를 회전시킵니다.
|
||||
/// 회전 축이 변하는 것을 방지하여 안정적인 회전을 구현합니다.
|
||||
/// </summary>
|
||||
private void HandleRotation()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(1))
|
||||
{
|
||||
isRotating = true;
|
||||
// 마우스 클릭 지점으로 Ray를 쏴서 회전의 중심점(pivot)을 설정합니다.
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, 1000f))
|
||||
{
|
||||
rotationPivot = hit.point;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ray가 아무 오브젝트에도 맞지 않았다면, 카메라 앞쪽의 특정 거리를 중심점으로 사용합니다.
|
||||
rotationPivot = transform.position + transform.forward * 10f;
|
||||
}
|
||||
}
|
||||
|
||||
if (Input.GetMouseButtonUp(1))
|
||||
{
|
||||
isRotating = false;
|
||||
}
|
||||
|
||||
if (isRotating && Input.GetMouseButton(1))
|
||||
{
|
||||
float mouseX = Input.GetAxis("Mouse X") * rotationSpeed * Time.deltaTime;
|
||||
float mouseY = Input.GetAxis("Mouse Y") * rotationSpeed * Time.deltaTime;
|
||||
|
||||
// 수평 회전으로 인해 수직 회전 축(transform.right)이 변질되는 것을 방지하기 위해
|
||||
// 회전 전의 right 벡터를 미리 저장해 둡니다.
|
||||
Vector3 verticalRotationAxis = transform.right;
|
||||
|
||||
// 설정된 중심점을 기준으로 카메라를 회전시킵니다.
|
||||
// 1. 수평 회전 (월드 Y축 기준)
|
||||
transform.RotateAround(rotationPivot, Vector3.up, -mouseX);
|
||||
// 2. 수직 회전 (미리 저장해 둔 카메라의 오른쪽 축 기준)
|
||||
transform.RotateAround(rotationPivot, verticalRotationAxis, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 휠로 카메라를 줌 인/아웃합니다.
|
||||
/// </summary>
|
||||
private void HandleZoom()
|
||||
{
|
||||
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||||
if (scroll != 0f)
|
||||
{
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
Vector3 zoomTarget;
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit))
|
||||
{
|
||||
zoomTarget = hit.point;
|
||||
}
|
||||
else
|
||||
{
|
||||
zoomTarget = ray.GetPoint(1000);
|
||||
}
|
||||
|
||||
Vector3 direction = zoomTarget - transform.position;
|
||||
|
||||
// 줌 실행
|
||||
transform.position += direction.normalized * scroll * zoomSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 Transform을 중심으로 카메라를 포커싱합니다.
|
||||
/// </summary>
|
||||
/// <param name="equipmentTransform">포커스할 대상의 Transform</param>
|
||||
/// <param name="distance">대상과의 거리</param>
|
||||
public void FocusOnTargetFast(Transform equipmentTransform, float distance)
|
||||
{
|
||||
if (equipmentTransform == null)
|
||||
return;
|
||||
|
||||
// 카메라가 바라볼 대상의 중심점
|
||||
Vector3 targetPosition = equipmentTransform.position;
|
||||
|
||||
// 현재 카메라의 회전각을 유지하면서 타겟을 바라보는 방향 설정
|
||||
Vector3 directionToTarget = (targetPosition - transform.position).normalized;
|
||||
|
||||
// 타겟으로부터 지정된 거리만큼 떨어진 위치 계산
|
||||
Vector3 newPosition = targetPosition - directionToTarget * distance;
|
||||
|
||||
// 카메라 위치 설정 및 타겟을 바라보도록 함
|
||||
transform.position = newPosition;
|
||||
transform.LookAt(targetPosition);
|
||||
|
||||
// 회전 피봇 포인트 업데이트
|
||||
rotationPivot = targetPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 Transform을 중심으로 카메라를 포커싱합니다.
|
||||
/// </summary>
|
||||
/// <param name="equipmentTransform">포커스할 대상의 Transform</param>
|
||||
/// <param name="distance">대상과의 거리</param>
|
||||
/// <param name="duration">이동에 걸리는 시간(초), 기본값 1초</param>
|
||||
public void FocusOnTarget(Transform equipmentTransform, float distance, float duration = 1.0f)
|
||||
{
|
||||
if (equipmentTransform == null)
|
||||
return;
|
||||
|
||||
// 코루틴을 사용하여 부드러운 이동 구현
|
||||
StartCoroutine(SmoothFocusOnTarget(equipmentTransform, distance, duration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 부드럽게 타겟까지 이동하는 코루틴
|
||||
/// </summary>
|
||||
private IEnumerator SmoothFocusOnTarget(Transform targetTransform, float distance, float duration)
|
||||
{
|
||||
// 카메라가 바라볼 대상의 중심점
|
||||
Vector3 targetPosition = targetTransform.position;
|
||||
|
||||
// 시작 위치와 회전 저장
|
||||
Vector3 startPosition = transform.position;
|
||||
Quaternion startRotation = transform.rotation;
|
||||
|
||||
// 타겟을 보는 최종 위치와 회전 계산
|
||||
Vector3 directionToTarget = (targetPosition - startPosition).normalized;
|
||||
Vector3 endPosition = targetPosition - directionToTarget * distance;
|
||||
|
||||
// 최종 회전값 계산
|
||||
Quaternion endRotation = Quaternion.LookRotation(targetPosition - endPosition);
|
||||
|
||||
// 이동 시간 계산을 위한 변수
|
||||
float elapsedTime = 0f;
|
||||
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsedTime / duration);
|
||||
|
||||
// 부드러운 이동을 위한 Easing 함수 적용
|
||||
float smoothT = EaseInOutCubic(t);
|
||||
|
||||
// 위치와 회전 보간
|
||||
transform.position = Vector3.Lerp(startPosition, endPosition, smoothT);
|
||||
transform.rotation = Quaternion.Slerp(startRotation, endRotation, smoothT);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// 정확한 최종 위치와 회전 설정
|
||||
transform.position = endPosition;
|
||||
transform.LookAt(targetPosition);
|
||||
|
||||
// 회전 피봇 포인트 업데이트
|
||||
rotationPivot = targetPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cubic ease-in/out 함수로 부드러운 이동 효과를 줍니다.
|
||||
/// </summary>
|
||||
private float EaseInOutCubic(float t)
|
||||
{
|
||||
return t < 0.5f ? 4f * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 3f) / 2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/Util/CameraController.cs.meta
Normal file
2
Assets/Scripts/UVC/Util/CameraController.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61deddb674c074049a9b43fd58f1b355
|
||||
Reference in New Issue
Block a user