(client)feat:实现按武器释放技能

This commit is contained in:
m0_75251201
2025-08-27 19:08:27 +08:00
parent 63efa89ac1
commit 5315e29147
25 changed files with 1184 additions and 5106 deletions

View File

@ -27,6 +27,7 @@ namespace UI
InitMonster();
InitBuilding();
InitItem();
InitWeapon();
}
private void InitReloadGameButton()
@ -101,7 +102,7 @@ namespace UI
"生成怪物",
"未定义任何怪物",
def => def.label,
GenerateEntityCallback);
GenerateMonsterEntityCallback);
}
private void InitBuilding()
@ -170,7 +171,16 @@ namespace UI
entityPlacementUI.snapEnabled = false;
UIInputControl.Instance.Show(entityPlacementUI);
}
private void GenerateMonsterEntityCallback(MonsterDef monsterDef)
{
entityPlacementUI.currentAction = () =>
{
Managers.EntityManage.Instance.GenerateMonsterEntity(Program.Instance.FocusedDimensionId,monsterDef, Utils.MousePosition.GetWorldPosition());
};
entityPlacementUI.Prompt = $"当前生成器:\n名称{monsterDef.label}\n描述{monsterDef.description}";
entityPlacementUI.snapEnabled = false;
UIInputControl.Instance.Show(entityPlacementUI);
}
private void GenerateBuildingCallback(BuildingDef def)
{
entityPlacementUI.currentAction = () =>

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Base;
using Entity;
using UnityEngine;
// 确保 Character 类在此命名空间下
namespace UI
{
@ -10,26 +10,27 @@ namespace UI
/// 负责管理和显示角色的装备用户界面。
/// 该组件会监听当前关注实体的变化,并根据所关注角色的库存数据动态更新装备槽位的显示,
/// 同时采用对象池技术高效管理 ItemUI 实例的创建和复用。
/// 除了显示,现在还支持通过滚轮选择物品,并同步更新焦点角色的 CurrentSelected 字段。
/// </summary>
public class EquipmentUI : MonoBehaviour,ITick
public class EquipmentUI : MonoBehaviour, ITick
{
[SerializeField]
[Tooltip("所有 ItemUI 实例的父级 GameObject用于布局。")]
[SerializeField]
[Tooltip("所有 ItemUI 实例的父级 GameObject用于布局。")]
private GameObject uiParent;
[SerializeField]
[Tooltip("用于实例化装备槽位的 ItemUI 预制件。")]
[SerializeField]
[Tooltip("用于实例化装备槽位的 ItemUI 预制件。")]
private ItemUI itemUIPrefab;
/// <summary>
/// 当前界面所关联和关注的角色实体。
/// </summary>
private Character focusedEntity = null;
private Character focusedEntity;
/// <summary>
/// ItemUI 实例的对象池,用于高效管理和复用 ItemUI。
/// </summary>
private List<ItemUI> itemUIPool = new List<ItemUI>();
private List<ItemUI> itemUIPool = new();
/// <summary>
/// MonoBehaviour 的 Start 生命周期方法。
@ -49,10 +50,10 @@ namespace UI
private void OnDestroy()
{
Program.Instance.OnFocusedEntityChanged -= UpdateFocusedEntity;
// 如果当前有关注的角色,取消注册其库存改变事件。
// 注意:此处需要确保 focusedEntity 不为 null,否则可能抛出 NullReferenceException
if (focusedEntity != null)
// 确保 focusedEntity 不为 null 且 Inventory 不为 null
if (focusedEntity != null && focusedEntity.Inventory != null)
{
focusedEntity.Inventory.OnInventoryChanged -= UpdateUI;
}
@ -60,7 +61,7 @@ namespace UI
// 销毁对象池中所有 ItemUI 的 GameObject。
foreach (var itemUI in itemUIPool)
{
if (itemUI != null && itemUI.gameObject != null)
if (itemUI != null && itemUI.gameObject != null)
{
Destroy(itemUI.gameObject);
}
@ -76,8 +77,8 @@ namespace UI
private void UpdateFocusedEntity(Entity.Entity entity)
{
// 如果之前有关注的角色,先取消注册其库存改变事件。
// 注意:此处需要确保 focusedEntity 不为 null。
if (focusedEntity != null)
// 确保 focusedEntity 不为 null 且 Inventory 不为 null。
if (focusedEntity != null && focusedEntity.Inventory != null)
{
focusedEntity.Inventory.OnInventoryChanged -= UpdateUI;
}
@ -88,17 +89,37 @@ namespace UI
{
focusedEntity = newCharacter;
}
// 如果传入的 entity 不是 Character 类型focusedEntity 将保持其当前值。
// 如果希望在非 Character 实体被关注时清除 focusedEntity则需要在此处添加 `else { focusedEntity = null; }`。
else
{
// 如果传入的 entity 不是 Character 类型,或者为 null则清除当前的 focusedEntity。
focusedEntity = null;
}
// 如果现在有关注的角色,注册其库存改变事件。
if (focusedEntity != null)
if (focusedEntity != null)
{
focusedEntity.Inventory.OnInventoryChanged += UpdateUI;
}
// 立即更新UI以反映新的关注实体或没有关注实体的状态。
UpdateUI();
UpdateUI();
// 在更新UI后确保UI的选中状态与角色当前选中字段同步
// 只有当有焦点角色且库存不为空时才更新选中状态
if (focusedEntity != null && focusedEntity.Inventory != null && focusedEntity.Inventory.Capacity > 0)
{
// 确保 CurrentSelected 在有效范围内否则重置为0
if (focusedEntity.CurrentSelected < 0 || focusedEntity.CurrentSelected >= focusedEntity.Inventory.Capacity)
{
focusedEntity.CurrentSelected = 0;
}
UpdateSelectionUI(focusedEntity.CurrentSelected);
}
else
{
// 如果没有焦点实体或库存为空,则清空所有选中状态
UpdateSelectionUI(-1); // 传入一个无效索引以取消所有选中
}
}
/// <summary>
@ -108,8 +129,7 @@ namespace UI
private void UpdateUI()
{
// 如果没有关注的角色或其库存,则禁用所有 ItemUI。
// 注意:此处需要先检查 focusedEntity 是否为 null以避免 NullReferenceException。
if (focusedEntity == null || focusedEntity.Inventory == null)
if (focusedEntity == null || focusedEntity.Inventory == null)
{
foreach (var itemUI in itemUIPool)
{
@ -127,6 +147,7 @@ namespace UI
{
Debug.LogError("ItemUIPrefab 未在 EquipmentUI 中指定。无法创建物品用户界面。", this);
foreach (var itemUI in itemUIPool) itemUI.gameObject.SetActive(false);
uiParent.SetActive(false); // 确保父级也被禁用
return;
}
@ -143,16 +164,17 @@ namespace UI
}
else
{
var itemObj=Instantiate(itemUIPrefab.gameObject, uiParent.transform);
// 使用 Instantiate(GameObject, Transform) 以确保父级设置正确且避免转换问题。
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.Select = false; 选中状态将由 UpdateSelectionUI 统一管理
}
// 如果库存槽位数量减少,禁用对象池中多余的 ItemUI 实例。
@ -161,14 +183,93 @@ namespace UI
if (itemUIPool[i] != null && itemUIPool[i].gameObject != null)
{
itemUIPool[i].gameObject.SetActive(false);
itemUIPool[i].Select = false; // 禁用时也确保清除选中状态
}
}
uiParent.SetActive(true);
// 首次更新UI时或者当Inventory改变时需要确保 CurrentSelected 的UI状态是正确的
// 但如果 UpdateFocusedEntity 已经处理了,这里可以省略,或者确保只在必要时调用
// 考虑到 UpdateUI 也会被 Inventory.OnInventoryChanged 调用,这里再次确保同步是合理的。
if (focusedEntity != null && focusedEntity.Inventory != null && focusedEntity.Inventory.Capacity > 0)
{
UpdateSelectionUI(focusedEntity.CurrentSelected);
}
else
{
UpdateSelectionUI(-1);
}
}
/// <summary>
/// 当当前选中物品改变时,更新所有 ItemUI 的选中状态。
/// </summary>
/// <param name="selectedItemIndex">当前选中的物品索引。传入 -1 将取消所有 ItemUI 的选中状态。</param>
private void UpdateSelectionUI(int selectedItemIndex)
{
// 如果对象池为空,则无需更新
if (itemUIPool == null || itemUIPool.Count == 0)
{
return;
}
for (int i = 0; i < itemUIPool.Count; i++)
{
ItemUI itemUI = itemUIPool[i];
if (itemUI != null && itemUI.gameObject != null)
{
// 只有在 ItemUI 激活状态下才设置其选中状态避免对禁用UI的操作
// 或者如果传入-1即使激活也全部设置为false
if (itemUI.gameObject.activeSelf || selectedItemIndex == -1) // 确保当取消所有选中时循环到所有激活的甚至当前禁用的ItemUI
{
itemUI.Select = (i == selectedItemIndex && itemUI.gameObject.activeSelf);
}
}
}
}
/// <summary>
/// 每帧调用的更新方法,用于处理滚轮输入以选择物品。
/// </summary>
public void Tick()
{
// 如果没有焦点实体、没有库存或库存为空,不进行选择操作
if (focusedEntity == null || focusedEntity.Inventory == null || focusedEntity.Inventory.Capacity == 0)
{
return;
}
float scrollInput = Input.GetAxis("Mouse ScrollWheel");
if (scrollInput != 0) // 检测到滚轮输入
{
int currentSelection = focusedEntity.CurrentSelected;
int inventoryCapacity = focusedEntity.Inventory.Capacity;
if (scrollInput > 0) // 滚轮向上,选择前一个
{
currentSelection--;
if (currentSelection < 0)
{
currentSelection = inventoryCapacity - 1; // 循环到最后一个
}
}
else // 滚轮向下,选择后一个
{
currentSelection++;
if (currentSelection >= inventoryCapacity)
{
currentSelection = 0; // 循环到第一个
}
}
// 如果选择发生变化则更新焦点实体和UI
if (focusedEntity.CurrentSelected != currentSelection)
{
focusedEntity.CurrentSelected = currentSelection;
UpdateSelectionUI(currentSelection); // 更新UI选中状态
}
}
}
}
}