Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/Managers/EntityManage.cs
2025-08-25 18:24:12 +08:00

505 lines
21 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 System;
using System.Collections.Generic;
using System.Linq;
using Base;
using Entity;
using Map;
using Prefab;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Managers
{
public class EntityManage : Utils.MonoSingleton<EntityManage>, ITick
{
// --- 新增:维度感知的实体存储结构 ---
// 外层字典DimensionId -> 内层字典
// 内层字典FactionKey -> LinkedList<EntityPrefab>
private Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities = new();
// --- 新增:当前场景中活跃的维度实例 ---
private Dictionary<string, Dimension> _activeDimensions = new();
// --- 新增:维度感知的层级缓存 ---
// DimensionId -> LayerName -> Transform
private Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
// --- 待添加实体列表,现在包含 DimensionId ---
private List<Tuple<string, string, EntityPrefab>>
_pendingAdditions = new(); // Item1: DimensionId, Item2: FactionKey, Item3: EntityPrefab
// --- 现有预制体 (保持不变) ---
public EntityPrefab characterPrefab;
public EntityPrefab buildingPrefab;
public EntityPrefab bulletPrefab;
public EntityPrefab defaultEntityPrefab;
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>
/// <param name="dimensionId">维度的唯一标识符。</param>
/// <param name="factionKey">派系键。</param>
/// <returns>指定派系下的实体列表,如果未找到则返回空列表。</returns>
public LinkedList<EntityPrefab> FindEntitiesByFaction(string dimensionId, string factionKey)
{
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
{
if (factionDict.TryGetValue(factionKey, out var entities))
{
return entities;
}
}
return new LinkedList<EntityPrefab>(); // 如果未找到,返回一个空列表
}
// --- Tick 方法 (现在维度感知) ---
public void Tick()
{
// 遍历每个活跃的维度
foreach (var dimensionEntry in _dimensionFactionEntities.ToList()) // ToList() 避免在迭代时修改字典
{
var dimensionId = dimensionEntry.Key;
var factionDict = dimensionEntry.Value;
// 检查维度对象本身是否仍然活跃在场景中
// 注意:这里检查的是 _activeDimensions确保 Dimension 对象实例仍然存在且被管理器追踪。
if (!_activeDimensions.ContainsKey(dimensionId))
{
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;
}
foreach (var faction in factionDict)
{
var entitiesToRemove = new List<EntityPrefab>();
var currentEntities = faction.Value.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)
{
faction.Value.Remove(entityToRemove);
if (entityToRemove != null) // 确保它没有被外部销毁
{
Destroy(entityToRemove.gameObject);
}
}
}
}
// 处理待添加实体 (现在维度感知)
if (_pendingAdditions.Any())
{
foreach (var pending in _pendingAdditions)
{
var dimensionId = pending.Item1;
var factionKey = pending.Item2;
var entityPrefab = pending.Item3;
// 再次检查维度是否活跃,防止在添加前维度被注销
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;
}
var factionDict = _dimensionFactionEntities[dimensionId];
if (!factionDict.ContainsKey(factionKey))
{
factionDict[factionKey] = new LinkedList<EntityPrefab>();
}
factionDict[factionKey].AddLast(entityPrefab);
}
_pendingAdditions.Clear();
}
}
/// <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, // 新增参数维度ID
GameObject prefab,
Vector3 pos,
Data.EntityDef def,
Action<EntityPrefab> extraInit = null)
{
// 验证维度是否活跃
if (!_activeDimensions.TryGetValue(dimensionId, out var dimension))
{
Debug.LogError($"Cannot generate entity: Dimension '{dimensionId}' is not active or registered.");
return null;
}
// 获取或创建实体所属的层级Transform并确保其在维度根下
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel"); // 使用一个默认层级名称
if (parentLayer == null)
{
Debug.LogError($"Failed to get or create parent layer for entity in dimension '{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(
$"EntityPrefab component missing on: {instantiatedEntity.name}");
}
entityComponent.Init(def);
extraInit?.Invoke(entityComponent);
var factionKey = def.attributes.defName ?? "default"; // 假设 attributes.defName 是派系键
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent)); // 添加维度ID
return entityComponent;
}
catch (System.Exception ex)
{
if (instantiatedEntity) Destroy(instantiatedEntity);
Debug.LogError($"Entity generation failed in dimension '{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))
{
Debug.LogError(
$"Dimension '{dimensionId}' not found in layer cache. This should not happen if dimension is registered.");
return null;
}
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform))
{
return layerTransform;
}
// 如果缓存中没有,尝试在维度根下查找
var dimension = GetDimension(dimensionId);
if (dimension == null || dimension.DimensionRoot == null)
{
Debug.LogError(
$"Dimension '{dimensionId}' or its root transform is null. Cannot create layer '{layerName}'.");
return null;
}
layerTransform = dimension.DimensionRoot.Find(layerName);
if (!layerTransform)
{
// 如果层不存在动态创建并将其父级设置为维度的根Transform
var layerObject = new GameObject(layerName);
layerTransform = layerObject.transform;
layerTransform.SetParent(dimension.DimensionRoot);
}
// 将新创建的层加入缓存
layerCacheForDimension[layerName] = layerTransform;
return layerTransform;
}
// --- 公共生成方法 (现在维度感知) ---
/// <summary>
/// 在指定维度中根据PawnDef生成普通实体。
/// </summary>
public void GenerateEntity(string dimensionId, Data.EntityDef entityDef, Vector3 pos)
{
if (!characterPrefab)
{
Debug.LogError("characterPrefab is null! Assign a valid prefab.");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (entityDef == null)
{
Debug.LogError("EntityDef is null! Cannot generate entity.");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var result = GenerateEntityInternal(
dimensionId,
characterPrefab.gameObject,
pos,
entityDef
);
if (!result) GenerateDefaultEntity(dimensionId, pos);
}
/// <summary>
/// 在指定维度中生成建筑实体位置使用Vector3Int
/// </summary>
public void GenerateBuildingEntity(string dimensionId, Data.BuildingDef buildingDef, Vector3Int pos)
{
if (!buildingPrefab)
{
Debug.LogError("buildingPrefab is null! Assign a valid prefab.");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (buildingDef == null)
{
Debug.LogError("BuildingDef is null! Cannot generate building.");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var worldPos = new Vector3(pos.x, pos.y, pos.z);
var result = GenerateEntityInternal(
dimensionId,
buildingPrefab.gameObject,
worldPos,
buildingDef
);
if (!result) GenerateDefaultEntity(dimensionId, worldPos);
}
/// <summary>
/// 在指定维度中,生成子弹实体(含方向设置)。
/// </summary>
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.");
GenerateDefaultEntity(dimensionId, pos);
return;
}
if (bulletDef == null)
{
Debug.LogError("BulletDef is null! Cannot generate bullet.");
GenerateDefaultEntity(dimensionId, pos);
return;
}
var result = GenerateEntityInternal(
dimensionId,
bulletPrefab.gameObject,
pos,
bulletDef,
// 子弹特有的方向设置
entityComponent => entityComponent.entity.SetTarget(pos + dir)
);
// 确保 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 (!result) GenerateDefaultEntity(dimensionId, pos);
}
/// <summary>
/// 在指定维度中,生成默认实体(错误回退)。
/// </summary>
public void GenerateDefaultEntity(string dimensionId, Vector3 pos)
{
if (!_activeDimensions.ContainsKey(dimensionId))
{
Debug.LogError(
$"Cannot generate default entity: Dimension '{dimensionId}' is not active or registered.");
return;
}
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
if (parentLayer == null)
{
Debug.LogError($"Failed to get parent transform for default entity in dimension '{dimensionId}'.");
return;
}
var entity = Instantiate(defaultEntityPrefab, pos, Quaternion.identity, parentLayer);
var entityComponent = entity.GetComponent<EntityPrefab>();
const string factionKey = "default";
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
entityComponent.DefaultInit();
}
// --- 单例生命周期与场景切换处理 ---
protected override void OnStart()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
/// <summary>
/// 场景加载完成时的回调。
/// 清理旧场景的实体数据,并重新扫描新场景中的维度。
/// </summary>
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
_dimensionFactionEntities.Clear(); // 清理所有维度下的实体数据
_dimensionLayerCache.Clear(); // 清理所有维度下的层级缓存
_pendingAdditions.Clear(); // 清理待添加实体列表
_activeDimensions.Clear(); // 清理活跃维度列表,因为旧场景的维度对象已被销毁
}
private void Start()
{
if (defaultEntityPrefab == null)
{
var pre = Resources.Load<GameObject>("Default/DefaultEntity");
if (pre != null)
{
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
}
else
{
Debug.LogError(
"Failed to load DefaultEntity prefab from Resources/Default/DefaultEntity. Please ensure it exists at 'Assets/Resources/Default/DefaultEntity.prefab'.");
}
}
}
}
}