using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.UI; namespace Ichni.Story.UI { /// /// 故事树中单个 block 的视图基类。负责通用的定位、状态视觉与点击分发。 /// 具体类型(文本 / 音乐 / 教程)由子类实现 。 /// public abstract class StoryBlockView : SerializedMonoBehaviour { [Header("Block State")] public string blockId; public StoryBlockState state; [Header("Common References")] public RectTransform blockRect; public RectTransform inPort; public RectTransform outPort; public Button button; // 关联的定义,供子类读取类型专属字段 protected StoryBlockDefinition definition; /// /// 初始化 block 视图。逻辑坐标已由控制器按网格换算为 /// (以 BlockContainer 左侧中线为原点、x 向右为正、y 向下为正的 anchoredPosition)。 /// /// block 定义(提供尺寸与类型专属数据)。 /// 已换算好的 anchoredPosition。 /// 推导得到的 block 状态。 public virtual void Initialize(StoryBlockDefinition def, Vector2 position, StoryBlockState blockState) { definition = def; blockId = def.blockId; state = blockState; // 统一坐标约定:以 BlockContainer 的左侧中线为原点,向右为 +x、向下为 +y。 blockRect.anchorMin = new Vector2(0f, 0.5f); blockRect.anchorMax = new Vector2(0f, 0.5f); blockRect.pivot = new Vector2(0f, 0.5f); blockRect.sizeDelta = new Vector2(400, 200); blockRect.anchoredPosition = position; if (button != null) button.onClick.AddListener(HandleClick); ApplyState(state); } /// /// 应用状态并更新视觉。基类默认按状态切换按钮可交互性; /// 子类可 override 以更新锁定图标、颜色等具体表现。 /// public virtual void ApplyState(StoryBlockState newState) { state = newState; if (button != null) button.interactable = state != StoryBlockState.Locked; } private void HandleClick() { if (state == StoryBlockState.Locked) return; if (StoryManager.instance != null && StoryManager.instance.treeController != null) StoryManager.instance.treeController.currentBlock = this; OnClicked(); } /// /// 点击(非锁定状态)时的具体行为,由子类实现。 /// protected abstract void OnClicked(); } }