Passion & UI
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
using Cielonos.MainGame.Inventory;
|
||||
using SLSUtilities.General;
|
||||
using SLSUtilities.UI;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cielonos.MainGame.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 背包网格中的单个物品槽位。
|
||||
/// 显示物品图标、名称,消耗品额外显示堆叠数量。
|
||||
/// 点击后通知 InventoryUIPage 选中该物品并更新详情面板。
|
||||
/// </summary>
|
||||
public class InventoryItemSelector : UIElementBase
|
||||
{
|
||||
private InventoryUIPage Page => PlayerCanvas.MainGamePages.inventoryPage;
|
||||
|
||||
// ─────────────────── 数据 ───────────────────
|
||||
|
||||
/// <summary>此槽位绑定的物品实例。</summary>
|
||||
public ItemBase Item { get; private set; }
|
||||
|
||||
// ─────────────────── UI 引用 ───────────────────
|
||||
|
||||
[Header("UI References")]
|
||||
public Button button;
|
||||
public Image background;
|
||||
public Image itemIcon;
|
||||
public Image rarityFrame;
|
||||
//public Image selectorHint;
|
||||
//public TMP_Text itemName;
|
||||
|
||||
[Tooltip("消耗品堆叠数量显示,非消耗品时隐藏。")]
|
||||
public TMP_Text stackText;
|
||||
|
||||
// ─────────────────── 视觉设置 ───────────────────
|
||||
|
||||
[Header("Visual Settings")]
|
||||
public Color normalColor = new Color(0.2f, 0.2f, 0.2f, 0.8f);
|
||||
public Color selectedColor = new Color(0.4f, 0.6f, 0.9f, 0.9f);
|
||||
|
||||
private bool isSelected;
|
||||
|
||||
// ================================================================
|
||||
// 公开接口
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// 用给定的物品数据配置此槽位的显示内容。
|
||||
/// </summary>
|
||||
public void Setup(ItemBase item)
|
||||
{
|
||||
Item = item;
|
||||
|
||||
if (item == null || item.contentData == null)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
ContentData data = item.contentData;
|
||||
|
||||
Sprite icon = data.itemIcon;
|
||||
if (itemIcon != null)
|
||||
{
|
||||
itemIcon.sprite = icon;
|
||||
itemIcon.enabled = icon != null;
|
||||
}
|
||||
|
||||
// 名称(本地化)
|
||||
/*if (itemName != null)
|
||||
{
|
||||
itemName.text = data.displayNameKey.Localize();
|
||||
}*/
|
||||
|
||||
// 堆叠数量(仅消耗品)
|
||||
RefreshStackDisplay();
|
||||
|
||||
// 默认取消选中
|
||||
SetSelected(false);
|
||||
|
||||
// 绑定按钮
|
||||
if (button != null)
|
||||
{
|
||||
button.onClick.RemoveAllListeners();
|
||||
button.onClick.AddListener(OnClicked);
|
||||
}
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置选中/取消选中的视觉状态。
|
||||
/// </summary>
|
||||
public void SetSelected(bool selected)
|
||||
{
|
||||
isSelected = selected;
|
||||
|
||||
if (background != null)
|
||||
{
|
||||
background.color = isSelected ? selectedColor : normalColor;
|
||||
}
|
||||
|
||||
/*if (selectorHint != null)
|
||||
{
|
||||
selectorHint.enabled = isSelected;
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新消耗品堆叠数量显示。非消耗品时隐藏堆叠文本。
|
||||
/// </summary>
|
||||
public void RefreshStackDisplay()
|
||||
{
|
||||
if (stackText == null) return;
|
||||
|
||||
if (Item is ConsumableBase consumable)
|
||||
{
|
||||
stackText.gameObject.SetActive(true);
|
||||
stackText.text = consumable.stackAmount.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
stackText.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────── 内部 ───────────────────
|
||||
|
||||
private void OnClicked()
|
||||
{
|
||||
Page?.SelectItem(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2abecad67abab4e4692a134234d722c8
|
||||
@@ -0,0 +1,284 @@
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Cielonos.MainGame.Inventory;
|
||||
using Cielonos.MainGame.Items;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cielonos.MainGame.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 背包/装备界面 UI 页面。
|
||||
/// 左侧为背包物品网格(GridLayoutGroup),支持按类型多选筛选和多种排序模式;
|
||||
/// 右侧为物品详情面板(ItemDetailPanel),选中物品后显示详细信息。
|
||||
/// </summary>
|
||||
public class InventoryUIPage : UIPageBase
|
||||
{
|
||||
// ─────────────────── 选择器 ───────────────────
|
||||
|
||||
[Title("Selectors")]
|
||||
[Tooltip("背包网格中每个物品槽位的预制体,需挂载 InventoryItemSelector 组件。")]
|
||||
public GameObject selectorPrefab;
|
||||
|
||||
[Tooltip("物品槽位的容器,应挂载 GridLayoutGroup 以实现网格布局。")]
|
||||
public RectTransform selectorContainer;
|
||||
|
||||
[ReadOnly]
|
||||
public List<InventoryItemSelector> selectors = new List<InventoryItemSelector>();
|
||||
|
||||
// ─────────────────── 详情面板 ───────────────────
|
||||
|
||||
[Title("Detail Panel")]
|
||||
public ItemDetailPanel itemDetailPanel;
|
||||
|
||||
// ─────────────────── 筛选与排序 ───────────────────
|
||||
|
||||
[Title("Filter & Sort")]
|
||||
[Tooltip("多选类别筛选下拉框,按 ItemType 过滤可见物品。")]
|
||||
public ItemCategoryDropdown categoryDropdown;
|
||||
|
||||
[Tooltip("单选排序模式下拉框,决定物品排列顺序。")]
|
||||
public ItemSortDropdown sortDropdown;
|
||||
|
||||
// ─────────────────── 按钮 ───────────────────
|
||||
|
||||
[Title("Buttons")]
|
||||
public Button closeButton;
|
||||
|
||||
// ─────────────────── 运行时数据 ───────────────────
|
||||
|
||||
private InventoryItemSelector currentSelected;
|
||||
|
||||
/// <summary>当前选中的物品,null 表示未选中。</summary>
|
||||
public ItemBase SelectedItem => currentSelected != null ? currentSelected.Item : null;
|
||||
|
||||
// ================================================================
|
||||
// 生命周期
|
||||
// ================================================================
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (closeButton != null)
|
||||
{
|
||||
closeButton.onClick.AddListener(OnCloseClicked);
|
||||
}
|
||||
|
||||
if (categoryDropdown != null)
|
||||
{
|
||||
categoryDropdown.OnFilterChanged += OnFilterChanged;
|
||||
}
|
||||
|
||||
if (sortDropdown != null)
|
||||
{
|
||||
sortDropdown.OnSortChanged += OnSortChanged;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPageOpened()
|
||||
{
|
||||
RefreshInventory();
|
||||
}
|
||||
|
||||
protected override void OnPageClosed()
|
||||
{
|
||||
currentSelected = null;
|
||||
ClearSelectors();
|
||||
|
||||
categoryDropdown?.ResetToAll();
|
||||
sortDropdown?.ResetToDefault();
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 公开接口
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// 从玩家背包读取所有物品,重新生成网格选择器并应用排序与筛选。
|
||||
/// </summary>
|
||||
public void RefreshInventory()
|
||||
{
|
||||
ClearSelectors();
|
||||
|
||||
PlayerInventorySubcontroller.BackpackSubmodule backpack =
|
||||
MainGameManager.Player.inventorySc.backpackSm;
|
||||
|
||||
PopulateFromList(backpack.mainWeapons);
|
||||
PopulateFromList(backpack.supportEquipments);
|
||||
PopulateFromList(backpack.passiveEquipments);
|
||||
PopulateFromList(backpack.consumables);
|
||||
|
||||
currentSelected = null;
|
||||
ApplySortAndFilter();
|
||||
|
||||
if (itemDetailPanel != null)
|
||||
{
|
||||
itemDetailPanel.ClearPanel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由 InventoryItemSelector 调用,选中指定的物品槽位。
|
||||
/// </summary>
|
||||
public void SelectItem(InventoryItemSelector selector)
|
||||
{
|
||||
if (selector == null || !selectors.Contains(selector)) return;
|
||||
|
||||
if (currentSelected != null)
|
||||
{
|
||||
currentSelected.SetSelected(false);
|
||||
}
|
||||
|
||||
currentSelected = selector;
|
||||
currentSelected.SetSelected(true);
|
||||
|
||||
if (itemDetailPanel != null)
|
||||
{
|
||||
itemDetailPanel.SetItem(currentSelected.Item);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 筛选与排序
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// 对选择器列表执行排序,然后按当前筛选条件显示/隐藏。
|
||||
/// 当任一 Dropdown 变化或背包刷新时调用。
|
||||
/// </summary>
|
||||
private void ApplySortAndFilter()
|
||||
{
|
||||
// ── 排序 ──
|
||||
ItemSortMode sortMode = sortDropdown != null ? sortDropdown.CurrentMode : ItemSortMode.Default;
|
||||
List<ItemBase> items = selectors.ConvertAll(s => s.Item);
|
||||
ItemSorter.Sort(items, sortMode);
|
||||
|
||||
// 按排序结果重排选择器列表
|
||||
Dictionary<ItemBase, InventoryItemSelector> itemToSelector =
|
||||
new Dictionary<ItemBase, InventoryItemSelector>(selectors.Count);
|
||||
|
||||
foreach (InventoryItemSelector sel in selectors)
|
||||
{
|
||||
itemToSelector.TryAdd(sel.Item, sel);
|
||||
}
|
||||
|
||||
List<InventoryItemSelector> sorted = new List<InventoryItemSelector>(selectors.Count);
|
||||
foreach (ItemBase item in items)
|
||||
{
|
||||
if (itemToSelector.TryGetValue(item, out InventoryItemSelector sel))
|
||||
{
|
||||
sorted.Add(sel);
|
||||
}
|
||||
}
|
||||
|
||||
selectors.Clear();
|
||||
selectors.AddRange(sorted);
|
||||
|
||||
// 更新 UI 层级顺序
|
||||
for (int i = 0; i < selectors.Count; i++)
|
||||
{
|
||||
selectors[i].transform.SetSiblingIndex(i);
|
||||
}
|
||||
|
||||
// ── 筛选 ──
|
||||
ItemFilter filter = categoryDropdown != null ? categoryDropdown.BuildFilter() : ItemFilter.None;
|
||||
bool selectedHidden = false;
|
||||
|
||||
foreach (InventoryItemSelector selector in selectors)
|
||||
{
|
||||
if (selector == null || selector.Item == null) continue;
|
||||
|
||||
bool visible = filter.Match(selector.Item);
|
||||
selector.gameObject.SetActive(visible);
|
||||
|
||||
if (!visible && selector == currentSelected)
|
||||
{
|
||||
selectedHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedHidden)
|
||||
{
|
||||
currentSelected.SetSelected(false);
|
||||
currentSelected = null;
|
||||
itemDetailPanel?.ClearPanel();
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────── Dropdown 回调 ───────────────────
|
||||
|
||||
private void OnFilterChanged(ItemFilter filter)
|
||||
{
|
||||
ApplySortAndFilter();
|
||||
}
|
||||
|
||||
private void OnSortChanged(ItemSortMode mode)
|
||||
{
|
||||
ApplySortAndFilter();
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 选择器管理
|
||||
// ================================================================
|
||||
|
||||
private void PopulateFromList<T>(List<T> items) where T : ItemBase
|
||||
{
|
||||
if (items == null) return;
|
||||
|
||||
foreach (T item in items)
|
||||
{
|
||||
if (item == null || item.contentData == null) continue;
|
||||
|
||||
InventoryItemSelector selector = CreateSelector();
|
||||
if (selector != null)
|
||||
{
|
||||
selector.Setup(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InventoryItemSelector CreateSelector()
|
||||
{
|
||||
if (selectorPrefab == null || selectorContainer == null)
|
||||
{
|
||||
Debug.LogError("[InventoryUIPage] selectorPrefab 或 selectorContainer 未配置。");
|
||||
return null;
|
||||
}
|
||||
|
||||
GameObject obj = Instantiate(selectorPrefab, selectorContainer);
|
||||
InventoryItemSelector selector = obj.GetComponent<InventoryItemSelector>();
|
||||
if (selector == null)
|
||||
{
|
||||
Debug.LogError("[InventoryUIPage] selectorPrefab 缺少 InventoryItemSelector 组件。");
|
||||
Destroy(obj);
|
||||
return null;
|
||||
}
|
||||
|
||||
selectors.Add(selector);
|
||||
return selector;
|
||||
}
|
||||
|
||||
private void ClearSelectors()
|
||||
{
|
||||
foreach (InventoryItemSelector selector in selectors)
|
||||
{
|
||||
if (selector != null)
|
||||
{
|
||||
Destroy(selector.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
selectors.Clear();
|
||||
}
|
||||
|
||||
// ─────────────────── 按钮回调 ───────────────────
|
||||
|
||||
private void OnCloseClicked()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8ea321de103c2e4ab8833abc7897452
|
||||
Reference in New Issue
Block a user