Danro/Assets/Scripts/UI/LoseBannerController.cs
Danil Iarosh 63898f9713
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat(ui, gameplay): update lose banner button flow and score display
- доработана плашка проигрыша `LoseBannerController`:
  добавлены отдельные поля `Menu Button` и `Menu Button Text`
  для кнопки возврата в главное меню
- добавлена логика переключения кнопок по настройке
  `gameplay.canContinueGame`:
  при включенной настройке показывается только кнопка продолжения,
  при выключенной — только кнопка возврата в меню
- добавлены отдельные форматы текста для обеих кнопок:
  `Continue Button Text Format` и `Menu Button Text Format`
  с подстановкой таймера автоматического действия
- реализован единый таймер автодействия для активной кнопки:
  обратный отсчет отображается либо на кнопке продолжения,
  либо на кнопке возврата в меню — в зависимости от текущего режима
- улучшено управление фокусом в плашке проигрыша:
  баннер автоматически назначает `firstSelected` и переводит
  `EventSystem` только на активную видимую кнопку
- обновлен показ очков в игровой витрине:
  подготовлены поля и связанный UI для отображения счета игрока
  в `Level UI` и связанных настройках
- синхронизированы изменения сцены и настроек:
  обновлены `Level UI.unity`, `MainSettingsDatabase.asset`
  и связанные TMP/font assets после правок интерфейса
2026-04-01 16:56:25 +03:00

