ADD : 카메라 스크립트 파일 업로드

This commit is contained in:
KINDNICK 2025-05-10 10:53:40 +09:00
parent cd6c31f54a
commit 01c3eb04e8
4 changed files with 232 additions and 257 deletions

View File

@ -1,10 +1,10 @@
using UnityEngine; using UnityEngine;
using Unity.Cinemachine;
using System.Collections.Generic; using System.Collections.Generic;
using UnityRawInput; using UnityRawInput;
using System.Linq; using System.Linq;
using Unity.Cinemachine;
public class CameraManager : MonoBehaviour public class CameraManager : MonoBehaviour, IController
{ {
#region Classes #region Classes
private static class KeyMapping private static class KeyMapping
@ -66,7 +66,6 @@ public class CameraManager : MonoBehaviour
return; return;
} }
// 현재 눌린 키 확인
foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode))) foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode)))
{ {
if (Input.GetKeyDown(keyCode) && KeyMapping.TryGetRawKey(keyCode, out RawKey rawKey)) 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)); bool allKeysReleased = rawKeys.Any() && rawKeys.All(key => !Input.GetKey(KeyMapping.TryGetKeyCode(key, out KeyCode keyCode) ? keyCode : KeyCode.None));
if (allKeysReleased) if (allKeysReleased)
@ -89,59 +87,34 @@ public class CameraManager : MonoBehaviour
public void InitializeUnityKeys() public void InitializeUnityKeys()
{ {
try unityKeys.Clear();
if (rawKeys == null || !rawKeys.Any()) return;
foreach (var rawKey in rawKeys)
{ {
unityKeys ??= new List<KeyCode>(); if (KeyMapping.TryGetKeyCode(rawKey, out KeyCode keyCode) && keyCode != KeyCode.None)
unityKeys.Clear();
if (rawKeys == null || !rawKeys.Any()) return;
foreach (var rawKey in rawKeys)
{ {
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() public bool IsTriggered()
{ {
if (isRecording) return false; 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; return unityKeys.All(key => Input.GetKey(key));
// 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 false;
} }
public override string ToString() => public override string ToString() =>
@ -176,15 +149,22 @@ public class CameraManager : MonoBehaviour
[SerializeField] public List<CameraPreset> cameraPresets = new List<CameraPreset>(); [SerializeField] public List<CameraPreset> cameraPresets = new List<CameraPreset>();
[Header("Camera Control Settings")] [Header("Camera Control Settings")]
[SerializeField, Range(0.1f, 10f)] private float rotationSensitivity = 2f; private float rotationSensitivity = 2f;
[SerializeField, Range(0.1f, 20f)] private float panSpeed = 10f; private float panSpeed = 0.02f;
[SerializeField, Range(0.1f, 20f)] private float zoomSpeed = 10f; private float zoomSpeed = 0.1f;
[SerializeField, Range(0.1f, 10f)] private float orbitSpeed = 5f; private float orbitSpeed = 10f;
private CinemachineCamera currentCamera; private CinemachineCamera currentCamera;
private InputHandler inputHandler; private InputHandler inputHandler;
private Vector3 targetPosition;
private CameraPreset currentPreset; 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 #endregion
#region Properties #region Properties
@ -195,27 +175,17 @@ public class CameraManager : MonoBehaviour
#region Unity Messages #region Unity Messages
private void Awake() private void Awake()
{ {
try InitializeInputHandler();
{ InitializeRawInput();
InitializeInputHandler(); InitializeCameraPresets();
InitializeRawInput();
InitializeCameraPresets();
}
catch (System.Exception e)
{
Debug.LogError($"초기화 중 오류 발생: {e.Message}");
}
} }
private void OnDestroy() private void OnDestroy()
{ {
try if (RawInput.IsRunning)
{ {
CleanupRawInput(); RawInput.OnKeyDown -= HandleRawKeyDown;
} RawInput.Stop();
catch (System.Exception e)
{
Debug.LogError($"정리 중 오류 발생: {e.Message}");
} }
} }
@ -223,16 +193,9 @@ public class CameraManager : MonoBehaviour
{ {
if (!IsValidSetup) return; if (!IsValidSetup) return;
try UpdateHotkeyRecording();
{ HandleCameraControls();
UpdateHotkeyRecording(); HandleHotkeys();
HandleCameraControls();
HandleUnityHotkeys();
}
catch (System.Exception e)
{
Debug.LogError($"카메라 제어 중 오류 발생: {e.Message}");
}
} }
private void UpdateHotkeyRecording() private void UpdateHotkeyRecording()
@ -254,7 +217,6 @@ public class CameraManager : MonoBehaviour
if (inputHandler == null) if (inputHandler == null)
{ {
inputHandler = gameObject.AddComponent<InputHandler>(); inputHandler = gameObject.AddComponent<InputHandler>();
Debug.Log("InputHandler 컴포넌트가 자동으로 추가되었습니다.");
} }
} }
@ -265,7 +227,7 @@ public class CameraManager : MonoBehaviour
RawInput.Start(); RawInput.Start();
RawInput.WorkInBackground = true; RawInput.WorkInBackground = true;
} }
RawInput.OnKeyDown += HandleKeyDown; RawInput.OnKeyDown += HandleRawKeyDown;
} }
private void InitializeCameraPresets() private void InitializeCameraPresets()
@ -273,12 +235,10 @@ public class CameraManager : MonoBehaviour
if (cameraPresets == null) if (cameraPresets == null)
{ {
cameraPresets = new List<CameraPreset>(); cameraPresets = new List<CameraPreset>();
Debug.LogWarning("카메라 프리셋 리스트가 null이어서 새로 생성되었습니다.");
} }
if (!cameraPresets.Any()) if (!cameraPresets.Any())
{ {
Debug.LogWarning("카메라 프리셋이 설정되어 있지 않습니다.");
return; return;
} }
@ -287,57 +247,47 @@ public class CameraManager : MonoBehaviour
preset.hotkey.InitializeUnityKeys(); preset.hotkey.InitializeUnityKeys();
} }
SwitchToCamera(0); Set(0);
}
// 초기 카메라 상태 저장
private void CleanupRawInput() if (currentCamera != null && !isInitialStateSet)
{
if (RawInput.IsRunning)
{ {
RawInput.OnKeyDown -= HandleKeyDown; initialPosition = currentCamera.transform.position;
RawInput.Stop(); initialRotation = currentCamera.transform.rotation;
isInitialStateSet = true;
} }
} }
#endregion #endregion
#region Input Handling #region Input Handling
private void HandleKeyDown(RawKey key) private void HandleRawKeyDown(RawKey key)
{ {
if (key == default(RawKey)) return; if (key == default(RawKey)) return;
TryActivatePresetByInput(preset => 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; if (preset?.hotkey == null) return false;
return preset.hotkey.IsTriggered(); 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<CameraPreset, bool> predicate) private void TryActivatePresetByInput(System.Func<CameraPreset, bool> predicate)
{ {
try var matchingPreset = cameraPresets?.FirstOrDefault(predicate);
if (matchingPreset != null)
{ {
var matchingPreset = cameraPresets? Set(cameraPresets.IndexOf(matchingPreset));
.FirstOrDefault(predicate);
if (matchingPreset != null)
{
SwitchToCamera(cameraPresets.IndexOf(matchingPreset));
}
}
catch (System.Exception e)
{
Debug.LogError($"프리셋 활성화 중 오류 발생: {e.Message}");
} }
} }
#endregion #endregion
@ -347,137 +297,175 @@ public class CameraManager : MonoBehaviour
{ {
if (!IsValidSetup) return; if (!IsValidSetup) return;
Transform cameraTransform = currentCamera.transform; var virtualCamera = currentCamera;
if (cameraTransform == null) return; if (virtualCamera == null) return;
HandleRotation(cameraTransform); // Alt+Q로 초기 위치로 복원
HandlePanning(cameraTransform); if (Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.Q))
HandleZooming(cameraTransform); {
HandleOrbiting(cameraTransform); 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(); Vector2 lookDelta = inputHandler.GetLookDelta();
if (lookDelta.sqrMagnitude < float.Epsilon) return; if (lookDelta.sqrMagnitude < float.Epsilon) return;
var rotation = new Vector3(-lookDelta.y, lookDelta.x, 0f) * rotationSensitivity; // 현재 회전값을 오일러 각도로 가져오기
cameraTransform.rotation *= Quaternion.Euler(rotation); 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; if (!inputHandler.IsMiddleMouseHeld()) return;
Vector2 panDelta = inputHandler.GetLookDelta(); Vector2 panDelta = inputHandler.GetLookDelta();
if (panDelta.sqrMagnitude < float.Epsilon) return; if (panDelta.sqrMagnitude < float.Epsilon) return;
Vector3 panMovement = Transform cameraTransform = virtualCamera.transform;
cameraTransform.right * -panDelta.x * panSpeed +
cameraTransform.up * -panDelta.y * panSpeed; // 이동 적용 (카메라의 right와 up 방향으로 이동)
cameraTransform.position += panMovement * Time.deltaTime; 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 (inputHandler.IsZoomActive())
if (Mathf.Abs(zoomDelta) <= 0.1f) return; {
// 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.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(); Vector2 orbitDelta = inputHandler.GetLookDelta();
if (orbitDelta.sqrMagnitude < float.Epsilon) return; if (orbitDelta.sqrMagnitude < float.Epsilon) return;
Vector3 directionToTarget = targetPosition - cameraTransform.position; Transform cameraTransform = virtualCamera.transform;
float distance = directionToTarget.magnitude;
// 현재 회전값을 오일러 각도로 가져오기
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;
}
// 수평 회전 private void RestoreInitialCameraState()
cameraTransform.RotateAround(targetPosition, Vector3.up, orbitDelta.x * orbitSpeed); {
if (!isInitialStateSet || currentCamera == null) return;
currentCamera.transform.position = initialPosition;
currentCamera.transform.rotation = initialRotation;
// 수직 회전 제한 추가 // 회전 중심점 초기화
Vector3 right = Vector3.Cross(Vector3.up, directionToTarget.normalized); rotationCenter = new Vector3(0f, initialPosition.y, 0f);
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;
} }
#endregion #endregion
#region Camera Management #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]; var newPreset = cameraPresets[index];
if (!newPreset.IsValid()) if (!newPreset.IsValid()) return;
{
Debug.LogWarning($"프리셋 {index}의 카메라가 설정되지 않았습니다.");
return;
}
var oldPreset = currentPreset; var oldPreset = currentPreset;
currentPreset = newPreset; currentPreset = newPreset;
UpdateCameraPriorities(newPreset.virtualCamera); UpdateCameraPriorities(newPreset.virtualCamera);
try OnCameraChanged?.Invoke(oldPreset, newPreset);
{
OnCameraChanged?.Invoke(oldPreset, newPreset);
}
catch (System.Exception e)
{
Debug.LogError($"카메라 변경 이벤트 처리 중 오류 발생: {e.Message}");
}
Debug.Log($"카메라 전환: {newPreset.presetName}");
} }
private bool IsValidCameraIndex(int index) =>
cameraPresets != null && index >= 0 && index < cameraPresets.Count;
private void UpdateCameraPriorities(CinemachineCamera newCamera) private void UpdateCameraPriorities(CinemachineCamera newCamera)
{ {
if (newCamera == null) if (newCamera == null) return;
{
Debug.LogError("새로운 카메라가 null입니다.");
return;
}
if (currentCamera != null) if (currentCamera != null)
{ {
@ -492,16 +480,11 @@ public class CameraManager : MonoBehaviour
#region Hotkey Management #region Hotkey Management
public void StartRecordingHotkey(int presetIndex) public void StartRecordingHotkey(int presetIndex)
{ {
if (!IsValidCameraIndex(presetIndex)) return; if (cameraPresets == null || presetIndex < 0 || presetIndex >= cameraPresets.Count) return;
var preset = cameraPresets[presetIndex]; var preset = cameraPresets[presetIndex];
if (!preset.IsValid()) if (!preset.IsValid()) return;
{
Debug.LogWarning($"프리셋 {presetIndex}가 유효하지 않습니다.");
return;
}
// 다른 프리셋의 레코딩 중지
foreach (var otherPreset in cameraPresets) foreach (var otherPreset in cameraPresets)
{ {
if (otherPreset?.hotkey?.isRecording == true) if (otherPreset?.hotkey?.isRecording == true)
@ -515,7 +498,7 @@ public class CameraManager : MonoBehaviour
public void StopRecordingHotkey(int presetIndex) public void StopRecordingHotkey(int presetIndex)
{ {
if (!IsValidCameraIndex(presetIndex)) return; if (cameraPresets == null || presetIndex < 0 || presetIndex >= cameraPresets.Count) return;
var preset = cameraPresets[presetIndex]; var preset = cameraPresets[presetIndex];
if (preset?.hotkey?.isRecording == true) if (preset?.hotkey?.isRecording == true)

View File

@ -0,0 +1,6 @@
using UnityEngine;
public interface IController
{
void Set(int index);
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8ce235b59de5fa1479db9cbff684d71a

View File

@ -1,63 +1,47 @@
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem;
public class InputHandler : MonoBehaviour public class InputHandler : MonoBehaviour
{ {
private Vector2 lookDelta; private Vector2 lastMousePosition;
private float zoomDelta; private float lastScrollValue;
private Vector2 mousePosition;
private bool isRightMouseHeld; private bool isRightMouseHeld;
private bool isMiddleMouseHeld; private bool isMiddleMouseHeld;
private bool isLeftMouseHeld; private bool isOrbitActive;
private bool isAltHeld; private bool isZoomActive;
private bool wasOrbitActiveLastFrame;
public void OnLook(InputValue value) private void Update()
{ {
lookDelta = value.Get<Vector2>(); // 마우스 버튼 상태 업데이트
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<float>();
}
public void OnMousePosition(InputValue value)
{
mousePosition = value.Get<Vector2>();
}
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 IsRightMouseHeld() => isRightMouseHeld;
public bool IsMiddleMouseHeld() => isMiddleMouseHeld; public bool IsMiddleMouseHeld() => isMiddleMouseHeld;
public bool IsOrbitActive() => isAltHeld && isLeftMouseHeld; public bool IsOrbitActive() => isOrbitActive;
public bool IsOrbitStarting() public bool IsZoomActive() => isZoomActive;
public Vector2 GetLookDelta()
{ {
bool isOrbitActive = IsOrbitActive(); return new Vector2(
bool isStarting = isOrbitActive && !wasOrbitActiveLastFrame; Input.GetAxis("Mouse X"),
wasOrbitActiveLastFrame = isOrbitActive; Input.GetAxis("Mouse Y")
return isStarting; );
}
public float GetZoomDelta()
{
return Input.mouseScrollDelta.y;
}
public Vector2 GetMousePosition()
{
return Input.mousePosition;
} }
} }