using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; using SepCore.DataTable; using SepCore.Definition; using SepCore.Entity; using GameFramework.Event; using SepCore.AsyncTask; using SepCore.CustomUtility; using UnityEngine; using UnityGameFramework.Runtime; namespace SepCore.EnemyManager { public class EnemyManagerComponent : GameFrameworkComponent { private const string EnemyGroupName = "Enemy"; private EntityComponent _entity; private EnemyRegistry _enemyRegistry; private EnemySpawnScheduler _spawnScheduler; public IReadOnlyCollection Enemies => _enemyRegistry?.Enemies; [SerializeField] private int _spawnEnemyMaxCount = 5000; [SerializeField] private int _spawnDistanceFromPlayer = 20; private int _currentSpawnEnemyId; private int _currentLevel; private float _duration; private float _baseDuration; private Transform _player; private ISpawnPositionStrategy _spawnPositionStrategy; private CancellationTokenSource _spawnCts; // 停战标志。OnReset 置 true,OnInit 置 false。 // ShowEntity 请求一旦发出,取消 await 并不能阻止框架真正创建实体, // 这些在途敌人会在 OnReset 之后才出生(fire ShowEntitySuccess)。 // 此时 _isStopped 为真,由 OnShowEntitySuccess 在出生瞬间兜底 Hide 掉。 private bool _isStopped; public float SpawnRateScale => _spawnScheduler?.SpawnRateScale ?? 1f; public float BattleDuration => _duration; public float ElapsedBattleTime => _spawnScheduler?.ElapsedTime ?? 0f; public int CurrentEnemyCount => _enemyRegistry?.Count ?? 0; #region Lifecycle private void Start() { _entity = GameEntry.Entity; _enemyRegistry = new EnemyRegistry(); _spawnScheduler = new EnemySpawnScheduler(); _spawnCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess); } private void OnDestroy() { GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess); _spawnCts?.Dispose(); _spawnCts = null; _enemyRegistry = null; _spawnScheduler?.Reset(); _spawnScheduler = null; _entity = null; } public void OnInit(DRLevel level, Player player) { _isStopped = false; _player = player != null ? player.CachedTransform : null; _spawnPositionStrategy = new RandomCircleSpawnStrategy(_spawnDistanceFromPlayer); _baseDuration = level.Duration; _duration = _baseDuration; _currentLevel = level.Id; _spawnScheduler.Init(level, OnWaveSpawn); _enemyRegistry.Clear(); _currentSpawnEnemyId = 0; } public void OnUpdate(float elapseSeconds, float realElapseSeconds) { _enemyRegistry.PruneInvalidEntries(); _spawnScheduler.Tick(elapseSeconds); } private void OnWaveSpawn(EnemyType enemyType, int count) { for (int j = 0; j < count; j++) { SpawnEnemyAsync(enemyType).Forget(); } } public void OnReset() { // 先置停战标志。Cancel() 会同步恢复在途的 SpawnEnemyAsync(enemy 拿不到引用, // 无法自行 hide),这些实体会延后出生,由 OnShowEntitySuccess 兜底。 _isStopped = true; _spawnCts.Cancel(); _spawnCts.Dispose(); _spawnCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); _spawnScheduler.Reset(); ClearEnemies(); _currentSpawnEnemyId = 0; _currentLevel = 0; _baseDuration = 0; _duration = 0; } #endregion private async UniTaskVoid SpawnEnemyAsync(EnemyType enemyType) { if (_player == null) return; if (_enemyRegistry.Count >= _spawnEnemyMaxCount) return; int entityId = _currentSpawnEnemyId++; var enemyData = EntityDataFactory.Create(entityId, enemyType, _currentLevel); enemyData.Position = _spawnPositionStrategy.GetSpawnPosition(_player); var ct = _spawnCts.Token; var (isCanceled, enemy) = await _entity.ShowEnemyAsync(enemyData, cancellationToken: ct).SuppressCancellationThrow(); if (isCanceled || ct.IsCancellationRequested || enemy == null || !enemy.Available) { // 取消通常发生在 OnReset 期间,此时实体往往尚未出生,enemy 为 null 无法 hide。 // 真正的兜底在 OnShowEntitySuccess:停战后出生的敌人会被立即 hide。 if (enemy != null && enemy.Available) { _entity.HideEntity(enemy); } return; } // await 恢复时若已停战,说明这是漏网的在途敌人,立即 hide,不注册。 if (_isStopped) { _entity.HideEntity(enemy); return; } if (_player != null) { enemy.SetTarget(_player); } _enemyRegistry.Register(enemy); } private void ClearEnemies() { var enemies = new List(_enemyRegistry.Enemies); foreach (var enemy in enemies) { if (enemy == null || !enemy.Available) continue; _entity.HideEntity(enemy); } _enemyRegistry.Clear(); } public bool TryGetEnemy(int entityId, out EntityBase enemy) { return _enemyRegistry.TryGet(entityId, out enemy); } public void SetSpawnRateScale(float scale) { _spawnScheduler.SetSpawnRateScale(scale); } #region Event Handler private void OnHideEntityComplete(object sender, GameEventArgs e) { if (e is not HideEntityCompleteEventArgs ne) return; string entityGroupName = ne.EntityGroup.Name; if (entityGroupName == EnemyGroupName) { _enemyRegistry.Remove(ne.EntityId); } } private void OnShowEntitySuccess(object sender, GameEventArgs e) { if (e is not ShowEntitySuccessEventArgs ne) return; if (ne.Entity.EntityGroup?.Name != EnemyGroupName) { return; } // 停战窗口(Shop/LevelUp)期间不应存在任何敌人。OnReset 之前发出的在途 // ShowEntity 请求会在此刻才真正出生,立即 hide 兜底。 if (_isStopped) { _entity.HideEntity(ne.Entity.Id); } } #endregion } }