using Sirenix.OdinInspector; using TMPro; using UnityEngine; using UnityEngine.UI; using Yarn.Unity; namespace Ichni.Story.Dialogue { /// /// 单个对话选项按钮的显示与交互控制器。 /// /// /// 可用选项:显示 ,点击时触发 onSelected 回调。 /// 不可用选项:显示 ,点击无效(但 Button 的 /// interactable 保持开启,以备未来扩展悬停提示等功能)。 /// /// /// /// 由 统一调用 / 。 /// /// public class ChoiceButton : MonoBehaviour { [Tooltip("按钮组件引用(挂载在同 GameObject 上的 Button)。")] public Button button; [Tooltip("按钮背景 Image,用于在 available / unavailable 状态间切换 Sprite。")] public Image buttonBackground; [Tooltip("选项文本 TMP。")] public TMP_Text buttonText; [SerializeField, HideInPlayMode] [Tooltip("选项可用时的背景 Sprite。")] private Sprite availableSprite; [SerializeField, HideInPlayMode] [Tooltip("选项不可用时的背景 Sprite。")] private Sprite unavailableSprite; // ── 运行时状态 ──────────────────────────────────────────────────── /// 当前绑定的选项是否可供玩家选择。 private bool _isAvailable; // ── 公共 API ───────────────────────────────────────────────────── /// /// 根据 配置按钮并激活显示。 /// /// Yarn 对话选项数据。option.IsAvailable 决定外观与交互行为。 /// /// 玩家点击且选项可用时触发的回调。 /// 不可用选项点击不会触发此回调,但按钮保持 interactable=true。 /// public void Setup(DialogueOption option, System.Action onSelected) { gameObject.SetActive(true); _isAvailable = option.IsAvailable; // 设置选项文本 if (buttonText != null) buttonText.text = option.Line.TextWithoutCharacterName.Text; // 根据可用性切换背景图 if (buttonBackground != null) buttonBackground.sprite = _isAvailable ? availableSprite : unavailableSprite; // 注册点击监听:不可用选项点击静默忽略 if (button != null) { button.onClick.RemoveAllListeners(); button.onClick.AddListener(() => { if (_isAvailable) onSelected?.Invoke(option); }); } } /// /// 清除按钮监听并隐藏此按钮。 /// 在 结束后统一调用。 /// public void Cleanup() { if (button != null) button.onClick.RemoveAllListeners(); gameObject.SetActive(false); } } }