#nullable enable
using System.Collections.Generic;
using RTGLite;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UVC.Core;
namespace UVC.Studio.Manager
{
///
/// 화면에서 마우스 클릭을 감지하여 객체 선택을 처리하는 컴포넌트
/// RTScene.Raycast를 사용하여 Collider 없이도 MeshRenderer 기반으로 객체 감지
///
public class SelectionInputHandler : MonoBehaviour
{
private SelectionManager? _selectionManager;
private StageObjectManager? _stageObjectManager;
///
/// 더블클릭 감지 시간 간격 (초)
///
private const float DoubleClickThreshold = 0.5f;
///
/// 마지막 클릭 시간
///
private float _lastClickTime = 0f;
///
/// 마지막 클릭된 StageObject (더블클릭 확인용)
///
private StageObjectManager.StageObject? _lastClickedStageObject;
protected async void Start()
{
// InjectorSceneContext 초기화 대기
await InjectorSceneContext.Instance.WaitForInitializationAsync();
_selectionManager = InjectorAppContext.Instance.Get();
_stageObjectManager = InjectorAppContext.Instance.Get();
if (_selectionManager == null)
{
Debug.LogWarning("[SelectionInputHandler] SelectionManager not found.");
}
if (_stageObjectManager == null)
{
Debug.LogWarning("[SelectionInputHandler] StageObjectManager not found.");
}
}
protected void Update()
{
// 마우스 왼쪽 버튼 클릭 감지
// 기즈모 UI 위에서는 선택하지 않음
if (Input.GetMouseButtonUp(0) &&
(RTGizmos.get == null || !RTGizmos.get.IsGizmoGUIHovered()))
{
HandleClick();
}
}
///
/// 마우스 클릭 처리
///
private void HandleClick()
{
// UI 위에서 클릭한 경우 무시
if (IsPointerOverInteractableUI())
{
return;
}
if (_selectionManager == null || _stageObjectManager == null)
{
return;
}
// 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)
{
// 빈 공간 클릭 시 선택 해제
// Shift/Ctrl 키를 누르고 있지 않으면 선택 해제
// 단, 기즈모 드래그 직후에는 선택 해제하지 않음
if (!addToSelection)
{
var gizmoUndoBridge = GizmoUndoBridge.Instance;
if (gizmoUndoBridge == null || !gizmoUndoBridge.IsDraggingOrJustEnded)
{
_selectionManager.DeselectAll();
}
}
return;
}
// 클릭된 객체 또는 부모에서 StageObject 찾기
var stageObject = FindStageObject(pickedObject);
if (stageObject != null)
{
// 더블클릭 감지
float currentTime = Time.unscaledTime;
bool isDoubleClick = (currentTime - _lastClickTime <= DoubleClickThreshold) &&
(_lastClickedStageObject == stageObject);
if (isDoubleClick)
{
// 더블클릭: 카메라 포커스 이동
_stageObjectManager.Focus(stageObject.GameObject);
_lastClickTime = 0f; // 연속 더블클릭 방지
_lastClickedStageObject = null;
return;
}
// 클릭 정보 저장
_lastClickTime = currentTime;
_lastClickedStageObject = stageObject;
if (addToSelection)
{
// 다중 선택: 토글
_selectionManager.ToggleSelection(stageObject, true);
}
else
{
// 단일 선택
_selectionManager.Select(stageObject, false);
}
}
else
{
// 빈 공간 클릭 시 더블클릭 상태 초기화
_lastClickTime = 0f;
_lastClickedStageObject = null;
}
}
///
/// Physics.Raycast를 사용하여 마우스 위치에서 객체를 픽킹합니다.
///
/// 픽킹된 GameObject, 없으면 null
private GameObject? PickObject()
{
if (Camera.main == null) return null;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
return hit.collider.gameObject;
}
return null;
}
///
/// GameObject 또는 부모에서 StageObject 찾기
///
private StageObjectManager.StageObject? FindStageObject(GameObject clickedObject)
{
if (_stageObjectManager == null) return null;
// 클릭된 객체에서 시작하여 부모까지 탐색
Transform? current = clickedObject.transform;
while (current != null)
{
var stageObject = _stageObjectManager.GetByGameObject(current.gameObject);
if (stageObject != null)
{
return stageObject;
}
current = current.parent;
}
return null;
}
///
/// 실제 상호작용 가능한 UI 위에 포인터가 있는지 확인
///
private bool IsPointerOverInteractableUI()
{
if (EventSystem.current == null) return false;
var pointerEventData = new PointerEventData(EventSystem.current)
{
position = Input.mousePosition
};
var raycastResults = new List();
EventSystem.current.RaycastAll(pointerEventData, raycastResults);
foreach (var result in raycastResults)
{
// UI 레이어인지 확인
if (result.gameObject.layer != LayerMask.NameToLayer("UI")) continue;
// Graphic 컴포넌트 확인 (Image, Text 등)
var graphic = result.gameObject.GetComponent();
if (graphic != null && graphic.raycastTarget)
{
var selectable = result.gameObject.GetComponentInParent();
var hasInteraction = selectable != null && selectable.interactable;
if (graphic.color.a > 0.1f || hasInteraction)
{
return true;
}
}
}
return false;
}
}
}