Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Managers/EntityManage.cs

843 lines
40 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 Base;
using Data;
using Entity;
using Prefab;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Managers
{
/// <summary>
/// 管理游戏中所有实体(角色、建筑、子弹等)的生命周期、存储和分发。
/// 实现了维度感知,这意味着不同维度的实体数据是独立管理的。
/// </summary>
public class EntityManage : Utils.MonoSingleton<EntityManage>, ITick
{
/// <summary>
/// 维度感知的实体存储结构。
/// 外层字典按维度IDDimensionId索引内层字典按派系键FactionKey索引
/// 存储该派系下的实体预制体链表。
/// </summary>
private Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities = new();
/// <summary>
/// 维度感知的层级Transform缓存。
/// 外层字典按维度IDDimensionId索引内层字典按层级名称LayerName索引
/// 存储其对应的Transform。
/// </summary>
private Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
/// <summary>
/// 待添加的实体列表用于在Tick循环中安全地将实体加入管理。
/// Tuple包含Item1=维度IDItem2=派系键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;
/// <summary> 子弹实体的预制体。 </summary>
public EntityPrefab bulletPrefab;
public EntityPrefab pickupPrefab;
/// <summary> 默认实体的预制体,用于生成失败时的回退。 </summary>
public EntityPrefab defaultEntityPrefab;
/// <summary>
/// 在指定维度中,根据派系键查找所有实体。
/// </summary>
/// <param name="dimensionId">维度的唯一标识符。</param>
/// <param name="factionKey">派系键。</param>
/// <returns>指定派系下的实体列表,如果未找到则返回空列表。</returns>
public LinkedList<EntityPrefab> FindEntitiesByFaction(string dimensionId, string factionKey)
{
// 如果在场景加载前或维度未在Program中注册_dimensionFactionEntities可能还没有该维度ID的条目。
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
{
Debug.LogWarning($"实体管理器:尝试在维度 '{dimensionId}' 中查找实体,但其内部存储未初始化。");
return new LinkedList<EntityPrefab>();
}
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
{
if (factionDict.TryGetValue(factionKey, out var entities))
{
return entities;
}
}
return new LinkedList<EntityPrefab>(); // 如果未找到,返回一个空列表
}
/// <summary>
/// 每帧更新Tick方法处理实体逻辑清理死亡实体并将待添加的实体加入管理。
/// </summary>
public void Tick()
{
// 获取当前管理器中所有活跃维度ID的副本以安全地迭代和移除。
var activeDimensionIdsInManager = _dimensionFactionEntities.Keys.ToList();
foreach (var dimensionId in activeDimensionIdsInManager)
{
// 从 Program 中获取维度对象,验证其是否仍然活跃。
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null)
{
Debug.LogWarning(
$"实体管理器:跳过维度 '{dimensionId}' 的Tick处理因为其维度对象在程序Program中已不再活跃或从未注册。正在清理相关内部数据。");
// 如果 Program 不再有这个维度说明它已经失效或被注销EntityManage 需要清理自己的相关数据。
_dimensionFactionEntities.Remove(dimensionId);
_dimensionLayerCache.Remove(dimensionId);
// 清理属于该维度的待添加实体。
RemovePendingAdditionsForDimension(dimensionId);
continue; // 跳过处理这个不存在的维度
}
// 现在真正处理的是 _dimensionFactionEntities 中该维度ID对应的字典。
var factionDict = _dimensionFactionEntities[dimensionId];
// 迭代 factionDict 的副本,以允许在迭代中修改 factionDict.Keys (即移除派系)。
// ToList() 创建 KeyValuePair 的副本。
foreach (var factionEntry in factionDict.ToList())
{
var factionKey = factionEntry.Key;
var entities = factionEntry.Value;
var entitiesToRemove = new List<EntityPrefab>();
// 创建副本以避免在迭代时修改列表。
var currentEntities = entities.ToList();
foreach (var entityPrefab in currentEntities)
{
// 检查实体预制体或其内部实体是否已销毁或死亡。
if (entityPrefab == null || entityPrefab.entity == null || entityPrefab.entity.IsDead)
{
entitiesToRemove.Add(entityPrefab);
}
else
{
ITick itike = entityPrefab.entity;
itike.Tick();
}
}
// 删除所有标记为死亡的实体。
foreach (var entityToRemove in entitiesToRemove)
{
entities.Remove(entityToRemove);
if (entityToRemove != null)
{
Destroy(entityToRemove.gameObject);
}
}
// 如果一个派系下没有任何实体了,清理掉这个派系条目。
if (entities.Count == 0)
{
factionDict.Remove(factionKey);
}
}
// 如果某个维度在清理后已经没有了任何派系,也一并清理掉这个维度的层级缓存和实体数据。
if (factionDict.Count == 0)
{
_dimensionFactionEntities.Remove(dimensionId);
_dimensionLayerCache.Remove(dimensionId); // 对应的层级缓存也可以清理。
}
}
// 处理待添加实体 (现在维度感知)。
if (_pendingAdditions.Any())
{
// 使用 ToList() 创建副本进行迭代,允许在循环内部修改或清空 _pendingAdditions。
foreach (var pending in _pendingAdditions.ToList())
{
var dimensionId = pending.Item1;
var factionKey = pending.Item2;
var entityPrefab = pending.Item3;
// 再次检查维度是否活跃(在 Program 中注册),防止在添加前维度被 Program 注销。
if (Program.Instance.GetDimension(dimensionId) == null)
{
Debug.LogError(
$"实体管理器:尝试将实体 '{entityPrefab?.name ?? "null"}' 添加到在程序Program中未注册或不活跃的维度 '{dimensionId}'。该实体将被销毁。");
if (entityPrefab != null && entityPrefab.gameObject != null) Destroy(entityPrefab.gameObject);
_pendingAdditions.Remove(pending); // 立即从待添加列表中移除已处理的条目。
continue;
}
// 确保 _dimensionFactionEntities 包含该 dimensionId否则初始化以防_OnSceneLoaded时Program中没有但之后又注册了
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
{
// 这情况意味着 Program 中有该维度,但在 EntityManage 内部数据结构中没有。
// 这可能发生在 Program 动态注册了一个维度,但 EntityManage 尚未在 Tick 中检测到并初始化。
// 或者 OnSceneLoaded 时 Program 中没有这个维度,但在游戏运行中被注册了。
_dimensionFactionEntities[dimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
Debug.LogWarning($"实体管理器:在处理待添加实体时,按需为维度 '{dimensionId}' 初始化内部数据结构。");
}
var factionDict = _dimensionFactionEntities[dimensionId];
if (!factionDict.ContainsKey(factionKey))
{
factionDict[factionKey] = new LinkedList<EntityPrefab>();
}
factionDict[factionKey].AddLast(entityPrefab);
_pendingAdditions.Remove(pending); // 成功添加后从待添加列表中移除。
}
}
}
/// <summary>
/// 辅助方法:清理属于特定维度的所有待添加实体。
/// </summary>
/// <param name="dimensionId">要清理的维度ID。</param>
private void RemovePendingAdditionsForDimension(string dimensionId)
{
// 创建一个新的列表来存储不属于指定维度的待添加实体。
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);
}
}
else
{
remainingPendingAdditions.Add(pending);
}
}
_pendingAdditions = remainingPendingAdditions;
}
/// <summary>
/// 根据给定的Def生成实体对象内部通用方法
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="prefab">要实例化的预制体。</param>
/// <param name="pos">生成位置。</param>
/// <param name="def">实体定义对象。</param>
/// <param name="extraInit">额外的初始化操作(如子弹方向设置)。</param>
/// <returns>成功时返回EntityPrefab组件失败时返回null。</returns>
private EntityPrefab GenerateEntityInternal(
string dimensionId,
GameObject prefab,
Vector3 pos,
EntityDef def,
Action<EntityPrefab> extraInit = null)
{
// 验证维度是否活跃 (通过 Program)。
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null)
{
Debug.LogError($"实体管理器:无法生成实体:维度 '{dimensionId}' 在程序Program中不活跃或未注册。");
return null;
}
// 获取或创建实体所属的层级Transform并确保其在维度根下。
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
if (parentLayer == null)
{
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取或创建实体的父层。");
return null;
}
GameObject instantiatedEntity = null;
try
{
// 实例化实体并将其父级设置为维度下的层级Transform。
instantiatedEntity = Instantiate(prefab, pos, Quaternion.identity, parentLayer);
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";
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
return entityComponent;
}
catch (Exception ex)
{
if (instantiatedEntity) Destroy(instantiatedEntity);
Debug.LogError($"实体管理器:在维度 '{dimensionId}' 中生成实体失败:{ex.Message}\n{ex.StackTrace}");
return null;
}
}
/// <summary>
/// 动态创建层(如果层不存在),现在是维度感知的。
/// 每个维度有自己的层级结构,根在 Dimension.DimensionRoot 下。
/// </summary>
/// <param name="dimensionId">维度的唯一标识符。</param>
/// <param name="layerName">要确保存在的层级名称。</param>
/// <returns>层级Transform如果维度不存在或其根Transform为空则返回null。</returns>
private Transform EnsureLayerExists(string dimensionId, string layerName)
{
// 尝试从维度层级缓存中获取。如果没有该维度的缓存,先尝试初始化。
if (!_dimensionLayerCache.TryGetValue(dimensionId, out var layerCacheForDimension))
{
// 如果 Program 中有这个维度,但在 EntityManage 内部没有初始化,则尝试进行初始化。
// 这种情况可能发生在 OnSceneLoaded 之后Program 动态注册了新的维度。
if (Program.Instance.GetDimension(dimensionId) != null)
{
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
layerCacheForDimension = _dimensionLayerCache[dimensionId]; // 更新引用。
}
else
{
return null;
}
}
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform))
{
return layerTransform;
}
// 如果缓存中没有,尝试在维度根下查找。
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null || dimension.DimensionRoot == null)
{
Debug.LogError($"实体管理器:维度 '{dimensionId}' (来自 Program) 或其根 Transform 为空。无法创建层级 '{layerName}'。");
return null;
}
layerTransform = dimension.DimensionRoot.Find(layerName);
if (!layerTransform)
{
// 如果层不存在动态创建并将其父级设置为维度的根Transform。
var layerObject = new GameObject(layerName);
layerTransform = layerObject.transform;
layerTransform.SetParent(dimension.DimensionRoot);
// 移除了此处的 Debug.Log因为它属于非必要的普通日志。
}
// 将新创建的层加入缓存。
layerCacheForDimension[layerName] = layerTransform;
return layerTransform;
}
/// <summary>
/// 在指定维度和位置生成一个通用实体。
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="entityDef">实体定义对象。</param>
/// <param name="pos">生成位置。</param>
public void GenerateEntity(string dimensionId, EntityDef entityDef, Vector3 pos)
{
if (!characterPrefab)
{
Debug.LogError("实体管理器characterPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (entityDef == null)
{
Debug.LogError("实体管理器EntityDef 为空!无法生成实体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var result = GenerateEntityInternal(
dimensionId,
characterPrefab.gameObject,
pos,
entityDef
);
if (result == null) GenerateDefaultEntity(dimensionId, pos);
}
public void GenerateMonsterEntity(string dimensionId, MonsterDef monsterDef, Vector3 pos)
{
if (!monsterPrefab)
{
Debug.LogError("实体管理器monsterPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (monsterDef == null)
{
Debug.LogError("实体管理器monsterDef 为空!无法生成实体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var result = GenerateEntityInternal(
dimensionId,
monsterPrefab.gameObject,
pos,
monsterDef
);
if (result == null) GenerateDefaultEntity(dimensionId, pos);
}
/// <summary>
/// 在指定维度和网格位置生成一个建筑实体。
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="buildingDef">建筑定义对象。</param>
/// <param name="pos">网格位置。</param>
public void GenerateBuildingEntity(string dimensionId, BuildingDef buildingDef, Vector3Int pos)
{
if (!buildingPrefab)
{
Debug.LogError("实体管理器buildingPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (buildingDef == null)
{
Debug.LogError("实体管理器BuildingDef 为空!无法生成建筑。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var worldPos = new Vector3(pos.x, pos.y, pos.z);
var result = GenerateEntityInternal(
dimensionId,
buildingPrefab.gameObject,
worldPos,
buildingDef
);
if (result == null) GenerateDefaultEntity(dimensionId, worldPos);
}
/// <summary>
/// 在指定维度、位置和方向上生成一个子弹实体。
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="bulletDef">子弹定义对象。</param>
/// <param name="pos">生成位置。</param>
/// <param name="dir">子弹的初始方向。</param>
/// <param name="source">发射子弹的源实体。</param>
public void GenerateBulletEntity(string dimensionId, BulletDef bulletDef, Vector3 pos, Vector3 dir,
Entity.Entity source = null)
{
if (!bulletPrefab)
{
Debug.LogError("实体管理器bulletPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (bulletDef == null)
{
Debug.LogError("实体管理器BulletDef 为空!无法生成子弹。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var result = GenerateEntityInternal(
dimensionId,
bulletPrefab.gameObject,
pos,
bulletDef,
entityComponent =>
{
if (entityComponent != null && entityComponent.entity != null)
{
entityComponent.entity.SetTarget(pos + dir);
}
else
{
Debug.LogWarning($"实体管理器:在维度 '{dimensionId}' 的额外初始化期间,子弹实体组件或其内部实体为空。");
}
}
);
if (result != null && result.entity is Bullet bullet)
{
bullet.bulletSource = source;
if (source != null) bullet.affiliation = source.affiliation;
}
if (result == null) GenerateDefaultEntity(dimensionId, pos);
}
public void GeneratePickupEntity(string dimensionId, ItemDef itemDef, Vector3 pos)
{
if (!pickupPrefab)
{
Debug.LogError("实体管理器pickupPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (itemDef == null)
{
Debug.LogError("实体管理器itemDef 为空!无法生成子弹。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null)
{
Debug.LogError($"实体管理器:无法生成实体:维度 '{dimensionId}' 在程序Program中不活跃或未注册。");
return;
}
// 获取或创建实体所属的层级Transform并确保其在维度根下。
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
if (parentLayer == null)
{
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取或创建实体的父层。");
return;
}
var result = Instantiate(pickupPrefab, pos, Quaternion.identity);
var pickup = result.GetComponent<Pickup>();
result.transform.SetParent(parentLayer);
pickup.Init(itemDef);
if (result == null) GenerateDefaultEntity(dimensionId, pos);
_pendingAdditions.Add(Tuple.Create(dimensionId, "default", result));
}
/// <summary>
/// 在指定维度和位置生成一个默认实体(通常作为回退选项)。
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="pos">生成位置。</param>
public void GenerateDefaultEntity(string dimensionId, Vector3 pos)
{
if (Program.Instance.GetDimension(dimensionId) == null)
{
Debug.LogError($"实体管理器:无法生成默认实体:维度 '{dimensionId}' 在程序Program中不活跃或未注册。");
return;
}
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
if (parentLayer == null)
{
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取默认实体的父 Transform。");
return;
}
if (defaultEntityPrefab == null || defaultEntityPrefab.gameObject == null)
{
Debug.LogError($"实体管理器defaultEntityPrefab 为空或没有GameObject。无法为维度 '{dimensionId}' 生成默认实体。");
return;
}
var entity = Instantiate(defaultEntityPrefab, pos, Quaternion.identity, parentLayer);
var entityComponent = entity.GetComponent<EntityPrefab>();
if (entityComponent == null)
{
Debug.LogError($"实体管理器:默认实体预制体 '{defaultEntityPrefab.name}' 缺少 EntityPrefab 组件。正在销毁已实例化的对象。");
Destroy(entity.gameObject);
return;
}
const string factionKey = "default";
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
entityComponent.DefaultInit();
}
/// <summary>
/// 在指定维度中,查找与源实体具有特定关系的最近实体。
/// </summary>
/// <param name="dimensionId">要搜索的维度ID。</param>
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
/// <param name="targetRelationship">目标实体与源实体之间的期望关系例如Friendly, Hostile。</param>
/// <returns>找到的最近实体预制体,如果没有找到符合条件的实体则返回 null。</returns>
public EntityPrefab FindNearestEntityByRelation(string dimensionId, EntityPrefab sourceEntityPrefab,
Relation targetRelationship)
{
// 参数校验:确保输入参数有效,避免空引用异常。
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
{
Debug.LogWarning("实体管理器FindNearestEntityByRelation 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
return null;
}
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
{
Debug.LogWarning($"实体管理器FindNearestEntityByRelation 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
return null;
}
// 初始化追踪变量:设置初始值,用于在遍历过程中追踪最近的实体及其距离。
// 使用平方距离 (SqrMagnitude) 可以避免在每次距离计算时进行昂贵的开方运算,从而提高性能。
EntityPrefab nearestTarget = null;
var minDistanceSqr = float.MaxValue;
var sourcePos = sourceEntityPrefab.transform.position;
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
var affiliationManager = AffiliationManager.Instance;
if (affiliationManager == null)
{
Debug.LogError("实体管理器FindNearestEntityByRelation 方法中AffiliationManager 实例为空。无法确定实体关系。");
return null;
}
// 遍历所有派系和实体_dimensionFactionEntities 按维度和派系组织,需要遍历所有派系才能找到维度内的所有实体。
foreach (var factionEntities in factionDict.Values) // factionDict.Values 是 LinkedList<EntityPrefab> 的集合
{
foreach (var currentEntityPrefab in factionEntities)
{
// 实体有效性及排除源实体自身:
// 1. 排除无效或已死亡的实体,确保只处理活跃的实体。
// 2. 在寻找“最近”实体时,通常指的是 *除了自身以外* 的实体。
// 如果需要包含自身(例如,当 targetRelationship 是 AffiliationManager.Relation.Self 时),
// 可以根据具体需求调整此逻辑,但默认行为是排除自身。
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
{
continue;
}
// 关系判断:使用 AffiliationManager 提供的接口判断源实体与当前遍历实体之间的关系。
var currentRelation =
affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity);
if (currentRelation == targetRelationship)
{
// 距离计算与最近实体更新:
// 1. 计算与源实体的距离(使用平方距离优化)。
// 2. 如果当前实体更近,则更新 nearestTarget 和 minDistanceSqr。
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
if (distanceSqr < minDistanceSqr)
{
minDistanceSqr = distanceSqr;
nearestTarget = currentEntityPrefab;
}
}
}
}
return nearestTarget; // 返回找到的最近实体
}
/// <summary>
/// 在指定维度中,判断是否存在任何与源实体敌对的活跃实体。
/// </summary>
/// <param name="dimensionId">要搜索的维度ID。</param>
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
/// <returns>如果存在敌对实体则返回 true否则返回 false。</returns>
public bool ExistsHostile(string dimensionId, EntityPrefab sourceEntityPrefab)
{
// 参数校验:确保输入参数有效,避免空引用异常。
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
{
Debug.LogWarning("实体管理器ExistsHostile 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
return false;
}
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
{
Debug.LogWarning($"实体管理器ExistsHostile 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
return false;
}
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
var affiliationManager = AffiliationManager.Instance;
if (affiliationManager == null)
{
Debug.LogError("实体管理器ExistsHostile 方法中AffiliationManager 实例为空。无法确定实体关系。");
return false;
}
// 遍历所有实体:遍历维度内的所有派系,再遍历每个派系下的所有实体。
foreach (var factionEntities in factionDict.Values)
{
foreach (var currentEntityPrefab in factionEntities)
{
// 实体有效性及排除源实体自身:
// 1. 排除无效或已死亡的实体。
// 2. 排除源实体自身,避免自身判断为敌对。
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
{
continue;
}
// 关系判断:判断当前实体与源实体是否为“敌对”关系。
if (affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity) ==
Relation.Hostile)
{
return true; // 找到第一个敌对实体即返回 true提高效率。
}
}
}
return false; // 遍历完所有实体都未找到敌对实体。
}
/// <summary>
/// 在指定维度中,判断源实体视野范围内是否存在任何与源实体敌对的活跃实体。
/// </summary>
/// <param name="dimensionId">要搜索的维度ID。</param>
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
/// <param name="sightRange">视野范围,实体间的距离小于等于此值视为在视野内。</param>
/// <returns>如果视野范围内存在敌对实体则返回 true否则返回 false。</returns>
public bool ExistsHostileInSightRange(string dimensionId, EntityPrefab sourceEntityPrefab, float sightRange)
{
// 参数校验:确保输入参数有效,避免空引用异常及不合理的范围值。
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
{
Debug.LogWarning("实体管理器ExistsHostileInSightRange 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
return false;
}
if (sightRange <= 0)
{
Debug.LogWarning($"实体管理器ExistsHostileInSightRange 方法中,视野范围必须为正值。接收到的值为: {sightRange}。");
return false;
}
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
{
Debug.LogWarning($"实体管理器ExistsHostileInSightRange 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
return false;
}
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
var affiliationManager = AffiliationManager.Instance;
if (affiliationManager == null)
{
Debug.LogError("实体管理器ExistsHostileInSightRange 方法中AffiliationManager 实例为空。无法确定实体关系。");
return false;
}
// 预计算平方距离:避免在循环内部重复计算平方根,提高性能。
var sightRangeSqr = sightRange * sightRange;
var sourcePos = sourceEntityPrefab.transform.position;
// 遍历所有实体:遍历维度内的所有派系,再遍历每个派系下的所有实体。
foreach (var factionEntities in factionDict.Values)
{
foreach (var currentEntityPrefab in factionEntities)
{
// 实体有效性及排除源实体自身:
// 1. 排除无效或已死亡的实体。
// 2. 排除源实体自身。
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
{
continue;
}
// 关系判断:判断当前实体与源实体是否为“敌对”关系。
if (affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity) ==
Relation.Hostile)
{
// 距离判断:如果关系敌对,进一步判断其是否在视野范围内。
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
if (distanceSqr <= sightRangeSqr)
{
return true; // 找到第一个视野范围内的敌对实体即返回 true。
}
}
}
}
return false; // 遍历完所有实体都未找到视野范围内的敌对实体。
}
/// <summary>
/// 单例管理器启动时调用,订阅场景加载事件。
/// </summary>
protected override void OnStart()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
/// <summary>
/// 单例管理器销毁时调用,取消订阅场景加载事件。
/// </summary>
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
/// <summary>
/// 场景加载完成时的回调。
/// 清理旧场景的实体数据,并基于 Program 中已注册的维度重新初始化内部结构。
/// </summary>
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
_dimensionFactionEntities.Clear(); // 清理所有旧场景的实体数据。
_dimensionLayerCache.Clear(); // 清理所有旧场景的层级缓存。
// 场景加载时清理待添加列表中的所有实体,因为它们可能属于旧场景或未在任何维度中被处理。
foreach (var pending in _pendingAdditions)
{
if (pending.Item3 != null && pending.Item3.gameObject != null)
{
Destroy(pending.Item3.gameObject);
}
}
_pendingAdditions.Clear(); // 清理待添加实体列表。
// 在新场景加载完成后,遍历 Program 中已注册的所有维度,并为每个维度初始化 EntityManage 的内部数据结构。
// 此时 Program.Awake() 应该已经完成,所有 Dimension 组件的 Awake() 也已执行Program.RegisteredDimensions 应该包含了当前场景的所有维度。
foreach (var registeredDimensionEntry in Program.Instance.RegisteredDimensions)
{
var dimensionId = registeredDimensionEntry.Key;
var dimension = registeredDimensionEntry.Value;
if (dimension != null) // 确保维度实例本身不是null。
{
// 为该维度ID初始化实体存储结构。
_dimensionFactionEntities[dimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
// 为该维度ID初始化层级缓存。
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
}
else
{
Debug.LogWarning(
$"实体管理器在场景加载初始化期间Program 为ID '{dimensionId}' 注册了一个空的 Dimension 对象。这可能表明维度注册存在问题。");
}
}
}
/// <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'。");
}
}
}
}
}