114 lines
6.5 KiB
C#
114 lines
6.5 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
|
// WLStatusText.cs — 상태 문구(「공격 속도 증가」 등) 월드 라벨 (#842)
|
|
// · 원본 데미지 HUD(NewGameUI 캔버스 · ScreenSpaceCamera)는 그 UI 카메라가 꺼져 있어 섬에서 보이지 않는다(2026-10-11 실측 · 데미지 숫자도 같은 상태).
|
|
// 그래서 섬 몹 라벨(「몬스터 n/10」 · WLIslandMobZones)과 같은 월드 TextMeshPro 로 머리 위에 띄운다 — 메인 카메라가 그린다.
|
|
// · 1.5 s 동안 떠오르며 뒤 절반에서 투명해진다. 같은 액터에 같은 문구가 살아 있으면 새로 띄우지 않고 시간만 되감는다(PD 「중복 재생 금지 · 시간 연장」).
|
|
// · 값(초·크기·상승 속도·페이드 시작 비율)은 WLStatus.json.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
namespace WL.Combat.Status
|
|
{
|
|
public static class WLStatusText
|
|
{
|
|
sealed class Item { public TextMeshPro tmp; public Actor actor; public string text; public Color color; public float startAt, lifeMax, yOff, rise, fadeStart; }
|
|
static readonly List<Item> s_items = new List<Item>(8);
|
|
static WLStatusTextRunner s_runner;
|
|
public static int Shown, Extended;
|
|
public static string LastLog = "";
|
|
|
|
public static void Show(Actor actor, string text, string colorTag, float seconds, float worldSize, float yOffset, float riseSpeed, float fadeStart)
|
|
{
|
|
if (actor == null || string.IsNullOrEmpty(text)) return;
|
|
float now = Time.time;
|
|
Color color = Color.white;
|
|
var m = System.Text.RegularExpressions.Regex.Match(colorTag ?? "", "#[0-9A-Fa-f]{6}");
|
|
if (m.Success) ColorUtility.TryParseHtmlString(m.Value, out color);
|
|
for (int i = 0; i < s_items.Count; i++)
|
|
{ // 같은 액터 · 같은 문구가 떠 있으면 연장
|
|
var e = s_items[i];
|
|
if (e.tmp == null || !e.tmp.gameObject.activeSelf || e.actor != actor || e.text != text) continue;
|
|
e.startAt = now; e.lifeMax = seconds; e.color = color; e.tmp.color = color;
|
|
Extended++; LastLog = "extend \"" + text + "\" → " + actor.name;
|
|
return;
|
|
}
|
|
var it = Get();
|
|
EnsureKoreanFont(it.tmp);
|
|
it.actor = actor; it.text = text; it.color = color; it.startAt = now; it.lifeMax = Mathf.Max(0.2f, seconds);
|
|
it.yOff = yOffset; it.rise = riseSpeed; it.fadeStart = Mathf.Clamp01(fadeStart);
|
|
it.tmp.text = text; it.tmp.fontSize = Mathf.Max(0.1f, worldSize); it.tmp.color = color;
|
|
Place(it, now);
|
|
it.tmp.gameObject.SetActive(true);
|
|
Shown++; LastLog = "show \"" + text + "\" → " + actor.name;
|
|
}
|
|
|
|
// TMP 기본 폰트(LiberationSans SDF)에는 한글이 없고 폴백도 0 이라 「공격 속도 증가」가 빨간 네모로 나온다(2026-10-11 실측).
|
|
// WLBossToken·WLEquipUi 와 같은 길로 인게임 한글 폰트(Font SDF)를 받아 붙인다 — 인게임에서 처음 띄울 때 받는다(타이틀 시점엔 아직 없다).
|
|
static TMP_FontAsset s_kr;
|
|
static void EnsureKoreanFont(TextMeshPro tmp)
|
|
{
|
|
if (tmp == null) return;
|
|
if (tmp.font != null && tmp.font.HasCharacter('가', true, true)) return;
|
|
if (s_kr == null || !s_kr.HasCharacter('가', true, true))
|
|
{
|
|
try { var kr = WL.Equip.WLEquipUi.ResolveKoreanFont(WL.Equip.WLEquipSettings.Instance); if (kr != null && kr.HasCharacter('가', true, true)) s_kr = kr; }
|
|
catch (System.Exception ex) { Debug.LogException(ex); }
|
|
}
|
|
if (s_kr != null) tmp.font = s_kr;
|
|
}
|
|
|
|
static Item Get()
|
|
{
|
|
for (int i = 0; i < s_items.Count; i++) if (s_items[i].tmp != null && !s_items[i].tmp.gameObject.activeSelf) return s_items[i];
|
|
if (s_runner == null) { var r = new GameObject("WL_StatusText"); Object.DontDestroyOnLoad(r); s_runner = r.AddComponent<WLStatusTextRunner>(); }
|
|
var go = new GameObject("WL_StatusTextItem");
|
|
go.transform.SetParent(s_runner.transform, false);
|
|
var tmp = go.AddComponent<TextMeshPro>(); // 섬 몹 라벨과 같은 기본 폰트(한글 포함)
|
|
tmp.alignment = TextAlignmentOptions.Center;
|
|
tmp.enableWordWrapping = false;
|
|
tmp.richText = false;
|
|
tmp.sortingOrder = 100;
|
|
go.SetActive(false);
|
|
var it = new Item { tmp = tmp };
|
|
s_items.Add(it);
|
|
return it;
|
|
}
|
|
|
|
static void Place(Item it, float now)
|
|
{
|
|
if (it.actor == null) return;
|
|
float t = now - it.startAt;
|
|
Vector3 p = it.actor.Get_Top_postion(); p.y += it.yOff + it.rise * t;
|
|
it.tmp.transform.position = p;
|
|
var cam = Camera.main;
|
|
if (cam != null) it.tmp.transform.rotation = cam.transform.rotation; // 빌보드
|
|
}
|
|
|
|
internal static void Tick()
|
|
{
|
|
float now = Time.time;
|
|
for (int i = 0; i < s_items.Count; i++)
|
|
{
|
|
var it = s_items[i];
|
|
if (it.tmp == null || !it.tmp.gameObject.activeSelf) continue;
|
|
float t = now - it.startAt;
|
|
if (it.actor == null || it.actor.IsDead() || !it.actor.gameObject.activeInHierarchy || t >= it.lifeMax) { it.tmp.gameObject.SetActive(false); continue; }
|
|
Place(it, now);
|
|
float k = it.lifeMax > 0f ? t / it.lifeMax : 1f;
|
|
float a = k <= it.fadeStart ? 1f : 1f - (k - it.fadeStart) / Mathf.Max(0.01f, 1f - it.fadeStart);
|
|
var c = it.color; c.a = Mathf.Clamp01(a); it.tmp.color = c;
|
|
}
|
|
}
|
|
|
|
/// <summary>프로브용 — 떠 있는 라벨 수.</summary>
|
|
public static int ActiveCount { get { int n = 0; for (int i = 0; i < s_items.Count; i++) if (s_items[i].tmp != null && s_items[i].tmp.gameObject.activeSelf) n++; return n; } }
|
|
}
|
|
|
|
public sealed class WLStatusTextRunner : MonoBehaviour
|
|
{
|
|
void LateUpdate() { WLStatusText.Tick(); }
|
|
}
|
|
}
|