1075 lines
40 KiB
C#

using UnityEngine;
using Unity.Cinemachine;
using UnityRawInput;
using UnityEngine.Rendering;
using System;
public class CameraControlSystem : MonoBehaviour
{
[Header("FOV Physics Settings")]
[SerializeField] private float[] fovForces = {80f, 200f, 400f}; // 포스 강도
[SerializeField] private int currentForceIndex = 1; // 기본값: 200
[SerializeField] private float fovDamping = 6f; // 감쇠력
[SerializeField] private float fovMaxVelocity = 100f; // 최대 속도
[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;
[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 포스가 적용 중인지
// Beautify Volume Override 참조
private object beautifyOverride;
private bool isDOFEnabled = false; // DOF 활성화 상태 (기본값: 꺼짐)
private void Awake()
{
// 스크린샷 폴더 생성
if (!System.IO.Directory.Exists(screenshotPath))
{
System.IO.Directory.CreateDirectory(screenshotPath);
}
}
private void Start()
{
cameraManager = FindObjectOfType<CameraManager>();
if (cameraManager != null)
{
cameraManager.OnCameraChanged += OnCameraChanged;
UpdateCurrentCamera();
}
// CameraInfoUI 생성
GameObject uiGO = new GameObject("CameraInfoUI");
cameraInfoUI = uiGO.AddComponent<CameraInfoUI>();
// Beautify 컴포넌트 찾기
Debug.Log("[CameraControlSystem] Beautify 초기화 시작");
InitializeBeautify();
InitializeRawInput();
}
private void OnDestroy()
{
// RawInput 정리
if (RawInput.IsRunning)
{
try
{
RawInput.OnKeyDown -= HandleRawKeyDown;
// 다른 컴포넌트가 사용 중일 수 있으므로 Stop은 호출하지 않음
}
catch (System.Exception ex)
{
// RawInput 정리 실패 무시
}
}
// CameraManager 이벤트 해제
if (cameraManager != null)
{
cameraManager.OnCameraChanged -= OnCameraChanged;
}
}
private void InitializeRawInput()
{
try
{
if (!RawInput.IsRunning)
{
RawInput.Start();
RawInput.WorkInBackground = true; // 백그라운드에서도 키 입력 감지
RawInput.InterceptMessages = false; // 다른 앱으로 키 메시지 전달
}
// 이벤트 중복 등록 방지
RawInput.OnKeyDown -= HandleRawKeyDown;
RawInput.OnKeyDown += HandleRawKeyDown;
}
catch (System.Exception ex)
{
// RawInput 실패 시 Unity Input으로 대체
}
}
private void Update()
{
HandleFOVControl();
UpdateFOVPhysics();
UpdateDOFPhysics();
}
private void HandleRawKeyDown(RawKey key)
{
switch (key)
{
case RawKey.F13:
IncreaseFOV();
break;
case RawKey.F14:
DecreaseFOV();
break;
case RawKey.F15:
CycleFOVSpeed();
break;
case RawKey.F16:
TakeScreenshot();
break;
case RawKey.F17:
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();
break;
}
}
private void HandleFOVControl()
{
// Unity Input으로도 처리 (RawInput과 병행하여 안정성 확보)
if (Input.GetKeyDown(KeyCode.F13))
{
IncreaseFOV();
}
else if (Input.GetKeyDown(KeyCode.F14))
{
DecreaseFOV();
}
else if (Input.GetKeyDown(KeyCode.F15))
{
CycleFOVSpeed();
}
else if (Input.GetKeyDown(KeyCode.F16))
{
TakeScreenshot();
}
else if (Input.GetKeyDown(KeyCode.F17))
{
ToggleCameraUI();
}
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();
}
}
private void OnCameraChanged(CameraManager.CameraPreset oldPreset, CameraManager.CameraPreset newPreset)
{
UpdateCurrentCamera();
}
private void UpdateCurrentCamera()
{
if (cameraManager?.CurrentPreset?.virtualCamera != null)
{
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); // 양의 포스 적용
}
private void DecreaseFOV()
{
if (currentVirtualCamera == null) return;
// 현재 FOV를 targetFOV로 초기화 (처음 호출 시)
if (!isApplyingForce)
{
targetFOV = currentVirtualCamera.Lens.FieldOfView;
isApplyingForce = true;
}
float currentForce = fovForces[currentForceIndex];
ApplyFOVForce(-currentForce); // 음의 포스 적용
}
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)
{
newFOV = minFOV;
fovVelocity = Mathf.Max(0f, fovVelocity * 0.3f); // 부드러운 반발
}
else if (newFOV >= maxFOV)
{
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<Volume>();
if (volume == null)
{
Debug.Log("[CameraControlSystem] Volume이 없어서 새로 생성합니다");
volume = gameObject.AddComponent<Volume>();
volume.priority = 10f; // 우선순위 설정
volume.isGlobal = true; // 전역 볼륨으로 설정
// VolumeProfile 생성
volume.sharedProfile = ScriptableObject.CreateInstance<VolumeProfile>();
volume.sharedProfile.name = "CameraControl_BeautifyProfile";
Debug.Log("[CameraControlSystem] Volume 생성 완료 (우선순위: 10)");
}
else
{
// 기존 Volume의 우선순위 확인/설정
if (volume.priority != 10f)
{
volume.priority = 10f;
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}");
}
else
{
Debug.LogError("[CameraControlSystem] Beautify 타입을 찾을 수 없음 (어셈블리명 포함)");
}
if (beautifyType != null)
{
try
{
// VolumeProfile.TryGet을 올바른 시그니처로 호출
Debug.Log("[CameraControlSystem] VolumeProfile.TryGet 직접 호출");
// out 파라미터를 위한 배열
object[] parameters = { null };
// TryGet<T>(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)
{
var paramTypes = method.GetParameters();
if (paramTypes.Length == 1 && paramTypes[0].ParameterType.IsByRef)
{
tryGetMethod = method;
Debug.Log("[CameraControlSystem] TryGet 메서드 발견");
break;
}
}
}
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];
Debug.Log($"[CameraControlSystem] Beautify Override 발견! {beautifyOverride.GetType().Name}");
SetupDOFField(beautifyType);
return; // 성공!
}
else
{
Debug.LogWarning("[CameraControlSystem] Profile에 Beautify가 없음, 새로 생성합니다");
CreateBeautifyOverride(volume, beautifyType);
return;
}
}
else
{
Debug.LogError("[CameraControlSystem] 올바른 TryGet 메서드를 찾을 수 없음");
}
}
catch (System.Exception ex)
{
Debug.LogError($"[CameraControlSystem] TryGet 호출 실패: {ex.Message}");
}
}
else
{
Debug.LogError("[CameraControlSystem] Beautify 타입을 찾을 수 없음");
}
}
else
{
Debug.LogError("[CameraControlSystem] Volume에 Profile이 없음");
}
}
else
{
Debug.LogError("[CameraControlSystem] 같은 게임오브젝트에 Volume이 없음");
}
// 실패한 경우 BeautifySettings 방식 시도 (백업)
try
{
var beautifySettingsType = System.Type.GetType("Beautify.Universal.BeautifySettings, Beautify");
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}");
}
else
{
Debug.LogWarning("[CameraControlSystem] value 속성을 찾을 수 없음");
}
}
else
{
Debug.LogWarning("[CameraControlSystem] depthOfFieldDistance 필드 값이 null");
}
}
else
{
Debug.LogWarning("[CameraControlSystem] depthOfFieldDistance 필드를 찾을 수 없음");
// 모든 필드 출력해보기
var allFields = beautifyOverride.GetType().GetFields();
Debug.Log($"[CameraControlSystem] 사용 가능한 모든 필드 ({allFields.Length}개):");
foreach (var field in allFields)
{
if (field.Name.ToLower().Contains("depth") || field.Name.ToLower().Contains("dof") || field.Name.ToLower().Contains("focus"))
{
var fieldValue = field.GetValue(beautifyOverride);
if (fieldValue != null)
{
var valueProperty = fieldValue.GetType().GetProperty("value");
var overrideProperty = fieldValue.GetType().GetProperty("overrideState");
if (valueProperty != null && overrideProperty != null)
{
var currentValue = valueProperty.GetValue(fieldValue);
var isOverridden = overrideProperty.GetValue(fieldValue);
Debug.Log($" - {field.Name} ({field.FieldType}) = {currentValue}, Override: {isOverridden}");
}
else
{
Debug.Log($" - {field.Name} ({field.FieldType}) = {fieldValue}");
}
}
else
{
Debug.Log($" - {field.Name} ({field.FieldType}) = null");
}
}
}
}
}
else
{
Debug.LogWarning("[CameraControlSystem] BeautifySettings.sharedSettings가 null");
}
}
else
{
Debug.LogWarning("[CameraControlSystem] sharedSettings 속성을 찾을 수 없음");
}
}
else
{
Debug.LogWarning("[CameraControlSystem] BeautifySettings 타입을 찾을 수 없음");
}
}
catch (System.Exception ex)
{
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); // 양의 포스 적용
}
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); // 음의 포스 적용
}
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 값을 가져오는 헬퍼 메서드
private float? GetCurrentDOFValue()
{
if (beautifyOverride == null) return null;
string[] possibleFieldNames = {
"depthOfFieldDistance",
"dofDistance",
"focusDistance",
"depthOfFieldFocusDistance",
"distance"
};
var overrideType = beautifyOverride.GetType();
foreach (string fieldName in possibleFieldNames)
{
var dofField = overrideType.GetField(fieldName);
if (dofField != null)
{
var fieldValue = dofField.GetValue(beautifyOverride);
if (fieldValue != null)
{
// VolumeParameter<float> 타입
var valueProperty = fieldValue.GetType().GetProperty("value");
if (valueProperty != null && valueProperty.PropertyType == typeof(float))
{
return (float)valueProperty.GetValue(fieldValue);
}
// 직접 float 타입
else if (fieldValue is float)
{
return (float)fieldValue;
}
}
}
}
return null;
}
// DOF 값을 설정하는 헬퍼 메서드
private void SetDOFValue(float newValue)
{
if (beautifyOverride == null)
{
Debug.LogWarning("[CameraControlSystem] beautifyOverride가 null입니다");
return;
}
string[] possibleFieldNames = {
"depthOfFieldDistance",
"dofDistance",
"focusDistance",
"depthOfFieldFocusDistance",
"distance"
};
var overrideType = beautifyOverride.GetType();
foreach (string fieldName in possibleFieldNames)
{
var dofField = overrideType.GetField(fieldName);
if (dofField != null)
{
var fieldValue = dofField.GetValue(beautifyOverride);
if (fieldValue != null)
{
// VolumeParameter<float> 타입
var valueProperty = fieldValue.GetType().GetProperty("value");
if (valueProperty != null && valueProperty.PropertyType == typeof(float))
{
if (valueProperty.CanWrite)
{
// Override 상태를 먼저 활성화
var overrideState = fieldValue.GetType().GetProperty("overrideState");
if (overrideState != null && overrideState.CanWrite)
{
overrideState.SetValue(fieldValue, true);
}
valueProperty.SetValue(fieldValue, newValue);
return;
}
}
// 직접 float 타입
else if (fieldValue is float && dofField.FieldType == typeof(float))
{
dofField.SetValue(beautifyOverride, newValue);
return;
}
}
}
}
Debug.LogError("[CameraControlSystem] DOF Distance 필드를 찾을 수 없거나 설정할 수 없습니다");
}
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<T>() 메서드들 확인
var methods = volume.sharedProfile.GetType().GetMethods();
foreach (var method in methods)
{
if (method.Name == "Add")
{
Debug.Log($"[CameraControlSystem] Add 메서드 발견: {method}");
var parameters = method.GetParameters();
foreach (var param in parameters)
{
Debug.Log($" - 파라미터: {param.ParameterType} {param.Name}");
}
// T Add<T>(bool overrides = false) where T : VolumeComponent, new() 시그니처 찾기
if (method.IsGenericMethodDefinition && parameters.Length <= 1)
{
try
{
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;
Debug.Log("[CameraControlSystem] Beautify Override가 Profile에 추가됨 (Add<T>)");
addSuccess = true;
break;
}
}
catch (System.Exception ex)
{
Debug.Log($"[CameraControlSystem] Add<T> 시도 실패: {ex.Message}");
}
}
}
}
// 방법 2: components 리스트에 직접 추가
if (!addSuccess)
{
try
{
var componentsField = volume.sharedProfile.GetType().GetField("components", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (componentsField != null)
{
var components = (System.Collections.IList)componentsField.GetValue(volume.sharedProfile);
components.Add(beautifyOverride);
Debug.Log("[CameraControlSystem] Beautify Override가 Profile에 추가됨 (직접 추가)");
addSuccess = true;
}
}
catch (System.Exception ex)
{
Debug.LogError($"[CameraControlSystem] 직접 추가 실패: {ex.Message}");
}
}
if (addSuccess)
{
SetupDOFField(beautifyType);
}
else
{
Debug.LogError("[CameraControlSystem] Beautify Override 추가 실패");
}
}
else
{
Debug.LogError("[CameraControlSystem] Beautify Override 인스턴스 생성 실패");
}
}
catch (System.Exception ex)
{
Debug.LogError($"[CameraControlSystem] Beautify Override 생성 실패: {ex.Message}");
}
}
private void SetupDOFField(System.Type beautifyType)
{
// 모든 DOF 관련 설정들
string[] dofFieldsToEnable = {
"depthOfField", // DOF 메인 활성화
"depthOfFieldDistance", // DOF 거리
"depthOfFieldBokeh", // 보케 효과
"depthOfFieldForegroundBlur", // 전경 블러
"depthOfFieldMaxSamples", // 최대 샘플
"depthOfFieldDownsampling", // 다운샘플링
"depthOfFieldMaxBrightness", // 최대 밝기
"depthOfFieldFocalLength" // 포커스 렌즈 길이
};
foreach (string fieldName in dofFieldsToEnable)
{
var field = beautifyType.GetField(fieldName);
if (field != null)
{
var fieldValue = field.GetValue(beautifyOverride);
if (fieldValue != null)
{
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;
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
}
break;
}
}
}
}
}
}
private void TakeScreenshot()
{
StartCoroutine(TakeScreenshotCoroutine());
}
private System.Collections.IEnumerator TakeScreenshotCoroutine()
{
// UI 임시 숨기기
bool wasUIVisible = cameraInfoUI?.IsUIVisible ?? false;
if (wasUIVisible && cameraInfoUI != null)
{
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)
{
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);
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, isDOFEnabled);
Debug.Log($"[CameraControlSystem] DOF {(isDOFEnabled ? "" : "")}");
}
}
}
}
// 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;
if (!System.IO.Directory.Exists(screenshotPath))
{
System.IO.Directory.CreateDirectory(screenshotPath);
}
}
// UI에서 사용할 Public 메서드들
public float GetCurrentForce()
{
return fovForces[currentForceIndex];
}
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];
}
public int GetCurrentDofForceIndex()
{
return currentDofForceIndex;
}
public float GetCurrentDofVelocity()
{
return dofVelocity;
}
}