151 lines
4.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public event Action<EnemyShip> EnemySpawned;
public List<EnemyShip> EnemyPrefabs;
[SerializeField] private List<BezierWay> ways;
[SerializeField] private float SpawnInterval = 5;
[SerializeField] private int minBudget = 3;
[SerializeField] private int maxBudget = 100;
[SerializeField] private int minBudgetScore = 0;
[SerializeField] private int maxBudgetScore = 5000;
[SerializeField] private AnimationCurve budgetCurve;
[SerializeField] private int currentBudget = 0;
[SerializeField] private int usedBudget = 0;
[SerializeField] private int maxSpawnedEnemies = 3;
private CancellationToken _token;
private int activeEnemies = 0;
public bool Initialized { get; private set; }
private int remainingBudget => currentBudget - usedBudget;
public void Initialize()
{
if (Initialized) return;
if (!Validate())
{
Debug.LogError("Валидация настроек спавнера не прошла", this);
return;
}
currentBudget = minBudget;
usedBudget = 0;
EnemyShip.ShipReleased += UntouchShipFromBudget;
EnemyShip.ShipDefeated += UntouchShipFromBudget;
G.ScoreModel.OnUpdate += UpdateBudgetByScore;
_token = this.GetCancellationTokenOnDestroy();
SpawnProcessAsync(_token).Forget();
Initialized = true;
}
private void UntouchShipFromBudget(EnemyShip releasedShip)
{
if (releasedShip == null) return;
usedBudget = Mathf.Max(0, usedBudget - releasedShip.SpawnCost);
activeEnemies--;
}
private void OnDisable()
{
if (!Initialized) return;
EnemyShip.ShipReleased -= UntouchShipFromBudget;
EnemyShip.ShipDefeated -= UntouchShipFromBudget;
G.ScoreModel.OnUpdate -= UpdateBudgetByScore;
}
private bool Validate()
{
if (EnemyPrefabs == null || EnemyPrefabs.Count == 0)
{
Debug.LogError("Не задан список врагов на спавнере", this);
return false;
}
foreach (var enemy in EnemyPrefabs)
{
if (!enemy.TryGetComponent<EnemyShip>(out _))
{
Debug.LogError($"Префаб врага {enemy.name} не содержит компонент AbstractEnemyShip");
return false;
}
}
return true;
}
private async UniTaskVoid SpawnProcessAsync(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
await UniTask.Yield(PlayerLoopTiming.Update);
if (!token.IsCancellationRequested)
TrySpawn();
await UniTask.Delay(delayTimeSpan: TimeSpan.FromSeconds(SpawnInterval),
delayTiming: PlayerLoopTiming.Update,
cancellationToken: token);
}
}
private BezierWay GetNotBusyWay() => ways.FirstOrDefault(way => !way.Busy);
private EnemyShip GetRandomAccessibleShip()
{
var accessibleShips = EnemyPrefabs.Where(e => e.SpawnCost <= remainingBudget).ToList();
if (accessibleShips.Count == 0) return null;
var randomIndex = UnityEngine.Random.Range(0, accessibleShips.Count);
return accessibleShips[randomIndex];
}
private EnemyShip GetTopAccessibleShip()
{
return EnemyPrefabs
.Where(e => e.SpawnCost <= remainingBudget)
.OrderByDescending(e => e.SpawnCost)
.FirstOrDefault();
}
private void TrySpawn()
{
if (activeEnemies >= maxSpawnedEnemies) return;
var way = GetNotBusyWay();
if(way == null) return;
//var shipPrefab = GetRandomAccessibleShip();
var shipPrefab = GetTopAccessibleShip();
if (shipPrefab == null) return;
var go = Instantiate(shipPrefab.gameObject, way.FirstPoint.position, Quaternion.identity);
var ship = go.GetComponent<EnemyShip>();
ship.AssignToWay(way);
ship.Initialize();
EnemySpawned?.Invoke(ship);
usedBudget += ship.SpawnCost;
activeEnemies++;
}
private void UpdateBudgetByScore(ScoreModel scoreModel)
{
float t = Mathf.InverseLerp(minBudgetScore, maxBudgetScore, scoreModel.Score);
currentBudget = Mathf.RoundToInt(Mathf.Lerp(minBudget, maxBudget, budgetCurve.Evaluate(t)));
}
}