108 lines
4.1 KiB
C#
108 lines
4.1 KiB
C#
using System.Collections.Generic;
|
||
using Ichni.Story.Dialogue;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
|
||
namespace Ichni.Story.UI
|
||
{
|
||
/// <summary>
|
||
/// 文本块视图。点击后(阶段 2)将通过 Yarn Spinner 进入对应节点的对话。
|
||
/// </summary>
|
||
public class TextBlockView : StoryBlockView
|
||
{
|
||
[Header("Text Block")]
|
||
public TMP_Text storyIdText;
|
||
public TMP_Text titleText;
|
||
|
||
[Tooltip("各状态对应的背景预制体。未配置 Forbidden 时会自动回退到 Locked 背景。")]
|
||
public Dictionary<StoryBlockState, GameObject> blockVisuals = new Dictionary<StoryBlockState, GameObject>();
|
||
|
||
/// <summary>该文本块对应的 Yarn 节点名。</summary>
|
||
public string YarnNodeName { get; private set; }
|
||
|
||
// 当前已实例化的状态背景对象;状态切换时销毁并替换
|
||
private GameObject _currentVisual;
|
||
|
||
public override void Initialize(StoryBlockDefinition def, Vector2 position, StoryBlockState blockState)
|
||
{
|
||
// 注意:base.Initialize 末尾会调用 ApplyState(state),届时已生成对应背景
|
||
base.Initialize(def, position, blockState);
|
||
|
||
YarnNodeName = def.yarnNodeName;
|
||
|
||
if (storyIdText != null)
|
||
storyIdText.text = def.blockId;
|
||
|
||
// 阶段 5 将接入 Unity Localization;此处占位直接显示 titleKey
|
||
if (titleText != null)
|
||
titleText.text = def.titleKey;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用状态:在基类逻辑(按钮可交互性)之外,切换到对应状态的背景视觉。
|
||
/// </summary>
|
||
public override void ApplyState(StoryBlockState newState)
|
||
{
|
||
base.ApplyState(newState);
|
||
UpdateVisual(newState);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按当前状态实例化对应的背景预制体,作为第一个子物体(位于文本/端口/按钮之下),
|
||
/// 并拉伸铺满整个 block。状态切换时先销毁旧背景。
|
||
/// </summary>
|
||
private void UpdateVisual(StoryBlockState newState)
|
||
{
|
||
if (_currentVisual != null)
|
||
{
|
||
Destroy(_currentVisual);
|
||
_currentVisual = null;
|
||
}
|
||
|
||
if (blockVisuals == null)
|
||
{
|
||
Debug.LogWarning($"[TextBlockView] block '{blockId}' 缺少状态 {newState} 的背景预制体(blockVisuals 未配置)。");
|
||
return;
|
||
}
|
||
|
||
// Forbidden 专用美术尚未配置前沿用 Locked 背景,保证互斥路线 Block 始终可见且不可点击。
|
||
// 后续只需在 Inspector 中补充 Forbidden 键,即可自动覆盖此回退表现。
|
||
if ((!blockVisuals.TryGetValue(newState, out GameObject prefab) || prefab == null) &&
|
||
newState == StoryBlockState.Forbidden)
|
||
{
|
||
blockVisuals.TryGetValue(StoryBlockState.Locked, out prefab);
|
||
}
|
||
|
||
if (prefab == null)
|
||
{
|
||
Debug.LogWarning($"[TextBlockView] block '{blockId}' 缺少状态 {newState} 的背景预制体。");
|
||
return;
|
||
}
|
||
|
||
_currentVisual = Instantiate(prefab, blockRect);
|
||
_currentVisual.transform.SetAsFirstSibling();
|
||
|
||
// 背景铺满整个 block
|
||
if (_currentVisual.transform is RectTransform visualRect)
|
||
{
|
||
visualRect.anchorMin = Vector2.zero;
|
||
visualRect.anchorMax = Vector2.one;
|
||
visualRect.offsetMin = Vector2.zero;
|
||
visualRect.offsetMax = Vector2.zero;
|
||
visualRect.localScale = Vector3.one;
|
||
}
|
||
}
|
||
|
||
protected override void OnClicked()
|
||
{
|
||
if (StoryDialogueController.instance == null)
|
||
{
|
||
Debug.LogWarning($"[TextBlockView] 场景中缺少 StoryDialogueController,无法播放 block '{blockId}' 的对话。");
|
||
return;
|
||
}
|
||
|
||
StoryDialogueController.instance.PlayBlock(this);
|
||
}
|
||
}
|
||
}
|