Danro/Assets/Scripts/Behaviour/EntitySpawnerManager.cs
Danil Iarosh cb2e83a596
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat(ui, prizes, spawners, editor): expand prize flow and add spawning/editor tooling
- доработан flow призовой витрины и связанного UI:
  добавлены `PrizeStandButtonView` и `RulesPrizeListPage`, обновлены
  `PrizeStandController`, `PrizeStandPrize`, `PrizeStandModal`,
  `PrizeStandSettingsModal`, `LevelPrizeSettingsModal`, `QRCodeSettingsModal`,
  `BalanceDisplay`, `ButtonTextFocusColor` и связанные prefab'ы,
  чтобы улучшить настройку, навигацию, отображение кнопок и prize flow
- расширена система настроек и сохранения значений:
  `OptionPicker` получил отдельный формат `ValueSelector`,
  обновлены `SettingsDatabase`, `SettingEntryDrawer`, `SettingUIItem`,
  `SettingsUIGenerator`, `ValueSelectorRow` и `MainSettingsDatabase.asset`,
  а сохранение option picker-значений, включая `gameplay.gameDifficulty`,
  исправлено и синхронизировано с остальными настройками
- добавлены универсальные спавнеры сущностей:
  реализованы `EntitySpawner` и `EntitySpawnerManager` с поддержкой
  весов префабов, точек спавна, заполнения по проценту, ограничений
  на количество и автоспавна при инициализации
- доработана логика игрового спавна и сложности:
  процент инициализационного спавна может зависеть от настройки
  `gameplay.gameDifficulty` с градациями 25/50/75/100,
  в одном цикле массового спавна повторно не используется та же точка,
  а gizmo спавнера теперь показывает только сами точки без линий
- упрощена работа в редакторе Unity и добавлены editor-инструменты:
  в `GameManager` лицензия пропускается при запуске в `UNITY_EDITOR`,
  а `HierarchySelectionCounter`, `HierarchySelectionCountOverlay`
  и `HierarchyBulkRenamerWindow` добавляют подсчет выделения,
  realtime overlay в `SceneView` и массовое переименование объектов
  по правилам нумерации из Unity Project Settings
- синхронизированы сцены, ресурсы и UI-ассеты под новый flow:
  обновлены `MainMenu`, `Level UI`, уровни `1-1` и `1-2`,
  строки настроек, кнопки, TMP/font assets, изображения UI,
  `prizes.json` и recovery-данные после правок интерфейса и логики
- очищены и переразложены часть внешних и служебных ресурсов:
  удалён пакет `Lava_Flowing_Shader`, переименованы/заменены
  отдельные UI-prefab'ы и добавлены сопутствующие проектные артефакты,
  которые сейчас используются обновлённой конфигурацией проекта
2026-04-28 14:05:05 +03:00

179 lines
5.3 KiB
C#

using System.Collections.Generic;
using System.Collections;
using RR.Common.Settings;
using UnityEngine;
namespace RR.Game2D.Behaviour
{
[DisallowMultipleComponent]
public sealed class EntitySpawnerManager : MonoBehaviour
{
[Header("Spawners")]
[SerializeField] private EntitySpawner[] spawners;
[SerializeField] private bool includeChildren = true;
[SerializeField] private bool includeInactiveChildren = true;
[Header("Initialize Spawn")]
[SerializeField] private bool spawnOnInitialize;
[Tooltip("Если включено, процент спавна берётся из настройки gameplay.gameDifficulty.")]
[SerializeField] private bool useSettings = true;
[Range(0f, 100f)]
[SerializeField] private float initializeSpawnPercent = 100f;
private readonly List<EntitySpawner> _resolvedSpawners = new();
private readonly HashSet<EntitySpawner> _deduplicationSet = new();
private const string GameDifficultyKey = "gameplay.gameDifficulty";
private const float SettingsWaitTimeout = 2f;
public int LastSpawnedCount { get; private set; }
private void Start()
{
if (!spawnOnInitialize)
return;
StartCoroutine(InitializeSpawnRoutine());
}
public void SpawnConfiguredPercent()
{
SpawnPercent(GetConfiguredSpawnPercent());
}
public void SpawnPercent(int percent)
{
SpawnPercent((float)percent);
}
public void SpawnPercent(float percent)
{
LastSpawnedCount = SpawnInternal(percent);
}
public void StartAllSpawners()
{
ResolveSpawners();
for (int i = 0; i < _resolvedSpawners.Count; i++)
{
if (_resolvedSpawners[i] != null)
_resolvedSpawners[i].StartSpawning();
}
}
public void StopAllSpawners()
{
ResolveSpawners();
for (int i = 0; i < _resolvedSpawners.Count; i++)
{
if (_resolvedSpawners[i] != null)
_resolvedSpawners[i].StopSpawning();
}
}
public void ClearAllSpawned()
{
ResolveSpawners();
for (int i = 0; i < _resolvedSpawners.Count; i++)
{
if (_resolvedSpawners[i] != null)
_resolvedSpawners[i].ClearSpawned();
}
}
private int SpawnInternal(float percent)
{
ResolveSpawners();
int totalSpawned = 0;
for (int i = 0; i < _resolvedSpawners.Count; i++)
{
EntitySpawner spawner = _resolvedSpawners[i];
if (spawner == null)
continue;
totalSpawned += spawner.SpawnPercent(percent);
}
return totalSpawned;
}
private void ResolveSpawners()
{
_resolvedSpawners.Clear();
_deduplicationSet.Clear();
if (spawners != null)
{
for (int i = 0; i < spawners.Length; i++)
TryAddSpawner(spawners[i]);
}
if (!includeChildren)
return;
EntitySpawner[] childSpawners = GetComponentsInChildren<EntitySpawner>(includeInactiveChildren);
for (int i = 0; i < childSpawners.Length; i++)
TryAddSpawner(childSpawners[i]);
}
private void TryAddSpawner(EntitySpawner spawner)
{
if (spawner == null || !_deduplicationSet.Add(spawner))
return;
_resolvedSpawners.Add(spawner);
}
private IEnumerator InitializeSpawnRoutine()
{
if (useSettings)
{
float elapsed = 0f;
while (SettingsManager.Instance == null && elapsed < SettingsWaitTimeout)
{
elapsed += Time.unscaledDeltaTime;
yield return null;
}
}
float percent = GetConfiguredSpawnPercent();
Debug.Log($"[{nameof(EntitySpawnerManager)}] Initialize spawn percent = {percent}", this);
SpawnPercent(percent);
}
private float GetConfiguredSpawnPercent()
{
if (!useSettings)
return initializeSpawnPercent;
SettingsManager settingsManager = SettingsManager.Instance;
if (settingsManager == null)
{
Debug.LogWarning(
$"[{nameof(EntitySpawnerManager)}] SettingsManager.Instance not found. Using max difficulty spawn percent.",
this);
return 100f;
}
int difficultyIndex = settingsManager.GetInt(GameDifficultyKey);
float percent = difficultyIndex switch
{
0 => 25f,
1 => 50f,
2 => 75f,
3 => 100f,
_ => 100f
};
Debug.Log(
$"[{nameof(EntitySpawnerManager)}] Difficulty index '{difficultyIndex}' from '{GameDifficultyKey}' resolved to {percent}%.",
this);
return percent;
}
}
}