(client)chore:将维度区分独立,将加载页面独立,降低代码耦合,写了更好看的注释

This commit is contained in:
m0_75251201
2025-08-26 00:11:36 +08:00
parent d8a3daaca8
commit efbf4f824a
22 changed files with 4026 additions and 755 deletions

View File

@ -10,126 +10,44 @@ using UnityEngine.SceneManagement;
namespace Managers
{
/// <summary>
/// 管理游戏中所有实体(角色、建筑、子弹等)的生命周期、存储和分发。
/// 实现了维度感知,这意味着不同维度的实体数据是独立管理的。
/// </summary>
public class EntityManage : Utils.MonoSingleton<EntityManage>, ITick
{
// --- 新增:维度感知的实体存储结构 ---
// 外层字典DimensionId -> 内层字典
// 内层字典FactionKey -> LinkedList<EntityPrefab>
/// <summary>
/// 维度感知的实体存储结构。
/// 外层字典按维度IDDimensionId索引内层字典按派系键FactionKey索引
/// 存储该派系下的实体预制体链表。
/// </summary>
private Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities = new();
// --- 新增:当前场景中活跃的维度实例 ---
private Dictionary<string, Dimension> _activeDimensions = new();
// --- 新增:维度感知的层级缓存 ---
// DimensionId -> LayerName -> Transform
/// <summary>
/// 维度感知的层级Transform缓存。
/// 外层字典按维度IDDimensionId索引内层字典按层级名称LayerName索引
/// 存储其对应的Transform。
/// </summary>
private Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
// --- 待添加实体列表,现在包含 DimensionId ---
/// <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;
/// <summary> 建筑实体的预制体。 </summary>
public EntityPrefab buildingPrefab;
/// <summary> 子弹实体的预制体。 </summary>
public EntityPrefab bulletPrefab;
/// <summary> 默认实体的预制体,用于生成失败时的回退。 </summary>
public EntityPrefab defaultEntityPrefab;
// 已移除RegisterDimension 和 UnregisterDimension 方法的相关注释,因为这些方法已不存在且与逻辑无关。
public void RegisterDimension(Dimension dimension)
{
if (dimension == null || string.IsNullOrEmpty(dimension.DimensionId))
{
Debug.LogError("Attempted to register a null or invalid Dimension.");
return;
}
if (_activeDimensions.TryGetValue(dimension.DimensionId, out var existingDimension))
{
if (existingDimension == dimension)
{
// 【逻辑修改】如果是同一个实例重复注册,只做信息提示,不警告
Debug.Log($"Dimension with ID '{dimension.DimensionId}' already registered with the same instance. Skipping re-registration.");
return; // 已经注册且是同一个实例,直接返回
}
else
{
// 【逻辑修改】如果是不同实例但ID相同这是严重错误
Debug.LogError(
$"CRITICAL ERROR: Dimension with ID '{dimension.DimensionId}' is already registered with a DIFFERENT instance ({existingDimension.name} vs {dimension.name}). This indicates a duplicate DimensionId in the scene. The new instance will overwrite the old one, which may lead to unexpected behavior. Please ensure all Dimension objects have unique IDs.");
// 允许覆盖,但以错误日志形式提示,强制开发者修复场景配置。
}
}
_activeDimensions[dimension.DimensionId] = dimension;
Debug.Log($"Dimension '{dimension.DimensionId}' registered with EntityManage.");
// 为新注册的维度初始化其数据结构
// 这些检查是必要的,以防止在重复注册时清空现有数据。
if (!_dimensionFactionEntities.ContainsKey(dimension.DimensionId))
{
_dimensionFactionEntities[dimension.DimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
}
if (!_dimensionLayerCache.ContainsKey(dimension.DimensionId))
{
_dimensionLayerCache[dimension.DimensionId] = new Dictionary<string, Transform>();
}
}
/// <summary>
/// 从实体管理器注销一个维度。
/// </summary>
/// <param name="dimension">要注销的维度实例。</param>
public void UnregisterDimension(Dimension dimension)
{
if (dimension == null || string.IsNullOrEmpty(dimension.DimensionId))
{
Debug.LogError("Attempted to unregister a null or invalid Dimension.");
return;
}
if (_activeDimensions.Remove(dimension.DimensionId))
{
// 当维度被注销时,清理其所有相关的实体和层级缓存
_dimensionFactionEntities.Remove(dimension.DimensionId);
_dimensionLayerCache.Remove(dimension.DimensionId);
// 【逻辑修改】立即清理_pendingAdditions中属于该维度的实体
// 创建一个新列表来存储不属于该维度的实体
var remainingPendingAdditions = new List<Tuple<string, string, EntityPrefab>>();
foreach (var pending in _pendingAdditions)
{
if (pending.Item1 == dimension.DimensionId)
{
// 销毁实体GameObject
if (pending.Item3 != null && pending.Item3.gameObject != null) // 增加gameObject的null检查
{
Destroy(pending.Item3.gameObject);
}
}
else
{
remainingPendingAdditions.Add(pending);
}
}
_pendingAdditions = remainingPendingAdditions; // 更新_pendingAdditions列表
}
}
/// <summary>
/// 根据ID获取一个活跃的维度实例。
/// </summary>
/// <param name="dimensionId">维度的唯一标识符。</param>
/// <returns>对应的 Dimension 实例,如果不存在则为 null。</returns>
public Dimension GetDimension(string dimensionId)
{
_activeDimensions.TryGetValue(dimensionId, out var dimension);
return dimension;
}
// --- 查找实体 (现在维度感知) ---
/// <summary>
/// 在指定维度中,根据派系键查找所有实体。
/// </summary>
@ -138,6 +56,13 @@ namespace Managers
/// <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))
@ -149,34 +74,46 @@ namespace Managers
return new LinkedList<EntityPrefab>(); // 如果未找到,返回一个空列表
}
// --- Tick 方法 (现在维度感知) ---
/// <summary>
/// 每帧更新Tick方法处理实体逻辑清理死亡实体并将待添加的实体加入管理。
/// </summary>
public void Tick()
{
// 遍历每个活跃维度
foreach (var dimensionEntry in _dimensionFactionEntities.ToList()) // ToList() 避免在迭代时修改字典
// 获取当前管理器中所有活跃维度ID的副本以安全地迭代和移除。
List<string> activeDimensionIdsInManager = _dimensionFactionEntities.Keys.ToList();
foreach (var dimensionId in activeDimensionIdsInManager)
{
var dimensionId = dimensionEntry.Key;
var factionDict = dimensionEntry.Value;
// 检查维度对象本身是否仍然活跃在场景中
// 注意:这里检查的是 _activeDimensions确保 Dimension 对象实例仍然存在且被管理器追踪。
if (!_activeDimensions.ContainsKey(dimensionId))
// 从 Program 中获取维度对象,验证其是否仍然活跃。
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null)
{
Debug.LogWarning(
$"Skipping Tick for dimension '{dimensionId}' as its Dimension object is no longer active. Clearing its entities.");
_dimensionFactionEntities.Remove(dimensionId); // 移除已失效维度的实体数据
_dimensionLayerCache.Remove(dimensionId); // 移除已失效维度的层级缓存
continue;
Debug.LogWarning($"实体管理器:跳过维度 '{dimensionId}' 的Tick处理因为其维度对象在程序Program中已不再活跃或从未注册。正在清理相关内部数据。");
// 如果 Program 不再有这个维度说明它已经失效或被注销EntityManage 需要清理自己的相关数据。
_dimensionFactionEntities.Remove(dimensionId);
_dimensionLayerCache.Remove(dimensionId);
// 清理属于该维度的待添加实体。
RemovePendingAdditionsForDimension(dimensionId);
continue; // 跳过处理这个不存在的维度
}
// 现在真正处理的是 _dimensionFactionEntities 中该维度ID对应的字典。
var factionDict = _dimensionFactionEntities[dimensionId];
foreach (var faction in factionDict)
// 迭代 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 = faction.Value.ToList(); // 创建副本以避免在迭代时修改列表
// 创建副本以避免在迭代时修改列表
var currentEntities = entities.ToList();
foreach (var entityPrefab in currentEntities)
{
// 检查实体预制体或其内部实体是否已销毁或死亡
// 检查实体预制体或其内部实体是否已销毁或死亡
if (entityPrefab == null || entityPrefab.entity == null || entityPrefab.entity.IsDead)
{
entitiesToRemove.Add(entityPrefab);
@ -188,34 +125,59 @@ namespace Managers
}
}
// 删除所有标记为死亡的实体
// 删除所有标记为死亡的实体
foreach (var entityToRemove in entitiesToRemove)
{
faction.Value.Remove(entityToRemove);
if (entityToRemove != null) // 确保它没有被外部销毁
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())
{
foreach (var pending in _pendingAdditions)
// 使用 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))
{
Debug.LogError(
$"Attempted to add entity '{entityPrefab.name}' to unregistered or inactive dimension '{dimensionId}'. Entity will be destroyed.");
if (entityPrefab != null) Destroy(entityPrefab.gameObject);
continue;
// 这情况意味着 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];
@ -225,67 +187,93 @@ namespace Managers
}
factionDict[factionKey].AddLast(entityPrefab);
_pendingAdditions.Remove(pending); // 成功添加后从待添加列表中移除。
}
_pendingAdditions.Clear();
}
}
/// <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>
/// <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, // 新增参数维度ID
string dimensionId,
GameObject prefab,
Vector3 pos,
Data.EntityDef def,
Action<EntityPrefab> extraInit = null)
{
// 验证维度是否活跃
if (!_activeDimensions.TryGetValue(dimensionId, out var dimension))
// 验证维度是否活跃 (通过 Program)。
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null)
{
Debug.LogError($"Cannot generate entity: Dimension '{dimensionId}' is not active or registered.");
Debug.LogError($"实体管理器:无法生成实体:维度 '{dimensionId}' 在程序Program中不活跃或未注册。");
return null;
}
// 获取或创建实体所属的层级Transform并确保其在维度根下
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel"); // 使用一个默认层级名称
// 获取或创建实体所属的层级Transform并确保其在维度根下
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
if (parentLayer == null)
{
Debug.LogError($"Failed to get or create parent layer for entity in dimension '{dimensionId}'.");
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取或创建实体的父层。");
return null;
}
GameObject instantiatedEntity = null;
try
{
// 实例化实体并将其父级设置为维度下的层级Transform
// 实例化实体并将其父级设置为维度下的层级Transform
instantiatedEntity = Instantiate(prefab, pos, Quaternion.identity, parentLayer);
var entityComponent = instantiatedEntity.GetComponent<EntityPrefab>();
if (!entityComponent)
{
throw new InvalidOperationException(
$"EntityPrefab component missing on: {instantiatedEntity.name}");
$"在 '{instantiatedEntity.name}' 上缺少 EntityPrefab 组件,无法完成实体初始化。");
}
entityComponent.Init(def);
extraInit?.Invoke(entityComponent);
var factionKey = def.attributes.defName ?? "default"; // 假设 attributes.defName 是派系键
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent)); // 添加维度ID
var factionKey = def.attributes.defName ?? "default";
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
return entityComponent;
}
catch (System.Exception ex)
{
if (instantiatedEntity) Destroy(instantiatedEntity);
Debug.LogError($"Entity generation failed in dimension '{dimensionId}': {ex.Message}\n{ex.StackTrace}");
Debug.LogError($"实体管理器:在维度 '{dimensionId}' 中生成实体失败:{ex.Message}\n{ex.StackTrace}");
return null;
}
}
@ -299,12 +287,22 @@ namespace Managers
/// <returns>层级Transform如果维度不存在或其根Transform为空则返回null。</returns>
private Transform EnsureLayerExists(string dimensionId, string layerName)
{
// 尝试从维度层级缓存中获取
// 尝试从维度层级缓存中获取。如果没有该维度的缓存,先尝试初始化。
if (!_dimensionLayerCache.TryGetValue(dimensionId, out var layerCacheForDimension))
{
Debug.LogError(
$"Dimension '{dimensionId}' not found in layer cache. This should not happen if dimension is registered.");
return null;
// 如果 Program 中有这个维度,但在 EntityManage 内部没有初始化,则尝试进行初始化。
// 这种情况可能发生在 OnSceneLoaded 之后Program 动态注册了新的维度。
if (Program.Instance.GetDimension(dimensionId) != null)
{
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
layerCacheForDimension = _dimensionLayerCache[dimensionId]; // 更新引用。
Debug.LogWarning($"实体管理器:按需为维度 '{dimensionId}' 初始化层级缓存。");
}
else
{
Debug.LogError($"实体管理器:在层级缓存中未找到维度 '{dimensionId}'且在程序Program中也未注册。无法创建层级 '{layerName}'。");
return null;
}
}
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform))
@ -312,12 +310,11 @@ namespace Managers
return layerTransform;
}
// 如果缓存中没有,尝试在维度根下查找
var dimension = GetDimension(dimensionId);
// 如果缓存中没有,尝试在维度根下查找
var dimension = Program.Instance.GetDimension(dimensionId);
if (dimension == null || dimension.DimensionRoot == null)
{
Debug.LogError(
$"Dimension '{dimensionId}' or its root transform is null. Cannot create layer '{layerName}'.");
Debug.LogError($"实体管理器:维度 '{dimensionId}' (来自 Program) 或其根 Transform 为空。无法创建层级 '{layerName}'。");
return null;
}
@ -325,33 +322,36 @@ namespace Managers
if (!layerTransform)
{
// 如果层不存在动态创建并将其父级设置为维度的根Transform
// 如果层不存在动态创建并将其父级设置为维度的根Transform
var layerObject = new GameObject(layerName);
layerTransform = layerObject.transform;
layerTransform.SetParent(dimension.DimensionRoot);
// 移除了此处的 Debug.Log因为它属于非必要的普通日志。
}
// 将新创建的层加入缓存
// 将新创建的层加入缓存
layerCacheForDimension[layerName] = layerTransform;
return layerTransform;
}
// --- 公共生成方法 (现在维度感知) ---
/// <summary>
/// 在指定维度根据PawnDef生成普通实体。
/// 在指定维度和位置生成一个通用实体。
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="entityDef">实体定义对象。</param>
/// <param name="pos">生成位置。</param>
public void GenerateEntity(string dimensionId, Data.EntityDef entityDef, Vector3 pos)
{
if (!characterPrefab)
{
Debug.LogError("characterPrefab is null! Assign a valid prefab.");
Debug.LogError("实体管理器:characterPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (entityDef == null)
{
Debug.LogError("EntityDef is null! Cannot generate entity.");
Debug.LogError("实体管理器:EntityDef 为空!无法生成实体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
@ -362,24 +362,27 @@ namespace Managers
pos,
entityDef
);
if (!result) GenerateDefaultEntity(dimensionId, pos);
if (result == null) GenerateDefaultEntity(dimensionId, pos);
}
/// <summary>
/// 在指定维度生成建筑实体位置使用Vector3Int
/// 在指定维度和网格位置生成一个建筑实体
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="buildingDef">建筑定义对象。</param>
/// <param name="pos">网格位置。</param>
public void GenerateBuildingEntity(string dimensionId, Data.BuildingDef buildingDef, Vector3Int pos)
{
if (!buildingPrefab)
{
Debug.LogError("buildingPrefab is null! Assign a valid prefab.");
Debug.LogError("实体管理器:buildingPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (buildingDef == null)
{
Debug.LogError("BuildingDef is null! Cannot generate building.");
Debug.LogError("实体管理器:BuildingDef 为空!无法生成建筑。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
@ -391,25 +394,30 @@ namespace Managers
worldPos,
buildingDef
);
if (!result) GenerateDefaultEntity(dimensionId, worldPos);
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, Data.BulletDef bulletDef, Vector3 pos, Vector3 dir,
Entity.Entity source = null)
{
if (!bulletPrefab)
{
Debug.LogError("bulletPrefab is null! Assign a valid prefab.");
Debug.LogError("实体管理器:bulletPrefab 为空!请分配一个有效的预制体。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (bulletDef == null)
{
Debug.LogError("BulletDef is null! Cannot generate bullet.");
Debug.LogError("实体管理器:BulletDef 为空!无法生成子弹。");
GenerateDefaultEntity(dimensionId, pos);
return;
}
@ -419,53 +427,80 @@ namespace Managers
bulletPrefab.gameObject,
pos,
bulletDef,
// 子弹特有的方向设置
entityComponent => entityComponent.entity.SetTarget(pos + dir)
entityComponent =>
{
if (entityComponent != null && entityComponent.entity != null)
{
entityComponent.entity.SetTarget(pos + dir);
}
else
{
Debug.LogWarning($"实体管理器:在维度 '{dimensionId}' 的额外初始化期间,子弹实体组件或其内部实体为空。");
}
}
);
// 确保 result 不为 null 且 entity 是 Bullet 类型
if (result != null && result.entity is Bullet bullet)
{
bullet.bulletSource = source;
if (source != null) bullet.affiliation = source.affiliation; // 确保 source 不为 null
if (source != null) bullet.affiliation = source.affiliation;
}
if (!result) GenerateDefaultEntity(dimensionId, pos);
if (result == null) GenerateDefaultEntity(dimensionId, pos);
}
/// <summary>
/// 在指定维度中,生成默认实体(错误回退)。
/// 在指定维度和位置生成一个默认实体(通常作为回退选项)。
/// </summary>
/// <param name="dimensionId">实体所属的维度ID。</param>
/// <param name="pos">生成位置。</param>
public void GenerateDefaultEntity(string dimensionId, Vector3 pos)
{
if (!_activeDimensions.ContainsKey(dimensionId))
if (Program.Instance.GetDimension(dimensionId) == null)
{
Debug.LogError(
$"Cannot generate default entity: Dimension '{dimensionId}' is not active or registered.");
Debug.LogError($"实体管理器:无法生成默认实体:维度 '{dimensionId}' 在程序Program中不活跃或未注册。");
return;
}
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
if (parentLayer == null)
{
Debug.LogError($"Failed to get parent transform for default entity in dimension '{dimensionId}'.");
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>
protected override void OnStart()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
/// <summary>
/// 单例管理器销毁时调用,取消订阅场景加载事件。
/// </summary>
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
@ -473,17 +508,47 @@ namespace Managers
/// <summary>
/// 场景加载完成时的回调。
/// 清理旧场景的实体数据,并重新扫描新场景中的维度
/// 清理旧场景的实体数据,并基于 Program 中已注册的维度重新初始化内部结构
/// </summary>
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
_dimensionFactionEntities.Clear(); // 清理所有维度下的实体数据
_dimensionLayerCache.Clear(); // 清理所有维度下的层级缓存
_pendingAdditions.Clear(); // 清理待添加实体列表
_activeDimensions.Clear(); // 清理活跃维度列表,因为旧场景的维度对象已被销毁
}
_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)
{
string dimensionId = registeredDimensionEntry.Key;
Dimension 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)
@ -492,14 +557,17 @@ namespace Managers
if (pre != null)
{
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
if (defaultEntityPrefab == null)
{
Debug.LogError($"实体管理器:已加载 DefaultEntity 预制体,但它缺少来自 Resources/Default/DefaultEntity 的 EntityPrefab 组件。请确保它包含该组件。");
}
}
else
{
Debug.LogError(
"Failed to load DefaultEntity prefab from Resources/Default/DefaultEntity. Please ensure it exists at 'Assets/Resources/Default/DefaultEntity.prefab'.");
"实体管理器:无法从 Resources/Default/DefaultEntity 加载 DefaultEntity 预制体。请确保它存在于 'Assets/Resources/Default/DefaultEntity.prefab'");
}
}
}
}
}
}