알람 수정 중
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8822a5b71db85f64483ede945af84867
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,203 +0,0 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
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 : SingletonScene<InfoWindow>
|
||||
{
|
||||
|
||||
[Tooltip("Label 정보 텍스트를 표시할 UI 요소")]
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI text;
|
||||
|
||||
[Tooltip("정보 창을 닫는 버튼")]
|
||||
[SerializeField]
|
||||
private Button closeButton;
|
||||
|
||||
[Tooltip("정보 창의 내용을 클립보드에 복사할 버튼")]
|
||||
[SerializeField]
|
||||
private Button copyButton;
|
||||
|
||||
[Tooltip("UI가 객체를 가리지 않도록 할 월드 좌표계 오프셋")]
|
||||
[SerializeField]
|
||||
private Vector2 screenOffset = new Vector2(10f, 10f);
|
||||
|
||||
[Tooltip("UI가 객체를 가리지 않도록 할 메뉴바 높이")]
|
||||
[SerializeField]
|
||||
private float menuBarHeight = 70f;
|
||||
|
||||
// 정보 창이 따라다닐 3D 객체의 Transform
|
||||
private Transform? target;
|
||||
|
||||
private string message = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 정보 창이 현재 화면에 표시되고 있는지 여부를 반환합니다.
|
||||
/// </summary>
|
||||
public bool IsVisible => gameObject.activeSelf;
|
||||
|
||||
private RectTransform? rectTransform;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
rectTransform = GetComponent<RectTransform>();
|
||||
|
||||
// 닫기 버튼이 할당되었으면 클릭 이벤트를 연결합니다.
|
||||
if (closeButton != null)
|
||||
{
|
||||
closeButton.onClick.AddListener(Hide);
|
||||
}
|
||||
|
||||
if (copyButton != null)
|
||||
{
|
||||
copyButton.onClick.AddListener(CopyToClipboard);
|
||||
}
|
||||
|
||||
|
||||
// 처음에는 정보 창을 숨깁니다.
|
||||
if (gameObject.activeSelf)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyToClipboard()
|
||||
{
|
||||
// 클립보드에 현재 메시지를 복사합니다.
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
GUIUtility.systemCopyBuffer = message;
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
// target이 설정되어 있고 활성화 상태일 때만 위치와 방향을 업데이트합니다.
|
||||
if (target != null && gameObject.activeSelf && Camera.main != null)
|
||||
{
|
||||
// 타겟의 렌더러 또는 콜라이더를 가져와 화면 상의 크기를 계산
|
||||
Bounds bounds = new Bounds(target.position, Vector3.one);
|
||||
Renderer? renderer = target.GetComponent<Renderer>();
|
||||
Collider? collider = target.GetComponent<Collider>();
|
||||
|
||||
// 렌더러가 있으면 렌더러의 바운드를, 없으면 콜라이더의 바운드를 사용
|
||||
if (renderer != null)
|
||||
{
|
||||
bounds = renderer.bounds;
|
||||
}
|
||||
else if (collider != null)
|
||||
{
|
||||
bounds = collider.bounds;
|
||||
}
|
||||
|
||||
// 바운드의 오른쪽 지점을 월드 좌표로 계산
|
||||
Vector3 rightPoint = bounds.center + new Vector3(bounds.extents.x, 0, 0);
|
||||
|
||||
// 바운드의 오른쪽 지점을 스크린 좌표로 변환
|
||||
Vector3 screenPosRight = Camera.main.WorldToScreenPoint(rightPoint);
|
||||
|
||||
// 추가 오프셋 적용
|
||||
screenPosRight.x += screenOffset.x;
|
||||
screenPosRight.y += screenOffset.y;
|
||||
// 메뉴바 영역 고려 및 화면 밖으로 나가지 않도록 제한
|
||||
screenPosRight.x = Mathf.Clamp(screenPosRight.x, 0, Screen.width - rectTransform!.rect.width);
|
||||
screenPosRight.y = Mathf.Clamp(screenPosRight.y, rectTransform!.rect.height, Screen.height - menuBarHeight);
|
||||
|
||||
// RectTransform을 사용하여 UI 위치 설정
|
||||
// 캔버스의 렌더링 모드에 따라 다르게 처리
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
rectTransform.position = screenPosRight;
|
||||
}
|
||||
else if (canvas.renderMode == RenderMode.ScreenSpaceCamera ||
|
||||
canvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
// 스크린 좌표를 캔버스 상의 로컬 좌표로 변환
|
||||
Vector2 localPoint;
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
canvas.GetComponent<RectTransform>(),
|
||||
screenPosRight,
|
||||
canvas.renderMode == RenderMode.ScreenSpaceCamera ? canvas.worldCamera : Camera.main,
|
||||
out localPoint);
|
||||
rectTransform.localPosition = new Vector3(localPoint.x, localPoint.y, rectTransform.localPosition.z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// UI가 항상 보이도록 카메라를 향하게 설정 (World Space Canvas인 경우에만 필요)
|
||||
Canvas parentCanvas = GetComponentInParent<Canvas>();
|
||||
if (parentCanvas != null && parentCanvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
transform.LookAt(transform.position + Camera.main.transform.rotation * Vector3.forward,
|
||||
Camera.main.transform.rotation * Vector3.up);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 정보 창을 특정 대상에 대해 표시합니다.
|
||||
/// </summary>
|
||||
/// <param name="targetObject">정보를 표시할 3D 객체의 Transform</param>
|
||||
/// <param name="information">표시할 정보 문자열</param>
|
||||
public void Show(Transform targetObject, Dictionary<string, object> information)
|
||||
{
|
||||
target = targetObject;
|
||||
|
||||
UpdateInformation(information);
|
||||
|
||||
gameObject.SetActive(true);
|
||||
|
||||
// 즉시 위치와 방향을 업데이트합니다.
|
||||
LateUpdate();
|
||||
}
|
||||
|
||||
public void UpdateInformation(Dictionary<string, object> information)
|
||||
{
|
||||
if (target == null) return;
|
||||
if (text != null)
|
||||
{
|
||||
string combinedString = string.Empty;
|
||||
foreach (var kvp in information)
|
||||
{
|
||||
// <indent> 태그를 사용하여 줄바꿈 시에도 정렬이 유지되도록 합니다.
|
||||
combinedString += $"{kvp.Key}<pos=40%><indent=40%>{kvp.Value ?? "null"}</indent>\n";
|
||||
}
|
||||
combinedString = combinedString.TrimEnd('\n'); // 마지막 줄바꿈 제거
|
||||
text.text = combinedString;
|
||||
message = combinedString;
|
||||
}
|
||||
// size를 text에 맞게 조정합니다.
|
||||
RectTransform? rect = GetComponent<RectTransform>();
|
||||
if (rect != null && text != null)
|
||||
{
|
||||
RectTransform textRect = text.GetComponent<RectTransform>();
|
||||
float marginHeight = rect.rect.height - textRect.rect.height; // 상하 여백
|
||||
rect.sizeDelta = new Vector2(rect.rect.width, text.preferredHeight + marginHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 정보 창을 숨깁니다.
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 620e52b408949c340adef1110323cb7c
|
||||
115
Assets/Scripts/UVC/UI/Modal/Toast.cs
Normal file
115
Assets/Scripts/UVC/UI/Modal/Toast.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System.Collections;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace UVC.UI.Modal
|
||||
{
|
||||
public class Toast : SingletonApp<Toast>
|
||||
{
|
||||
protected static string prefabPath = "Prefabs/UI/Modal/Toast";
|
||||
protected static GameObject prefab;
|
||||
protected static Toast toast;
|
||||
|
||||
|
||||
public static void Show(string message, float duration = 3f, float fadeInOutTime = 0.25f)
|
||||
{
|
||||
if (prefab == null) prefab = Resources.Load(prefabPath, typeof(GameObject)) as GameObject;
|
||||
if (prefab != null && toast == null)
|
||||
{
|
||||
GameObject go = UnityEngine.Object.Instantiate(prefab);
|
||||
toast = go.GetComponent<Toast>();
|
||||
toast.change(0);
|
||||
}
|
||||
if (toast != null)
|
||||
{
|
||||
toast.gameObject.SetActive(true);
|
||||
var canvass = GameObject.FindObjectsByType<Canvas>(FindObjectsSortMode.None);
|
||||
Canvas canvas = null;
|
||||
foreach (var c in canvass)
|
||||
{
|
||||
if (c.tag == "ModalCanvas")
|
||||
{
|
||||
canvas = c;
|
||||
toast.transform.SetParent(canvas.transform);
|
||||
toast.show(message, duration, fadeInOutTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected CanvasGroup canvasGroup;
|
||||
protected TextMeshProUGUI txt;
|
||||
|
||||
protected void show(string message, float duration = 3, float fadeInOutTime = 0.25f)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(showCoroutine(message, duration, fadeInOutTime));
|
||||
}
|
||||
|
||||
protected IEnumerator showCoroutine(string message, float duration = 3, float fadeInOutTime = 0.25f)
|
||||
{
|
||||
if (txt == null) txt = GetComponentInChildren<TextMeshProUGUI>();
|
||||
fitTextWidth(GetComponent<RectTransform>(), txt, message);
|
||||
|
||||
yield return fadeInOut(fadeInOutTime, true);
|
||||
change(1);
|
||||
float elapsedTime = 0.0f;
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
yield return fadeInOut(fadeInOutTime, false);
|
||||
change(0);
|
||||
transform.SetParent(null);
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
protected IEnumerator fadeInOut(float durationTime, bool inOut)
|
||||
{
|
||||
float start = 0.0f;
|
||||
float end = 1.0f;
|
||||
if (!inOut)
|
||||
{
|
||||
start = 1.0f;
|
||||
end = 0.0f;
|
||||
}
|
||||
|
||||
float elapsedTime = 0.0f;
|
||||
|
||||
while (elapsedTime < durationTime)
|
||||
{
|
||||
float alpha = Mathf.Lerp(start, end, elapsedTime / durationTime);
|
||||
|
||||
change(alpha);
|
||||
|
||||
elapsedTime += Time.deltaTime;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void change(float alpha)
|
||||
{
|
||||
if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
|
||||
canvasGroup.alpha = alpha;
|
||||
}
|
||||
|
||||
private void fitTextWidth(RectTransform rect, TextMeshProUGUI text, string textValue)
|
||||
{
|
||||
text.text = textValue;
|
||||
|
||||
Vector2 rectSize = rect.sizeDelta;
|
||||
rectSize.x = text.preferredWidth + 20;
|
||||
rect.sizeDelta = rectSize;
|
||||
rect.anchoredPosition = new Vector2(0, rectSize.y + 10);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/Modal/Toast.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/Modal/Toast.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d007f3f774d241a44a69d7349dd7c464
|
||||
Reference in New Issue
Block a user