From 38849f5019d983edef7510e6a1eae3343acc01ff Mon Sep 17 00:00:00 2001 From: basil <2428390463@qq.com> Date: Thu, 18 Jun 2026 15:40:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8E=20EnemyManager=20=E9=87=8C=E6=8B=86?= =?UTF-8?q?=E5=88=86=E5=87=BA=E6=95=8C=E4=BA=BA=E7=9A=84=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EnemyManager/EnemyManagerComponent.cs | 96 ++------------ .../EnemyManager/EnemySpawnScheduler.cs | 122 ++++++++++++++++++ .../EnemyManager/EnemySpawnScheduler.cs.meta | 11 ++ 3 files changed, 147 insertions(+), 82 deletions(-) create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs create mode 100644 Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs.meta diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs index dfcf504..99d0164 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs @@ -13,48 +13,37 @@ namespace SepCore.EnemyManager { public class EnemyManagerComponent : GameFrameworkComponent { - private const float MinSpawnRateScale = 0.1f; private const string EnemyGroupName = "Enemy"; private EntityComponent _entity; private EnemyRegistry _enemyRegistry; + private EnemySpawnScheduler _spawnScheduler; public List Enemies => _enemyRegistry?.Enemies; - private float _spawnEnemyTimer; - [SerializeField] private int _spawnEnemyMaxCount = 5000; - [SerializeField] private int _spawnDistanceFromPlayer = 20; private int _currentSpawnEnemyId; - private int _currentLevel; - - private float[] _baseSpawnEnemyIntervals; - private float[] _spawnEnemyIntervals; - private EnemyType[] _spawnEnemyTypes; - private int[] _spawnEnemyCounts; private float _duration; private float _baseDuration; - private float[] _nextSpawnTimes; - private float _spawnRateScale = 1f; - private Transform _player; private ISpawnPositionStrategy _spawnPositionStrategy; - public float SpawnRateScale => _spawnRateScale; + public float SpawnRateScale => _spawnScheduler?.SpawnRateScale ?? 1f; public float BattleDuration => _duration; - public float ElapsedBattleTime => _spawnEnemyTimer; + public float ElapsedBattleTime => _spawnScheduler?.ElapsedTime ?? 0f; public int CurrentEnemyCount => _enemyRegistry?.Count ?? 0; - #region FSM + #region Lifecycle private void Start() { _entity = GameEntry.Entity; _enemyRegistry = new EnemyRegistry(); + _spawnScheduler = new EnemySpawnScheduler(); GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); } @@ -64,6 +53,7 @@ namespace SepCore.EnemyManager GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); _enemyRegistry = null; + _spawnScheduler = null; _entity = null; } @@ -72,52 +62,34 @@ namespace SepCore.EnemyManager _player = player != null ? player.CachedTransform : null; _spawnPositionStrategy = new RandomCircleSpawnStrategy(_spawnDistanceFromPlayer); - _baseSpawnEnemyIntervals = (float[])level.Intervals.Clone(); - _spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone(); - _spawnEnemyTypes = level.EntityTypes; - _spawnEnemyCounts = level.EntityCounts; _baseDuration = level.Duration; _duration = _baseDuration; - SetSpawnRateScale(_spawnRateScale); - + _spawnScheduler.Init(level); _enemyRegistry.Clear(); _currentSpawnEnemyId = 0; } public void OnUpdate(float elapseSeconds, float realElapseSeconds) { - _spawnEnemyTimer += elapseSeconds; - - for (int i = 0; i < _nextSpawnTimes.Length; i++) + var spawnRequests = _spawnScheduler.Tick(elapseSeconds); + foreach (var request in spawnRequests) { - float nextSpawnTime = _nextSpawnTimes[i]; - if (_spawnEnemyTimer < nextSpawnTime) continue; - for (int j = 0; j < _spawnEnemyCounts[i]; j++) + for (int j = 0; j < request.Count; j++) { - SpawnEnemyAsync(_spawnEnemyTypes[i]).Forget(); + SpawnEnemyAsync(request.EnemyType).Forget(); } - - _nextSpawnTimes[i] += _spawnEnemyIntervals[i]; } } public void OnReset() { - _spawnEnemyTimer = 0; + _spawnScheduler.Reset(); + _enemyRegistry.Clear(); _currentSpawnEnemyId = 0; _currentLevel = 0; - - _baseSpawnEnemyIntervals = null; - _spawnEnemyIntervals = null; - _spawnEnemyTypes = null; - _spawnEnemyCounts = null; _baseDuration = 0; _duration = 0; - - _nextSpawnTimes = null; - - ClearEnemies(); } #endregion @@ -160,47 +132,7 @@ namespace SepCore.EnemyManager public void SetSpawnRateScale(float scale) { - float newScale = Mathf.Max(MinSpawnRateScale, scale); - if (_baseSpawnEnemyIntervals == null || _baseSpawnEnemyIntervals.Length == 0) - { - _spawnRateScale = newScale; - return; - } - - bool hasRuntimeState = _nextSpawnTimes != null && _spawnEnemyIntervals != null && - _nextSpawnTimes.Length == _baseSpawnEnemyIntervals.Length && - _spawnEnemyIntervals.Length == _baseSpawnEnemyIntervals.Length; - float oldScale = _spawnRateScale; - _spawnRateScale = newScale; - - if (!hasRuntimeState) - { - for (int i = 0; i < _baseSpawnEnemyIntervals.Length; i++) - { - _spawnEnemyIntervals[i] = GetScaledInterval(_baseSpawnEnemyIntervals[i], _spawnRateScale); - } - - _nextSpawnTimes = (float[])_spawnEnemyIntervals.Clone(); - return; - } - - for (int i = 0; i < _baseSpawnEnemyIntervals.Length; i++) - { - float oldInterval = GetScaledInterval(_baseSpawnEnemyIntervals[i], oldScale); - float newInterval = GetScaledInterval(_baseSpawnEnemyIntervals[i], _spawnRateScale); - - float remainTime = Mathf.Max(0f, _nextSpawnTimes[i] - _spawnEnemyTimer); - float remainRatio = oldInterval > Mathf.Epsilon ? Mathf.Clamp01(remainTime / oldInterval) : 0f; - - _spawnEnemyIntervals[i] = newInterval; - _nextSpawnTimes[i] = _spawnEnemyTimer + newInterval * remainRatio; - } - } - - private static float GetScaledInterval(float baseInterval, float scale) - { - float safeScale = Mathf.Max(MinSpawnRateScale, scale); - return baseInterval / safeScale; + _spawnScheduler.SetSpawnRateScale(scale); } #region Event Handler diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs new file mode 100644 index 0000000..4961edc --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using SepCore.DataTable; +using SepCore.Definition; +using UnityEngine; + +namespace SepCore.EnemyManager +{ + public class EnemySpawnScheduler + { + private const float MinSpawnRateScale = 0.1f; + + private float _elapsedTime; + private float[] _baseIntervals; + private float[] _currentIntervals; + private EnemyType[] _enemyTypes; + private int[] _spawnCounts; + private float[] _nextSpawnTimes; + private float _spawnRateScale = 1f; + + public float SpawnRateScale => _spawnRateScale; + public float ElapsedTime => _elapsedTime; + public int WaveCount => _enemyTypes?.Length ?? 0; + + public void Init(DRLevel level) + { + _baseIntervals = (float[])level.Intervals.Clone(); + _currentIntervals = (float[])_baseIntervals.Clone(); + _enemyTypes = level.EntityTypes; + _spawnCounts = level.EntityCounts; + + SetSpawnRateScale(_spawnRateScale); + } + + public List Tick(float deltaTime) + { + _elapsedTime += deltaTime; + var requests = new List(); + + if (_nextSpawnTimes == null) return requests; + + for (int i = 0; i < _nextSpawnTimes.Length; i++) + { + if (_elapsedTime < _nextSpawnTimes[i]) continue; + + requests.Add(new SpawnRequest + { + EnemyType = _enemyTypes[i], + Count = _spawnCounts[i], + WaveIndex = i + }); + + _nextSpawnTimes[i] += _currentIntervals[i]; + } + + return requests; + } + + public void SetSpawnRateScale(float scale) + { + float newScale = Mathf.Max(MinSpawnRateScale, scale); + if (_baseIntervals == null || _baseIntervals.Length == 0) + { + _spawnRateScale = newScale; + return; + } + + bool hasRuntimeState = _nextSpawnTimes != null && _currentIntervals != null && + _nextSpawnTimes.Length == _baseIntervals.Length && + _currentIntervals.Length == _baseIntervals.Length; + float oldScale = _spawnRateScale; + _spawnRateScale = newScale; + + if (!hasRuntimeState) + { + for (int i = 0; i < _baseIntervals.Length; i++) + { + _currentIntervals[i] = GetScaledInterval(_baseIntervals[i], _spawnRateScale); + } + + _nextSpawnTimes = (float[])_currentIntervals.Clone(); + return; + } + + for (int i = 0; i < _baseIntervals.Length; i++) + { + float oldInterval = GetScaledInterval(_baseIntervals[i], oldScale); + float newInterval = GetScaledInterval(_baseIntervals[i], _spawnRateScale); + + float remainTime = Mathf.Max(0f, _nextSpawnTimes[i] - _elapsedTime); + float remainRatio = oldInterval > Mathf.Epsilon ? Mathf.Clamp01(remainTime / oldInterval) : 0f; + + _currentIntervals[i] = newInterval; + _nextSpawnTimes[i] = _elapsedTime + newInterval * remainRatio; + } + } + + public void Reset() + { + _elapsedTime = 0; + _baseIntervals = null; + _currentIntervals = null; + _enemyTypes = null; + _spawnCounts = null; + _nextSpawnTimes = null; + _spawnRateScale = 1f; + } + + private static float GetScaledInterval(float baseInterval, float scale) + { + float safeScale = Mathf.Max(MinSpawnRateScale, scale); + return baseInterval / safeScale; + } + } + + public struct SpawnRequest + { + public EnemyType EnemyType; + public int Count; + public int WaveIndex; + } +} diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs.meta b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs.meta new file mode 100644 index 0000000..a69885b --- /dev/null +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8380f7f752e62b41a102a7f5a739309 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: