- 调整优化了 CombatNodeComponent 相关实体的生命周期管理

- 总结了一份 CombatNodeComponent 文档 CombatNodeArchitecture.md
This commit is contained in:
SepComet
2026-02-28 20:20:02 +08:00
parent dfd37778b6
commit 26dc1a5600
10 changed files with 364 additions and 73 deletions
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.CustomEvent;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using UnityEngine;
@@ -65,9 +66,8 @@ namespace GeometryTD.CustomComponent
}
DRLevelPhase[] levelPhases = dtLevelPhase.GetAllDataRows();
for (int i = 0; i < levelPhases.Length; i++)
foreach (var phase in levelPhases)
{
DRLevelPhase phase = levelPhases[i];
int levelId = phase.Id / 1000;
if (!_levelsById.ContainsKey(levelId))
{
@@ -85,9 +85,8 @@ namespace GeometryTD.CustomComponent
}
DRLevelSpawnEntry[] spawnEntries = dtSpawnEntry.GetAllDataRows();
for (int i = 0; i < spawnEntries.Length; i++)
foreach (var spawnEntry in spawnEntries)
{
DRLevelSpawnEntry spawnEntry = spawnEntries[i];
int phaseId = spawnEntry.Id / 1000;
int levelId = phaseId / 1000;
if (!_levelsById.ContainsKey(levelId))
@@ -171,6 +170,12 @@ namespace GeometryTD.CustomComponent
CurrentLevel = selectedLevel;
_combatScheduler.Start(selectedLevel, phaseList, _selectedSpawnEntriesByPhaseId);
GameEntry.Event.Fire(this, NodeEnterEventArgs.Create());
}
public void EndCombat()
{
GameEntry.Event.Fire(this, NodeCompleteEventArgs.Create());
}
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
@@ -252,4 +257,4 @@ namespace GeometryTD.CustomComponent
return count;
}
}
}
}
@@ -23,9 +23,9 @@ namespace GeometryTD.CustomComponent
Failed = 4
}
private readonly List<DRLevelPhase> _phaseBuffer = new List<DRLevelPhase>();
private readonly Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>> _spawnEntriesByPhaseId =
new Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>>();
private readonly List<DRLevelPhase> _phaseBuffer = new();
private readonly Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>> _spawnEntriesByPhaseId = new();
private readonly EnemyManager _enemyManager = new EnemyManager();
@@ -79,8 +79,8 @@ namespace GeometryTD.CustomComponent
return;
}
CleanupBattleEntities();
_enemyManager.EndPhase();
HideCurrentMapIfNeeded();
ResetRuntime();
_currentLevel = level;
@@ -145,8 +145,8 @@ namespace GeometryTD.CustomComponent
return;
}
CleanupBattleEntities();
_enemyManager.OnDestroy();
HideCurrentMapIfNeeded();
ResetRuntime();
UnsubscribeEntityEvents();
_entity = null;
@@ -253,7 +253,12 @@ namespace GeometryTD.CustomComponent
{
_state = SchedulerState.Completed;
_currentPhase = null;
if (_currentMap != null)
{
_entity.HideEntity(_currentMap);
}
Log.Info("CombatScheduler level completed. Level={0}.", _currentLevel != null ? _currentLevel.Id : 0);
GameEntry.CombatNode.EndCombat();
return;
}
@@ -273,39 +278,6 @@ namespace GeometryTD.CustomComponent
spawnEntries != null ? spawnEntries.Count : 0);
}
private void HideCurrentMapIfNeeded()
{
if (_entity == null)
{
_currentMap = null;
_loadingMapEntityId = 0;
_loadedMapEntityId = 0;
return;
}
if (_loadingMapEntityId != 0)
{
EntityBase loadingMap = _entity.GetGameEntity(_loadingMapEntityId);
if (loadingMap != null)
{
_entity.HideEntity(loadingMap);
}
}
if (_loadedMapEntityId != 0)
{
EntityBase loadedMap = _entity.GetGameEntity(_loadedMapEntityId);
if (loadedMap != null)
{
_entity.HideEntity(loadedMap);
}
}
_loadingMapEntityId = 0;
_loadedMapEntityId = 0;
_currentMap = null;
}
private void EnsureEntityEventSubscribed()
{
if (_isEntityEventSubscribed)
@@ -340,9 +312,40 @@ namespace GeometryTD.CustomComponent
_currentPhase = null;
_currentPhaseIndex = -1;
_currentPhaseElapsed = 0f;
_loadingMapEntityId = 0;
_loadedMapEntityId = 0;
_currentMap = null;
_state = SchedulerState.Idle;
}
private void CleanupBattleEntities()
{
if (_entity == null)
{
return;
}
if (_loadingMapEntityId != 0 && _entity.IsLoadingEntity(_loadingMapEntityId))
{
_entity.HideEntity(_loadingMapEntityId);
}
if (_currentMap != null)
{
_entity.HideEntity(_currentMap);
}
else if (_loadedMapEntityId != 0)
{
UnityGameFramework.Runtime.Entity loadedMapEntity = _entity.GetEntity(_loadedMapEntityId);
if (loadedMapEntity != null)
{
_entity.HideEntity(loadedMapEntity);
}
}
_enemyManager.CleanupTrackedEnemies();
}
#region Event Handlers
private void OnShowEntitySuccess(object sender, GameEventArgs e)
@@ -29,6 +29,8 @@ namespace GeometryTD.CustomComponent
private readonly Dictionary<int, Spawner> _spawnerByOrder = new Dictionary<int, Spawner>();
private readonly List<Vector3> _pathBuffer = new List<Vector3>();
private readonly List<SpawnEntryRuntime> _spawnRuntimes = new List<SpawnEntryRuntime>();
private readonly HashSet<int> _trackedEnemyEntityIds = new HashSet<int>();
private readonly List<int> _trackedEnemyIdBuffer = new List<int>();
private CombatScheduler _combatScheduler;
private EntityComponent _entity;
@@ -66,6 +68,8 @@ namespace GeometryTD.CustomComponent
_spawnerByOrder.Clear();
_pathBuffer.Clear();
_spawnRuntimes.Clear();
_trackedEnemyEntityIds.Clear();
_trackedEnemyIdBuffer.Clear();
_phaseElapsed = 0f;
_isPhaseRunning = false;
IsPhaseSpawnCompleted = true;
@@ -134,6 +138,7 @@ namespace GeometryTD.CustomComponent
return;
}
CleanupTrackedEnemies();
EndPhase();
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
@@ -142,12 +147,47 @@ namespace GeometryTD.CustomComponent
_spawners.Clear();
_spawnerByOrder.Clear();
_pathBuffer.Clear();
_trackedEnemyEntityIds.Clear();
_trackedEnemyIdBuffer.Clear();
_currentEnemyCount = 0;
_currentMapEntityId = 0;
_nextSpawnerIndex = 0;
_combatScheduler = null;
_initialized = false;
}
public void CleanupTrackedEnemies()
{
if (_trackedEnemyEntityIds.Count <= 0)
{
_currentEnemyCount = 0;
return;
}
_trackedEnemyIdBuffer.Clear();
foreach (int trackedEnemyEntityId in _trackedEnemyEntityIds)
{
_trackedEnemyIdBuffer.Add(trackedEnemyEntityId);
}
_trackedEnemyEntityIds.Clear();
_currentEnemyCount = 0;
if (_entity == null)
{
return;
}
for (int i = 0; i < _trackedEnemyIdBuffer.Count; i++)
{
int trackedEnemyEntityId = _trackedEnemyIdBuffer[i];
if (_entity.HasEntity(trackedEnemyEntityId) || _entity.IsLoadingEntity(trackedEnemyEntityId))
{
_entity.HideEntity(trackedEnemyEntityId);
}
}
}
private SpawnEntryRuntime BuildSpawnRuntime(DRLevelSpawnEntry entry)
{
if (entry == null || entry.EntryType == EntryType.None)
@@ -167,12 +207,12 @@ namespace GeometryTD.CustomComponent
switch (entry.EntryType)
{
case EntryType.Stream:
{
float duration = Mathf.Max(0f, entry.Duration);
runtime.EndTime = duration > 0f ? runtime.NextTriggerTime + duration : runtime.NextTriggerTime;
runtime.Completed = entry.Count <= 0;
return runtime;
}
{
float duration = Mathf.Max(0f, entry.Duration);
runtime.EndTime = duration > 0f ? runtime.NextTriggerTime + duration : runtime.NextTriggerTime;
runtime.Completed = entry.Count <= 0;
return runtime;
}
case EntryType.Burst:
case EntryType.Boss:
runtime.Completed = runtime.RemainingCount <= 0;
@@ -311,6 +351,7 @@ namespace GeometryTD.CustomComponent
}
int enemyEntityId = _entity.GenerateSerialId();
_trackedEnemyEntityIds.Add(enemyEntityId);
EnemyData enemyData = new EnemyData(
enemyEntityId,
enemyConfig.EntityId,
@@ -434,17 +475,28 @@ namespace GeometryTD.CustomComponent
private void OnShowEntitySuccess(object sender, GameEventArgs e)
{
if (e is ShowEntitySuccessEventArgs ne)
if (!(e is ShowEntitySuccessEventArgs ne)) return;
if (ne.EntityLogicType == typeof(EnemyEntity) &&
_trackedEnemyEntityIds.Contains(ne.Entity.Id))
{
if (ne.EntityLogicType == typeof(EnemyEntity))
{
_currentEnemyCount++;
}
_currentEnemyCount++;
}
}
private void OnShowEntityFailure(object sender, GameEventArgs e)
{
if (!(e is ShowEntityFailureEventArgs ne))
{
return;
}
if (ne.EntityLogicType != typeof(EnemyEntity))
{
return;
}
_trackedEnemyEntityIds.Remove(ne.EntityId);
}
private void OnHideEntityComplete(object sender, GameEventArgs e)
@@ -454,7 +506,7 @@ namespace GeometryTD.CustomComponent
return;
}
if (ne.EntityGroup.Name != "Enemy")
if (!_trackedEnemyEntityIds.Remove(ne.EntityId))
{
return;
}
@@ -462,4 +514,4 @@ namespace GeometryTD.CustomComponent
_currentEnemyCount = Mathf.Max(0, _currentEnemyCount - 1);
}
}
}
}