sega d61c8ec9c8 Добавлены флаги инициализации для систем
Чтобы ошибки не сыпало при старте на gameplay сцене
А то OnDestroy отрабатывают и  спамят консоль
2026-05-18 12:36:49 +03:00

151 lines
4.5 KiB
C#
Raw 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)));
}
}