vampire-like/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs

123 lines
3.9 KiB
C#

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;
}
}