Danro/Assets/Scripts/UI/LevelSelectModal.cs

139 lines
5.2 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 System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
namespace RR.Common.UI
{
public class LevelSelectModal : MonoBehaviour
{
[Header("Управление")]
[SerializeField] private InputActionReference close;
[Header("UI")]
[SerializeField] private CanvasGroup canvasGroup;
[SerializeField] private Transform content; // Grid parent
[SerializeField] private LevelButton levelButtonPrefab;
[SerializeField] private Button closeButton;
[SerializeField] private float fadeDuration = 0.15f;
private readonly List<LevelButton> _levelsPool = new();
void Awake()
{
if (closeButton) closeButton.onClick.AddListener(Close);
gameObject.SetActive(false);
}
// ==== ПУБЛИЧНЫЙ API ====
/// <summary>
/// Метод для открытия модального окна
/// </summary>
public void Open()
{
BuildIfNeeded();
gameObject.SetActive(true);
StopAllCoroutines();
StartCoroutine(Fade(0f, 1f, fadeDuration));
}
/// <summary>
/// Метод для закрытия модального окна
/// </summary>
public void Close()
{
StopAllCoroutines();
StartCoroutine(Fade(1f, 0f, fadeDuration, () => gameObject.SetActive(false)));
}
// ==== ВНУТРЕННЕЕ ====
/// <summary>
/// Метод для сборки кнопок уровней
/// </summary>
void BuildIfNeeded()
{
// Очистим/пересоберём (можно кэшировать, но пересборка надёжнее при изменениях Build Settings)
foreach (var b in _levelsPool) if (b) Destroy(b.gameObject);
_levelsPool.Clear();
var levels = EnumerateLevelScenesInBuild()
.OrderBy(e => e.world)
.ThenBy(e => e.stage)
.ToList();
foreach (var lv in levels)
{
var btn = Instantiate(levelButtonPrefab, content);
btn.Setup(lv.name, lv.world, lv.stage);
if (lv.world == 1) btn.SetColor(Color.green);
else if (lv.world == 2) btn.SetColor(Color.blue);
_levelsPool.Add(btn);
}
}
struct LevelEntry { public string name; public int world; public int stage; }
/// <summary>
/// Метод для перебора уровней в игре
/// </summary>
/// <returns>LevelEntry - текущий мир при переборе</returns>
IEnumerable<LevelEntry> EnumerateLevelScenesInBuild()
{
int n = SceneManager.sceneCountInBuildSettings;
for (int i = 0; i < n; i++)
{
string path = SceneUtility.GetScenePathByBuildIndex(i);
string name = System.IO.Path.GetFileNameWithoutExtension(path);
// фильтр "только уровни" по формату имени W-S:
var parts = name.Split('-');
if (parts.Length != 2) continue;
if (!int.TryParse(parts[0], out int world)) continue;
if (!int.TryParse(parts[1], out int stage)) continue;
// Исключим служебные сцены, даже если вдруг совпадут по формату (на всякий случай)
if (name.Equals("Level UI", System.StringComparison.OrdinalIgnoreCase)) continue;
if (name.Equals("MainMenu", System.StringComparison.OrdinalIgnoreCase)) continue;
yield return new LevelEntry { name = name, world = world, stage = stage };
}
}
/// <summary>
/// Метод для плавного перехода (затухания)
/// </summary>
/// <param name="from">Начальная прозрачность</param>
/// <param name="to">Конечная прозрачнеость</param>
/// <param name="time">Время перехода</param>
/// <param name="onDone">Действие при выполнении перехода</param>
IEnumerator Fade(float from, float to, float time, System.Action onDone = null)
{
if (!canvasGroup) { onDone?.Invoke(); yield break; }
float t = 0f;
canvasGroup.alpha = from;
canvasGroup.blocksRaycasts = true;
while (t < time)
{
t += Time.unscaledDeltaTime;
canvasGroup.alpha = Mathf.Lerp(from, to, t / time);
yield return null;
}
canvasGroup.alpha = to;
canvasGroup.blocksRaycasts = to > 0.99f;
onDone?.Invoke();
}
void Update()
{
// ESC закрыть
if (gameObject.activeSelf && close.action.WasPressedThisFrame())
Close();
}
}
}