From 24c5145ff469386b1617a6700ec953018560ad0f Mon Sep 17 00:00:00 2001 From: "qsxft258@gmail.com" Date: Mon, 6 Oct 2025 18:22:07 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=EC=BB=A8=ED=8A=B8=EB=A1=A4=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/CameraControlSystem.cs | 676 ++++++++++-------- .../Controllers/CameraInfoUI.cs | 15 +- 2 files changed, 368 insertions(+), 323 deletions(-) diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraControlSystem.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraControlSystem.cs index 98c83851..898a2a2c 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraControlSystem.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraControlSystem.cs @@ -3,48 +3,56 @@ using Unity.Cinemachine; using UnityRawInput; using UnityEngine.Rendering; using System; +using System.Collections.Generic; +[DefaultExecutionOrder(2)] public class CameraControlSystem : MonoBehaviour { [Header("FOV Physics Settings")] - [SerializeField] private float[] fovForces = {80f, 200f, 400f}; // 포스 강도 - [SerializeField] private int currentForceIndex = 1; // 기본값: 200 + [SerializeField] private float fovForce = 400f; // 포스 강도 (고정값) - 2배 증가 [SerializeField] private float fovDamping = 6f; // 감쇠력 - [SerializeField] private float fovMaxVelocity = 100f; // 최대 속도 + [SerializeField] private float fovMaxVelocity = 200f; // 최대 속도 - 2배 증가 [SerializeField] private float minFOV = 0.1f; [SerializeField] private float maxFOV = 60f; - + [Header("DOF Physics Settings")] - [SerializeField] private float[] dofForces = {10f, 20f, 50f}; // DOF 포스 강도 - [SerializeField] private int currentDofForceIndex = 1; // 기본값: 0.5 - [SerializeField] private float dofDamping = 6f; // DOF 감쇠력 - [SerializeField] private float dofMaxVelocity = 5f; // DOF 최대 속도 - [SerializeField] private float minDOF = 0.1f; - [SerializeField] private float maxDOF = 50f; - + [SerializeField] private float dofStep = 0.01f; // DOF Focal Length 증가/감소 단위 + [SerializeField] private float minDOF = 0.01f; // Focal Length 최소값 + [SerializeField] private float maxDOF = 1f; // Focal Length 최대값 + [Header("Screenshot Settings")] [SerializeField] private string screenshotPath = "Screenshots"; [SerializeField] private int screenshotResolutionMultiplier = 1; - - + + private CameraManager cameraManager; private CinemachineCamera currentVirtualCamera; private CameraInfoUI cameraInfoUI; - + // FOV 물리 시스템 변수들 private float fovVelocity = 0f; // 현재 FOV 변화 속도 private float targetFOV = 60f; // 목표 FOV private bool isApplyingForce = false; // 현재 포스가 적용 중인지 - - // DOF 물리 시스템 변수들 - private float dofVelocity = 0f; // 현재 DOF 변화 속도 - private float targetDOF = 1f; // 목표 DOF - private bool isApplyingDofForce = false; // 현재 DOF 포스가 적용 중인지 - + + // DOF 값 변수 + private float currentDOFValue = 0.3f; // 현재 DOF Focal Length 값 + // Beautify Volume Override 참조 private object beautifyOverride; private bool isDOFEnabled = false; // DOF 활성화 상태 (기본값: 꺼짐) - + + // F13/F14 제어 모드 전환 (true: FOV, false: DOF) + private bool isFOVMode = true; + + // F18 더블클릭 감지용 + private float lastF18ClickTime = 0f; + private const float doubleClickThreshold = 0.3f; + + // DOF 타겟팅용 + private Transform currentDOFTarget = null; + private List availableDOFTargets = new List(); + private int currentTargetIndex = 0; + private void Awake() { // 스크린샷 폴더 생성 @@ -53,7 +61,7 @@ public class CameraControlSystem : MonoBehaviour System.IO.Directory.CreateDirectory(screenshotPath); } } - + private void Start() { cameraManager = FindObjectOfType(); @@ -62,18 +70,21 @@ public class CameraControlSystem : MonoBehaviour cameraManager.OnCameraChanged += OnCameraChanged; UpdateCurrentCamera(); } - + // CameraInfoUI 생성 GameObject uiGO = new GameObject("CameraInfoUI"); cameraInfoUI = uiGO.AddComponent(); - + // Beautify 컴포넌트 찾기 Debug.Log("[CameraControlSystem] Beautify 초기화 시작"); InitializeBeautify(); - + + // DOF 타겟 찾기 + FindDOFTargets(); + InitializeRawInput(); } - + private void OnDestroy() { // RawInput 정리 @@ -89,14 +100,14 @@ public class CameraControlSystem : MonoBehaviour // RawInput 정리 실패 무시 } } - + // CameraManager 이벤트 해제 if (cameraManager != null) { cameraManager.OnCameraChanged -= OnCameraChanged; } } - + private void InitializeRawInput() { try @@ -107,7 +118,7 @@ public class CameraControlSystem : MonoBehaviour RawInput.WorkInBackground = true; // 백그라운드에서도 키 입력 감지 RawInput.InterceptMessages = false; // 다른 앱으로 키 메시지 전달 } - + // 이벤트 중복 등록 방지 RawInput.OnKeyDown -= HandleRawKeyDown; RawInput.OnKeyDown += HandleRawKeyDown; @@ -117,26 +128,29 @@ public class CameraControlSystem : MonoBehaviour // RawInput 실패 시 Unity Input으로 대체 } } - + private void Update() { HandleFOVControl(); UpdateFOVPhysics(); UpdateDOFPhysics(); + UpdateDOFTargetTracking(); } - + private void HandleRawKeyDown(RawKey key) { switch (key) { case RawKey.F13: - IncreaseFOV(); + if (isFOVMode) IncreaseFOV(); + else IncreaseDOF(); break; case RawKey.F14: - DecreaseFOV(); + if (isFOVMode) DecreaseFOV(); + else DecreaseDOF(); break; case RawKey.F15: - CycleFOVSpeed(); + ToggleControlMode(); break; case RawKey.F16: TakeScreenshot(); @@ -145,37 +159,27 @@ public class CameraControlSystem : MonoBehaviour ToggleCameraUI(); break; case RawKey.F18: - ToggleDOF(); - break; - case RawKey.F19: - Debug.Log("[CameraControlSystem] F19 키 입력 감지 (RawInput)"); - CycleDOFSpeed(); - break; - case RawKey.F20: - Debug.Log("[CameraControlSystem] F20 키 입력 감지 (RawInput)"); - IncreaseDOF(); - break; - case RawKey.F21: - Debug.Log("[CameraControlSystem] F21 키 입력 감지 (RawInput)"); - DecreaseDOF(); + HandleF18Click(); break; } } - + private void HandleFOVControl() { // Unity Input으로도 처리 (RawInput과 병행하여 안정성 확보) if (Input.GetKeyDown(KeyCode.F13)) { - IncreaseFOV(); + if (isFOVMode) IncreaseFOV(); + else IncreaseDOF(); } else if (Input.GetKeyDown(KeyCode.F14)) { - DecreaseFOV(); + if (isFOVMode) DecreaseFOV(); + else DecreaseDOF(); } else if (Input.GetKeyDown(KeyCode.F15)) { - CycleFOVSpeed(); + ToggleControlMode(); } else if (Input.GetKeyDown(KeyCode.F16)) { @@ -187,32 +191,15 @@ public class CameraControlSystem : MonoBehaviour } else if (Input.GetKeyDown(KeyCode.F18)) { - ToggleDOF(); - } - - // DOF 제어 (F19-F21) - if (Input.GetKeyDown(KeyCode.F19)) - { - Debug.Log("[CameraControlSystem] F19 키 입력 감지 (Unity Input)"); - CycleDOFSpeed(); - } - else if (Input.GetKeyDown(KeyCode.F20)) - { - Debug.Log("[CameraControlSystem] F20 키 입력 감지 (Unity Input)"); - IncreaseDOF(); - } - else if (Input.GetKeyDown(KeyCode.F21)) - { - Debug.Log("[CameraControlSystem] F21 키 입력 감지 (Unity Input)"); - DecreaseDOF(); + HandleF18Click(); } } - + private void OnCameraChanged(CameraManager.CameraPreset oldPreset, CameraManager.CameraPreset newPreset) { UpdateCurrentCamera(); } - + private void UpdateCurrentCamera() { if (cameraManager?.CurrentPreset?.virtualCamera != null) @@ -220,74 +207,67 @@ public class CameraControlSystem : MonoBehaviour currentVirtualCamera = cameraManager.CurrentPreset.virtualCamera; } } - + private void IncreaseFOV() { if (currentVirtualCamera == null) return; - + // 현재 FOV를 targetFOV로 초기화 (처음 호출 시) if (!isApplyingForce) { targetFOV = currentVirtualCamera.Lens.FieldOfView; isApplyingForce = true; } - - float currentForce = fovForces[currentForceIndex]; - ApplyFOVForce(currentForce); // 양의 포스 적용 + + ApplyFOVForce(fovForce); // 양의 포스 적용 } - + private void DecreaseFOV() { if (currentVirtualCamera == null) return; - + // 현재 FOV를 targetFOV로 초기화 (처음 호출 시) if (!isApplyingForce) { targetFOV = currentVirtualCamera.Lens.FieldOfView; isApplyingForce = true; } - - float currentForce = fovForces[currentForceIndex]; - ApplyFOVForce(-currentForce); // 음의 포스 적용 + + ApplyFOVForce(-fovForce); // 음의 포스 적용 } - - private void CycleFOVSpeed() - { - currentForceIndex = (currentForceIndex + 1) % fovForces.Length; - } - + private void ApplyFOVForce(float force) { // 포스를 속도에 더함 (가속도 = 포스, 질량 = 1로 가정) fovVelocity += force * Time.deltaTime; - + // 최대 속도 제한 fovVelocity = Mathf.Clamp(fovVelocity, -fovMaxVelocity, fovMaxVelocity); } - + private void UpdateFOVPhysics() { if (currentVirtualCamera == null) return; - + var lens = currentVirtualCamera.Lens; float currentFOV = lens.FieldOfView; - + // 더 부드러운 감쇠력 적용 (지수적 감쇠) float dampingFactor = Mathf.Exp(-fovDamping * Time.deltaTime); fovVelocity *= dampingFactor; - + // 속도가 거의 0에 가까우면 완전히 정지 (더 작은 임계값) if (Mathf.Abs(fovVelocity) < 0.05f) { fovVelocity = 0f; isApplyingForce = false; } - + // 속도가 있을 때만 FOV 업데이트 if (Mathf.Abs(fovVelocity) > 0.001f) { float newFOV = currentFOV + (fovVelocity * Time.deltaTime); - + // FOV 범위 제한 및 경계에서 부드러운 반발 if (newFOV <= minFOV) { @@ -299,18 +279,18 @@ public class CameraControlSystem : MonoBehaviour newFOV = maxFOV; fovVelocity = Mathf.Min(0f, fovVelocity * 0.3f); // 부드러운 반발 } - + lens.FieldOfView = newFOV; currentVirtualCamera.Lens = lens; - + targetFOV = newFOV; } } - + private void InitializeBeautify() { Debug.Log("[CameraControlSystem] Volume 생성 또는 찾기 시작..."); - + // 같은 게임오브젝트에서 Volume 컴포넌트 찾기 또는 생성 Volume volume = GetComponent(); if (volume == null) @@ -319,11 +299,11 @@ public class CameraControlSystem : MonoBehaviour volume = gameObject.AddComponent(); volume.priority = 10f; // 우선순위 설정 volume.isGlobal = true; // 전역 볼륨으로 설정 - + // VolumeProfile 생성 volume.sharedProfile = ScriptableObject.CreateInstance(); volume.sharedProfile.name = "CameraControl_BeautifyProfile"; - + Debug.Log("[CameraControlSystem] Volume 생성 완료 (우선순위: 10)"); } else @@ -335,17 +315,17 @@ public class CameraControlSystem : MonoBehaviour Debug.Log("[CameraControlSystem] 기존 Volume의 우선순위를 10으로 설정"); } } - + if (volume != null) { Debug.Log($"[CameraControlSystem] Volume 발견: {volume.name}"); if (volume.sharedProfile != null) { Debug.Log($"[CameraControlSystem] Profile: {volume.sharedProfile.name}"); - + // 정확한 어셈블리명으로 Beautify 타입 찾기 System.Type beautifyType = System.Type.GetType("Beautify.Universal.Beautify, Unity.RenderPipelines.Universal.Runtime"); - + if (beautifyType != null) { Debug.Log($"[CameraControlSystem] Beautify 타입 발견: {beautifyType.FullName}"); @@ -354,21 +334,21 @@ public class CameraControlSystem : MonoBehaviour { Debug.LogError("[CameraControlSystem] Beautify 타입을 찾을 수 없음 (어셈블리명 포함)"); } - + if (beautifyType != null) { try { // VolumeProfile.TryGet을 올바른 시그니처로 호출 Debug.Log("[CameraControlSystem] VolumeProfile.TryGet 직접 호출"); - + // out 파라미터를 위한 배열 object[] parameters = { null }; - + // TryGet(out T component) 메서드 찾기 var methods = volume.sharedProfile.GetType().GetMethods(); System.Reflection.MethodInfo tryGetMethod = null; - + foreach (var method in methods) { if (method.Name == "TryGet" && method.IsGenericMethodDefinition) @@ -382,15 +362,15 @@ public class CameraControlSystem : MonoBehaviour } } } - + if (tryGetMethod != null) { var genericMethod = tryGetMethod.MakeGenericMethod(beautifyType); - + // Invoke 호출 object[] args = new object[1]; bool result = (bool)genericMethod.Invoke(volume.sharedProfile, args); - + if (result && args[0] != null) { beautifyOverride = args[0]; @@ -429,7 +409,7 @@ public class CameraControlSystem : MonoBehaviour { Debug.LogError("[CameraControlSystem] 같은 게임오브젝트에 Volume이 없음"); } - + // 실패한 경우 BeautifySettings 방식 시도 (백업) try { @@ -437,33 +417,33 @@ public class CameraControlSystem : MonoBehaviour if (beautifySettingsType != null) { Debug.Log("[CameraControlSystem] BeautifySettings 타입 발견"); - + var sharedSettingsProperty = beautifySettingsType.GetProperty("sharedSettings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); if (sharedSettingsProperty != null) { beautifyOverride = sharedSettingsProperty.GetValue(null); - + if (beautifyOverride != null) { Debug.Log($"[CameraControlSystem] Beautify Override 발견: {beautifyOverride.GetType().Name}"); - + // depthOfFieldDistance 필드 확인 var dofDistanceField = beautifyOverride.GetType().GetField("depthOfFieldDistance"); if (dofDistanceField != null) { Debug.Log($"[CameraControlSystem] depthOfFieldDistance 필드 발견: {dofDistanceField.FieldType}"); - + var fieldValue = dofDistanceField.GetValue(beautifyOverride); if (fieldValue != null) { Debug.Log($"[CameraControlSystem] 필드 값 타입: {fieldValue.GetType()}"); - + // FloatParameter 타입의 value 속성 가져오기 var valueProperty = fieldValue.GetType().GetProperty("value"); if (valueProperty != null && valueProperty.PropertyType == typeof(float)) { - targetDOF = (float)valueProperty.GetValue(fieldValue); - Debug.Log($"[CameraControlSystem] 초기 DOF 거리: {targetDOF}"); + currentDOFValue = (float)valueProperty.GetValue(fieldValue); + Debug.Log($"[CameraControlSystem] 초기 DOF 거리: {currentDOFValue}"); } else { @@ -478,7 +458,7 @@ public class CameraControlSystem : MonoBehaviour else { Debug.LogWarning("[CameraControlSystem] depthOfFieldDistance 필드를 찾을 수 없음"); - + // 모든 필드 출력해보기 var allFields = beautifyOverride.GetType().GetFields(); Debug.Log($"[CameraControlSystem] 사용 가능한 모든 필드 ({allFields.Length}개):"); @@ -530,143 +510,44 @@ public class CameraControlSystem : MonoBehaviour Debug.LogError($"[CameraControlSystem] Beautify 초기화 실패: {ex.Message}"); } } - + // DOF 제어 메서드들 private void IncreaseDOF() { - Debug.Log("[CameraControlSystem] IncreaseDOF() 호출됨"); - if (beautifyOverride == null) - { - Debug.LogWarning("[CameraControlSystem] beautifyOverride가 null이어서 IncreaseDOF() 중단됨"); - return; - } - - if (!isDOFEnabled) - { - Debug.Log("[CameraControlSystem] DOF가 비활성화되어 있어서 조작 불가"); - return; - } - - // 현재 DOF를 targetDOF로 초기화 (처음 호출 시) - if (!isApplyingDofForce) - { - var currentDOF = GetCurrentDOFValue(); - if (currentDOF.HasValue) - { - targetDOF = currentDOF.Value; - } - isApplyingDofForce = true; - } - - float currentForce = dofForces[currentDofForceIndex]; - ApplyDOFForce(currentForce); // 양의 포스 적용 + if (beautifyOverride == null || !isDOFEnabled) return; + + currentDOFValue = Mathf.Clamp(currentDOFValue + dofStep, minDOF, maxDOF); + SetDOFValue(currentDOFValue); + Debug.Log($"[CameraControlSystem] DOF 증가: {currentDOFValue:F3}"); } - + private void DecreaseDOF() { - Debug.Log("[CameraControlSystem] DecreaseDOF() 호출됨"); - if (beautifyOverride == null) - { - Debug.LogWarning("[CameraControlSystem] beautifyOverride가 null이어서 DecreaseDOF() 중단됨"); - return; - } - - if (!isDOFEnabled) - { - Debug.Log("[CameraControlSystem] DOF가 비활성화되어 있어서 조작 불가"); - return; - } - - // 현재 DOF를 targetDOF로 초기화 (처음 호출 시) - if (!isApplyingDofForce) - { - Debug.Log("[CameraControlSystem] 현재 DOF 값 가져오는 중..."); - var currentDOF = GetCurrentDOFValue(); - if (currentDOF.HasValue) - { - targetDOF = currentDOF.Value; - Debug.Log($"[CameraControlSystem] 현재 DOF 값: {currentDOF.Value}"); - } - else - { - Debug.LogWarning("[CameraControlSystem] 현재 DOF 값을 가져올 수 없음"); - } - isApplyingDofForce = true; - } - - float currentForce = dofForces[currentDofForceIndex]; - ApplyDOFForce(-currentForce); // 음의 포스 적용 + if (beautifyOverride == null || !isDOFEnabled) return; + + currentDOFValue = Mathf.Clamp(currentDOFValue - dofStep, minDOF, maxDOF); + SetDOFValue(currentDOFValue); + Debug.Log($"[CameraControlSystem] DOF 감소: {currentDOFValue:F3}"); } - - private void CycleDOFSpeed() - { - currentDofForceIndex = (currentDofForceIndex + 1) % dofForces.Length; - } - - private void ApplyDOFForce(float force) - { - // 포스를 속도에 더함 (가속도 = 포스, 질량 = 1로 가정) - dofVelocity += force * Time.deltaTime; - - // 최대 속도 제한 - dofVelocity = Mathf.Clamp(dofVelocity, -dofMaxVelocity, dofMaxVelocity); - } - + private void UpdateDOFPhysics() { - if (beautifyOverride == null || !isDOFEnabled) return; - - var currentDOF = GetCurrentDOFValue(); - if (!currentDOF.HasValue) return; - - // 더 부드러운 감쇠력 적용 (지수적 감쇠) - float dampingFactor = Mathf.Exp(-dofDamping * Time.deltaTime); - dofVelocity *= dampingFactor; - - // 속도가 거의 0에 가까우면 완전히 정지 (더 작은 임계값) - if (Mathf.Abs(dofVelocity) < 0.01f) - { - dofVelocity = 0f; - isApplyingDofForce = false; - } - - // 속도가 있을 때만 DOF 업데이트 - if (Mathf.Abs(dofVelocity) > 0.001f) - { - float newDOF = currentDOF.Value + (dofVelocity * Time.deltaTime); - - // DOF 범위 제한 및 경계에서 부드러운 반발 - if (newDOF <= minDOF) - { - newDOF = minDOF; - dofVelocity = Mathf.Max(0f, dofVelocity * 0.3f); // 부드러운 반발 - } - else if (newDOF >= maxDOF) - { - newDOF = maxDOF; - dofVelocity = Mathf.Min(0f, dofVelocity * 0.3f); // 부드러운 반발 - } - - SetDOFValue(newDOF); - targetDOF = newDOF; - } + // 더 이상 물리 시스템 사용 안 함 } - - // DOF 값을 가져오는 헬퍼 메서드 + + // DOF Focal Length 값을 가져오는 헬퍼 메서드 private float? GetCurrentDOFValue() { if (beautifyOverride == null) return null; - + string[] possibleFieldNames = { - "depthOfFieldDistance", - "dofDistance", - "focusDistance", - "depthOfFieldFocusDistance", - "distance" + "depthOfFieldFocalLength", + "focalLength", + "dofFocalLength" }; - + var overrideType = beautifyOverride.GetType(); - + foreach (string fieldName in possibleFieldNames) { var dofField = overrideType.GetField(fieldName); @@ -689,29 +570,27 @@ public class CameraControlSystem : MonoBehaviour } } } - + return null; } - - // DOF 값을 설정하는 헬퍼 메서드 + + // DOF Focal Length 값을 설정하는 헬퍼 메서드 private void SetDOFValue(float newValue) { - if (beautifyOverride == null) + if (beautifyOverride == null) { Debug.LogWarning("[CameraControlSystem] beautifyOverride가 null입니다"); return; } - + string[] possibleFieldNames = { - "depthOfFieldDistance", - "dofDistance", - "focusDistance", - "depthOfFieldFocusDistance", - "distance" + "depthOfFieldFocalLength", + "focalLength", + "dofFocalLength" }; - + var overrideType = beautifyOverride.GetType(); - + foreach (string fieldName in possibleFieldNames) { var dofField = overrideType.GetField(fieldName); @@ -732,7 +611,7 @@ public class CameraControlSystem : MonoBehaviour { overrideState.SetValue(fieldValue, true); } - + valueProperty.SetValue(fieldValue, newValue); return; } @@ -746,26 +625,26 @@ public class CameraControlSystem : MonoBehaviour } } } - - Debug.LogError("[CameraControlSystem] DOF Distance 필드를 찾을 수 없거나 설정할 수 없습니다"); + + Debug.LogError("[CameraControlSystem] DOF Focal Length 필드를 찾을 수 없거나 설정할 수 없습니다"); } - + private void CreateBeautifyOverride(Volume volume, System.Type beautifyType) { try { Debug.Log("[CameraControlSystem] Beautify Override 생성 중..."); - + // Beautify 인스턴스를 ScriptableObject.CreateInstance로 생성 beautifyOverride = ScriptableObject.CreateInstance(beautifyType); - + if (beautifyOverride != null) { Debug.Log("[CameraControlSystem] Beautify Override 인스턴스 생성 성공 (ScriptableObject)"); - + // VolumeProfile에 추가하는 다양한 방법 시도 bool addSuccess = false; - + // 방법 1: Add() 메서드들 확인 var methods = volume.sharedProfile.GetType().GetMethods(); foreach (var method in methods) @@ -778,7 +657,7 @@ public class CameraControlSystem : MonoBehaviour { Debug.Log($" - 파라미터: {param.ParameterType} {param.Name}"); } - + // T Add(bool overrides = false) where T : VolumeComponent, new() 시그니처 찾기 if (method.IsGenericMethodDefinition && parameters.Length <= 1) { @@ -786,9 +665,9 @@ public class CameraControlSystem : MonoBehaviour { var genericMethod = method.MakeGenericMethod(beautifyType); object[] args = parameters.Length == 0 ? new object[0] : new object[] { false }; - + var addedComponent = genericMethod.Invoke(volume.sharedProfile, args); - + if (addedComponent != null) { beautifyOverride = addedComponent; @@ -804,7 +683,7 @@ public class CameraControlSystem : MonoBehaviour } } } - + // 방법 2: components 리스트에 직접 추가 if (!addSuccess) { @@ -815,7 +694,7 @@ public class CameraControlSystem : MonoBehaviour { var components = (System.Collections.IList)componentsField.GetValue(volume.sharedProfile); components.Add(beautifyOverride); - + Debug.Log("[CameraControlSystem] Beautify Override가 Profile에 추가됨 (직접 추가)"); addSuccess = true; } @@ -825,7 +704,7 @@ public class CameraControlSystem : MonoBehaviour Debug.LogError($"[CameraControlSystem] 직접 추가 실패: {ex.Message}"); } } - + if (addSuccess) { SetupDOFField(beautifyType); @@ -845,7 +724,7 @@ public class CameraControlSystem : MonoBehaviour Debug.LogError($"[CameraControlSystem] Beautify Override 생성 실패: {ex.Message}"); } } - + private void SetupDOFField(System.Type beautifyType) { // 모든 DOF 관련 설정들 @@ -859,7 +738,7 @@ public class CameraControlSystem : MonoBehaviour "depthOfFieldMaxBrightness", // 최대 밝기 "depthOfFieldFocalLength" // 포커스 렌즈 길이 }; - + foreach (string fieldName in dofFieldsToEnable) { var field = beautifyType.GetField(fieldName); @@ -870,57 +749,58 @@ public class CameraControlSystem : MonoBehaviour { var valueProperty = fieldValue.GetType().GetProperty("value"); var overrideProperty = fieldValue.GetType().GetProperty("overrideState"); - + if (valueProperty != null && overrideProperty != null) { // Override 상태 활성화 overrideProperty.SetValue(fieldValue, true); - + // 필드별 기본값 설정 switch (fieldName) { case "depthOfField": valueProperty.SetValue(fieldValue, false); // 기본값: 비활성화 break; - + case "depthOfFieldDistance": valueProperty.SetValue(fieldValue, 1f); - targetDOF = 1f; + currentDOFValue = 1f; break; - + case "depthOfFieldBokeh": valueProperty.SetValue(fieldValue, true); break; - + case "depthOfFieldForegroundBlur": valueProperty.SetValue(fieldValue, true); break; - + case "depthOfFieldMaxSamples": if (valueProperty.PropertyType == typeof(int)) { valueProperty.SetValue(fieldValue, 8); } break; - + case "depthOfFieldDownsampling": if (valueProperty.PropertyType == typeof(int)) { valueProperty.SetValue(fieldValue, 2); } break; - + case "depthOfFieldMaxBrightness": if (valueProperty.PropertyType == typeof(float)) { valueProperty.SetValue(fieldValue, 1000f); } break; - + case "depthOfFieldFocalLength": if (valueProperty.PropertyType == typeof(float)) { valueProperty.SetValue(fieldValue, 0.3f); // 포커스 렌즈 길이 0.3 + currentDOFValue = 0.3f; } break; } @@ -929,12 +809,12 @@ public class CameraControlSystem : MonoBehaviour } } } - + private void TakeScreenshot() { StartCoroutine(TakeScreenshotCoroutine()); } - + private System.Collections.IEnumerator TakeScreenshotCoroutine() { // UI 임시 숨기기 @@ -943,32 +823,32 @@ public class CameraControlSystem : MonoBehaviour { cameraInfoUI.ToggleUI(); } - + // 한 프레임 대기 (UI 숨김 적용) yield return new WaitForEndOfFrame(); - + string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string filename = $"Screenshot_{timestamp}.png"; string fullPath = System.IO.Path.Combine(screenshotPath, filename); - + // 고해상도 스크린샷 촬영 (플래시 효과 전에!) ScreenCapture.CaptureScreenshot(fullPath, screenshotResolutionMultiplier); - + // 스크린샷 촬영 후 플래시 효과 시작 if (cameraInfoUI != null) { cameraInfoUI.TriggerScreenshotFlash(); } - + // 0.1초 후 UI 복원 (스크린샷 완료 보장) yield return new WaitForSeconds(0.1f); - + if (wasUIVisible && cameraInfoUI != null) { cameraInfoUI.ToggleUI(); } } - + private void ToggleCameraUI() { if (cameraInfoUI != null) @@ -976,16 +856,16 @@ public class CameraControlSystem : MonoBehaviour cameraInfoUI.ToggleUI(); } } - + private void ToggleDOF() { if (beautifyOverride == null) return; - + isDOFEnabled = !isDOFEnabled; - + var overrideType = beautifyOverride.GetType(); var dofField = overrideType.GetField("depthOfField"); - + if (dofField != null) { var fieldValue = dofField.GetValue(beautifyOverride); @@ -993,39 +873,208 @@ public class CameraControlSystem : MonoBehaviour { var valueProperty = fieldValue.GetType().GetProperty("value"); var overrideProperty = fieldValue.GetType().GetProperty("overrideState"); - + if (valueProperty != null && overrideProperty != null) { overrideProperty.SetValue(fieldValue, true); valueProperty.SetValue(fieldValue, isDOFEnabled); - + Debug.Log($"[CameraControlSystem] DOF {(isDOFEnabled ? "활성화" : "비활성화")}"); } } } } - + + private void ToggleControlMode() + { + isFOVMode = !isFOVMode; + Debug.Log($"[CameraControlSystem] 제어 모드 전환: {(isFOVMode ? "FOV" : "DOF")} 모드"); + } + + private void HandleF18Click() + { + float currentTime = Time.time; + float timeSinceLastClick = currentTime - lastF18ClickTime; + + if (timeSinceLastClick <= doubleClickThreshold) + { + // 더블클릭: DOF ON/OFF + ToggleDOF(); + } + else + { + // 싱글클릭: DOF 타겟 전환 + CycleDOFTarget(); + } + + lastF18ClickTime = currentTime; + } + + private void FindDOFTargets() + { + availableDOFTargets.Clear(); + + // 씬의 모든 CustomRetargetingScript 찾기 + var retargetingScripts = FindObjectsByType(FindObjectsSortMode.None); + Debug.Log($"[CameraControlSystem] CustomRetargetingScript {retargetingScripts.Length}개 발견"); + + foreach (var script in retargetingScripts) + { + Debug.Log($"[CameraControlSystem] 스크립트 검사 중: {script.gameObject.name}, targetAnimator = {script.targetAnimator}"); + + if (script.targetAnimator != null) + { + // Head 본 찾기 + Transform headBone = script.targetAnimator.GetBoneTransform(HumanBodyBones.Head); + Debug.Log($"[CameraControlSystem] Head 본 찾기: {(headBone != null ? headBone.name : "null")}"); + + if (headBone != null) + { + // Head에 콜라이더가 없으면 추가 + bool hasCollider = headBone.TryGetComponent(out _); + Debug.Log($"[CameraControlSystem] {headBone.name}에 기존 콜라이더: {hasCollider}"); + + if (!hasCollider) + { + var collider = headBone.gameObject.AddComponent(); + collider.radius = 0.1f; + collider.isTrigger = true; + Debug.Log($"[CameraControlSystem] {headBone.name}에 SphereCollider 추가 완료 (radius: 0.1)"); + } + + availableDOFTargets.Add(headBone); + Debug.Log($"[CameraControlSystem] DOF 타겟 추가: {headBone.name} ({script.targetAnimator.gameObject.name})"); + } + } + } + + Debug.Log($"[CameraControlSystem] 총 {availableDOFTargets.Count}개의 DOF 타겟 발견"); + } + + private void CycleDOFTarget() + { + if (currentVirtualCamera == null) + { + Debug.LogWarning("[CameraControlSystem] 가상 카메라가 없습니다."); + return; + } + + // 카메라 중앙에서 레이 발사 + Camera mainCamera = Camera.main; + if (mainCamera == null) + { + Debug.LogWarning("[CameraControlSystem] 메인 카메라를 찾을 수 없습니다."); + return; + } + + Ray ray = mainCamera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0)); + RaycastHit[] hits = Physics.RaycastAll(ray, 100f); + + Debug.Log($"[CameraControlSystem] 레이캐스트: {hits.Length}개의 오브젝트 감지"); + + // SphereCollider가 있는 Head 본만 필터링 + List validHits = new List(); + foreach (var hit in hits) + { + if (hit.collider is SphereCollider && hit.transform.name.Contains("Head")) + { + validHits.Add(hit); + Debug.Log($"[CameraControlSystem] 유효한 타겟 발견: {hit.transform.name} (거리: {hit.distance:F2}m)"); + } + } + + if (validHits.Count == 0) + { + Debug.LogWarning("[CameraControlSystem] 레이에 맞은 Head 타겟이 없습니다."); + return; + } + + // 거리순으로 정렬 + validHits.Sort((a, b) => a.distance.CompareTo(b.distance)); + + // 현재 타겟 찾기 + int nextIndex = 0; + if (currentDOFTarget != null) + { + for (int i = 0; i < validHits.Count; i++) + { + if (validHits[i].transform == currentDOFTarget) + { + nextIndex = (i + 1) % validHits.Count; + break; + } + } + } + + // 다음 타겟 설정 + currentDOFTarget = validHits[nextIndex].transform; + float distance = validHits[nextIndex].distance; + + // depthOfFieldDistance 설정 + SetDOFDistance(distance); + + Debug.Log($"[CameraControlSystem] DOF 타겟 설정: {currentDOFTarget.name} (거리: {distance:F2}m)"); + } + + private void SetDOFDistance(float distance) + { + if (beautifyOverride == null) + { + Debug.LogWarning("[CameraControlSystem] beautifyOverride가 null입니다"); + return; + } + + var overrideType = beautifyOverride.GetType(); + var dofDistanceField = overrideType.GetField("depthOfFieldDistance"); + + if (dofDistanceField != null) + { + var fieldValue = dofDistanceField.GetValue(beautifyOverride); + if (fieldValue != null) + { + var valueProperty = fieldValue.GetType().GetProperty("value"); + var overrideProperty = fieldValue.GetType().GetProperty("overrideState"); + + if (valueProperty != null && overrideProperty != null) + { + overrideProperty.SetValue(fieldValue, true); + valueProperty.SetValue(fieldValue, distance); + } + } + } + } + + private void UpdateDOFTargetTracking() + { + // 현재 타겟이 설정되어 있고, DOF가 활성화되어 있으면 지속적으로 거리 업데이트 + if (currentDOFTarget != null && isDOFEnabled && currentVirtualCamera != null) + { + float distance = Vector3.Distance(currentVirtualCamera.transform.position, currentDOFTarget.position); + SetDOFDistance(distance); + } + } + // Public 메서드들 (외부에서 호출 가능) public void SetFOV(float fov) { if (currentVirtualCamera == null) return; - + var lens = currentVirtualCamera.Lens; lens.FieldOfView = Mathf.Clamp(fov, minFOV, maxFOV); currentVirtualCamera.Lens = lens; } - + public float GetCurrentFOV() { return currentVirtualCamera?.Lens.FieldOfView ?? 0f; } - + public void SetFOVLimits(float min, float max) { minFOV = min; maxFOV = max; } - + public void SetScreenshotPath(string path) { screenshotPath = path; @@ -1034,42 +1083,37 @@ public class CameraControlSystem : MonoBehaviour System.IO.Directory.CreateDirectory(screenshotPath); } } - + // UI에서 사용할 Public 메서드들 public float GetCurrentForce() { - return fovForces[currentForceIndex]; + return fovForce; } - - public int GetCurrentForceIndex() - { - return currentForceIndex; - } - + public float GetCurrentVelocity() { return fovVelocity; } - + // DOF UI에서 사용할 Public 메서드들 public float GetCurrentDOF() { var dofValue = GetCurrentDOFValue(); return dofValue.HasValue ? dofValue.Value : 0f; } - + public float GetCurrentDofForce() { - return dofForces[currentDofForceIndex]; + return dofStep; } - - public int GetCurrentDofForceIndex() - { - return currentDofForceIndex; - } - + public float GetCurrentDofVelocity() { - return dofVelocity; + return 0f; // 더 이상 velocity 사용 안 함 + } + + public bool IsFOVMode() + { + return isFOVMode; } } \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraInfoUI.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraInfoUI.cs index b2f9cbb1..3c411fbe 100644 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraInfoUI.cs +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/CameraInfoUI.cs @@ -89,9 +89,9 @@ public class CameraInfoUI : MonoBehaviour // 텍스트들 생성 CreateInfoText("Camera Name: ", out cameraNameText, 0); CreateInfoText("FOV: ", out fovValueText, 1); - CreateInfoText("FOV Force: ", out fovSpeedText, 2); + CreateInfoText("Control Mode: ", out fovSpeedText, 2); CreateInfoText("FOV Velocity: ", out velocityText, 3); - CreateInfoText("DOF Distance: ", out dofValueText, 4); + CreateInfoText("DOF Focal Length: ", out dofValueText, 4); CreateInfoText("DOF Force: ", out dofSpeedText, 5); CreateInfoText("DOF Velocity: ", out dofVelocityText, 6); @@ -146,18 +146,19 @@ public class CameraInfoUI : MonoBehaviour if (cameraControlSystem != null) { - fovSpeedText.text = $"FOV Force: {cameraControlSystem.GetCurrentForce():F0} ({cameraControlSystem.GetCurrentForceIndex() + 1}/3)"; + string currentMode = cameraControlSystem.IsFOVMode() ? "FOV" : "DOF"; + fovSpeedText.text = $"Mode: {currentMode}"; velocityText.text = $"FOV Velocity: {cameraControlSystem.GetCurrentVelocity():F1}"; - - dofValueText.text = $"DOF Distance: {cameraControlSystem.GetCurrentDOF():F1}"; - dofSpeedText.text = $"DOF Force: {cameraControlSystem.GetCurrentDofForce():F1} ({cameraControlSystem.GetCurrentDofForceIndex() + 1}/3)"; + + dofValueText.text = $"DOF Focal Length: {cameraControlSystem.GetCurrentDOF():F3}"; + dofSpeedText.text = $"DOF Force: {cameraControlSystem.GetCurrentDofForce():F1}"; dofVelocityText.text = $"DOF Velocity: {cameraControlSystem.GetCurrentDofVelocity():F2}"; } else { fovSpeedText.text = "FOV Force: --"; velocityText.text = "FOV Velocity: --"; - dofValueText.text = "DOF Distance: --"; + dofValueText.text = "DOF Focal Length: --"; dofSpeedText.text = "DOF Force: --"; dofVelocityText.text = "DOF Velocity: --"; }