Files
ichni_Official/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs
SoulliesOfficial 810d019619 剧情+对话完善
2026-07-21 15:24:42 -04:00

116 lines
4.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using Ichni.Story.Dialogue;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ichni.Story.UI
{
/// <summary>
/// 文本块视图。点击后通过 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),届时已生成对应背景
YarnNodeName = def.yarnNodeName;
base.Initialize(def, position, blockState);
if (storyIdText != null)
storyIdText.text = def.blockId;
// 当前仍直接显示 titleKey正式内容接入时需要由 Unity Localization 解析玩家可见标题。
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)
{
// 旧背景中的 Button 即将被销毁,先主动解绑,防止基类继续持有失效引用。
BindInteractionButton(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();
// 状态视觉可自由调整层级;不再依赖“第一个子物体就是 Button”的脆弱约定。
// 通过基类重新绑定,确保每一次状态背景替换后 TextBlock 仍能响应点击。
Button interactionButton = _currentVisual.GetComponentInChildren<Button>(true);
if (interactionButton == null)
Debug.LogWarning($"[TextBlockView] block '{blockId}' 的状态视觉 '{prefab.name}' 缺少 Button无法点击。");
BindInteractionButton(interactionButton);
// 背景铺满整个 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);
}
}
}