Files
2026-08-17 18:18:13 +08:00

172 lines
6.7 KiB
C#

#nullable enable
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace ReplacedPerson.Runtime
{
[DisallowMultipleComponent]
public sealed class ReplacedBattlePhaseMotion : MonoBehaviour
{
private RectTransform? _panel;
private CanvasGroup? _group;
private Image? _background;
private Image? _accent;
private TMP_Text? _title;
private TMP_Text? _detail;
public bool IsVisible => _panel != null && _panel.gameObject.activeSelf;
public string CurrentTitle => _title?.text ?? string.Empty;
private void Awake()
{
CreateOverlay();
SetVisible(false);
}
public IEnumerator PlayStage(string title, string detail, Color accent, float holdSeconds = .68f)
{
if (!Prepare(title, detail, accent)) yield break;
yield return FadeIn();
yield return new WaitForSecondsRealtime(holdSeconds);
yield return FadeOut();
}
public IEnumerator PlayDie(string title, int finalFace, string result, Color accent)
{
if (!Prepare(title, "D6 ·", accent)) yield break;
yield return FadeIn();
for (var frame = 0; frame < 8; frame++)
{
if (_detail != null) _detail.text = "D6 " + ((frame * 5 + 2) % 6 + 1);
if (_panel != null) _panel.localRotation = Quaternion.Euler(0, 0, frame % 2 == 0 ? -1.2f : 1.2f);
yield return new WaitForSecondsRealtime(.065f);
}
if (_panel != null) _panel.localRotation = Quaternion.identity;
if (_detail != null) _detail.text = $"D6 {finalFace}\n{result}";
yield return new WaitForSecondsRealtime(.75f);
yield return FadeOut();
}
private bool Prepare(string title, string detail, Color accent)
{
if (_panel == null || _group == null || _background == null || _accent == null || _title == null || _detail == null)
return false;
_title.text = title;
_detail.text = detail;
_accent.color = accent;
_background.color = new Color(.035f, .041f, .043f, .96f);
_panel.localScale = Vector3.one * .92f;
_panel.localRotation = Quaternion.identity;
_group.alpha = 0f;
SetVisible(true);
_panel.SetAsLastSibling();
return true;
}
private IEnumerator FadeIn()
{
if (_panel == null || _group == null) yield break;
var elapsed = 0f;
const float duration = .16f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
var t = 1f - Mathf.Pow(1f - Mathf.Clamp01(elapsed / duration), 3f);
_group.alpha = t;
_panel.localScale = Vector3.one * Mathf.Lerp(.92f, 1f, t);
yield return null;
}
_group.alpha = 1f;
_panel.localScale = Vector3.one;
}
private IEnumerator FadeOut()
{
if (_panel == null || _group == null) yield break;
var elapsed = 0f;
const float duration = .16f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
var t = Mathf.Clamp01(elapsed / duration);
_group.alpha = 1f - t;
_panel.localScale = Vector3.one * Mathf.Lerp(1f, .97f, t);
yield return null;
}
SetVisible(false);
}
private void CreateOverlay()
{
var panelObject = new GameObject("ResolutionPhaseOverlay", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(CanvasGroup));
_panel = panelObject.GetComponent<RectTransform>();
_panel.SetParent(transform, false);
_panel.anchorMin = new Vector2(.25f, .37f);
_panel.anchorMax = new Vector2(.75f, .64f);
_panel.offsetMin = Vector2.zero;
_panel.offsetMax = Vector2.zero;
_background = panelObject.GetComponent<Image>();
_background.raycastTarget = false;
_group = panelObject.GetComponent<CanvasGroup>();
_group.blocksRaycasts = false;
_group.interactable = false;
var accentObject = new GameObject("Accent", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
var accentRect = accentObject.GetComponent<RectTransform>();
accentRect.SetParent(_panel, false);
accentRect.anchorMin = new Vector2(0, 0);
accentRect.anchorMax = new Vector2(0, 1);
accentRect.pivot = new Vector2(0, .5f);
accentRect.sizeDelta = new Vector2(9, 0);
_accent = accentObject.GetComponent<Image>();
_accent.raycastTarget = false;
var font = GetComponentInChildren<TMP_Text>(true)?.font;
_title = CreateText("Title", _panel, font, new Vector2(.08f, .55f), new Vector2(.92f, .92f), 31, FontStyles.Bold);
_detail = CreateText("Detail", _panel, font, new Vector2(.08f, .10f), new Vector2(.92f, .58f), 22, FontStyles.Normal);
_detail.enableAutoSizing = true;
_detail.fontSizeMin = 14;
_detail.fontSizeMax = 22;
}
private static TMP_Text CreateText(string name, Transform parent, TMP_FontAsset? font, Vector2 anchorMin,
Vector2 anchorMax, float fontSize, FontStyles style)
{
var textObject = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
var rect = textObject.GetComponent<RectTransform>();
rect.SetParent(parent, false);
rect.anchorMin = anchorMin;
rect.anchorMax = anchorMax;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
var text = textObject.GetComponent<TextMeshProUGUI>();
text.font = font;
text.fontSize = fontSize;
text.fontStyle = style;
text.alignment = TextAlignmentOptions.Center;
text.color = new Color(.95f, .93f, .87f, 1f);
text.raycastTarget = false;
return text;
}
private void SetVisible(bool visible)
{
if (_panel != null) _panel.gameObject.SetActive(visible);
}
private void OnDisable()
{
if (_panel != null)
{
_panel.localScale = Vector3.one;
_panel.localRotation = Quaternion.identity;
}
if (_group != null) _group.alpha = 0f;
SetVisible(false);
}
}
}