115 lines
3.5 KiB
C#
115 lines
3.5 KiB
C#
|
|
#nullable enable
|
|||
|
|
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using TMPro;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
using UVC.Core;
|
|||
|
|
|
|||
|
|
namespace UVC.UI.Info
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 3D 객체를 따라다니며 정보를 표시하는 UI 창입니다.
|
|||
|
|
/// 이 컴포넌트는 World Space Canvas 내의 UI 요소에 추가되어야 합니다.
|
|||
|
|
/// </summary>
|
|||
|
|
public class InfoWindow : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
[Tooltip("InfoWindow의 프리팹")]
|
|||
|
|
[SerializeField]
|
|||
|
|
private static GameObject infoWindowPrefab;
|
|||
|
|
|
|||
|
|
[Tooltip("정보 텍스트를 표시할 UI 요소")]
|
|||
|
|
[SerializeField]
|
|||
|
|
private TextMeshProUGUI infoText;
|
|||
|
|
|
|||
|
|
[Tooltip("정보 창을 닫을 버튼")]
|
|||
|
|
[SerializeField]
|
|||
|
|
private Button closeButton;
|
|||
|
|
|
|||
|
|
[Tooltip("UI가 객체를 가리지 않도록 할 월드 좌표계 오프셋")]
|
|||
|
|
[SerializeField]
|
|||
|
|
private Vector3 worldOffset = new Vector3(0, 1.5f, 0);
|
|||
|
|
|
|||
|
|
// 정보 창이 따라다닐 3D 객체의 Transform
|
|||
|
|
private Transform? target;
|
|||
|
|
|
|||
|
|
// 메인 카메라 참조
|
|||
|
|
private Camera mainCamera;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 정보 창이 현재 화면에 표시되고 있는지 여부를 반환합니다.
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsVisible => gameObject.activeSelf;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 정보 창을 생성하여 반환합니다.
|
|||
|
|
/// </summary>
|
|||
|
|
public static InfoWindow? Create()
|
|||
|
|
{
|
|||
|
|
if(infoWindowPrefab == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError("InfoWindow 프리팹이 할당되지 않았습니다. Inspector에서 할당해주세요.");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
return Instantiate(infoWindowPrefab).GetComponent<InfoWindow>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Awake()
|
|||
|
|
{
|
|||
|
|
mainCamera = Camera.main;
|
|||
|
|
|
|||
|
|
// 닫기 버튼이 할당되었으면 클릭 이벤트를 연결합니다.
|
|||
|
|
if (closeButton != null)
|
|||
|
|
{
|
|||
|
|
closeButton.onClick.AddListener(Hide);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 처음에는 정보 창을 숨깁니다.
|
|||
|
|
if (gameObject.activeSelf)
|
|||
|
|
{
|
|||
|
|
Hide();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void LateUpdate()
|
|||
|
|
{
|
|||
|
|
// target이 설정되어 있고 활성화 상태일 때만 위치와 방향을 업데이트합니다.
|
|||
|
|
if (target != null && gameObject.activeSelf)
|
|||
|
|
{
|
|||
|
|
// 위치 업데이트
|
|||
|
|
transform.position = target.position + worldOffset;
|
|||
|
|
|
|||
|
|
// 항상 카메라를 바라보도록 방향 업데이트 (빌보드 효과)
|
|||
|
|
transform.rotation = mainCamera.transform.rotation;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 정보 창을 특정 대상에 대해 표시합니다.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="targetObject">정보를 표시할 3D 객체의 Transform</param>
|
|||
|
|
/// <param name="information">표시할 정보 문자열</param>
|
|||
|
|
public void Show(Transform targetObject, Dictionary<string, object> information)
|
|||
|
|
{
|
|||
|
|
target = targetObject;
|
|||
|
|
if (infoText != null)
|
|||
|
|
{
|
|||
|
|
infoText.text = information.ToString();
|
|||
|
|
}
|
|||
|
|
gameObject.SetActive(true);
|
|||
|
|
|
|||
|
|
// 즉시 위치와 방향을 업데이트합니다.
|
|||
|
|
LateUpdate();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 정보 창을 숨깁니다.
|
|||
|
|
/// </summary>
|
|||
|
|
public void Hide()
|
|||
|
|
{
|
|||
|
|
gameObject.SetActive(false);
|
|||
|
|
target = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|