Passion & UI
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.Core.Interaction;
|
||||
using DG.Tweening;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 交互选项列表 UI 页面。
|
||||
/// 玩家进入可交互物体范围时由 PlayerInteractionSubcontroller 调用 Show(),离开时调用 Hide()。
|
||||
/// 当选项数量为 1 时,直接显示单个选项而不需要导航;
|
||||
/// 当选项数量大于 1 时,玩家可用鼠标滚轮或上/下键切换高亮选项,R 键执行。
|
||||
/// </summary>
|
||||
public class InteractionUIArea : UIElementBase
|
||||
{
|
||||
[Title("容器")]
|
||||
[Tooltip("单个选项条目的预制体,需包含 InteractionChoiceItem 组件。")]
|
||||
[SerializeField] private GameObject choiceItemPrefab;
|
||||
|
||||
[Tooltip("选项条目的父节点(Layout Group)。")]
|
||||
[SerializeField] private RectTransform choiceContainer;
|
||||
|
||||
private readonly List<InteractionChoiceItem> _items = new List<InteractionChoiceItem>();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 对外接口
|
||||
// ====================================================================
|
||||
|
||||
/// <summary>
|
||||
/// 显示选项列表并高亮指定索引的选项。
|
||||
/// 由 PlayerInteractionSubcontroller.SetCurrentInteractable() 调用。
|
||||
/// </summary>
|
||||
public void Show(List<InteractionChoice> choices, int selectedIndex)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
canvasGroup.DOFade(1f, 0.25f).From(0).OnComplete(() =>
|
||||
{
|
||||
canvasGroup.interactable = true;
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
}).Play();
|
||||
rectTransform.DOLocalMoveY(-50f, 0.25f).From(-250f).Play();
|
||||
ClearItems();
|
||||
|
||||
if (choices == null || choices.Count == 0)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (InteractionChoice choice in choices)
|
||||
{
|
||||
InteractionChoiceItem item = CreateItem();
|
||||
if (item != null)
|
||||
{
|
||||
item.Setup(choice.choiceName, choice.isInteractable);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateHighlight(selectedIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新高亮选项。由 PlayerInteractionSubcontroller.NavigateChoice() 调用。
|
||||
/// </summary>
|
||||
public void UpdateHighlight(int selectedIndex)
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
_items[i].SetHighlighted(i == selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏选项列表。由 PlayerInteractionSubcontroller.RemoveCurrentInteractable() 调用。
|
||||
/// </summary>
|
||||
public override void Hide()
|
||||
{
|
||||
canvasGroup.interactable = false;
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
canvasGroup.DOFade(0f, 0.25f).OnComplete(() =>
|
||||
{
|
||||
ClearItems();
|
||||
gameObject.SetActive(false);
|
||||
}).Play();
|
||||
rectTransform.DOLocalMoveY(-250f, 0.25f).Play();
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 内部工具
|
||||
// ====================================================================
|
||||
|
||||
private InteractionChoiceItem CreateItem()
|
||||
{
|
||||
if (choiceItemPrefab == null || choiceContainer == null)
|
||||
{
|
||||
Debug.LogError("[InteractionUIPage] choiceItemPrefab 或 choiceContainer 未在 Inspector 中配置。");
|
||||
return null;
|
||||
}
|
||||
|
||||
GameObject go = Instantiate(choiceItemPrefab, choiceContainer);
|
||||
InteractionChoiceItem item = go.GetComponent<InteractionChoiceItem>();
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
Debug.LogError("[InteractionUIPage] choiceItemPrefab 缺少 InteractionChoiceItem 组件。");
|
||||
Destroy(go);
|
||||
return null;
|
||||
}
|
||||
|
||||
_items.Add(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
private void ClearItems()
|
||||
{
|
||||
foreach (InteractionChoiceItem item in _items)
|
||||
{
|
||||
if (item != null) Destroy(item.gameObject);
|
||||
}
|
||||
_items.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user