292 lines
10 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;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
using RR.Common.Settings;
using RR.Game2D.Core.Managers;
namespace RR.Common.UI
{
/// <summary>
/// Контроллер баннера проигрыша.
/// Модальное окно, которое отображается при поражении игрока.
/// </summary>
public class LoseBannerController : Modal
{
[Header("Элементы баннера проигрыша")]
[SerializeField] private TextMeshProUGUI loseText;
[SerializeField] private TextMeshProUGUI creditText;
[SerializeField] private Button continueButton;
[SerializeField] private TextMeshProUGUI continueButtonText;
[SerializeField] private Button menuButton;
[SerializeField] private TextMeshProUGUI menuButtonText;
[SerializeField] private float continueButtonCountdown = 30f;
[Header("Тексты")]
[SerializeField] private string creditTextFormat = "У вас осталось: {0}";
[SerializeField] private string continueButtonTextFormat = "Продолжить? ({0})";
[SerializeField] private string menuButtonTextFormat = "В меню ({0})";
private RR.Common.Managers.BalanceManager _balanceManager;
private Coroutine _continueCountdownCoroutine;
private bool _isSubscribedToBalance = false;
/// <summary>
/// Событие, вызываемое при нажатии "Продолжить" или закрытии баннера.
/// </summary>
public event Action ContinueClicked;
protected override void Awake()
{
base.Awake();
if (continueButton)
{
continueButton.onClick.AddListener(OnContinueButtonClicked);
}
if (menuButton)
{
menuButton.onClick.AddListener(OnMenuButtonClicked);
}
}
protected override void OnEnable()
{
base.OnEnable();
// Инициализируем BalanceManager
if (_balanceManager == null)
{
_balanceManager = GameManager.Instance?.Balance;
}
// Подписываемся на событие только если еще не подписаны
if (_balanceManager != null && !_isSubscribedToBalance)
{
int balance = _balanceManager.CurrentBalance;
if (creditText)
{
creditText.text = string.Format(creditTextFormat, balance);
}
_balanceManager.OnBalanceChanged.AddListener(UnlockContinueButton);
_isSubscribedToBalance = true;
}
}
protected override void OnDisable()
{
// Остановим корутину, если объект деактивируется
if (_continueCountdownCoroutine != null)
{
StopCoroutine(_continueCountdownCoroutine);
_continueCountdownCoroutine = null;
}
// Безопасная отписка от события
if (_balanceManager != null && _isSubscribedToBalance)
{
_balanceManager.OnBalanceChanged.RemoveListener(UnlockContinueButton);
_isSubscribedToBalance = false;
}
base.OnDisable();
}
/// <summary>
/// Показывает баннер проигрыша с указанным временем игры.
/// </summary>
/// <param name="gameTime">Время игры в секундах</param>
public void ShowLose(float gameTime)
{
// Инициализируем BalanceManager до открытия окна
if (_balanceManager == null)
{
_balanceManager = GameManager.Instance?.Balance;
}
// Подписываемся на событие изменения баланса, если еще не подписаны
if (_balanceManager != null && !_isSubscribedToBalance)
{
_balanceManager.OnBalanceChanged.AddListener(UnlockContinueButton);
_isSubscribedToBalance = true;
}
if (loseText)
{
loseText.text = $"Время игры: {TimeFormatHelper.FormatTime(gameTime)}";
}
if (creditText && _balanceManager != null)
{
creditText.text = string.Format(creditTextFormat, (object)_balanceManager.CurrentBalance);
}
if (continueButtonCountdown <= 0f)
{
ReturnToMainMenu();
return;
}
Button activeButton = ConfigureVisibleButtons();
if (activeButton != null)
{
SetFirstSelected(activeButton);
}
Open();
FocusActiveButton(activeButton);
// Остановим предыдущую корутину, если есть
if (_continueCountdownCoroutine != null)
{
StopCoroutine(_continueCountdownCoroutine);
}
_continueCountdownCoroutine = StartCoroutine(ContinueCountdownCoroutine(continueButtonCountdown));
}
private IEnumerator ContinueCountdownCoroutine(float duration)
{
float elapsed = 0f;
int initialSeconds = Mathf.RoundToInt(duration);
UpdateContinueText(initialSeconds);
while (elapsed < duration)
{
float startTime = Time.unscaledTime;
yield return null;
while (Time.unscaledTime - startTime < 1.0f)
{
yield return null;
}
elapsed += 1f;
int remaining = Mathf.Max(0, Mathf.RoundToInt(duration - elapsed));
UpdateContinueText(remaining);
}
// Таймер истёк — закрываем баннер и выходим в меню
Close();
ContinueClicked?.Invoke();
if (GameManager.Instance)
{
GameManager.Instance.SetPause(false);
GameManager.Instance.LoadScene("MainMenu", false);
}
_continueCountdownCoroutine = null;
}
private void UpdateContinueText(int secondsRemaining)
{
if (continueButton != null && continueButton.gameObject.activeSelf && continueButtonText)
{
continueButtonText.text = string.Format(continueButtonTextFormat, secondsRemaining);
}
if (menuButton != null && menuButton.gameObject.activeSelf && menuButtonText)
{
menuButtonText.text = string.Format(menuButtonTextFormat, secondsRemaining);
}
}
private void UnlockContinueButton(int newBalance)
{
if (_balanceManager == null) return;
// Проверяем возможность продолжить через BalanceManager
if (continueButton)
{
continueButton.interactable = _balanceManager.CanContinueGame();
}
// Обновляем текст баланса
if (creditText)
{
creditText.text = string.Format(creditTextFormat, newBalance);
}
}
private void OnContinueButtonClicked()
{
// При клике на "Продолжить" — остановим таймер и продолжим игру
if (_continueCountdownCoroutine != null)
{
StopCoroutine(_continueCountdownCoroutine);
_continueCountdownCoroutine = null;
}
Close();
ContinueClicked?.Invoke();
if (GameManager.Instance)
{
GameManager.Instance.SetPause(false);
GameManager.Instance.ContinueGame();
}
}
private void OnMenuButtonClicked()
{
if (_continueCountdownCoroutine != null)
{
StopCoroutine(_continueCountdownCoroutine);
_continueCountdownCoroutine = null;
}
ReturnToMainMenu();
}
private Button ConfigureVisibleButtons()
{
bool canContinueGameSetting = SettingsManager.Instance != null
&& SettingsManager.Instance.GetBool("gameplay.canContinueGame");
if (continueButton != null)
{
continueButton.gameObject.SetActive(canContinueGameSetting);
continueButton.interactable = canContinueGameSetting
&& _balanceManager != null
&& _balanceManager.CanContinueGame();
}
if (menuButton != null)
{
menuButton.gameObject.SetActive(!canContinueGameSetting);
menuButton.interactable = true;
}
return canContinueGameSetting ? continueButton : menuButton;
}
private void FocusActiveButton(Button activeButton)
{
if (activeButton == null || !activeButton.gameObject.activeInHierarchy || EventSystem.current == null)
{
return;
}
EventSystem.current.SetSelectedGameObject(null);
EventSystem.current.SetSelectedGameObject(activeButton.gameObject);
activeButton.Select();
}
private void ReturnToMainMenu()
{
Close();
ContinueClicked?.Invoke();
if (GameManager.Instance)
{
GameManager.Instance.SetPause(false);
GameManager.Instance.LoadScene("MainMenu", false);
}
}
}
}