263 lines
10 KiB
C#
263 lines
10 KiB
C#
using DG.Tweening;
|
|
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UVC.Data;
|
|
using UVC.Factory.Component;
|
|
using UVC.Util;
|
|
|
|
namespace UVC.Factory.Alarm
|
|
{
|
|
public class AlarmUIController : MonoBehaviour, IPointerClickHandler
|
|
{
|
|
[Tooltip("알람 클러스터 뷰입니다.")]
|
|
[SerializeField] private GameObject clusterView;
|
|
[Tooltip("알람 개수를 표시하는 텍스트입니다.")]
|
|
[SerializeField] private TextMeshProUGUI alarmCountText;
|
|
[Tooltip("확장된 알람 뷰입니다. 개별 알람 아이콘을 표시합니다.")]
|
|
[SerializeField] private GameObject expandedView;
|
|
[Tooltip("개별 알람 아이콘 프리팹입니다. 이 프리팹은 개별 알람 정보를 표시하는 UI 요소를 포함해야 합니다.")]
|
|
[SerializeField] private GameObject alarmSingleIconPrefab; // 개별 알람 아이콘
|
|
|
|
private Transform targetObject;
|
|
private List<DataObject> alarms = new List<DataObject>();
|
|
private bool isExpanded = false;
|
|
private AlarmSingleIcon singleAlarmIcon1 = null;
|
|
|
|
private RectTransform rectTransform;
|
|
private Canvas mainCanvas;
|
|
|
|
private float uiSpacing = 20f; // 간격
|
|
private Tweener uiSpacingTweener;
|
|
|
|
private bool isZoomIn = false; // 줌 인 상태를 추적하기 위한 변수
|
|
|
|
private void Awake()
|
|
{
|
|
rectTransform = GetComponent<RectTransform>();
|
|
mainCanvas = GetComponentInParent<Canvas>();
|
|
}
|
|
|
|
public void Initialize(FactoryObject target, DataObject initialAlarm)
|
|
{
|
|
this.targetObject = target.transform;
|
|
AddAlarm(initialAlarm);
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (targetObject == null || Camera.main == null || mainCanvas == null)
|
|
{
|
|
// 필수 컴포넌트가 없으면 UI를 비활성화합니다.
|
|
if (clusterView.activeSelf) clusterView.SetActive(false);
|
|
if (expandedView.activeSelf) expandedView.SetActive(false);
|
|
if (alarmCountText != null && alarmCountText.gameObject.activeSelf) alarmCountText.gameObject.SetActive(false);
|
|
if (singleAlarmIcon1 != null && singleAlarmIcon1.gameObject.activeSelf) singleAlarmIcon1.gameObject.SetActive(false);
|
|
return;
|
|
}
|
|
|
|
// 카메라의 정면 방향과 타겟을 향하는 방향을 계산합니다.
|
|
Vector3 cameraForward = Camera.main.transform.forward;
|
|
Vector3 toTarget = (targetObject.position - Camera.main.transform.position).normalized;
|
|
|
|
// 두 벡터의 내적을 계산하여 타겟이 카메라 앞에 있는지 확인합니다.
|
|
// 내적 값이 0보다 크면 타겟이 카메라 앞에 있는 것입니다.
|
|
if (Vector3.Dot(cameraForward, toTarget) > 0)
|
|
{
|
|
// 타겟이 앞에 있을 때만 UI를 활성화하고 위치를 업데이트합니다.
|
|
if (!gameObject.activeSelf)
|
|
{
|
|
UpdateView(); // 비활성화 상태였다면 뷰를 다시 활성화합니다.
|
|
}
|
|
|
|
// targetObject의 월드 좌표를 스크린 좌표로 변환
|
|
Vector3 screenPoint = Camera.main.WorldToScreenPoint(targetObject.position + Vector3.up);
|
|
|
|
// Canvas Render Mode가 Screen Space - Camera일 경우
|
|
if (mainCanvas.renderMode == RenderMode.ScreenSpaceCamera)
|
|
{
|
|
Vector2 localPoint;
|
|
RectTransformUtility.ScreenPointToLocalPointInRectangle(mainCanvas.transform as RectTransform, screenPoint, mainCanvas.worldCamera, out localPoint);
|
|
localPoint.y += uiSpacing;
|
|
rectTransform.localPosition = localPoint;
|
|
}
|
|
// Canvas Render Mode가 Screen Space - Overlay일 경우
|
|
else
|
|
{
|
|
screenPoint.y += uiSpacing;
|
|
rectTransform.position = screenPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 타겟이 카메라 뒤에 있으면 모든 관련 UI를 비활성화합니다.
|
|
if (clusterView.activeSelf) clusterView.SetActive(false);
|
|
if (expandedView.activeSelf) expandedView.SetActive(false);
|
|
if (alarmCountText != null && alarmCountText.gameObject.activeSelf) alarmCountText.gameObject.SetActive(false);
|
|
if (singleAlarmIcon1 != null && singleAlarmIcon1.gameObject.activeSelf) singleAlarmIcon1.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
public bool ContainsAlarm(DataObject alarm)
|
|
{
|
|
return alarms.Exists(a => a.Id == alarm.Id);
|
|
}
|
|
|
|
public void AddAlarm(DataObject alarm)
|
|
{
|
|
alarms.Add(alarm);
|
|
UpdateView();
|
|
}
|
|
|
|
public void UpdateAlarm(DataObject alarm)
|
|
{
|
|
// 실제로는 alarmId로 찾아서 업데이트해야 함
|
|
int index = alarms.FindIndex(a => a.Id == alarm.Id);
|
|
if (index >= 0)
|
|
{
|
|
foreach (var key in alarm.Keys)
|
|
{
|
|
alarms[index][key] = alarm[key]; // 기존 알람 데이터 업데이트
|
|
}
|
|
UpdateView();
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"Alarm with ID {alarm.Id} not found for update.");
|
|
}
|
|
}
|
|
|
|
public void RemoveAlarm(DataObject alarm)
|
|
{
|
|
// 실제로는 alarmId로 찾아서 지워야 함
|
|
alarms.Remove(alarm);
|
|
UpdateView();
|
|
}
|
|
|
|
private void UpdateView()
|
|
{
|
|
if (isExpanded)
|
|
{
|
|
// 확장된 상태라면 개별 아이콘들을 다시 그림
|
|
ExpandCluster();
|
|
}
|
|
else
|
|
{
|
|
// 일반 상태
|
|
if (alarms.Count > 1)
|
|
{
|
|
clusterView.SetActive(true);
|
|
expandedView.SetActive(false);
|
|
if (singleAlarmIcon1 != null) singleAlarmIcon1.gameObject.SetActive(false); // 단일 뷰도 꺼야 함
|
|
alarmCountText.gameObject.SetActive(true);
|
|
alarmCountText.text = alarms.Count.ToString();
|
|
}
|
|
else if (alarms.Count == 1)
|
|
{
|
|
clusterView.SetActive(false);
|
|
expandedView.SetActive(false);
|
|
alarmCountText.gameObject.SetActive(false); // 알람 개수 텍스트 숨김
|
|
// 여기에 단일 알람 아이콘을 보여주는 로직 추가
|
|
if (singleAlarmIcon1 == null)
|
|
{
|
|
singleAlarmIcon1 = Instantiate(alarmSingleIconPrefab, transform).GetComponent<AlarmSingleIcon>();
|
|
}
|
|
singleAlarmIcon1.gameObject.SetActive(true);
|
|
singleAlarmIcon1.SetData(alarms[0], targetObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnPointerClick() // 이 함수를 Event Trigger 등으로 호출
|
|
{
|
|
if (isExpanded)
|
|
{
|
|
CollapseCluster();
|
|
}
|
|
else
|
|
{
|
|
if (alarms.Count > 1)
|
|
{
|
|
isZoomIn = true;
|
|
// 클러스터 확장
|
|
CameraController.Instance.FocusOnTarget(targetObject.position, 15.0f); // 예시: 5미터 거리로 줌
|
|
ExpandCluster();
|
|
}
|
|
else if (alarms.Count == 1)
|
|
{
|
|
isZoomIn = true;
|
|
//AnimateUISpace(1f); // 간격을 0으로 애니메이션
|
|
|
|
// 단일 알람 클릭
|
|
CameraController.Instance.FocusOnTarget(targetObject.position, 10.0f);
|
|
// 추가로 알람 상세 정보 UI를 띄울 수 있음
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ExpandCluster()
|
|
{
|
|
isExpanded = true;
|
|
clusterView.SetActive(false);
|
|
expandedView.SetActive(true);
|
|
singleAlarmIcon1.gameObject.SetActive(false);
|
|
|
|
// 기존 아이콘들 삭제
|
|
foreach (Transform child in expandedView.transform)
|
|
{
|
|
Destroy(child.gameObject);
|
|
}
|
|
|
|
// 원형으로 아이콘 배치
|
|
float radius = (alarms.Count - 1) * 20.0f; // Canvas 좌표계에 맞는 반지름 값
|
|
|
|
for (int i = 0; i < alarms.Count; i++)
|
|
{
|
|
float angle = i * Mathf.PI * 2f / alarms.Count;
|
|
Vector3 pos = new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0) * radius;
|
|
|
|
GameObject iconObj = Instantiate(alarmSingleIconPrefab, expandedView.transform);
|
|
iconObj.transform.localPosition = pos;
|
|
// iconObj의 AlarmSingleIcon 스크립트에 알람 데이터 전달
|
|
iconObj.GetComponent<AlarmSingleIcon>().SetData(alarms[i], targetObject);
|
|
}
|
|
|
|
//AnimateUISpace(0f); // 간격을 0으로 애니메이션
|
|
}
|
|
|
|
private void CollapseCluster()
|
|
{
|
|
isExpanded = false;
|
|
UpdateView();
|
|
}
|
|
|
|
public int GetAlarmCount() => alarms.Count;
|
|
|
|
public void OnPointerClick(PointerEventData eventData)
|
|
{
|
|
OnPointerClick();
|
|
}
|
|
|
|
private void AnimateUISpace(float targetSpacing, float duration = 1.0f)
|
|
{
|
|
if (uiSpacingTweener != null && uiSpacingTweener.IsActive() && uiSpacingTweener.IsPlaying())
|
|
{
|
|
uiSpacingTweener.Kill();
|
|
}
|
|
uiSpacingTweener = DOVirtual.Float(uiSpacing, targetSpacing, duration, (value) =>
|
|
{
|
|
uiSpacing = value;
|
|
});
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (uiSpacingTweener != null && uiSpacingTweener.IsActive() && uiSpacingTweener.IsPlaying())
|
|
{
|
|
uiSpacingTweener.Kill();
|
|
}
|
|
}
|
|
}
|
|
}
|