Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/UI/EquipmentUI.cs
2025-08-27 14:45:12 +08:00

175 lines
6.9 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;
using System.Collections.Generic;
using Base;
using Entity;
using UnityEngine;
namespace UI
{
/// <summary>
/// 负责管理和显示角色的装备用户界面。
/// 该组件会监听当前关注实体的变化,并根据所关注角色的库存数据动态更新装备槽位的显示,
/// 同时采用对象池技术高效管理 ItemUI 实例的创建和复用。
/// </summary>
public class EquipmentUI : MonoBehaviour,ITick
{
[SerializeField]
[Tooltip("所有 ItemUI 实例的父级 GameObject用于布局。")]
private GameObject uiParent;
[SerializeField]
[Tooltip("用于实例化装备槽位的 ItemUI 预制件。")]
private ItemUI itemUIPrefab;
/// <summary>
/// 当前界面所关联和关注的角色实体。
/// </summary>
private Character focusedEntity = null;
/// <summary>
/// ItemUI 实例的对象池,用于高效管理和复用 ItemUI。
/// </summary>
private List<ItemUI> itemUIPool = new List<ItemUI>();
/// <summary>
/// MonoBehaviour 的 Start 生命周期方法。
/// 在此方法中,注册当游戏主要程序中关注的实体发生变化时,调用 <see cref="UpdateFocusedEntity"/> 方法进行更新。
/// </summary>
private void Start()
{
Program.Instance.OnFocusedEntityChanged += UpdateFocusedEntity;
uiParent.SetActive(false);
}
/// <summary>
/// MonoBehaviour 的 OnDestroy 生命周期方法。
/// 在此方法中,取消注册所有已订阅的事件监听器,并清理对象池中创建的所有 ItemUI 实例,
/// 以防止内存泄漏和不必要的引用。
/// </summary>
private void OnDestroy()
{
Program.Instance.OnFocusedEntityChanged -= UpdateFocusedEntity;
// 如果当前有关注的角色,取消注册其库存改变事件。
// 注意:此处需要确保 focusedEntity 不为 null否则可能抛出 NullReferenceException。
if (focusedEntity != null)
{
focusedEntity.Inventory.OnInventoryChanged -= UpdateUI;
}
// 销毁对象池中所有 ItemUI 的 GameObject。
foreach (var itemUI in itemUIPool)
{
if (itemUI != null && itemUI.gameObject != null)
{
Destroy(itemUI.gameObject);
}
}
itemUIPool.Clear();
}
/// <summary>
/// 当游戏程序中关注的实体发生变化时调用此方法。
/// 该方法会更新当前 EquipmentUI 所关联的角色,并相应地注册或取消注册库存改变事件。
/// </summary>
/// <param name="entity">新的关注实体,可能为 null 或非 Character 类型。</param>
private void UpdateFocusedEntity(Entity.Entity entity)
{
// 如果之前有关注的角色,先取消注册其库存改变事件。
// 注意:此处需要确保 focusedEntity 不为 null。
if (focusedEntity != null)
{
focusedEntity.Inventory.OnInventoryChanged -= UpdateUI;
}
// 尝试将新的实体转换为角色类型。
Character newCharacter = entity as Character;
if (newCharacter != null)
{
focusedEntity = newCharacter;
}
// 如果传入的 entity 不是 Character 类型focusedEntity 将保持其当前值。
// 如果希望在非 Character 实体被关注时清除 focusedEntity则需要在此处添加 `else { focusedEntity = null; }`。
// 如果现在有关注的角色,注册其库存改变事件。
if (focusedEntity != null)
{
focusedEntity.Inventory.OnInventoryChanged += UpdateUI;
}
// 立即更新UI以反映新的关注实体或没有关注实体的状态。
UpdateUI();
}
/// <summary>
/// 根据当前关注角色的库存数据更新装备UI的显示。
/// 该方法通过对象池机制高效地管理 ItemUI 实例的创建、复用和禁用。
/// </summary>
private void UpdateUI()
{
// 如果没有关注的角色或其库存,则禁用所有 ItemUI。
// 注意:此处需要先检查 focusedEntity 是否为 null以避免 NullReferenceException。
if (focusedEntity == null || focusedEntity.Inventory == null)
{
foreach (var itemUI in itemUIPool)
{
if (itemUI != null && itemUI.gameObject != null)
{
itemUI.gameObject.SetActive(false);
}
}
uiParent.SetActive(false);
return;
}
// 检查用于创建物品UI的预制件是否已在 Inspector 中赋值。
if (itemUIPrefab == null)
{
Debug.LogError("ItemUIPrefab 未在 EquipmentUI 中指定。无法创建物品用户界面。", this);
foreach (var itemUI in itemUIPool) itemUI.gameObject.SetActive(false);
return;
}
int requiredUIs = focusedEntity.Inventory.Capacity;
int currentUIPoolSize = itemUIPool.Count; // 当前对象池中 ItemUI 实例的总数。
// 遍历所有必要的物品槽位,复用对象池中的 ItemUI或在不足时创建新的 ItemUI。
for (int i = 0; i < requiredUIs; i++)
{
ItemUI itemUI;
if (i < currentUIPoolSize)
{
itemUI = itemUIPool[i];
}
else
{
var itemObj=Instantiate(itemUIPrefab.gameObject, uiParent.transform);
itemUI = itemObj.GetComponent<ItemUI>();
itemUIPool.Add(itemUI);
currentUIPoolSize++; // 更新池的大小计数。
}
// 确保 ItemUI GameObject 处于激活状态,并使用当前物品槽位的数据进行初始化。
itemUI.gameObject.SetActive(true);
itemUI.Init(focusedEntity.Inventory.GetSlot(i), i);
itemUI.Select = false;
}
// 如果库存槽位数量减少,禁用对象池中多余的 ItemUI 实例。
for (int i = requiredUIs; i < currentUIPoolSize; i++)
{
if (itemUIPool[i] != null && itemUIPool[i].gameObject != null)
{
itemUIPool[i].gameObject.SetActive(false);
}
}
uiParent.SetActive(true);
}
public void Tick()
{
}
}
}