diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs index 126ab3fe..987592ca 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraController.cs @@ -1,10 +1,10 @@ using UnityEngine; -using Unity.Cinemachine; using System.Collections.Generic; using UnityRawInput; using System.Linq; +using Unity.Cinemachine; -public class CameraManager : MonoBehaviour +public class CameraManager : MonoBehaviour, IController { #region Classes private static class KeyMapping @@ -66,7 +66,6 @@ public class CameraManager : MonoBehaviour return; } - // 현재 눌린 키 확인 foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode))) { if (Input.GetKeyDown(keyCode) && KeyMapping.TryGetRawKey(keyCode, out RawKey rawKey)) @@ -78,7 +77,6 @@ public class CameraManager : MonoBehaviour } } - // 모든 키가 떼어졌는지 확인 bool allKeysReleased = rawKeys.Any() && rawKeys.All(key => !Input.GetKey(KeyMapping.TryGetKeyCode(key, out KeyCode keyCode) ? keyCode : KeyCode.None)); if (allKeysReleased) @@ -89,59 +87,34 @@ public class CameraManager : MonoBehaviour public void InitializeUnityKeys() { - try + unityKeys.Clear(); + + if (rawKeys == null || !rawKeys.Any()) return; + + foreach (var rawKey in rawKeys) { - unityKeys ??= new List(); - unityKeys.Clear(); - - if (rawKeys == null || !rawKeys.Any()) return; - - foreach (var rawKey in rawKeys) + if (KeyMapping.TryGetKeyCode(rawKey, out KeyCode keyCode) && keyCode != KeyCode.None) { - if (KeyMapping.TryGetKeyCode(rawKey, out KeyCode keyCode) && keyCode != KeyCode.None) - { - unityKeys.Add(keyCode); - } + unityKeys.Add(keyCode); } } - catch (System.Exception e) - { - Debug.LogError($"핫키 초기화 중 오류 발생: {e.Message}"); - } } public bool IsTriggered() { if (isRecording) return false; - try + if (rawKeys == null || !rawKeys.Any()) return false; + + bool allRawKeysPressed = rawKeys.All(key => RawInput.IsKeyDown(key)); + if (allRawKeysPressed) return true; + + if (unityKeys.Any()) { - if (rawKeys == null || !rawKeys.Any()) return false; - - // RawInput으로 체크 (우선순위) - bool allRawKeysPressed = rawKeys.All(key => RawInput.IsKeyDown(key)); - if (allRawKeysPressed) - { - return true; - } - - // Unity Input으로 체크 (백업) - if (unityKeys != null && unityKeys.Any()) - { - bool allKeysPressed = unityKeys.All(key => Input.GetKey(key)); - if (allKeysPressed) - { - return true; - } - } - - return false; - } - catch (System.Exception e) - { - Debug.LogError($"핫키 트리거 확인 중 오류 발생: {e.Message}"); - return false; + return unityKeys.All(key => Input.GetKey(key)); } + + return false; } public override string ToString() => @@ -176,15 +149,22 @@ public class CameraManager : MonoBehaviour [SerializeField] public List cameraPresets = new List(); [Header("Camera Control Settings")] - [SerializeField, Range(0.1f, 10f)] private float rotationSensitivity = 2f; - [SerializeField, Range(0.1f, 20f)] private float panSpeed = 10f; - [SerializeField, Range(0.1f, 20f)] private float zoomSpeed = 10f; - [SerializeField, Range(0.1f, 10f)] private float orbitSpeed = 5f; + private float rotationSensitivity = 2f; + private float panSpeed = 0.02f; + private float zoomSpeed = 0.1f; + private float orbitSpeed = 10f; private CinemachineCamera currentCamera; private InputHandler inputHandler; - private Vector3 targetPosition; private CameraPreset currentPreset; + private Vector3 rotationCenter = Vector3.zero; + private Vector3 rotationStartPosition; + private bool isRotating = false; + + // 초기 카메라 상태 저장 + private Vector3 initialPosition; + private Quaternion initialRotation; + private bool isInitialStateSet = false; #endregion #region Properties @@ -195,27 +175,17 @@ public class CameraManager : MonoBehaviour #region Unity Messages private void Awake() { - try - { - InitializeInputHandler(); - InitializeRawInput(); - InitializeCameraPresets(); - } - catch (System.Exception e) - { - Debug.LogError($"초기화 중 오류 발생: {e.Message}"); - } + InitializeInputHandler(); + InitializeRawInput(); + InitializeCameraPresets(); } private void OnDestroy() { - try + if (RawInput.IsRunning) { - CleanupRawInput(); - } - catch (System.Exception e) - { - Debug.LogError($"정리 중 오류 발생: {e.Message}"); + RawInput.OnKeyDown -= HandleRawKeyDown; + RawInput.Stop(); } } @@ -223,16 +193,9 @@ public class CameraManager : MonoBehaviour { if (!IsValidSetup) return; - try - { - UpdateHotkeyRecording(); - HandleCameraControls(); - HandleUnityHotkeys(); - } - catch (System.Exception e) - { - Debug.LogError($"카메라 제어 중 오류 발생: {e.Message}"); - } + UpdateHotkeyRecording(); + HandleCameraControls(); + HandleHotkeys(); } private void UpdateHotkeyRecording() @@ -254,7 +217,6 @@ public class CameraManager : MonoBehaviour if (inputHandler == null) { inputHandler = gameObject.AddComponent(); - Debug.Log("InputHandler 컴포넌트가 자동으로 추가되었습니다."); } } @@ -265,7 +227,7 @@ public class CameraManager : MonoBehaviour RawInput.Start(); RawInput.WorkInBackground = true; } - RawInput.OnKeyDown += HandleKeyDown; + RawInput.OnKeyDown += HandleRawKeyDown; } private void InitializeCameraPresets() @@ -273,12 +235,10 @@ public class CameraManager : MonoBehaviour if (cameraPresets == null) { cameraPresets = new List(); - Debug.LogWarning("카메라 프리셋 리스트가 null이어서 새로 생성되었습니다."); } if (!cameraPresets.Any()) { - Debug.LogWarning("카메라 프리셋이 설정되어 있지 않습니다."); return; } @@ -287,57 +247,47 @@ public class CameraManager : MonoBehaviour preset.hotkey.InitializeUnityKeys(); } - SwitchToCamera(0); - } - - private void CleanupRawInput() - { - if (RawInput.IsRunning) + Set(0); + + // 초기 카메라 상태 저장 + if (currentCamera != null && !isInitialStateSet) { - RawInput.OnKeyDown -= HandleKeyDown; - RawInput.Stop(); + initialPosition = currentCamera.transform.position; + initialRotation = currentCamera.transform.rotation; + isInitialStateSet = true; } } #endregion #region Input Handling - private void HandleKeyDown(RawKey key) + private void HandleRawKeyDown(RawKey key) { if (key == default(RawKey)) return; TryActivatePresetByInput(preset => - { - if (preset?.hotkey == null) return false; - bool contains = preset.hotkey.rawKeys?.Contains(key) == true; - bool triggered = preset.hotkey.IsTriggered(); - return contains && triggered; - }); - } - - private void HandleUnityHotkeys() - { - if (!Input.anyKeyDown) return; - TryActivatePresetByInput(preset => { if (preset?.hotkey == null) return false; return preset.hotkey.IsTriggered(); }); } + private void HandleHotkeys() + { + if (Input.anyKeyDown) + { + TryActivatePresetByInput(preset => + { + if (preset?.hotkey == null) return false; + return preset.hotkey.IsTriggered(); + }); + } + } + private void TryActivatePresetByInput(System.Func predicate) { - try + var matchingPreset = cameraPresets?.FirstOrDefault(predicate); + if (matchingPreset != null) { - var matchingPreset = cameraPresets? - .FirstOrDefault(predicate); - - if (matchingPreset != null) - { - SwitchToCamera(cameraPresets.IndexOf(matchingPreset)); - } - } - catch (System.Exception e) - { - Debug.LogError($"프리셋 활성화 중 오류 발생: {e.Message}"); + Set(cameraPresets.IndexOf(matchingPreset)); } } #endregion @@ -347,137 +297,175 @@ public class CameraManager : MonoBehaviour { if (!IsValidSetup) return; - Transform cameraTransform = currentCamera.transform; - if (cameraTransform == null) return; + var virtualCamera = currentCamera; + if (virtualCamera == null) return; - HandleRotation(cameraTransform); - HandlePanning(cameraTransform); - HandleZooming(cameraTransform); - HandleOrbiting(cameraTransform); + // Alt+Q로 초기 위치로 복원 + if (Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.Q)) + { + RestoreInitialCameraState(); + return; + } + + HandleRotation(virtualCamera); + HandlePanning(virtualCamera); + HandleZooming(virtualCamera); + HandleOrbiting(virtualCamera); } - private void HandleRotation(Transform cameraTransform) + private void HandleRotation(CinemachineCamera virtualCamera) { - if (!inputHandler.IsRightMouseHeld()) return; + Transform cameraTransform = virtualCamera.transform; + + if (inputHandler.IsRightMouseHeld()) + { + if (!isRotating) + { + // 회전 시작 시 현재 위치 저장 + isRotating = true; + rotationStartPosition = cameraTransform.position; + // 현재 카메라의 y값을 기준으로 회전 중심점 설정 + rotationCenter = new Vector3(0f, rotationStartPosition.y, 0f); + } - Vector2 lookDelta = inputHandler.GetLookDelta(); - if (lookDelta.sqrMagnitude < float.Epsilon) return; - - var rotation = new Vector3(-lookDelta.y, lookDelta.x, 0f) * rotationSensitivity; - cameraTransform.rotation *= Quaternion.Euler(rotation); + Vector2 lookDelta = inputHandler.GetLookDelta(); + if (lookDelta.sqrMagnitude < float.Epsilon) return; + + // 현재 회전값을 오일러 각도로 가져오기 + Vector3 currentEuler = cameraTransform.eulerAngles; + + // X축 회전값을 -80도에서 80도 사이로 제한하기 위해 360도 형식에서 변환 + float currentX = currentEuler.x; + if (currentX > 180f) currentX -= 360f; + + // 새로운 회전값 계산 + float newX = currentX - lookDelta.y * rotationSensitivity; + float newY = currentEuler.y + lookDelta.x * rotationSensitivity; + + // X축 회전 제한 (-80도 ~ 80도) + newX = Mathf.Clamp(newX, -80f, 80f); + + // 회전 적용 + Quaternion targetRotation = Quaternion.Euler(newX, newY, 0f); + cameraTransform.rotation = targetRotation; + + // 회전 시작 위치를 기준으로 회전 (y값 유지) + Vector3 relativePosition = rotationStartPosition - rotationCenter; + Vector3 rotatedPosition = targetRotation * new Vector3(relativePosition.x, 0f, relativePosition.z); + Vector3 newPosition = rotationCenter + rotatedPosition; + cameraTransform.position = newPosition; + } + else + { + isRotating = false; + } } - private void HandlePanning(Transform cameraTransform) + private void HandlePanning(CinemachineCamera virtualCamera) { if (!inputHandler.IsMiddleMouseHeld()) return; Vector2 panDelta = inputHandler.GetLookDelta(); if (panDelta.sqrMagnitude < float.Epsilon) return; - Vector3 panMovement = - cameraTransform.right * -panDelta.x * panSpeed + - cameraTransform.up * -panDelta.y * panSpeed; - cameraTransform.position += panMovement * Time.deltaTime; + 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(Transform cameraTransform) + private void HandleZooming(CinemachineCamera virtualCamera) { - float zoomDelta = inputHandler.GetZoomDelta(); - if (Mathf.Abs(zoomDelta) <= 0.1f) return; + if (inputHandler.IsZoomActive()) + { + // Ctrl + 좌클릭으로 줌 + Vector2 lookDelta = inputHandler.GetLookDelta(); + if (lookDelta.sqrMagnitude < float.Epsilon) return; - cameraTransform.position += cameraTransform.forward * zoomDelta * zoomSpeed * Time.deltaTime; + 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(Transform cameraTransform) + private void HandleOrbiting(CinemachineCamera virtualCamera) { if (!inputHandler.IsOrbitActive()) return; - if (inputHandler.IsOrbitStarting()) - { - UpdateOrbitTarget(cameraTransform); - } - - PerformOrbitRotation(cameraTransform); - } - - private void UpdateOrbitTarget(Transform cameraTransform) - { - if (Camera.main == null) - { - targetPosition = cameraTransform.position + cameraTransform.forward * 10f; - return; - } - - Ray ray = Camera.main.ScreenPointToRay(inputHandler.GetMousePosition()); - targetPosition = Physics.Raycast(ray, out RaycastHit hit) - ? hit.point - : cameraTransform.position + cameraTransform.forward * 10f; - } - - private void PerformOrbitRotation(Transform cameraTransform) - { Vector2 orbitDelta = inputHandler.GetLookDelta(); if (orbitDelta.sqrMagnitude < float.Epsilon) return; - Vector3 directionToTarget = targetPosition - cameraTransform.position; - float distance = directionToTarget.magnitude; + 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; + } - // 수평 회전 - cameraTransform.RotateAround(targetPosition, Vector3.up, orbitDelta.x * orbitSpeed); + private void RestoreInitialCameraState() + { + if (!isInitialStateSet || currentCamera == null) return; + + currentCamera.transform.position = initialPosition; + currentCamera.transform.rotation = initialRotation; - // 수직 회전 제한 추가 - Vector3 right = Vector3.Cross(Vector3.up, directionToTarget.normalized); - float currentAngle = Vector3.Angle(Vector3.up, directionToTarget); - float newAngle = currentAngle - orbitDelta.y * orbitSpeed; - - if (newAngle > 5f && newAngle < 175f) - { - cameraTransform.RotateAround(targetPosition, right, -orbitDelta.y * orbitSpeed); - } - - // 거리 유지 - cameraTransform.position = targetPosition - cameraTransform.forward * distance; + // 회전 중심점 초기화 + rotationCenter = new Vector3(0f, initialPosition.y, 0f); } #endregion #region Camera Management - public void SwitchToCamera(int index) + public void Set(int index) { - if (!IsValidCameraIndex(index)) return; + if (cameraPresets == null || index < 0 || index >= cameraPresets.Count) return; var newPreset = cameraPresets[index]; - if (!newPreset.IsValid()) - { - Debug.LogWarning($"프리셋 {index}의 카메라가 설정되지 않았습니다."); - return; - } + if (!newPreset.IsValid()) return; var oldPreset = currentPreset; currentPreset = newPreset; UpdateCameraPriorities(newPreset.virtualCamera); - try - { - OnCameraChanged?.Invoke(oldPreset, newPreset); - } - catch (System.Exception e) - { - Debug.LogError($"카메라 변경 이벤트 처리 중 오류 발생: {e.Message}"); - } - - Debug.Log($"카메라 전환: {newPreset.presetName}"); + OnCameraChanged?.Invoke(oldPreset, newPreset); } - private bool IsValidCameraIndex(int index) => - cameraPresets != null && index >= 0 && index < cameraPresets.Count; - private void UpdateCameraPriorities(CinemachineCamera newCamera) { - if (newCamera == null) - { - Debug.LogError("새로운 카메라가 null입니다."); - return; - } + if (newCamera == null) return; if (currentCamera != null) { @@ -492,16 +480,11 @@ public class CameraManager : MonoBehaviour #region Hotkey Management public void StartRecordingHotkey(int presetIndex) { - if (!IsValidCameraIndex(presetIndex)) return; + if (cameraPresets == null || presetIndex < 0 || presetIndex >= cameraPresets.Count) return; var preset = cameraPresets[presetIndex]; - if (!preset.IsValid()) - { - Debug.LogWarning($"프리셋 {presetIndex}가 유효하지 않습니다."); - return; - } + if (!preset.IsValid()) return; - // 다른 프리셋의 레코딩 중지 foreach (var otherPreset in cameraPresets) { if (otherPreset?.hotkey?.isRecording == true) @@ -515,7 +498,7 @@ public class CameraManager : MonoBehaviour public void StopRecordingHotkey(int presetIndex) { - if (!IsValidCameraIndex(presetIndex)) return; + if (cameraPresets == null || presetIndex < 0 || presetIndex >= cameraPresets.Count) return; var preset = cameraPresets[presetIndex]; if (preset?.hotkey?.isRecording == true) diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/IController.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/IController.cs new file mode 100644 index 00000000..d78bfe82 --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/IController.cs @@ -0,0 +1,6 @@ +using UnityEngine; + +public interface IController +{ + void Set(int index); +} \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/IController.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/IController.cs.meta new file mode 100644 index 00000000..aff27eeb --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/IController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8ce235b59de5fa1479db9cbff684d71a \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/InputHandler.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/InputHandler.cs index fdec533b..518aa10b 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/InputHandler.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/InputHandler.cs @@ -1,63 +1,47 @@ using UnityEngine; -using UnityEngine.InputSystem; public class InputHandler : MonoBehaviour { - private Vector2 lookDelta; - private float zoomDelta; - private Vector2 mousePosition; + private Vector2 lastMousePosition; + private float lastScrollValue; private bool isRightMouseHeld; private bool isMiddleMouseHeld; - private bool isLeftMouseHeld; - private bool isAltHeld; - private bool wasOrbitActiveLastFrame; + private bool isOrbitActive; + private bool isZoomActive; - public void OnLook(InputValue value) + private void Update() { - lookDelta = value.Get(); + // 마우스 버튼 상태 업데이트 + 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 + 좌클릭으로 줌 + + // 마우스 위치 업데이트 + lastMousePosition = Input.mousePosition; + lastScrollValue = Input.mouseScrollDelta.y; } - public void OnZoom(InputValue value) - { - zoomDelta = value.Get(); - } - - public void OnMousePosition(InputValue value) - { - mousePosition = value.Get(); - } - - public void OnRightMouse(InputValue value) - { - isRightMouseHeld = value.isPressed; - } - - public void OnMiddleMouse(InputValue value) - { - isMiddleMouseHeld = value.isPressed; - } - - public void OnLeftMouse(InputValue value) - { - isLeftMouseHeld = value.isPressed; - } - - public void OnAlt(InputValue value) - { - isAltHeld = value.isPressed; - } - - public Vector2 GetLookDelta() => lookDelta; - public float GetZoomDelta() => zoomDelta; - public Vector2 GetMousePosition() => mousePosition; public bool IsRightMouseHeld() => isRightMouseHeld; public bool IsMiddleMouseHeld() => isMiddleMouseHeld; - public bool IsOrbitActive() => isAltHeld && isLeftMouseHeld; - public bool IsOrbitStarting() + public bool IsOrbitActive() => isOrbitActive; + public bool IsZoomActive() => isZoomActive; + + public Vector2 GetLookDelta() { - bool isOrbitActive = IsOrbitActive(); - bool isStarting = isOrbitActive && !wasOrbitActiveLastFrame; - wasOrbitActiveLastFrame = isOrbitActive; - return isStarting; + return new Vector2( + Input.GetAxis("Mouse X"), + Input.GetAxis("Mouse Y") + ); + } + + public float GetZoomDelta() + { + return Input.mouseScrollDelta.y; + } + + public Vector2 GetMousePosition() + { + return Input.mousePosition; } } \ No newline at end of file