#nullable enable
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UVC.Core;
using UVC.Factory.Modal;
namespace UVC.Factory.Component
{
///
/// 씬에 배치된 FactoryObject의 선택을 관리하는 싱글톤 클래스입니다.
/// 단일 선택, 다중 선택(추후 확장), 선택 해제 로직을 처리합니다.
///
///
/// 이 관리자는 다음 기능을 제공합니다:
/// - FactoryObject 클릭 시 선택 및 외곽선 표시.
/// - 다른 객체 선택 시 이전에 선택된 객체의 외곽선 숨김.
/// - UI가 아닌 빈 공간 클릭 시 모든 선택 해제.
/// - 다중 선택을 위한 기반 제공.
///
/// 이 클래스가 올바르게 작동하려면 씬에 Unity의 EventSystem이 존재해야 합니다.
///
///
///
/// // FactoryObjectSelectionManager는 자동으로 씬에 생성되므로 별도의 인스턴스화가 필요 없습니다.
///
/// // FactoryObject에서 선택을 요청하는 방법:
/// public class MyFactoryObject : FactoryObject
/// {
/// public override void OnPointerClick(PointerEventData eventData)
/// {
/// // base.OnPointerClick(eventData); // InfoWindow를 표시하려면 기본 로직 호출
///
/// // 다중 선택을 원하면 (예: Shift 키 누름) isMultiSelect를 true로 설정
/// bool isMultiSelect = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
///
/// // FactoryObjectSelectionManager에 선택 요청
/// FactoryObjectSelectionManager.Instance.Select(this, isMultiSelect);
/// }
/// }
///
/// // 외부에서 선택된 객체를 가져오는 방법:
/// public class UIManager : MonoBehaviour
/// {
/// void Update()
/// {
/// if (Input.GetKeyDown(KeyCode.I))
/// {
/// var selectedObjects = FactoryObjectSelectionManager.Instance.GetSelectedObjects();
/// if (selectedObjects.Any())
/// {
/// Debug.Log($"현재 {selectedObjects.Count}개의 객체가 선택되었습니다.");
/// foreach (var obj in selectedObjects)
/// {
/// Debug.Log($"- {obj.Info?.Name}");
/// }
/// }
/// else
/// {
/// Debug.Log("선택된 객체가 없습니다.");
/// }
/// }
/// }
/// }
///
///
public class FactoryObjectSelectionManager : MonoBehaviour
{
private static FactoryObjectSelectionManager? _instance;
private static readonly object _lock = new object();
private static bool _applicationIsQuitting = false;
///
/// SelectionManager의 싱글톤 인스턴스를 가져옵니다.
/// 씬에 인스턴스가 없으면 자동으로 생성합니다.
///
public static FactoryObjectSelectionManager Instance
{
get
{
if (_applicationIsQuitting)
{
// 애플리케이션 종료 시점에 이미 파괴된 싱글톤에 접근하는 것을 방지합니다.
Debug.LogWarning("[Singleton] Instance 'FactoryObjectSelectionManager' already destroyed on application quit. Won't create again - returning null.");
return null!;
}
lock (_lock)
{
if (_instance == null)
{
// 씬에서 기존 인스턴스를 찾아봅니다.
_instance = FindFirstObjectByType();
if (_instance == null)
{
// 씬에 인스턴스가 없으면 새로 생성합니다.
var singletonObject = new GameObject();
_instance = singletonObject.AddComponent();
singletonObject.name = typeof(FactoryObjectSelectionManager).ToString() + " (Singleton)";
// 씬 전환 시 파괴되지 않도록 설정합니다.
DontDestroyOnLoad(singletonObject);
}
}
return _instance;
}
}
}
// 현재 선택된 객체들을 저장하는 리스트입니다.
private readonly List _selectedObjects = new List();
private void Awake()
{
// 싱글톤 인스턴스가 중복으로 생성되는 것을 방지합니다.
if (_instance != null && _instance != this)
{
Debug.LogWarning("Another instance of FactoryObjectSelectionManager exists, destroying this one.");
Destroy(gameObject);
}
}
private void Update()
{
// 마우스 왼쪽 버튼 클릭을 감지합니다.
if (Input.GetMouseButtonDown(0))
{
// 포인터가 UI 요소 위에 있는지 확인합니다. UI 클릭 시에는 선택/해제 로직을 무시합니다.
//Debug.Log($"IsPointerOverUIObject() : {IsPointerOverUIObject()}");
if (IsPointerOverUIObject())
{
return;
}
// 메인 카메라에서 마우스 위치로 레이를 쏩니다.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
bool raycastHit = Physics.Raycast(ray, out RaycastHit hit);
Debug.Log($"Raycast : {raycastHit}");
// 레이캐스트로 무언가 감지되었는지 확인합니다.
if (raycastHit)
{
// 감지된 객체 또는 그 부모 중에 FactoryObject 컴포넌트가 있는지 확인합니다.
if (hit.collider.GetComponentInParent() == null)
{
// FactoryObject가 아닌 다른 객체를 클릭한 경우, 모든 선택을 해제합니다.
DeselectAll();
}
// FactoryObject를 클릭한 경우는 FactoryObject의 OnPointerClick에서 처리하므로 여기서는 별도 처리를 하지 않습니다.
}
else
{
// 레이캐스트로 아무것도 감지되지 않았을 경우 (빈 공간 클릭), 모든 선택을 해제합니다.
DeselectAll();
}
}
}
///
/// 포인터가 UI 객체 위에 있는지 확인합니다.
///
/// UI 객체 위에 있으면 true, 그렇지 않으면 false를 반환합니다.
private bool IsPointerOverUIObject()
{
// EventSystem이 없는 경우 false를 반환합니다.
if (EventSystem.current == null) return false;
// 현재 포인터 위치에 대한 이벤트 데이터를 생성합니다.
PointerEventData eventData = new PointerEventData(EventSystem.current)
{
position = Input.mousePosition
};
// 레이캐스트 결과를 저장할 리스트를 생성합니다.
List results = new List();
// 현재 포인터 위치에 있는 모든 UI 객체를 가져옵니다.
EventSystem.current.RaycastAll(eventData, results);
bool hasCanvas = false;
foreach (var result in results)
{
if(result.gameObject.GetComponentInParent