#nullable enable using System.Collections.Generic; using RTGLite; using UnityEngine; using UVC.Core; using UVC.Studio.Command; namespace UVC.Studio.Manager { /// /// RTGLite Gizmo와 UndoRedoManager를 연결하는 브릿지 컴포넌트 /// Gizmo 드래그 시작/종료를 감지하여 TransformChangeCommand를 기록합니다. /// [DefaultExecutionOrder(200)] // RTGizmos 초기화 후 실행 public class GizmoUndoBridge : SingletonScene { #region Private Fields /// /// 드래그 중인지 여부 /// private bool _isDragging = false; /// /// 드래그 종료 후 프레임 카운터 (클릭 무시용) /// private int _framesSinceDragEnd = int.MaxValue; /// /// 현재 드래그 중인 Gizmo /// private Gizmo? _activeGizmo = null; /// /// 드래그 시작 시 캡처한 TransformChangeCommand /// private TransformChangeCommand? _pendingCommand = null; /// /// 드래그 대상 Transform 리스트 (캐시) /// private readonly List _dragTargets = new(); #endregion #region Unity Lifecycle protected override void Init() { Debug.Log("[GizmoUndoBridge] Initialized"); } private void Start() { // UVC UndoRedoManager 이벤트 구독 if (UndoRedoManager.Instance != null) { UndoRedoManager.Instance.OnUndo += OnUVCUndo; UndoRedoManager.Instance.OnRedo += OnUVCRedo; } } private void Update() { // 드래그 종료 후 프레임 카운터 증가 if (_framesSinceDragEnd < int.MaxValue) { _framesSinceDragEnd++; } // RTGizmos가 없으면 스킵 if (RTGizmos.get == null) return; // 활성화된 드래그 Gizmo 찾기 (RTGizmos.draggedGizmo 사용) var currentDraggingGizmo = RTGizmos.get.draggedGizmo; // 드래그 시작 감지 if (!_isDragging && currentDraggingGizmo != null) { OnDragStart(currentDraggingGizmo); } // 드래그 종료 감지 else if (_isDragging && currentDraggingGizmo == null) { OnDragEnd(); } } protected override void OnDestroy() { if (UndoRedoManager.Instance != null) { UndoRedoManager.Instance.OnUndo -= OnUVCUndo; UndoRedoManager.Instance.OnRedo -= OnUVCRedo; } base.OnDestroy(); } #endregion #region Drag Detection /// /// 드래그 시작 시 호출 /// private void OnDragStart(Gizmo gizmo) { _isDragging = true; _activeGizmo = gizmo; // 드래그 대상 수집 _dragTargets.Clear(); CollectDragTargets(gizmo); if (_dragTargets.Count == 0) { Debug.LogWarning("[GizmoUndoBridge] No drag targets found"); return; } // Gizmo 타입에 따른 설명 생성 string description = GetGizmoOperationDescription(gizmo); // TransformChangeCommand 생성 (Before 상태 캡처) _pendingCommand = new TransformChangeCommand(_dragTargets, description); Debug.Log($"[GizmoUndoBridge] Drag started: {_dragTargets.Count}개 객체, {description}"); } /// /// 드래그 종료 시 호출 /// private void OnDragEnd() { _isDragging = false; _framesSinceDragEnd = 0; // 프레임 카운터 리셋 if (_pendingCommand != null) { // After 상태 캡처 _pendingCommand.CaptureAfterState(); // 변경사항이 있으면 UndoRedoManager에 기록 if (_pendingCommand.HasChanges()) { var undoRedoManager = UndoRedoManager.Instance; if (undoRedoManager != null) { undoRedoManager.RecordCommand(_pendingCommand); Debug.Log($"[GizmoUndoBridge] Drag ended, recorded: {_pendingCommand.Description}"); } } else { Debug.Log("[GizmoUndoBridge] Drag ended, no changes detected"); } } _pendingCommand = null; _activeGizmo = null; _dragTargets.Clear(); } /// /// Gizmo에서 드래그 대상 Transform을 수집합니다. /// private void CollectDragTargets(Gizmo gizmo) { ObjectTransformGizmo? objectTransformGizmo = null; // Gizmo 타입에 따라 ObjectTransformGizmo 가져오기 if (gizmo is MoveGizmo moveGizmo) { objectTransformGizmo = moveGizmo.objectTransformGizmo; } else if (gizmo is RotateGizmo rotateGizmo) { objectTransformGizmo = rotateGizmo.objectTransformGizmo; } else if (gizmo is ScaleGizmo scaleGizmo) { objectTransformGizmo = scaleGizmo.objectTransformGizmo; } if (objectTransformGizmo == null || objectTransformGizmo.targetCount == 0) { return; } // SelectionManager에서 선택된 객체 가져오기 if (InjectorAppContext.Instance != null) { var selectionManager = InjectorAppContext.Instance.Get(); if (selectionManager != null) { foreach (var stageObject in selectionManager.SelectedObjects) { if (stageObject.GameObject != null) { _dragTargets.Add(stageObject.GameObject.transform); } } } } } /// /// Gizmo 타입에 따른 작업 설명을 반환합니다. /// private static string GetGizmoOperationDescription(Gizmo gizmo) { return gizmo switch { MoveGizmo => "객체 이동", RotateGizmo => "객체 회전", ScaleGizmo => "객체 크기 변경", _ => "객체 변환" }; } #endregion #region UVC Undo/Redo Integration /// /// UVC UndoRedoManager의 Undo 이벤트 핸들러 /// private void OnUVCUndo(UVC.UI.Commands.IUndoableCommand command) { // Transform 관련 커맨드면 RTGizmos 새로고침 if (command is TransformChangeCommand) { RefreshGizmos(); } } /// /// UVC UndoRedoManager의 Redo 이벤트 핸들러 /// private void OnUVCRedo(UVC.UI.Commands.IUndoableCommand command) { // Transform 관련 커맨드면 RTGizmos 새로고침 if (command is TransformChangeCommand) { RefreshGizmos(); } } /// /// 모든 활성 Gizmo를 새로고침합니다. /// PropertyWindow에서 Transform 속성 변경 시에도 호출됩니다. /// public void RefreshGizmos() { if (RTGizmos.get == null) return; // CollectObjectTransformGizmos를 사용하여 모든 ObjectTransformGizmo 수집 var objectTransformGizmos = new List(); RTGizmos.get.CollectObjectTransformGizmos(objectTransformGizmos); foreach (var transformGizmo in objectTransformGizmos) { transformGizmo?.Refresh(); } Debug.Log("[GizmoUndoBridge] Gizmos refreshed after Undo/Redo"); } #endregion #region Public Properties /// /// 드래그 중이거나 드래그 직후인지 여부 /// 빈 공간 클릭 시 선택 해제 방지용 /// public bool IsDraggingOrJustEnded => _isDragging || _framesSinceDragEnd < 2; #endregion } }