mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 13:57:12 +08:00
(client) feat:健康给予,路径优化,结算界面,商店界面 (#60)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/60
This commit is contained in:
@@ -1,45 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Base;
|
||||
using Data;
|
||||
using Entity;
|
||||
using Prefab;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Utils;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理游戏中所有实体(角色、建筑、子弹等)的生命周期、存储和分发。
|
||||
/// 实现了维度感知,这意味着不同维度的实体数据是独立管理的。
|
||||
/// 管理游戏中所有实体(角色、建筑、子弹等)的生命周期、存储和分发。
|
||||
/// 实现了维度感知,这意味着不同维度的实体数据是独立管理的。
|
||||
/// </summary>
|
||||
public class EntityManager : Utils.MonoSingleton<EntityManager>, ITick
|
||||
public class EntityManager : MonoSingleton<EntityManager>, ITick
|
||||
{
|
||||
/// <summary>
|
||||
/// 维度感知的实体存储结构。
|
||||
/// 外层字典按维度ID(DimensionId)索引,内层字典按派系键(FactionKey)索引,
|
||||
/// 存储该派系下的实体预制体链表。
|
||||
/// </summary>
|
||||
private Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities = new();
|
||||
|
||||
/// <summary>
|
||||
/// 维度感知的层级Transform缓存。
|
||||
/// 外层字典按维度ID(DimensionId)索引,内层字典按层级名称(LayerName)索引,
|
||||
/// 存储其对应的Transform。
|
||||
/// </summary>
|
||||
private Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 待添加的实体列表,用于在Tick循环中安全地将实体加入管理。
|
||||
/// Tuple包含:Item1=维度ID,Item2=派系键,Item3=实体预制体。
|
||||
/// </summary>
|
||||
private List<Tuple<string, string, EntityPrefab>>
|
||||
_pendingAdditions = new(); // Item1: DimensionId, Item2: FactionKey, Item3: EntityPrefab
|
||||
|
||||
/// <summary> 角色实体的预制体。 </summary>
|
||||
public EntityPrefab characterPrefab;
|
||||
|
||||
public EntityPrefab monsterPrefab;
|
||||
|
||||
/// <summary> 建筑实体的预制体。 </summary>
|
||||
public EntityPrefab buildingPrefab;
|
||||
|
||||
@@ -51,48 +33,60 @@ namespace Managers
|
||||
/// <summary> 默认实体的预制体,用于生成失败时的回退。 </summary>
|
||||
public EntityPrefab defaultEntityPrefab;
|
||||
|
||||
public EntityPrefab[] GetAllEntities(string dimensionId)
|
||||
{
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:尝试在维度 '{dimensionId}' 中查找实体,但其内部存储未初始化。");
|
||||
return null;
|
||||
}
|
||||
// 获取对应维度的内层字典
|
||||
var factionEntitiesForDimension = _dimensionFactionEntities[dimensionId];
|
||||
// 使用 LINQ 的 SelectMany 方法来扁平化所有 LinkedList 中的 EntityPrefab
|
||||
return factionEntitiesForDimension.Values // 获取所有 LinkedList<EntityPrefab> 的集合
|
||||
.SelectMany(linkedList => linkedList) // 将每个 LinkedList 中的元素展开到一个单一的序列中
|
||||
.ToArray(); // 将该序列转换为一个数组
|
||||
}
|
||||
/// <summary>
|
||||
/// 在指定维度中,根据派系键查找所有实体。
|
||||
/// 维度感知的实体存储结构。
|
||||
/// 外层字典按维度ID(DimensionId)索引,内层字典按派系键(FactionKey)索引,
|
||||
/// 存储该派系下的实体预制体链表。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <param name="factionKey">派系键。</param>
|
||||
/// <returns>指定派系下的实体列表,如果未找到则返回空列表。</returns>
|
||||
public EntityPrefab[] FindEntitiesByFaction(string dimensionId, string factionKey)
|
||||
{
|
||||
// 如果在场景加载前或维度未在Program中注册,_dimensionFactionEntities可能还没有该维度ID的条目。
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:尝试在维度 '{dimensionId}' 中查找实体,但其内部存储未初始化。");
|
||||
return null;
|
||||
}
|
||||
private readonly Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities =
|
||||
new();
|
||||
|
||||
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
/// <summary>
|
||||
/// 维度感知的层级Transform缓存。
|
||||
/// 外层字典按维度ID(DimensionId)索引,内层字典按层级名称(LayerName)索引,
|
||||
/// 存储其对应的Transform。
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 待添加的实体列表,用于在Tick循环中安全地将实体加入管理。
|
||||
/// Tuple包含:Item1=维度ID,Item2=派系键,Item3=实体预制体。
|
||||
/// </summary>
|
||||
private List<Tuple<string, string, EntityPrefab>>
|
||||
_pendingAdditions = new(); // Item1: DimensionId, Item2: FactionKey, Item3: EntityPrefab
|
||||
|
||||
private void Start()
|
||||
{
|
||||
|
||||
if (!defaultEntityPrefab)
|
||||
{
|
||||
if (factionDict.TryGetValue(factionKey, out var entities))
|
||||
var pre = Resources.Load<GameObject>("Default/DefaultEntity");
|
||||
if (pre)
|
||||
{
|
||||
return entities.ToArray();
|
||||
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
|
||||
if (defaultEntityPrefab == null)
|
||||
Debug.LogError(
|
||||
"实体管理器:已加载 DefaultEntity 预制体,但它缺少来自 Resources/Default/DefaultEntity 的 EntityPrefab 组件。请确保它包含该组件。");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
"实体管理器:无法从 Resources/Default/DefaultEntity 加载 DefaultEntity 预制体。请确保它存在于 'Assets/Resources/Default/DefaultEntity.prefab'。");
|
||||
}
|
||||
}
|
||||
|
||||
return null; // 如果未找到,返回一个空列表
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新(Tick)方法:处理实体逻辑,清理死亡实体,并将待添加的实体加入管理。
|
||||
/// 单例管理器销毁时调用,取消订阅场景加载事件。
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
Clock.RemoveTick(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新(Tick)方法:处理实体逻辑,清理死亡实体,并将待添加的实体加入管理。
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
@@ -103,7 +97,7 @@ namespace Managers
|
||||
{
|
||||
// 从 Program 中获取维度对象,验证其是否仍然活跃。
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (dimension == null)
|
||||
if (!dimension)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"实体管理器:跳过维度 '{dimensionId}' 的Tick处理,因为其维度对象在程序(Program)中已不再活跃(或从未注册)。正在清理相关内部数据。");
|
||||
@@ -130,7 +124,6 @@ namespace Managers
|
||||
var currentEntities = entities.ToList();
|
||||
|
||||
foreach (var entityPrefab in currentEntities)
|
||||
{
|
||||
// 检查实体预制体或其内部实体是否已销毁或死亡。
|
||||
if (!entityPrefab || !entityPrefab.entity || entityPrefab.entity.IsDead)
|
||||
{
|
||||
@@ -141,29 +134,23 @@ namespace Managers
|
||||
ITick itike = entityPrefab.entity;
|
||||
itike.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
// 删除所有标记为死亡的实体。
|
||||
foreach (var entityToRemove in entitiesToRemove)
|
||||
{
|
||||
entities.Remove(entityToRemove);
|
||||
var drawNode = entityToRemove.entity?.entityDef?.drawingOrder.GetDrawNodeDef(
|
||||
entityToRemove.entity.CurrentState, entityToRemove.entity.CurrentOrientation,out _);
|
||||
var drawNode = entityToRemove.entity.entityDef?.drawingOrder.GetDrawNodeDef(EntityState.Death,
|
||||
entityToRemove.entity.CurrentOrientation, out _);
|
||||
if (drawNode != null)
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(drawNode,
|
||||
entityToRemove.Position, drawNode.GetMinAnimationDuration());
|
||||
|
||||
if (entityToRemove != null)
|
||||
{
|
||||
|
||||
if (entityToRemove)
|
||||
Destroy(entityToRemove.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果一个派系下没有任何实体了,清理掉这个派系条目。
|
||||
if (entities.Count == 0)
|
||||
{
|
||||
factionDict.Remove(factionKey);
|
||||
}
|
||||
if (entities.Count == 0) factionDict.Remove(factionKey);
|
||||
}
|
||||
|
||||
// 如果某个维度在清理后已经没有了任何派系,也一并清理掉这个维度的层级缓存和实体数据。
|
||||
@@ -176,7 +163,6 @@ namespace Managers
|
||||
|
||||
// 处理待添加实体 (现在维度感知)。
|
||||
if (_pendingAdditions.Any())
|
||||
{
|
||||
// 使用 ToList() 创建副本进行迭代,允许在循环内部修改或清空 _pendingAdditions。
|
||||
foreach (var pending in _pendingAdditions.ToList())
|
||||
{
|
||||
@@ -205,19 +191,51 @@ namespace Managers
|
||||
}
|
||||
|
||||
var factionDict = _dimensionFactionEntities[dimensionId];
|
||||
if (!factionDict.ContainsKey(factionKey))
|
||||
{
|
||||
factionDict[factionKey] = new LinkedList<EntityPrefab>();
|
||||
}
|
||||
if (!factionDict.ContainsKey(factionKey)) factionDict[factionKey] = new LinkedList<EntityPrefab>();
|
||||
|
||||
factionDict[factionKey].AddLast(entityPrefab);
|
||||
_pendingAdditions.Remove(pending); // 成功添加后从待添加列表中移除。
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public event Action<Entity.Entity> OnCreateEntity;
|
||||
|
||||
|
||||
public EntityPrefab[] GetAllEntities(string dimensionId)
|
||||
{
|
||||
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionEntitiesForDimension))
|
||||
return factionEntitiesForDimension.Values // 获取所有 LinkedList<EntityPrefab> 的集合
|
||||
.SelectMany(linkedList => linkedList) // 将每个 LinkedList 中的元素展开到一个单一的序列中
|
||||
.ToArray(); // 将该序列转换为一个数组
|
||||
Debug.LogWarning($"实体管理器:尝试在维度 '{dimensionId}' 中查找实体,但其内部存储未初始化。");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:清理属于特定维度的所有待添加实体。
|
||||
/// 在指定维度中,根据派系键查找所有实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <param name="factionKey">派系键。</param>
|
||||
/// <returns>指定派系下的实体列表,如果未找到则返回空列表。</returns>
|
||||
public EntityPrefab[] FindEntitiesByFaction(string dimensionId, string factionKey)
|
||||
{
|
||||
// 如果在场景加载前或维度未在Program中注册,_dimensionFactionEntities可能还没有该维度ID的条目。
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:尝试在维度 '{dimensionId}' 中查找实体,但其内部存储未初始化。");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
if (factionDict.TryGetValue(factionKey, out var entities))
|
||||
return entities.ToArray();
|
||||
|
||||
return null; // 如果未找到,返回一个空列表
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:清理属于特定维度的所有待添加实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要清理的维度ID。</param>
|
||||
private void RemovePendingAdditionsForDimension(string dimensionId)
|
||||
@@ -225,26 +243,21 @@ namespace Managers
|
||||
// 创建一个新的列表来存储不属于指定维度的待添加实体。
|
||||
var remainingPendingAdditions = new List<Tuple<string, string, EntityPrefab>>();
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
if (pending.Item1 == dimensionId)
|
||||
{
|
||||
// 如果实体属于要清理的维度,则销毁其GameObject。
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null)
|
||||
{
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null) Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
remainingPendingAdditions.Add(pending);
|
||||
}
|
||||
}
|
||||
|
||||
_pendingAdditions = remainingPendingAdditions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定的Def生成实体对象(内部通用方法)。
|
||||
/// 根据给定的Def生成实体对象(内部通用方法)。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="prefab">要实例化的预制体。</param>
|
||||
@@ -261,7 +274,7 @@ namespace Managers
|
||||
{
|
||||
// 验证维度是否活跃 (通过 Program)。
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (dimension == null)
|
||||
if (!dimension)
|
||||
{
|
||||
Debug.LogError($"实体管理器:无法生成实体:维度 '{dimensionId}' 在程序(Program)中不活跃或未注册。");
|
||||
return null;
|
||||
@@ -269,7 +282,7 @@ namespace Managers
|
||||
|
||||
// 获取或创建实体所属的层级Transform,并确保其在维度根下。
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
|
||||
if (parentLayer == null)
|
||||
if (!parentLayer)
|
||||
{
|
||||
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取或创建实体的父层。");
|
||||
return null;
|
||||
@@ -283,17 +296,15 @@ namespace Managers
|
||||
|
||||
var entityComponent = instantiatedEntity.GetComponent<EntityPrefab>();
|
||||
if (!entityComponent)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"在 '{instantiatedEntity.name}' 上缺少 EntityPrefab 组件,无法完成实体初始化。");
|
||||
}
|
||||
entityComponent.entity.currentDimensionId = dimensionId;
|
||||
entityComponent.Init(def);
|
||||
extraInit?.Invoke(entityComponent);
|
||||
|
||||
var factionKey = def.attributes.defName ?? "default";
|
||||
var factionKey = string.IsNullOrEmpty(def.affiliation?.defName) ? "default" : def.affiliation.defName;
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
|
||||
|
||||
OnCreateEntity?.Invoke(entityComponent.entity);
|
||||
return entityComponent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -305,8 +316,8 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动态创建层(如果层不存在),现在是维度感知的。
|
||||
/// 每个维度有自己的层级结构,根在 Dimension.DimensionRoot 下。
|
||||
/// 动态创建层(如果层不存在),现在是维度感知的。
|
||||
/// 每个维度有自己的层级结构,根在 Dimension.DimensionRoot 下。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <param name="layerName">要确保存在的层级名称。</param>
|
||||
@@ -329,10 +340,7 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
|
||||
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform))
|
||||
{
|
||||
return layerTransform;
|
||||
}
|
||||
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform)) return layerTransform;
|
||||
|
||||
// 如果缓存中没有,尝试在维度根下查找。
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
@@ -359,7 +367,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度和位置生成一个通用实体。
|
||||
/// 在指定维度和位置生成一个通用实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="entityDef">实体定义对象。</param>
|
||||
@@ -386,6 +394,7 @@ namespace Managers
|
||||
);
|
||||
return !result ? GenerateDefaultEntity(dimensionId, pos) : result;
|
||||
}
|
||||
|
||||
public void GenerateMonsterEntity(string dimensionId, MonsterDef monsterDef, Vector3 pos)
|
||||
{
|
||||
if (!monsterPrefab)
|
||||
@@ -408,10 +417,11 @@ namespace Managers
|
||||
pos,
|
||||
monsterDef
|
||||
);
|
||||
if (result == null) GenerateDefaultEntity(dimensionId, pos);
|
||||
if (!result) GenerateDefaultEntity(dimensionId, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度和网格位置生成一个建筑实体。
|
||||
/// 在指定维度和网格位置生成一个建筑实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="buildingDef">建筑定义对象。</param>
|
||||
@@ -439,11 +449,12 @@ namespace Managers
|
||||
worldPos,
|
||||
buildingDef
|
||||
);
|
||||
result.entity.AttributesNow.health = result.entity.AttributesNow.attackTargetCount;
|
||||
if (!result) GenerateDefaultEntity(dimensionId, worldPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度、位置和方向上生成一个子弹实体。
|
||||
/// 在指定维度、位置和方向上生成一个子弹实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="bulletDef">子弹定义对象。</param>
|
||||
@@ -475,35 +486,26 @@ namespace Managers
|
||||
entityComponent =>
|
||||
{
|
||||
if (entityComponent && entityComponent.entity)
|
||||
{
|
||||
entityComponent.entity.SetTarget(pos + dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:在维度 '{dimensionId}' 的额外初始化期间,子弹实体组件或其内部实体为空。");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (result && result.entity is Bullet bullet)
|
||||
{
|
||||
bullet.SetBulletSource(source);
|
||||
|
||||
if (source) bullet.affiliation = source.affiliation;
|
||||
}
|
||||
if (result && result.entity is Bullet bullet) bullet.SetBulletSource(source);
|
||||
|
||||
if (!result) GenerateDefaultEntity(dimensionId, pos);
|
||||
}
|
||||
|
||||
public void GeneratePickupEntity(string dimensionId, ItemDef itemDef, Vector3 pos)
|
||||
{
|
||||
|
||||
if (!pickupPrefab)
|
||||
{
|
||||
Debug.LogError("实体管理器:pickupPrefab 为空!请分配一个有效的预制体。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemDef == null)
|
||||
{
|
||||
// --- 逻辑修改点 1:更正错误信息 ---
|
||||
@@ -511,14 +513,13 @@ namespace Managers
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (!dimension) // 更安全的检查
|
||||
{
|
||||
Debug.LogError($"实体管理器:无法生成实体:维度 '{dimensionId}' 在程序(Program)中不活跃或未注册。");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
|
||||
if (!parentLayer)
|
||||
{
|
||||
@@ -526,37 +527,38 @@ namespace Managers
|
||||
return;
|
||||
}
|
||||
|
||||
var result = GameObject.Instantiate(pickupPrefab.gameObject, pos, Quaternion.identity);
|
||||
var result = Instantiate(pickupPrefab, pos, Quaternion.identity, parentLayer);
|
||||
if (!result)
|
||||
{
|
||||
Debug.LogError($"实体管理器:无法实例化预制体 '{pickupPrefab.name}'。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var entityPrefab = result.GetComponent<EntityPrefab>();
|
||||
if (!entityPrefab)
|
||||
{
|
||||
Debug.LogError($"实体管理器:实例化出的拾取物实体 '{pickupPrefab.name}' 缺少 'EntityPrefab' 组件!无法完成初始化。已销毁实例。");
|
||||
GameObject.Destroy(result); // 清理未正确初始化的实例
|
||||
Destroy(result); // 清理未正确初始化的实例
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var pickup = entityPrefab.entity as Pickup; // 使用 as 运算符进行安全类型转换
|
||||
if (!pickup)
|
||||
{
|
||||
Debug.LogError($"实体管理器:实例化出的实体 '{entityPrefab.name}' 的核心实体对象无法转换为 'Pickup' 类型或为空。无法完成初始化。已销毁实例。");
|
||||
GameObject.Destroy(result); // 清理未正确初始化的实例
|
||||
Destroy(result); // 清理未正确初始化的实例
|
||||
return;
|
||||
}
|
||||
|
||||
result.transform.SetParent(parentLayer);
|
||||
pickup.Init(itemDef);
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, "default", entityPrefab));
|
||||
OnCreateEntity?.Invoke(pickup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度和位置生成一个默认实体(通常作为回退选项)。
|
||||
/// 在指定维度和位置生成一个默认实体(通常作为回退选项)。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="pos">生成位置。</param>
|
||||
@@ -594,62 +596,70 @@ namespace Managers
|
||||
const string factionKey = "default";
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
|
||||
entityComponent.DefaultInit();
|
||||
OnCreateEntity?.Invoke(entityComponent.entity);
|
||||
return entityComponent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,查找与源实体具有特定关系的最近实体。
|
||||
/// 在指定维度中,查找与源实体具有特定关系的最近的可攻击实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
/// <param name="sourceEntityInstance">作为参照的源实体实例。此参数指代场景中已实例化且活跃的实体。</param>
|
||||
/// <param name="targetRelationship">目标实体与源实体之间的期望关系(例如,Friendly, Hostile)。</param>
|
||||
/// <returns>找到的最近实体预制体,如果没有找到符合条件的实体则返回 null。</returns>
|
||||
public EntityPrefab FindNearestEntityByRelation(string dimensionId, EntityPrefab sourceEntityPrefab,
|
||||
public EntityPrefab FindNearestEntityByRelation(string dimensionId, EntityPrefab sourceEntityInstance,
|
||||
Relation targetRelationship)
|
||||
{
|
||||
// 参数校验:确保输入参数有效,避免空引用异常。
|
||||
if (!sourceEntityPrefab || !sourceEntityPrefab.entity)
|
||||
if (!sourceEntityInstance || !sourceEntityInstance.entity)
|
||||
{
|
||||
Debug.LogWarning("实体管理器:FindNearestEntityByRelation 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
Debug.LogWarning("实体管理器:FindNearestEntityByRelation 方法中,源实体实例或其内部实体为空。无法执行搜索。");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(dimensionId))
|
||||
{
|
||||
Debug.LogWarning("实体管理器:FindNearestEntityByRelation 方法中,维度ID为空或为Null。无法执行搜索。");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
|
||||
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:FindNearestEntityByRelation 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- 2. 变量初始化与缓存 ---
|
||||
EntityPrefab nearestTarget = null;
|
||||
var minDistanceSqr = float.MaxValue;
|
||||
|
||||
|
||||
var sourcePos = sourceEntityPrefab.transform.position;
|
||||
var affiliationManager = AffiliationManager.Instance;
|
||||
|
||||
// 逻辑修改 3: 缓存 AffiliationManager.Instance
|
||||
var affiliationManager = AffiliationManager.Instance; // 单例实例在方法开始处获取一次
|
||||
// 获取源实体的位置,用于距离计算
|
||||
var sourcePos = sourceEntityInstance.transform.position;
|
||||
// --- 3. 遍历搜索 ---
|
||||
foreach (var (currentFactionKey, factionEntities) in factionDict)
|
||||
{
|
||||
var factionRelation = affiliationManager.GetRelation(sourceEntityPrefab.entity.affiliation, currentFactionKey);
|
||||
// 获取源实体与当前派系的关系
|
||||
var factionRelation =
|
||||
affiliationManager.GetRelation(sourceEntityInstance.entity.affiliation, currentFactionKey);
|
||||
// 如果关系不符合目标要求,则跳过此派系
|
||||
if (factionRelation != targetRelationship) continue;
|
||||
|
||||
if (factionRelation != targetRelationship)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 遍历当前派系的所有实体
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
// 确保目标实体有效、未死亡,且不是源实体自身
|
||||
// 对 !currentEntityPrefab 或 !currentEntityPrefab.entity 的检查作为防御性编程,
|
||||
// 用于处理列表中可能存在的空引用或无效实体(可能是在外部销毁但未及时从列表中移除的情况)。
|
||||
if (!currentEntityPrefab || !currentEntityPrefab.entity ||
|
||||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityInstance)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentEntityPrefab.entity is not CombatantEntity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查实体是否是可攻击的战斗实体
|
||||
if (currentEntityPrefab.entity is not CombatantEntity) continue;
|
||||
|
||||
// 计算与源实体的距离平方(性能优化,避免开方)
|
||||
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
|
||||
// 如果找到更近的实体,则更新最近目标
|
||||
if (distanceSqr < minDistanceSqr)
|
||||
{
|
||||
minDistanceSqr = distanceSqr;
|
||||
@@ -662,9 +672,9 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,判断是否存在任何与源实体敌对的活跃实体。
|
||||
/// 此版本修正了将掉落物或子弹识别为敌对派系的问题,现在只考虑可转换为 CombatantEntity 的实体。
|
||||
/// 并优化了搜索逻辑,通过先判断派系关系来减少不必要的实体遍历。
|
||||
/// 在指定维度中,判断是否存在任何与源实体敌对的活跃实体。
|
||||
/// 此版本修正了将掉落物或子弹识别为敌对派系的问题,现在只考虑可转换为 CombatantEntity 的实体。
|
||||
/// 并优化了搜索逻辑,通过先判断派系关系来减少不必要的实体遍历。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
@@ -676,7 +686,7 @@ namespace Managers
|
||||
Debug.LogWarning("实体管理器:ExistsHostile 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var sourceCombatant = sourceEntityPrefab.entity as CombatantEntity;
|
||||
|
||||
if (!sourceCombatant)
|
||||
@@ -686,10 +696,7 @@ namespace Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceCombatant.IsDead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (sourceCombatant.IsDead) return false;
|
||||
|
||||
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
@@ -701,26 +708,17 @@ namespace Managers
|
||||
|
||||
foreach (var (currentFactionKey, factionEntities) in factionDict)
|
||||
{
|
||||
|
||||
var factionRelation = affiliationManager.GetRelation(sourceCombatant.affiliation, currentFactionKey);
|
||||
if (factionRelation != Relation.Hostile)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (factionRelation != Relation.Hostile) continue;
|
||||
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
if (!currentEntityPrefab || !currentEntityPrefab.entity ||
|
||||
currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var currentCombatant = currentEntityPrefab.entity as CombatantEntity;
|
||||
if (currentCombatant == null || currentCombatant.IsDead)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!currentCombatant || currentCombatant.IsDead) continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -729,7 +727,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,判断源实体视野范围内是否存在任何与源实体敌对的活跃实体。
|
||||
/// 在指定维度中,判断源实体视野范围内是否存在任何与源实体敌对的活跃实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
@@ -754,15 +752,12 @@ namespace Managers
|
||||
}
|
||||
|
||||
if (sourceCombatant.IsDead)
|
||||
{
|
||||
// 源 CombatantEntity 已经失效,它无法判断敌对实体。
|
||||
// 此时它本身可能也无法移动或感知,所以直接返回false。
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sightRange <= 0)
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:ExistsHostileInSightRange 方法中,视野范围必须为正值。接收到的值为: {sightRange}。");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -786,52 +781,57 @@ namespace Managers
|
||||
var sourcePos = sourceCombatant.transform.position; // 修改点:使用 sourceCombatant 的位置
|
||||
// 遍历所有实体:遍历维度内的所有派系,再遍历每个派系下的所有实体。
|
||||
foreach (var factionEntities in factionDict.Values)
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效的实体预制体或内部实体。
|
||||
// 2. 排除源实体自身。
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
currentEntityPrefab == sourceEntityPrefab) // 源实体自身已被转换为 sourceCombatant 并检查,这里只检查 prefab 引用是否相同
|
||||
continue;
|
||||
|
||||
// 【核心改动】新增类型检查:只有能转换为 CombatantEntity 的实体才会被进一步判断。
|
||||
var currentCombatant = currentEntityPrefab.entity as CombatantEntity;
|
||||
if (currentCombatant == null) continue; // 如果不是 CombatantEntity,则跳过此实体。
|
||||
|
||||
// 检查目标 CombatantEntity 是否已失效(死亡或摧毁)。
|
||||
if (currentCombatant.IsDead) continue; // 目标 CombatantEntity 已经失效,不是活跃的敌对实体。
|
||||
|
||||
// 关系判断:判断当前 CombatantEntity 与源 CombatantEntity 是否为“敌对”关系。
|
||||
if (affiliationManager.GetRelation(sourceCombatant, currentCombatant) ==
|
||||
Relation.Hostile) // 修改点:使用 CombatantEntity 类型参数
|
||||
{
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效的实体预制体或内部实体。
|
||||
// 2. 排除源实体自身。
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
currentEntityPrefab == sourceEntityPrefab) // 源实体自身已被转换为 sourceCombatant 并检查,这里只检查 prefab 引用是否相同
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 【核心改动】新增类型检查:只有能转换为 CombatantEntity 的实体才会被进一步判断。
|
||||
var currentCombatant = currentEntityPrefab.entity as CombatantEntity;
|
||||
if (currentCombatant == null)
|
||||
{
|
||||
continue; // 如果不是 CombatantEntity,则跳过此实体。
|
||||
}
|
||||
|
||||
// 检查目标 CombatantEntity 是否已失效(死亡或摧毁)。
|
||||
if (currentCombatant.IsDead)
|
||||
{
|
||||
continue; // 目标 CombatantEntity 已经失效,不是活跃的敌对实体。
|
||||
}
|
||||
|
||||
// 关系判断:判断当前 CombatantEntity 与源 CombatantEntity 是否为“敌对”关系。
|
||||
if (affiliationManager.GetRelation(sourceCombatant, currentCombatant) ==
|
||||
Relation.Hostile) // 修改点:使用 CombatantEntity 类型参数
|
||||
{
|
||||
// 距离判断:如果关系敌对,进一步判断其是否在视野范围内。
|
||||
var distanceSqr =
|
||||
Vector3.SqrMagnitude(currentCombatant.transform.position -
|
||||
sourcePos); // currentCombatant.transform
|
||||
if (distanceSqr <= sightRangeSqr)
|
||||
{
|
||||
return true; // 找到第一个视野范围内的敌对实体即返回 true。
|
||||
}
|
||||
}
|
||||
// 距离判断:如果关系敌对,进一步判断其是否在视野范围内。
|
||||
var distanceSqr =
|
||||
Vector3.SqrMagnitude(currentCombatant.transform.position -
|
||||
sourcePos); // currentCombatant.transform
|
||||
if (distanceSqr <= sightRangeSqr) return true; // 找到第一个视野范围内的敌对实体即返回 true。
|
||||
}
|
||||
}
|
||||
|
||||
return false; // 遍历完所有实体都未找到视野范围内的敌对实体。
|
||||
}
|
||||
|
||||
public static TEntityType[] FindEntity<TEntityType>(Vector2 position, float radius)
|
||||
where TEntityType : Entity.Entity
|
||||
{
|
||||
var result = Physics2D.OverlapCircleAll(position, radius, LayerMask.GetMask("Entity"));
|
||||
List<TEntityType> entity = new();
|
||||
foreach (var collider2D1 in result)
|
||||
{
|
||||
var mono = collider2D1.GetComponent<EntityPrefab>();
|
||||
if (!mono)
|
||||
continue;
|
||||
var entityType = mono.entity as TEntityType;
|
||||
if (entityType)
|
||||
entity.Add(entityType);
|
||||
}
|
||||
|
||||
return entity.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单例管理器启动时调用,订阅场景加载事件。
|
||||
/// 单例管理器启动时调用,订阅场景加载事件。
|
||||
/// </summary>
|
||||
protected override void OnStart()
|
||||
{
|
||||
@@ -840,17 +840,8 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单例管理器销毁时调用,取消订阅场景加载事件。
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
Clock.RemoveTick(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 场景加载完成时的回调。
|
||||
/// 清理旧场景的实体数据,并基于 Program 中已注册的维度重新初始化内部结构。
|
||||
/// 场景加载完成时的回调。
|
||||
/// 清理旧场景的实体数据,并基于 Program 中已注册的维度重新初始化内部结构。
|
||||
/// </summary>
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
@@ -859,12 +850,8 @@ namespace Managers
|
||||
|
||||
// 场景加载时清理待添加列表中的所有实体,因为它们可能属于旧场景或未在任何维度中被处理。
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null)
|
||||
{
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
_pendingAdditions.Clear(); // 清理待添加实体列表。
|
||||
|
||||
@@ -888,34 +875,8 @@ namespace Managers
|
||||
$"实体管理器:在场景加载初始化期间,Program 为ID '{dimensionId}' 注册了一个空的 Dimension 对象。这可能表明维度注册存在问题。");
|
||||
}
|
||||
}
|
||||
|
||||
Clock.AddTick(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MonoBehaviour 的 Start 方法,用于初始化默认实体预制体。
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (defaultEntityPrefab == null)
|
||||
{
|
||||
var pre = Resources.Load<GameObject>("Default/DefaultEntity");
|
||||
if (pre != null)
|
||||
{
|
||||
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
|
||||
if (defaultEntityPrefab == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"实体管理器:已加载 DefaultEntity 预制体,但它缺少来自 Resources/Default/DefaultEntity 的 EntityPrefab 组件。请确保它包含该组件。");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
"实体管理器:无法从 Resources/Default/DefaultEntity 加载 DefaultEntity 预制体。请确保它存在于 'Assets/Resources/Default/DefaultEntity.prefab'。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user