Compare commits
3 Commits
b6a13b18fc
...
49c300a10e
| Author | SHA1 | Date |
|---|---|---|
|
|
49c300a10e | |
|
|
38849f5019 | |
|
|
921193f469 |
|
|
@ -13,53 +13,37 @@ namespace SepCore.EnemyManager
|
||||||
{
|
{
|
||||||
public class EnemyManagerComponent : GameFrameworkComponent
|
public class EnemyManagerComponent : GameFrameworkComponent
|
||||||
{
|
{
|
||||||
private const float MinSpawnRateScale = 0.1f;
|
|
||||||
private const string EnemyGroupName = "Enemy";
|
private const string EnemyGroupName = "Enemy";
|
||||||
|
|
||||||
private EntityComponent _entity;
|
private EntityComponent _entity;
|
||||||
|
private EnemyRegistry _enemyRegistry;
|
||||||
|
private EnemySpawnScheduler _spawnScheduler;
|
||||||
|
|
||||||
private List<EntityBase> _enemies;
|
public List<EntityBase> Enemies => _enemyRegistry?.Enemies;
|
||||||
private Dictionary<int, EntityBase> _enemyById;
|
|
||||||
|
|
||||||
public List<EntityBase> Enemies => _enemies;
|
|
||||||
|
|
||||||
private float _spawnEnemyTimer;
|
|
||||||
|
|
||||||
[SerializeField] private int _spawnEnemyMaxCount = 5000;
|
[SerializeField] private int _spawnEnemyMaxCount = 5000;
|
||||||
|
|
||||||
private int _currentEnemyCount;
|
|
||||||
|
|
||||||
[SerializeField] private int _spawnDistanceFromPlayer = 20;
|
[SerializeField] private int _spawnDistanceFromPlayer = 20;
|
||||||
|
|
||||||
private int _currentSpawnEnemyId;
|
private int _currentSpawnEnemyId;
|
||||||
|
|
||||||
private int _currentLevel;
|
private int _currentLevel;
|
||||||
|
|
||||||
private float[] _baseSpawnEnemyIntervals;
|
|
||||||
private float[] _spawnEnemyIntervals;
|
|
||||||
private EnemyType[] _spawnEnemyTypes;
|
|
||||||
private int[] _spawnEnemyCounts;
|
|
||||||
private float _duration;
|
private float _duration;
|
||||||
private float _baseDuration;
|
private float _baseDuration;
|
||||||
|
|
||||||
private float[] _nextSpawnTimes;
|
|
||||||
private float _spawnRateScale = 1f;
|
|
||||||
|
|
||||||
private Transform _player;
|
private Transform _player;
|
||||||
private ISpawnPositionStrategy _spawnPositionStrategy;
|
private ISpawnPositionStrategy _spawnPositionStrategy;
|
||||||
|
|
||||||
public float SpawnRateScale => _spawnRateScale;
|
public float SpawnRateScale => _spawnScheduler?.SpawnRateScale ?? 1f;
|
||||||
public float BattleDuration => _duration;
|
public float BattleDuration => _duration;
|
||||||
public float ElapsedBattleTime => _spawnEnemyTimer;
|
public float ElapsedBattleTime => _spawnScheduler?.ElapsedTime ?? 0f;
|
||||||
public int CurrentEnemyCount => _currentEnemyCount;
|
public int CurrentEnemyCount => _enemyRegistry?.Count ?? 0;
|
||||||
|
|
||||||
#region FSM
|
#region Lifecycle
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
_entity = GameEntry.Entity;
|
_entity = GameEntry.Entity;
|
||||||
_enemies = new List<EntityBase>();
|
_enemyRegistry = new EnemyRegistry();
|
||||||
_enemyById = new Dictionary<int, EntityBase>();
|
_spawnScheduler = new EnemySpawnScheduler();
|
||||||
|
|
||||||
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
}
|
}
|
||||||
|
|
@ -68,8 +52,8 @@ namespace SepCore.EnemyManager
|
||||||
{
|
{
|
||||||
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
|
||||||
_enemies = null;
|
_enemyRegistry = null;
|
||||||
_enemyById = null;
|
_spawnScheduler = null;
|
||||||
_entity = null;
|
_entity = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,54 +62,35 @@ namespace SepCore.EnemyManager
|
||||||
_player = player != null ? player.CachedTransform : null;
|
_player = player != null ? player.CachedTransform : null;
|
||||||
_spawnPositionStrategy = new RandomCircleSpawnStrategy(_spawnDistanceFromPlayer);
|
_spawnPositionStrategy = new RandomCircleSpawnStrategy(_spawnDistanceFromPlayer);
|
||||||
|
|
||||||
_baseSpawnEnemyIntervals = (float[])level.Intervals.Clone();
|
|
||||||
_spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone();
|
|
||||||
_spawnEnemyTypes = level.EntityTypes;
|
|
||||||
_spawnEnemyCounts = level.EntityCounts;
|
|
||||||
_baseDuration = level.Duration;
|
_baseDuration = level.Duration;
|
||||||
_duration = _baseDuration;
|
_duration = _baseDuration;
|
||||||
|
_currentLevel = level.Id;
|
||||||
|
|
||||||
SetSpawnRateScale(_spawnRateScale);
|
_spawnScheduler.Init(level);
|
||||||
|
_enemyRegistry.Clear();
|
||||||
_currentEnemyCount = 0;
|
|
||||||
_currentSpawnEnemyId = 0;
|
_currentSpawnEnemyId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
{
|
{
|
||||||
_spawnEnemyTimer += elapseSeconds;
|
var spawnRequests = _spawnScheduler.Tick(elapseSeconds);
|
||||||
|
foreach (var request in spawnRequests)
|
||||||
for (int i = 0; i < _nextSpawnTimes.Length; i++)
|
|
||||||
{
|
{
|
||||||
float nextSpawnTime = _nextSpawnTimes[i];
|
for (int j = 0; j < request.Count; j++)
|
||||||
if (_spawnEnemyTimer < nextSpawnTime) continue;
|
|
||||||
for (int j = 0; j < _spawnEnemyCounts[i]; j++)
|
|
||||||
{
|
{
|
||||||
SpawnEnemyAsync(_spawnEnemyTypes[i]).Forget();
|
SpawnEnemyAsync(request.EnemyType).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
_nextSpawnTimes[i] += _spawnEnemyIntervals[i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReset()
|
public void OnReset()
|
||||||
{
|
{
|
||||||
_spawnEnemyTimer = 0;
|
_spawnScheduler.Reset();
|
||||||
|
ClearEnemies();
|
||||||
_currentSpawnEnemyId = 0;
|
_currentSpawnEnemyId = 0;
|
||||||
_currentLevel = 0;
|
_currentLevel = 0;
|
||||||
|
|
||||||
_baseSpawnEnemyIntervals = null;
|
|
||||||
_spawnEnemyIntervals = null;
|
|
||||||
_spawnEnemyTypes = null;
|
|
||||||
_spawnEnemyCounts = null;
|
|
||||||
_baseDuration = 0;
|
_baseDuration = 0;
|
||||||
_duration = 0;
|
_duration = 0;
|
||||||
|
|
||||||
_nextSpawnTimes = null;
|
|
||||||
|
|
||||||
ClearEnemies();
|
|
||||||
|
|
||||||
_currentEnemyCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -134,7 +99,7 @@ namespace SepCore.EnemyManager
|
||||||
{
|
{
|
||||||
if (_player == null) return;
|
if (_player == null) return;
|
||||||
|
|
||||||
if (_currentEnemyCount >= _spawnEnemyMaxCount) return;
|
if (_enemyRegistry.Count >= _spawnEnemyMaxCount) return;
|
||||||
int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount;
|
int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount;
|
||||||
var enemyData = EntityDataFactory.Create(entityPoolId, enemyType, _currentLevel);
|
var enemyData = EntityDataFactory.Create(entityPoolId, enemyType, _currentLevel);
|
||||||
enemyData.Position = _spawnPositionStrategy.GetSpawnPosition(_player);
|
enemyData.Position = _spawnPositionStrategy.GetSpawnPosition(_player);
|
||||||
|
|
@ -146,100 +111,36 @@ namespace SepCore.EnemyManager
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterEnemy(enemy);
|
if (_player != null)
|
||||||
|
{
|
||||||
|
enemy.SetTarget(_player);
|
||||||
|
}
|
||||||
|
_enemyRegistry.Register(enemy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearEnemies()
|
public void ClearEnemies()
|
||||||
{
|
{
|
||||||
foreach (var enemy in _enemies)
|
foreach (var enemy in _enemyRegistry.Enemies)
|
||||||
{
|
{
|
||||||
if (enemy == null || !enemy.Available) continue;
|
if (enemy == null || !enemy.Available) continue;
|
||||||
_entity.HideEntity(enemy);
|
_entity.HideEntity(enemy);
|
||||||
}
|
}
|
||||||
|
|
||||||
_enemies.Clear();
|
_enemyRegistry.Clear();
|
||||||
_enemyById?.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetEnemy(int entityId, out EntityBase enemy)
|
public bool TryGetEnemy(int entityId, out EntityBase enemy)
|
||||||
{
|
{
|
||||||
enemy = null;
|
return _enemyRegistry.TryGet(entityId, out enemy);
|
||||||
if (_enemyById == null || !_enemyById.TryGetValue(entityId, out EntityBase cachedEnemy))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachedEnemy == null || !cachedEnemy.Available)
|
|
||||||
{
|
|
||||||
_enemyById.Remove(entityId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
enemy = cachedEnemy;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSpawnRateScale(float scale)
|
public void SetSpawnRateScale(float scale)
|
||||||
{
|
{
|
||||||
float newScale = Mathf.Max(MinSpawnRateScale, scale);
|
_spawnScheduler.SetSpawnRateScale(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Event Handler
|
#region Event Handler
|
||||||
|
|
||||||
private void RegisterEnemy(EnemyBase enemy)
|
|
||||||
{
|
|
||||||
if (enemy == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentEnemyCount++;
|
|
||||||
enemy.SetTarget(_player);
|
|
||||||
RemoveEnemyFromCache(enemy.Id);
|
|
||||||
_enemies.Add(enemy);
|
|
||||||
_enemyById[enemy.Id] = enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
||||||
{
|
{
|
||||||
if (e is not HideEntityCompleteEventArgs ne) return;
|
if (e is not HideEntityCompleteEventArgs ne) return;
|
||||||
|
|
@ -247,34 +148,7 @@ namespace SepCore.EnemyManager
|
||||||
string entityGroupName = ne.EntityGroup.Name;
|
string entityGroupName = ne.EntityGroup.Name;
|
||||||
if (entityGroupName == EnemyGroupName)
|
if (entityGroupName == EnemyGroupName)
|
||||||
{
|
{
|
||||||
if (_currentEnemyCount > 0)
|
_enemyRegistry.Remove(ne.EntityId);
|
||||||
{
|
|
||||||
_currentEnemyCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveEnemyFromCache(ne.EntityId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveEnemyFromCache(int entityId)
|
|
||||||
{
|
|
||||||
if (_enemyById != null)
|
|
||||||
{
|
|
||||||
_enemyById.Remove(entityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = _enemies.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
EntityBase cachedEnemy = _enemies[i];
|
|
||||||
if (cachedEnemy == null || cachedEnemy.Id == entityId)
|
|
||||||
{
|
|
||||||
if (cachedEnemy != null && _enemyById != null)
|
|
||||||
{
|
|
||||||
_enemyById.Remove(cachedEnemy.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
_enemies.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using SepCore.Entity;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.EnemyManager
|
||||||
|
{
|
||||||
|
public class EnemyRegistry
|
||||||
|
{
|
||||||
|
private readonly List<EntityBase> _enemies;
|
||||||
|
private readonly Dictionary<int, EntityBase> _enemyById;
|
||||||
|
|
||||||
|
public int Count { get; private set; }
|
||||||
|
public List<EntityBase> Enemies => _enemies;
|
||||||
|
|
||||||
|
public EnemyRegistry()
|
||||||
|
{
|
||||||
|
_enemies = new List<EntityBase>();
|
||||||
|
_enemyById = new Dictionary<int, EntityBase>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register(EnemyBase enemy)
|
||||||
|
{
|
||||||
|
if (enemy == null) return;
|
||||||
|
|
||||||
|
Count++;
|
||||||
|
RemoveFromCache(enemy.Id);
|
||||||
|
_enemies.Add(enemy);
|
||||||
|
_enemyById[enemy.Id] = enemy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(int entityId)
|
||||||
|
{
|
||||||
|
if (Count > 0)
|
||||||
|
{
|
||||||
|
Count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveFromCache(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGet(int entityId, out EntityBase enemy)
|
||||||
|
{
|
||||||
|
enemy = null;
|
||||||
|
if (!_enemyById.TryGetValue(entityId, out EntityBase cachedEnemy))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedEnemy == null || !cachedEnemy.Available)
|
||||||
|
{
|
||||||
|
_enemyById.Remove(entityId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enemy = cachedEnemy;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_enemies.Clear();
|
||||||
|
_enemyById.Clear();
|
||||||
|
Count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveFromCache(int entityId)
|
||||||
|
{
|
||||||
|
_enemyById.Remove(entityId);
|
||||||
|
|
||||||
|
for (int i = _enemies.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
EntityBase cachedEnemy = _enemies[i];
|
||||||
|
if (cachedEnemy == null || cachedEnemy.Id == entityId)
|
||||||
|
{
|
||||||
|
if (cachedEnemy != null)
|
||||||
|
{
|
||||||
|
_enemyById.Remove(cachedEnemy.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
_enemies.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7a24dd17510328f4392432d9da7ab6a5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -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<SpawnRequest> Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
_elapsedTime += deltaTime;
|
||||||
|
var requests = new List<SpawnRequest>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c8380f7f752e62b41a102a7f5a739309
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Loading…
Reference in New Issue