diff --git a/Assets/External/Fullscreen/Editor/FullscreenContainerInternal.cs b/Assets/External/Fullscreen/Editor/FullscreenContainerInternal.cs
index 52252305..b64ef1b4 100644
--- a/Assets/External/Fullscreen/Editor/FullscreenContainerInternal.cs
+++ b/Assets/External/Fullscreen/Editor/FullscreenContainerInternal.cs
@@ -74,7 +74,9 @@ namespace FullscreenEditor {
/// The ContainerWindow to freeze the repaints.
/// Wheter to freeze or unfreeze the container.
protected void SetFreezeContainer(ContainerWindow containerWindow, bool freeze) {
- containerWindow.InvokeMethod("SetFreezeDisplay", freeze);
+ // SetFreezeDisplay was removed in Unity 6 - skip if not available
+ if (containerWindow.HasMethod("SetFreezeDisplay", new[] { typeof(bool) }))
+ containerWindow.InvokeMethod("SetFreezeDisplay", freeze);
}
/// Method that will be called just before creating the ContainerWindow for this fullscreen.
diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs
index 931d0a6d..a0941c41 100644
--- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs
+++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs
@@ -4,6 +4,7 @@ using UnityRawInput;
using System.Linq;
using Unity.Cinemachine;
using Streamingle;
+using KindRetargeting;
public class CameraManager : MonoBehaviour, IController
{
@@ -130,6 +131,11 @@ public class CameraManager : MonoBehaviour, IController
public HotkeyCommand hotkey;
[System.NonSerialized] public bool isEditingHotkey = false;
+ // 프리셋별 초기 상태 저장
+ [System.NonSerialized] public Vector3 savedPosition;
+ [System.NonSerialized] public Quaternion savedRotation;
+ [System.NonSerialized] public bool hasSavedState = false;
+
public CameraPreset(CinemachineCamera camera)
{
virtualCamera = camera;
@@ -138,6 +144,21 @@ public class CameraManager : MonoBehaviour, IController
}
public bool IsValid() => virtualCamera != null && hotkey != null;
+
+ public void SaveCurrentState()
+ {
+ if (virtualCamera == null) return;
+ savedPosition = virtualCamera.transform.position;
+ savedRotation = virtualCamera.transform.rotation;
+ hasSavedState = true;
+ }
+
+ public void RestoreSavedState()
+ {
+ if (!hasSavedState || virtualCamera == null) return;
+ virtualCamera.transform.position = savedPosition;
+ virtualCamera.transform.rotation = savedRotation;
+ }
}
#endregion
@@ -148,31 +169,49 @@ public class CameraManager : MonoBehaviour, IController
#region Fields
[SerializeField] public List cameraPresets = new List();
-
+
[Header("Camera Control Settings")]
- private float rotationSensitivity = 2f;
- private float panSpeed = 0.02f;
- private float zoomSpeed = 0.1f;
- private float orbitSpeed = 10f;
-
+ [SerializeField, Range(0.5f, 10f)] private float rotationSensitivity = 2f;
+ [SerializeField, Range(0.005f, 0.1f)] private float panSpeed = 0.02f;
+ [SerializeField, Range(0.05f, 0.5f)] private float zoomSpeed = 0.1f;
+ [SerializeField, Range(1f, 20f)] private float orbitSpeed = 10f;
+
+ [Header("Smoothing")]
+ [SerializeField, Range(0f, 0.95f)] private float movementSmoothing = 0.1f;
+ [SerializeField, Range(0f, 0.95f)] private float rotationSmoothing = 0.1f;
+
+ [Header("Zoom Limits")]
+ [SerializeField] private float minZoomDistance = 0.5f;
+ [SerializeField] private float maxZoomDistance = 50f;
+
+ [Header("Rotation Target")]
+ [Tooltip("체크하면 아바타 머리를 자동으로 찾아 회전 중심점으로 사용합니다.")]
+ [SerializeField] private bool useAvatarHeadAsTarget = true;
+ [Tooltip("수동으로 회전 중심점을 지정합니다. (useAvatarHeadAsTarget이 false일 때 사용)")]
+ [SerializeField] private Transform manualRotationTarget;
+
private CinemachineCamera currentCamera;
private InputHandler inputHandler;
private CameraPreset currentPreset;
- private Vector3 rotationCenter = Vector3.zero;
- private float currentOrbitDistance = 0f;
- private float currentOrbitHeight = 0f;
- private Vector3 rotationStartPosition;
- private bool isRotating = false;
-
- // 초기 카메라 상태 저장
- private Vector3 initialPosition;
- private Quaternion initialRotation;
- private bool isInitialStateSet = false;
-
+
+ // 오빗 카메라 상태 (각도 기반)
+ private float horizontalAngle;
+ private float verticalAngle;
+ private float currentDistance;
+ private Vector3 focusPoint;
+
+ // 타겟 값 (스무딩용)
+ private float targetHorizontalAngle;
+ private float targetVerticalAngle;
+ private float targetDistance;
+ private Vector3 targetFocusPoint;
+
+ // 아바타 머리 추적
+ private Transform avatarHeadTransform;
+
// 스트림덱 연동
private StreamDeckServerManager streamDeckManager;
-
#endregion
#region Properties
@@ -186,15 +225,24 @@ public class CameraManager : MonoBehaviour, IController
InitializeInputHandler();
InitializeRawInput();
InitializeCameraPresets();
-
+
// StreamDeckServerManager 찾기
- streamDeckManager = FindObjectOfType();
+ streamDeckManager = FindAnyObjectByType();
if (streamDeckManager == null)
{
Debug.LogWarning("[CameraManager] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다.");
}
}
+ private void Start()
+ {
+ // Start에서 아바타 머리 다시 찾기 (다른 스크립트들이 초기화된 후)
+ if (useAvatarHeadAsTarget && avatarHeadTransform == null)
+ {
+ FindAvatarHead();
+ }
+ }
+
private void OnDestroy()
{
if (RawInput.IsRunning)
@@ -263,16 +311,71 @@ public class CameraManager : MonoBehaviour, IController
{
preset.hotkey.InitializeUnityKeys();
}
-
- Set(0);
-
- // 초기 카메라 상태 저장
- if (currentCamera != null && !isInitialStateSet)
+
+ // 모든 프리셋의 초기 상태 저장
+ foreach (var preset in cameraPresets.Where(p => p?.IsValid() == true))
{
- initialPosition = currentCamera.transform.position;
- initialRotation = currentCamera.transform.rotation;
- isInitialStateSet = true;
+ if (!preset.hasSavedState)
+ {
+ preset.SaveCurrentState();
+ }
}
+
+ // 아바타 머리 찾기
+ FindAvatarHead();
+
+ Set(0);
+ }
+
+ private void FindAvatarHead()
+ {
+ if (!useAvatarHeadAsTarget) return;
+
+ // CustomRetargetingScript를 가진 아바타 찾기
+ var retargetingScripts = FindObjectsByType(FindObjectsSortMode.None);
+
+ foreach (var script in retargetingScripts)
+ {
+ if (script == null || !script.gameObject.activeInHierarchy) continue;
+
+ // targetAnimator가 설정되어 있으면 사용
+ Animator animator = script.targetAnimator;
+
+ // targetAnimator가 null이면 같은 GameObject의 Animator 시도
+ if (animator == null)
+ {
+ animator = script.GetComponent();
+ }
+
+ if (animator != null)
+ {
+ avatarHeadTransform = animator.GetBoneTransform(HumanBodyBones.Head);
+ if (avatarHeadTransform != null)
+ {
+ Debug.Log($"[CameraManager] 아바타 머리를 회전 중심점으로 설정: {avatarHeadTransform.name}");
+ return;
+ }
+ }
+ }
+
+ Debug.LogWarning("[CameraManager] 활성화된 아바타의 Head 본을 찾을 수 없습니다. 수동 타겟을 사용하거나 원점을 사용합니다.");
+ }
+
+ ///
+ /// 현재 회전 중심점을 반환합니다.
+ /// 우선순위: 아바타 머리 > 수동 타겟 > 원점
+ ///
+ private Vector3 GetRotationCenter()
+ {
+ if (useAvatarHeadAsTarget && avatarHeadTransform != null)
+ {
+ return avatarHeadTransform.position;
+ }
+ else if (manualRotationTarget != null)
+ {
+ return manualRotationTarget.position;
+ }
+ return Vector3.zero;
}
@@ -312,12 +415,36 @@ public class CameraManager : MonoBehaviour, IController
#endregion
#region Camera Controls
+
+ ///
+ /// 현재 카메라 위치에서 각도와 거리를 초기화합니다.
+ ///
+ private void InitializeOrbitState()
+ {
+ if (currentCamera == null) return;
+
+ focusPoint = GetRotationCenter();
+ targetFocusPoint = focusPoint;
+
+ Vector3 direction = currentCamera.transform.position - focusPoint;
+ currentDistance = direction.magnitude;
+ targetDistance = currentDistance;
+
+ if (currentDistance > 0.01f)
+ {
+ direction.Normalize();
+ horizontalAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
+ verticalAngle = Mathf.Asin(Mathf.Clamp(direction.y, -1f, 1f)) * Mathf.Rad2Deg;
+ }
+
+ targetHorizontalAngle = horizontalAngle;
+ targetVerticalAngle = verticalAngle;
+ }
+
private void HandleCameraControls()
{
if (!IsValidSetup) return;
-
- var virtualCamera = currentCamera;
- if (virtualCamera == null) return;
+ if (currentCamera == null) return;
// Alt+Q로 초기 위치로 복원
if (Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.Q))
@@ -326,141 +453,166 @@ public class CameraManager : MonoBehaviour, IController
return;
}
- HandleRotation(virtualCamera);
- HandlePanning(virtualCamera);
- HandleZooming(virtualCamera);
- HandleOrbiting(virtualCamera);
+ HandleInput();
+ UpdateCameraPosition();
}
- private void HandleRotation(CinemachineCamera virtualCamera)
+ private void HandleInput()
{
- Transform cameraTransform = virtualCamera.transform;
-
- if (inputHandler.IsRightMouseHeld())
+ // 입력 우선순위 처리: Orbit > AltRightZoom > Zoom > Rotation > Panning
+ if (inputHandler.IsOrbitActive())
{
- if (!isRotating)
- {
- // 회전 시작 시 현재 상태 저장
- isRotating = true;
- rotationStartPosition = cameraTransform.position;
- rotationCenter = new Vector3(0f, rotationStartPosition.y, 0f);
-
- // 현재 카메라의 회전 중심점으로부터의 거리와 높이 계산
- Vector3 toCamera = cameraTransform.position - rotationCenter;
- currentOrbitDistance = new Vector3(toCamera.x, 0f, toCamera.z).magnitude;
- currentOrbitHeight = toCamera.y;
- }
+ HandleOrbiting();
+ }
+ else if (inputHandler.IsCtrlRightZoomActive())
+ {
+ HandleCtrlRightZoom();
+ }
+ else if (inputHandler.IsZoomActive())
+ {
+ HandleDragZoom();
+ }
+ else if (inputHandler.IsRightMouseHeld())
+ {
+ HandleRotation();
+ }
+ else if (inputHandler.IsMiddleMouseHeld())
+ {
+ HandlePanning();
+ }
- Vector2 lookDelta = inputHandler.GetLookDelta();
- if (lookDelta.sqrMagnitude < float.Epsilon) return;
-
- // 현재 회전값을 오일러 각도로 가져오기
- Vector3 currentEuler = cameraTransform.eulerAngles;
-
- // Y축 회전만 적용 (수평 회전)
- float newY = currentEuler.y + lookDelta.x * rotationSensitivity;
-
- // 회전 적용 (X축 회전은 0으로 고정)
- Quaternion targetRotation = Quaternion.Euler(0f, newY, 0f);
-
- // 오비탈 회전을 위한 새로운 위치 계산
- Vector3 orbitPosition = targetRotation * Vector3.back * currentOrbitDistance;
- orbitPosition.y = currentOrbitHeight; // 높이 유지
-
- // 회전 중심점을 기준으로 새로운 위치 설정
- cameraTransform.position = rotationCenter + orbitPosition;
- cameraTransform.rotation = targetRotation;
+ // 휠 줌은 항상 처리
+ HandleWheelZoom();
+ }
+
+ ///
+ /// 우클릭 드래그: 수평 + 수직 회전 (현재 비활성화 - Alt+우클릭 사용)
+ ///
+ private void HandleRotation()
+ {
+ // 우클릭만으로는 회전하지 않음 - Alt+우클릭(Orbit)으로 대체
+ }
+
+ ///
+ /// Alt + 우클릭: 자유 궤도 회전 (X, Y축)
+ ///
+ private void HandleOrbiting()
+ {
+ Vector2 delta = inputHandler.GetLookDelta();
+ if (delta.sqrMagnitude < float.Epsilon) return;
+
+ // 회전 속도
+ targetHorizontalAngle += delta.x * rotationSensitivity;
+ targetVerticalAngle -= delta.y * rotationSensitivity;
+ targetVerticalAngle = Mathf.Clamp(targetVerticalAngle, -80f, 80f);
+ }
+
+ ///
+ /// 휠클릭 드래그: 패닝 (초점 이동)
+ ///
+ private void HandlePanning()
+ {
+ Vector2 delta = inputHandler.GetLookDelta();
+ if (delta.sqrMagnitude < float.Epsilon) return;
+
+ // 거리에 비례하여 패닝 속도 조절 - 0.2배
+ float speedMultiplier = targetDistance * panSpeed * 0.175f;
+
+ // 카메라의 로컬 축 기준으로 초점 이동
+ Vector3 right = currentCamera.transform.right;
+ Vector3 up = currentCamera.transform.up;
+
+ Vector3 panOffset = (-right * delta.x - up * delta.y) * speedMultiplier;
+ targetFocusPoint += panOffset;
+ }
+
+ ///
+ /// 마우스 휠: 줌
+ ///
+ private void HandleWheelZoom()
+ {
+ float scroll = inputHandler.GetZoomDelta();
+ if (Mathf.Abs(scroll) < 0.01f) return;
+
+ // 거리에 비례하여 줌 속도 조절 (더 자연스러운 느낌) - 0.4배
+ float zoomDelta = scroll * zoomSpeed * targetDistance * 0.4f;
+ targetDistance -= zoomDelta;
+ targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
+ }
+
+ ///
+ /// Ctrl + 좌클릭 드래그: 줌
+ ///
+ private void HandleDragZoom()
+ {
+ Vector2 delta = inputHandler.GetLookDelta();
+ if (delta.sqrMagnitude < float.Epsilon) return;
+
+ float zoomDelta = delta.y * zoomSpeed * targetDistance * 0.5f;
+ targetDistance -= zoomDelta;
+ targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
+ }
+
+ ///
+ /// Ctrl + 우클릭 드래그: 줌 (밀기 = 확대, 당기기 = 축소)
+ ///
+ private void HandleCtrlRightZoom()
+ {
+ Vector2 delta = inputHandler.GetLookDelta();
+ if (delta.sqrMagnitude < float.Epsilon) return;
+
+ // 위로 밀면 확대 (거리 감소), 아래로 밀면 축소 (거리 증가)
+ float zoomDelta = delta.y * zoomSpeed * targetDistance * 0.5f;
+ targetDistance -= zoomDelta;
+ targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
+ }
+
+ ///
+ /// 스무딩을 적용하여 카메라 위치 업데이트
+ ///
+ private void UpdateCameraPosition()
+ {
+ float dt = Time.deltaTime;
+
+ // 스무딩 적용
+ if (movementSmoothing > 0.01f)
+ {
+ float smoothSpeed = (1f - movementSmoothing) * 15f;
+ horizontalAngle = Mathf.Lerp(horizontalAngle, targetHorizontalAngle, dt * smoothSpeed);
+ verticalAngle = Mathf.Lerp(verticalAngle, targetVerticalAngle, dt * smoothSpeed);
+ currentDistance = Mathf.Lerp(currentDistance, targetDistance, dt * smoothSpeed);
+ focusPoint = Vector3.Lerp(focusPoint, targetFocusPoint, dt * smoothSpeed);
}
else
{
- isRotating = false;
+ horizontalAngle = targetHorizontalAngle;
+ verticalAngle = targetVerticalAngle;
+ currentDistance = targetDistance;
+ focusPoint = targetFocusPoint;
}
- }
- private void HandlePanning(CinemachineCamera virtualCamera)
- {
- if (!inputHandler.IsMiddleMouseHeld()) return;
+ // 구면 좌표계에서 카메라 위치 계산
+ float horizontalRad = horizontalAngle * Mathf.Deg2Rad;
+ float verticalRad = verticalAngle * Mathf.Deg2Rad;
- Vector2 panDelta = inputHandler.GetLookDelta();
- if (panDelta.sqrMagnitude < float.Epsilon) return;
+ Vector3 offset = new Vector3(
+ Mathf.Sin(horizontalRad) * Mathf.Cos(verticalRad),
+ Mathf.Sin(verticalRad),
+ Mathf.Cos(horizontalRad) * Mathf.Cos(verticalRad)
+ ) * currentDistance;
- Transform cameraTransform = virtualCamera.transform;
-
- // 이동 적용 (카메라의 right와 up 방향으로 이동)
- Vector3 right = cameraTransform.right * -panDelta.x * panSpeed;
- Vector3 up = cameraTransform.up * -panDelta.y * panSpeed;
-
- cameraTransform.position += right + up;
- }
-
- private void HandleZooming(CinemachineCamera virtualCamera)
- {
- if (inputHandler.IsZoomActive())
- {
- // Ctrl + 좌클릭으로 줌
- Vector2 lookDelta = inputHandler.GetLookDelta();
- if (lookDelta.sqrMagnitude < float.Epsilon) return;
-
- Transform cameraTransform = virtualCamera.transform;
- Vector3 forward = cameraTransform.forward * lookDelta.y * zoomSpeed * 10f;
- cameraTransform.position += forward;
- }
- else
- {
- // 마우스 휠로 줌
- float zoomDelta = inputHandler.GetZoomDelta();
- if (Mathf.Abs(zoomDelta) <= 0.1f) return;
-
- Transform cameraTransform = virtualCamera.transform;
- Vector3 forward = cameraTransform.forward * zoomDelta * zoomSpeed;
- cameraTransform.position += forward;
- }
- }
-
- private void HandleOrbiting(CinemachineCamera virtualCamera)
- {
- if (!inputHandler.IsOrbitActive()) return;
-
- Vector2 orbitDelta = inputHandler.GetLookDelta();
- if (orbitDelta.sqrMagnitude < float.Epsilon) return;
-
- Transform cameraTransform = virtualCamera.transform;
-
- // 현재 회전값을 오일러 각도로 가져오기
- Vector3 currentEuler = cameraTransform.eulerAngles;
-
- // X축 회전값을 -80도에서 80도 사이로 제한하기 위해 360도 형식에서 변환
- float currentX = currentEuler.x;
- if (currentX > 180f) currentX -= 360f;
-
- // 새로운 회전값 계산
- float newX = currentX - orbitDelta.y * orbitSpeed;
- float newY = currentEuler.y + orbitDelta.x * orbitSpeed;
-
- // X축 회전 제한 (-80도 ~ 80도)
- newX = Mathf.Clamp(newX, -80f, 80f);
-
- // 회전 적용
- Quaternion targetRotation = Quaternion.Euler(newX, newY, 0f);
- cameraTransform.rotation = targetRotation;
-
- // 원점으로부터의 거리 유지
- float distance = cameraTransform.position.magnitude;
-
- // 새로운 위치 계산 (원점으로부터의 거리 유지)
- Vector3 newPosition = targetRotation * Vector3.back * distance;
- cameraTransform.position = newPosition;
+ currentCamera.transform.position = focusPoint + offset;
+ currentCamera.transform.LookAt(focusPoint);
}
private void RestoreInitialCameraState()
{
- if (!isInitialStateSet || currentCamera == null) return;
+ if (currentPreset == null || !currentPreset.hasSavedState) return;
- currentCamera.transform.position = initialPosition;
- currentCamera.transform.rotation = initialRotation;
-
- // 회전 중심점 초기화
- rotationCenter = new Vector3(0f, initialPosition.y, 0f);
+ currentPreset.RestoreSavedState();
+
+ // 각도 상태 다시 초기화
+ InitializeOrbitState();
}
#endregion
@@ -494,15 +646,18 @@ public class CameraManager : MonoBehaviour, IController
currentPreset = newPreset;
UpdateCameraPriorities(newPreset.virtualCamera);
-
+
+ // 오빗 상태 초기화 (각도, 거리 계산)
+ InitializeOrbitState();
+
OnCameraChanged?.Invoke(oldPreset, newPreset);
-
+
// 스트림덱에 카메라 변경 알림 전송
if (streamDeckManager != null)
{
streamDeckManager.NotifyCameraChanged();
}
-
+
Debug.Log($"[CameraManager] 카메라 전환 완료: {newCameraName}");
}
diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Input/InputHandler.cs b/Assets/Scripts/Streamingle/StreamingleControl/Input/InputHandler.cs
index 3a08eb81..0d76cdec 100644
--- a/Assets/Scripts/Streamingle/StreamingleControl/Input/InputHandler.cs
+++ b/Assets/Scripts/Streamingle/StreamingleControl/Input/InputHandler.cs
@@ -8,14 +8,28 @@ public class InputHandler : MonoBehaviour
private bool isMiddleMouseHeld;
private bool isOrbitActive;
private bool isZoomActive;
-
+ private bool isCtrlRightZoomActive;
+
+ // 현재 활성화된 입력 모드 (충돌 방지)
+ private InputMode currentMode = InputMode.None;
+
+ private enum InputMode
+ {
+ None,
+ Orbit, // Alt + 우클릭 또는 Alt + 좌클릭
+ Zoom, // Ctrl + 좌클릭
+ CtrlRightZoom, // Ctrl + 우클릭
+ Rotation, // 우클릭
+ Pan // 휠클릭
+ }
+
// 카메라 컨트롤 시스템 참조
private CameraControlSystem cameraControlSystem;
private void Start()
{
// CameraControlSystem 찾기
- cameraControlSystem = FindObjectOfType();
+ cameraControlSystem = FindAnyObjectByType();
if (cameraControlSystem == null)
{
Debug.LogWarning("[InputHandler] CameraControlSystem을 찾을 수 없습니다.");
@@ -24,11 +38,72 @@ public class InputHandler : MonoBehaviour
private void Update()
{
- // 마우스 버튼 상태 업데이트
- isRightMouseHeld = Input.GetMouseButton(1);
- isMiddleMouseHeld = Input.GetMouseButton(2);
- isOrbitActive = Input.GetKey(KeyCode.LeftAlt) && Input.GetMouseButton(0); // Alt + 좌클릭으로 궤도 회전
- isZoomActive = Input.GetKey(KeyCode.LeftControl) && Input.GetMouseButton(0); // Ctrl + 좌클릭으로 줌
+ // 마우스 버튼 Raw 상태
+ bool leftMouse = Input.GetMouseButton(0);
+ bool rightMouse = Input.GetMouseButton(1);
+ bool middleMouse = Input.GetMouseButton(2);
+ bool altKey = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
+ bool ctrlKey = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
+
+ // 모든 마우스 버튼이 해제되면 모드 리셋
+ if (!leftMouse && !rightMouse && !middleMouse)
+ {
+ currentMode = InputMode.None;
+ }
+
+ // 입력 우선순위에 따라 모드 결정 (이미 활성화된 모드가 없을 때만)
+ if (currentMode == InputMode.None)
+ {
+ if (altKey && (rightMouse || leftMouse))
+ {
+ currentMode = InputMode.Orbit;
+ }
+ else if (ctrlKey && rightMouse)
+ {
+ currentMode = InputMode.CtrlRightZoom;
+ }
+ else if (ctrlKey && leftMouse)
+ {
+ currentMode = InputMode.Zoom;
+ }
+ else if (rightMouse)
+ {
+ currentMode = InputMode.Rotation;
+ }
+ else if (middleMouse)
+ {
+ currentMode = InputMode.Pan;
+ }
+ }
+
+ // 현재 모드에 따라 상태 설정
+ isOrbitActive = (currentMode == InputMode.Orbit) && (rightMouse || leftMouse) && altKey;
+ isZoomActive = (currentMode == InputMode.Zoom) && leftMouse && ctrlKey;
+ isCtrlRightZoomActive = (currentMode == InputMode.CtrlRightZoom) && rightMouse && ctrlKey;
+ isRightMouseHeld = (currentMode == InputMode.Rotation) && rightMouse;
+ isMiddleMouseHeld = (currentMode == InputMode.Pan) && middleMouse;
+
+ // 모드가 해제되면 None으로 전환
+ if (currentMode == InputMode.Orbit && ((!rightMouse && !leftMouse) || !altKey))
+ {
+ currentMode = InputMode.None;
+ }
+ else if (currentMode == InputMode.CtrlRightZoom && (!rightMouse || !ctrlKey))
+ {
+ currentMode = InputMode.None;
+ }
+ else if (currentMode == InputMode.Zoom && (!leftMouse || !ctrlKey))
+ {
+ currentMode = InputMode.None;
+ }
+ else if (currentMode == InputMode.Rotation && !rightMouse)
+ {
+ currentMode = InputMode.None;
+ }
+ else if (currentMode == InputMode.Pan && !middleMouse)
+ {
+ currentMode = InputMode.None;
+ }
// 마우스 위치 업데이트
lastMousePosition = Input.mousePosition;
@@ -39,6 +114,12 @@ public class InputHandler : MonoBehaviour
public bool IsMiddleMouseHeld() => isMiddleMouseHeld;
public bool IsOrbitActive() => isOrbitActive;
public bool IsZoomActive() => isZoomActive;
+ public bool IsCtrlRightZoomActive() => isCtrlRightZoomActive;
+
+ ///
+ /// 현재 어떤 입력 모드도 활성화되지 않았는지 확인합니다.
+ ///
+ public bool IsIdle() => currentMode == InputMode.None;
public Vector2 GetLookDelta()
{