175 lines
6.9 KiB
C#
175 lines
6.9 KiB
C#
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()
|
||
{
|
||
|
||
}
|
||
}
|
||
}
|