단축키 일단 정리. 꼼꼼이 테스트 해봐야 함

This commit is contained in:
logonkhi
2025-12-19 20:13:13 +09:00
parent ddec52df13
commit ac071813f4
12 changed files with 1524 additions and 1871 deletions

View File

@@ -218,7 +218,7 @@ namespace UVC.Studio.Config
/// <returns>저장 성공 여부</returns>
public async UniTask<bool> SaveAsync(CancellationToken cancellationToken = default)
{
string path = Path.Combine(Application.streamingAssetsPath, FileName);
string path = Path.Combine(Application.streamingAssetsPath, "Studio", FileName);
if (useAppDataPath)
{
path = Path.Combine(Application.persistentDataPath, FileName);
@@ -258,7 +258,7 @@ namespace UVC.Studio.Config
#region Sync Methods
/// <summary>
/// StreamingAssets/Settings.json 파일에서 설정을 동기로 로드합니다.
/// StreamingAssets/Studio/Settings.json 파일에서 설정을 동기로 로드합니다.
/// </summary>
/// <returns>로드 성공 여부</returns>
/// <remarks>
@@ -267,7 +267,11 @@ namespace UVC.Studio.Config
/// </remarks>
public bool Load()
{
string path = Path.Combine(Application.streamingAssetsPath, FileName);
string path = Path.Combine(Application.streamingAssetsPath, "Studio", FileName);
if (useAppDataPath)
{
path = Path.Combine(Application.persistentDataPath, FileName);
}
if (!File.Exists(path))
{
@@ -303,8 +307,11 @@ namespace UVC.Studio.Config
/// </remarks>
public bool Save()
{
string path = Path.Combine(Application.streamingAssetsPath, FileName);
string path = Path.Combine(Application.streamingAssetsPath, "Studio", FileName);
if (useAppDataPath)
{
path = Path.Combine(Application.persistentDataPath, FileName);
}
try
{
// Newtonsoft.Json 사용으로 float 정밀도 문제 해결

View File

@@ -1,5 +1,6 @@
#nullable enable
using System.Collections.Generic;
using RTGLite;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
@@ -9,21 +10,10 @@ namespace UVC.Studio.Manager
{
/// <summary>
/// 화면에서 마우스 클릭을 감지하여 객체 선택을 처리하는 컴포넌트
/// RTScene.Raycast를 사용하여 Collider 없이도 MeshRenderer 기반으로 객체 감지
/// </summary>
public class SelectionInputHandler : MonoBehaviour
{
[SerializeField]
[Tooltip("클릭 감지에 사용할 카메라 (null이면 Camera.main 사용)")]
private Camera? raycastCamera;
[SerializeField]
[Tooltip("레이캐스트 대상 레이어")]
private LayerMask raycastLayerMask = ~0; // 기본값: 모든 레이어
[SerializeField]
[Tooltip("레이캐스트 최대 거리")]
private float raycastDistance = 1000f;
private SelectionManager? _selectionManager;
private StageObjectManager? _stageObjectManager;
@@ -49,7 +39,9 @@ namespace UVC.Studio.Manager
protected void Update()
{
// 마우스 왼쪽 버튼 클릭 감지
if (Input.GetMouseButtonDown(0))
// 기즈모 UI 위에서는 선택하지 않음
if (Input.GetMouseButtonUp(0) &&
(RTGizmos.get == null || !RTGizmos.get.IsGizmoGUIHovered()))
{
HandleClick();
}
@@ -60,62 +52,71 @@ namespace UVC.Studio.Manager
/// </summary>
private void HandleClick()
{
Debug.Log("[SelectionInputHandler] HandleClick called");
// UI 위에서 클릭한 경우 무시 (실제 상호작용 가능한 UI만 체크)
// UI 위에서 클릭한 경우 무시
if (IsPointerOverInteractableUI())
{
Debug.Log("[SelectionInputHandler] Clicked on interactable UI, ignoring.");
return;
}
if (_selectionManager == null || _stageObjectManager == null)
{
Debug.LogWarning($"[SelectionInputHandler] Manager is null - SelectionManager: {_selectionManager != null}, StageObjectManager: {_stageObjectManager != null}");
return;
}
var camera = raycastCamera != null ? raycastCamera : Camera.main;
if (camera == null)
// RTScene을 사용한 레이캐스트 (Collider 없이 MeshRenderer로 감지)
var pickedObject = PickObject();
Debug.Log($"[SelectionInputHandler] Picked Object: {(pickedObject != null ? pickedObject.name : "None")}");
// Shift 또는 Ctrl 키로 다중 선택
bool addToSelection = Input.GetKey(KeyCode.LeftShift) ||
Input.GetKey(KeyCode.RightShift) ||
Input.GetKey(KeyCode.LeftControl) ||
Input.GetKey(KeyCode.RightControl);
if (pickedObject == null)
{
Debug.LogWarning("[SelectionInputHandler] No camera available for raycast.");
return;
}
// 마우스 위치에서 레이 생성
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
Debug.Log($"[SelectionInputHandler] Raycast from {Input.mousePosition}, camera: {camera.name}, layerMask: {raycastLayerMask.value}");
// 레이캐스트 수행
if (Physics.Raycast(ray, out RaycastHit hit, raycastDistance, raycastLayerMask))
{
// 클릭된 객체 또는 부모에서 StageObject 찾기
var stageObject = FindStageObject(hit.collider.gameObject);
if (stageObject != null)
// 빈 공간 클릭 시 선택 해제 (다중 선택 키가 눌려있지 않을 때만)
if (!addToSelection)
{
// Shift 또는 Ctrl 키로 다중 선택
bool addToSelection = Input.GetKey(KeyCode.LeftShift) ||
Input.GetKey(KeyCode.RightShift) ||
Input.GetKey(KeyCode.LeftControl) ||
Input.GetKey(KeyCode.RightControl);
_selectionManager.DeselectAll();
}
return;
}
_selectionManager.Select(stageObject, addToSelection);
Debug.Log($"[SelectionInputHandler] Selected: {stageObject.GameObject?.name}");
// 클릭된 객체 또는 부모에서 StageObject 찾기
var stageObject = FindStageObject(pickedObject);
if (stageObject != null)
{
if (addToSelection)
{
// 다중 선택: 토글
_selectionManager.ToggleSelection(stageObject, true);
}
else
{
// StageObject가 아닌 곳 클릭 시 선택 해제
_selectionManager.DeselectAll();
Debug.Log("[SelectionInputHandler] Clicked on non-stage object, deselected all.");
// 단일 선택
_selectionManager.Select(stageObject, false);
}
}
else
}
/// <summary>
/// Physics.Raycast를 사용하여 마우스 위치에서 객체를 픽킹합니다.
/// </summary>
/// <returns>픽킹된 GameObject, 없으면 null</returns>
private GameObject? PickObject()
{
if (Camera.main == null) return null;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
// 빈 공간 클릭 시 선택 해제
_selectionManager.DeselectAll();
Debug.Log("[SelectionInputHandler] Clicked on empty space, deselected all.");
return hit.collider.gameObject;
}
return null;
}
/// <summary>
@@ -141,25 +142,8 @@ namespace UVC.Studio.Manager
return null;
}
/// <summary>
/// 레이캐스트 카메라 설정
/// </summary>
public void SetCamera(Camera camera)
{
raycastCamera = camera;
}
/// <summary>
/// 레이캐스트 레이어 마스크 설정
/// </summary>
public void SetLayerMask(LayerMask layerMask)
{
raycastLayerMask = layerMask;
}
/// <summary>
/// 실제 상호작용 가능한 UI 위에 포인터가 있는지 확인
/// raycastTarget이 true이고, 불투명한 UI 요소만 체크
/// </summary>
private bool IsPointerOverInteractableUI()
{
@@ -182,12 +166,9 @@ namespace UVC.Studio.Manager
var graphic = result.gameObject.GetComponent<Graphic>();
if (graphic != null && graphic.raycastTarget)
{
// 알파값이 일정 이상인 경우에만 UI로 인식
// 또는 상호작용 가능한 컴포넌트가 있는 경우
var selectable = result.gameObject.GetComponentInParent<Selectable>();
var hasInteraction = selectable != null && selectable.interactable;
// 불투명한 UI이거나 상호작용 가능한 UI인 경우
if (graphic.color.a > 0.1f || hasInteraction)
{
return true;

View File

@@ -103,6 +103,11 @@ namespace UVC.Studio.Manager
/// </summary>
private bool _gizmosInitialized = false;
/// <summary>
/// 기즈모 타겟 객체 리스트 (캐시)
/// </summary>
private readonly List<GameObject> _gizmoTargetObjects = new();
#endregion
#region Events
@@ -191,6 +196,9 @@ namespace UVC.Studio.Manager
// PropertyWindow에 선택된 객체의 속성 표시
DisplayEquipmentProperties(stageObject);
// 기즈모 타겟 업데이트
UpdateGizmoTargets();
}
/// <summary>
@@ -235,7 +243,11 @@ namespace UVC.Studio.Manager
_propertyWindow.Clear();
}
_currentDisplayedObject = null;
_gizmoTargetObjects.Clear();
}
// 기즈모 타겟 업데이트
UpdateGizmoTargets();
}
}
@@ -879,6 +891,10 @@ namespace UVC.Studio.Manager
_rotateGizmo = RTGizmos.get.CreateObjectRotateGizmo();
_scaleGizmo = RTGizmos.get.CreateObjectScaleGizmo();
_moveGizmo.objectTransformGizmo.pivot = EGizmoPivot.Center;
_rotateGizmo.objectTransformGizmo.pivot = EGizmoPivot.Center;
_scaleGizmo.objectTransformGizmo.pivot = EGizmoPivot.Center;
// 기본적으로 모든 기즈모 비활성화 (Select 모드)
_moveGizmo.enabled = false;
_rotateGizmo.enabled = false;
@@ -890,96 +906,6 @@ namespace UVC.Studio.Manager
Debug.Log("[SelectionManager] 기즈모가 초기화되었습니다.");
}
/// <summary>
/// 마우스 위치에서 객체를 픽킹합니다. RTScene.Raycast를 사용합니다.
/// </summary>
/// <returns>픽킹된 GameObject, 없으면 null</returns>
public GameObject? PickObject()
{
if (RTScene.get == null) return null;
// 마우스 위치에서 Ray 생성
Ray ray = Camera.main.ScreenPointToRay(UnityEngine.Input.mousePosition);
// RTScene의 Raycast 사용 (Collider 없이도 MeshRenderer로 감지)
if (RTScene.get.Raycast(ray, null, false, out SceneRayHit rayHit))
{
return rayHit.objectHit.gameObject;
}
return null;
}
/// <summary>
/// 마우스 클릭으로 객체를 선택합니다. Update에서 호출됩니다.
/// </summary>
/// <param name="addToSelection">다중 선택 여부 (Ctrl 키 누름)</param>
public void HandleMouseSelection(bool addToSelection = false)
{
var pickedObject = PickObject();
if (pickedObject == null)
{
// 빈 공간 클릭 시 선택 해제 (Ctrl 키가 눌려있지 않을 때만)
if (!addToSelection)
{
DeselectAll();
UpdateGizmoTargets();
}
return;
}
// 픽킹된 객체가 StageObject인지 확인
var stageObject = FindStageObjectForGameObject(pickedObject);
if (stageObject != null)
{
if (addToSelection)
{
// Ctrl+클릭: 토글 선택
ToggleSelection(stageObject, true);
}
else
{
// 일반 클릭: 단일 선택
Select(stageObject, false);
}
// 기즈모 타겟 업데이트
UpdateGizmoTargets();
}
}
/// <summary>
/// GameObject로부터 해당하는 StageObject를 찾습니다.
/// </summary>
/// <param name="gameObject">검색할 GameObject</param>
/// <returns>찾은 StageObject, 없으면 null</returns>
private StageObjectManager.StageObject? FindStageObjectForGameObject(GameObject gameObject)
{
if (gameObject == null) return null;
// StageObjectManager의 GetByGameObject 사용
var stageObject = _stageObjectManager.GetByGameObject(gameObject);
if (stageObject != null)
{
return stageObject;
}
// 부모 계층에서 StageObject 검색
Transform? current = gameObject.transform.parent;
while (current != null)
{
stageObject = _stageObjectManager.GetByGameObject(current.gameObject);
if (stageObject != null)
{
return stageObject;
}
current = current.parent;
}
return null;
}
/// <summary>
/// 선택된 모든 객체를 기즈모 타겟으로 설정합니다.
/// </summary>
@@ -987,34 +913,36 @@ namespace UVC.Studio.Manager
{
if (!_gizmosInitialized) return;
// 선택된 객체들의 GameObject 리스트 생성
var targetObjects = new List<GameObject>();
Debug.Log($"[SelectionManager] _currentToolType:{_currentToolType} _selectedObjects.Count:{_selectedObjects.Count}");
// 캐시된 리스트 재사용
_gizmoTargetObjects.Clear();
foreach (var stageObject in _selectedObjects)
{
if (stageObject.GameObject != null)
{
targetObjects.Add(stageObject.GameObject);
_gizmoTargetObjects.Add(stageObject.GameObject);
}
}
// 모든 기즈모에 타겟 설정
if (_moveGizmo != null)
{
_moveGizmo.objectTransformGizmo.SetTargets(targetObjects);
_moveGizmo.objectTransformGizmo.SetTargets(_gizmoTargetObjects);
}
if (_rotateGizmo != null)
{
_rotateGizmo.objectTransformGizmo.SetTargets(targetObjects);
_rotateGizmo.objectTransformGizmo.SetTargets(_gizmoTargetObjects);
}
if (_scaleGizmo != null)
{
_scaleGizmo.objectTransformGizmo.SetTargets(targetObjects);
_scaleGizmo.objectTransformGizmo.SetTargets(_gizmoTargetObjects);
}
// 현재 활성 기즈모 새로고침
RefreshActiveGizmo();
Debug.Log($"[SelectionManager] 기즈모 타겟 업데이트: {targetObjects.Count}개 객체");
Debug.Log($"[SelectionManager] 기즈모 타겟 업데이트: {_gizmoTargetObjects.Count}개 객체");
}
/// <summary>
@@ -1029,6 +957,8 @@ namespace UVC.Studio.Manager
return;
}
Debug.Log($"[SelectionManager] SetActiveTool called: {toolType}");
_currentToolType = toolType;
// 모든 기즈모 비활성화
@@ -1036,6 +966,12 @@ namespace UVC.Studio.Manager
if (_rotateGizmo != null) _rotateGizmo.enabled = false;
if (_scaleGizmo != null) _scaleGizmo.enabled = false;
if(_selectedObjects.Count == 0)
{
Debug.Log("[SelectionManager] No selected objects. Gizmos will remain disabled.");
return;
}
// 선택된 도구에 해당하는 기즈모 활성화
switch (toolType)
{
@@ -1075,21 +1011,43 @@ namespace UVC.Studio.Manager
/// <summary>
/// 현재 활성화된 기즈모를 새로고침합니다.
/// 선택된 객체가 없으면 기즈모를 비활성화합니다.
/// </summary>
public void RefreshActiveGizmo()
{
if (!_gizmosInitialized) return;
// 선택된 객체가 없으면 모든 기즈모 비활성화
if (_selectedObjects.Count == 0)
{
if (_moveGizmo != null) _moveGizmo.enabled = false;
if (_rotateGizmo != null) _rotateGizmo.enabled = false;
if (_scaleGizmo != null) _scaleGizmo.enabled = false;
return;
}
switch (_currentToolType)
{
case TransformToolType.Move:
_moveGizmo?.objectTransformGizmo.Refresh();
if (_moveGizmo != null)
{
_moveGizmo.enabled = true;
_moveGizmo.objectTransformGizmo.Refresh();
}
break;
case TransformToolType.Rotate:
_rotateGizmo?.objectTransformGizmo.Refresh();
if (_rotateGizmo != null)
{
_rotateGizmo.enabled = true;
_rotateGizmo.objectTransformGizmo.Refresh();
}
break;
case TransformToolType.Scale:
_scaleGizmo?.objectTransformGizmo.Refresh();
if (_scaleGizmo != null)
{
_scaleGizmo.enabled = true;
_scaleGizmo.objectTransformGizmo.Refresh();
}
break;
}
}

View File

@@ -91,26 +91,6 @@ namespace UVC.Studio
UILoading.Hide();
}
private void Update()
{
// 마우스 왼쪽 버튼 클릭 시 객체 선택 처리
if (UnityEngine.Input.GetMouseButtonDown(0))
{
// UI 위에서 클릭한 경우 무시
if (UnityEngine.EventSystems.EventSystem.current != null &&
UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
{
return;
}
// Ctrl 키가 눌려있으면 다중 선택
bool addToSelection = UnityEngine.Input.GetKey(KeyCode.LeftControl) ||
UnityEngine.Input.GetKey(KeyCode.RightControl);
selectionManager?.HandleMouseSelection(addToSelection);
}
}
private void SetupTopMenu()
{
if (topMenu == null)
@@ -308,11 +288,13 @@ namespace UVC.Studio
shortcutManager.RegisterToolShortcut("snap", () =>
{
Debug.Log("[Shortcut] Snap Tool 토글");
if( toolBox != null) toolBox.SetToggleButtonState("button_snap", !toolBox.GetToggleButtonState("button_snap"));
});
shortcutManager.RegisterToolShortcut("guide", () =>
{
Debug.Log("[Shortcut] Guide Tool 토글");
if( toolBox != null) toolBox.SetToggleButtonState("button_guide", !toolBox.GetToggleButtonState("button_guide"));
});
shortcutManager.RegisterToolShortcut("node", () =>
@@ -347,7 +329,7 @@ namespace UVC.Studio
"Studio/Images/toolbar_icon_save_off_40",
onToggle: (isSelected) => Debug.Log($"화면 녹화 상태: {(isSelected ? " " : "")} (OnToggle 콜백)"),
command: new DebugLogCommand("저장 버튼 클릭됨 (Command 실행)"),
tooltip:"tooltip_save");
tooltip: "tooltip_save");
// undo
toolbarModel.AddStandardButton("button_undo",
@@ -368,7 +350,11 @@ namespace UVC.Studio
"Studio/Images/toolbar_icon_select_on_40",
"Studio/Images/toolbar_icon_select_off_40",
onToggle: (isSelected) => { if (isSelected) Debug.Log("Selection Tool 선택됨"); },
command: new ActionCommand(() => Debug.Log("Selection Tool Command 실행")),
command: new ActionCommand(() =>
{
Debug.Log("Selection Tool Command 실행");
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Select);
}),
"tooltip_selection_tool");
@@ -377,7 +363,11 @@ namespace UVC.Studio
"Studio/Images/toolbar_icon_move_on_40",
"Studio/Images/toolbar_icon_move_off_40",
onToggle: (isSelected) => { if (isSelected) Debug.Log("Movement Tool 선택됨"); },
command: new ActionCommand(() => Debug.Log("Movement Tool Command 실행")),
command: new ActionCommand(() =>
{
Debug.Log("Movement Tool Command 실행");
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Move);
}),
"tooltip_movement_tool");
// rotate
@@ -385,7 +375,11 @@ namespace UVC.Studio
"Studio/Images/toolbar_icon_rotate_on_40",
"Studio/Images/toolbar_icon_rotate_off_40",
onToggle: (isSelected) => { if (isSelected) Debug.Log("Rotation Tool 선택됨"); },
command: new ActionCommand(() => Debug.Log("Rotation Tool Command 실행")),
command: new ActionCommand(() =>
{
Debug.Log("Rotation Tool Command 실행");
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Rotate);
}),
"tooltip_rotation_tool");
//scale
@@ -393,7 +387,11 @@ namespace UVC.Studio
"Studio/Images/toolbar_icon_scale_on_40",
"Studio/Images/toolbar_icon_scale_off_40",
onToggle: (isSelected) => { if (isSelected) Debug.Log("Scale Tool 선택됨"); },
command: new ActionCommand(() => Debug.Log("Scale Tool Command 실행")),
command: new ActionCommand(() =>
{
Debug.Log("Scale Tool Command 실행");
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Scale);
}),
"tooltip_scale_tool");
toolbarModel.AddSeparator();
@@ -405,10 +403,11 @@ namespace UVC.Studio
tooltip: "tooltip_duplicate");
//align
var alignBtnModel = new ToolbarExpandableButton {
Text = "button_align",
IconSpritePath = "Studio/Images/toolbar_icon_align_40",
ClickCommand = new ActionCommand(() => Debug.Log("align 주 버튼 클릭됨 (Command)")),
var alignBtnModel = new ToolbarExpandableButton
{
Text = "button_align",
IconSpritePath = "Studio/Images/toolbar_icon_align_40",
ClickCommand = new ActionCommand(() => Debug.Log("align 주 버튼 클릭됨 (Command)")),
Tooltip = "tooltip_align",
UpdateIconOnSelection = false,
OnSubButtonSelected = (selectedSubButtonModel) =>
@@ -418,7 +417,7 @@ namespace UVC.Studio
Debug.Log($"정렬 옵션 '{localizedSubButtonText}' 선택됨 (OnSubButtonSelected 콜백). 주 버튼 업데이트 로직 실행 가능.");
}
};
// 확장 버튼 모델에 하위 버튼 추가
alignBtnModel.SubButtons.Add(new ToolbarStandardButton
@@ -535,15 +534,15 @@ namespace UVC.Studio
toolBox.OnAction += (itemModel) =>
{
Debug.Log($"툴박스 아이템 '{itemModel.Text}' 실행됨 (OnAction 콜백). 추가 동작 로직 실행 가능.");
if(itemModel.ActionType == ToolbarActionType.Radio)
if (itemModel.ActionType == ToolbarActionType.Radio)
{
if(itemModel.Text == "SizeControlGroup")
if (itemModel.Text == "SizeControlGroup")
{
// 선택, 이동, 회전, 크기 조절 툴 관련 동작 처리
toolBox.ClearRadioButtonSelection("NodeControlGroup");
CursorManager.Instance.SetDefaultCursor();
}
else if(itemModel.Text == "NodeControlGroup")
else if (itemModel.Text == "NodeControlGroup")
{
// 노드, 링크, 아크 툴 관련 동작 처리
toolBox.ClearRadioButtonSelection("SizeControlGroup");