Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Добавлена система ввода текста через модальное окно с ячейками для аркадного автомата - Реализован TextInputModal с поддержкой кастомных алфавитов и навигации - Добавлен TextInputCell для ввода отдельных символов с циклической навигацией - Создан TextInputSettingsBinder для интеграции ввода текста с системой настроек - Реализована компактная сериализация настроек (только нужные поля в JSON) - Внедрена система баланса игрока (BalanceManager) - Баланс начисляется при оплате импульсом, списывается при начале/продолжении игры - Добавлен InactivityManager для выхода в меню при отсутствии активности (5 минут) - Улучшена система настроек - Добавлены флаги shownInSettings и exposed для управления видимостью настроек - Реализована автоматическая регистрация/отписка SettingsButtonBinder при активации/деактивации - Исправлена загрузка настроек с проверкой типов через Sanitize() - SettingsApplier упрощен - оставлена только поддержка AudioManager - Добавлен ValueSelectorRow для выбора целочисленных значений с шагом - Удалены примеры и документация из production кода (QUICK_SETUP.md, TextInputExample, AdvancedLoggingExample) - Убран весь отладочный вывод Debug.Log из системы настроек и ввода текста - Очищены неразрывные пробелы из файлов проекта - Добавлены префабы UI (InputCell)
293 lines
10 KiB
C#
293 lines
10 KiB
C#
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
using RR.Common.Controls;
|
||
using RR.Common.Settings;
|
||
using RR.Game2D.Core.Managers;
|
||
|
||
namespace RR.Common.Managers
|
||
{
|
||
/// <summary>
|
||
/// Менеджер отслеживания бездействия игрока.
|
||
/// При отсутствии ввода в течение заданного времени выкидывает игрока в главное меню.
|
||
/// </summary>
|
||
public class InactivityManager : MonoBehaviour
|
||
{
|
||
public static InactivityManager Instance { get; private set; }
|
||
|
||
[Header("Настройки бездействия")]
|
||
[SerializeField] private float inactivityTimeout = 300f; // 5 минут в секундах
|
||
[SerializeField] private bool enableInactivityCheck = true;
|
||
|
||
[Header("Отладка")]
|
||
[SerializeField] private bool showDebugInfo;
|
||
|
||
private float lastInputTime;
|
||
private bool isInMainMenu = true;
|
||
private bool isGameActive;
|
||
|
||
private void Awake()
|
||
{
|
||
if (Instance != null && Instance != this)
|
||
{
|
||
Destroy(gameObject);
|
||
return;
|
||
}
|
||
|
||
Instance = this;
|
||
DontDestroyOnLoad(gameObject);
|
||
|
||
Initialize();
|
||
}
|
||
|
||
private void Initialize()
|
||
{
|
||
lastInputTime = Time.unscaledTime;
|
||
|
||
// Подписываемся на все возможные источники ввода
|
||
SubscribeToInputEvents();
|
||
|
||
// Подписываемся на события смены сцены
|
||
UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
|
||
|
||
// Подписываемся на изменения настроек
|
||
var settingsManager = SettingsManager.Instance;
|
||
if (settingsManager != null)
|
||
{
|
||
settingsManager.OnSettingsLoaded += LoadSettingsValues;
|
||
settingsManager.OnSettingsSaved += LoadSettingsValues;
|
||
LoadSettingsValues();
|
||
}
|
||
|
||
Debug.Log("[InactivityManager] Инициализирован");
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
UnsubscribeFromInputEvents();
|
||
UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
|
||
|
||
var settingsManager = SettingsManager.Instance;
|
||
if (settingsManager != null)
|
||
{
|
||
settingsManager.OnSettingsLoaded -= LoadSettingsValues;
|
||
settingsManager.OnSettingsSaved -= LoadSettingsValues;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Загружает значения из настроек
|
||
/// </summary>
|
||
private void LoadSettingsValues()
|
||
{
|
||
var settings = SettingsManager.Instance;
|
||
if (settings == null)
|
||
{
|
||
Debug.LogWarning("[InactivityManager] SettingsManager is null!");
|
||
return;
|
||
}
|
||
|
||
inactivityTimeout = settings.GetInt("gameplay.inactivityTimeout");
|
||
|
||
if (inactivityTimeout == 0)
|
||
{
|
||
Debug.Log("[InactivityManager] Отслеживание бездействия отключено (таймаут = 0)");
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"[InactivityManager] Таймаут бездействия: {inactivityTimeout}s");
|
||
}
|
||
}
|
||
|
||
private void SubscribeToInputEvents()
|
||
{
|
||
// Подписываемся на события ComWrapper (кнопки автомата)
|
||
if (ComWrapper.OnInputReceived != null)
|
||
{
|
||
ComWrapper.OnInputReceived.AddListener(OnComInput);
|
||
}
|
||
|
||
// InputSystem.onAnyButtonPress - это событие, которое срабатывает каждый раз
|
||
// когда нажимается любая кнопка. Мы просто слушаем его в Update через CheckForAnyInput
|
||
}
|
||
|
||
private void UnsubscribeFromInputEvents()
|
||
{
|
||
if (ComWrapper.OnInputReceived != null)
|
||
{
|
||
ComWrapper.OnInputReceived.RemoveListener(OnComInput);
|
||
}
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// Проверяем на наличие любого ввода каждый кадр
|
||
CheckForAnyInput();
|
||
|
||
// Проверяем таймер бездействия
|
||
if (enableInactivityCheck && !isInMainMenu && isGameActive)
|
||
{
|
||
float timeSinceLastInput = Time.unscaledTime - lastInputTime;
|
||
|
||
if (showDebugInfo && Time.frameCount % 60 == 0)
|
||
{
|
||
Debug.Log($"[InactivityManager] Время бездействия: {timeSinceLastInput:F1}s / {inactivityTimeout}s");
|
||
}
|
||
|
||
if (timeSinceLastInput >= inactivityTimeout)
|
||
{
|
||
OnInactivityTimeout();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет на наличие любого ввода с клавиатуры, мыши или геймпада
|
||
/// </summary>
|
||
private void CheckForAnyInput()
|
||
{
|
||
if (isInMainMenu || !isGameActive)
|
||
return;
|
||
|
||
// Проверяем клавиатуру
|
||
if (Keyboard.current != null && Keyboard.current.anyKey.wasPressedThisFrame)
|
||
{
|
||
ResetInactivityTimer();
|
||
return;
|
||
}
|
||
|
||
// Проверяем мышь
|
||
if (Mouse.current != null)
|
||
{
|
||
if (Mouse.current.leftButton.wasPressedThisFrame ||
|
||
Mouse.current.rightButton.wasPressedThisFrame ||
|
||
Mouse.current.middleButton.wasPressedThisFrame ||
|
||
Mouse.current.delta.ReadValue().sqrMagnitude > 0.1f)
|
||
{
|
||
ResetInactivityTimer();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Проверяем геймпад
|
||
if (Gamepad.current != null)
|
||
{
|
||
foreach (var control in Gamepad.current.allControls)
|
||
{
|
||
if (control is UnityEngine.InputSystem.Controls.ButtonControl button && button.wasPressedThisFrame)
|
||
{
|
||
ResetInactivityTimer();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnComInput(string input)
|
||
{
|
||
if (!isInMainMenu && isGameActive)
|
||
{
|
||
ResetInactivityTimer();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Сбрасывает таймер бездействия
|
||
/// </summary>
|
||
public void ResetInactivityTimer()
|
||
{
|
||
lastInputTime = Time.unscaledTime;
|
||
|
||
if (showDebugInfo)
|
||
{
|
||
Debug.Log("[InactivityManager] Таймер сброшен");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вызывается при достижении таймаута бездействия
|
||
/// </summary>
|
||
private void OnInactivityTimeout()
|
||
{
|
||
Debug.Log("[InactivityManager] Таймаут бездействия! Возврат в главное меню...");
|
||
|
||
enableInactivityCheck = false; // Отключаем проверку до следующей игры
|
||
|
||
// Останавливаем игру
|
||
if (GameManager.Instance != null)
|
||
{
|
||
GameManager.Instance.SetPause(false);
|
||
GameManager.Instance.LoadScene("MainMenu", false);
|
||
}
|
||
|
||
ResetInactivityTimer();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обработчик загрузки сцены
|
||
/// </summary>
|
||
private void OnSceneLoaded(UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode)
|
||
{
|
||
isInMainMenu = scene.name == "MainMenu";
|
||
|
||
if (isInMainMenu)
|
||
{
|
||
enableInactivityCheck = false;
|
||
isGameActive = false;
|
||
Debug.Log("[InactivityManager] Главное меню загружено - проверка отключена");
|
||
}
|
||
|
||
ResetInactivityTimer();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Включает проверку бездействия (вызывается при старте игры)
|
||
/// </summary>
|
||
public void EnableInactivityCheck()
|
||
{
|
||
enableInactivityCheck = true;
|
||
isGameActive = true;
|
||
ResetInactivityTimer();
|
||
Debug.Log("[InactivityManager] Проверка бездействия включена");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Отключает проверку бездействия
|
||
/// </summary>
|
||
public void DisableInactivityCheck()
|
||
{
|
||
enableInactivityCheck = false;
|
||
isGameActive = false;
|
||
Debug.Log("[InactivityManager] Проверка бездействия отключена");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Устанавливает время таймаута бездействия.
|
||
/// Если установлено 0, отслеживание бездействия отключается.
|
||
/// </summary>
|
||
public void SetInactivityTimeout(float timeoutSeconds)
|
||
{
|
||
inactivityTimeout = Mathf.Max(0f, timeoutSeconds); // 0 = отключено, минимум 0
|
||
|
||
if (inactivityTimeout == 0f)
|
||
{
|
||
Debug.Log("[InactivityManager] Отслеживание бездействия отключено (таймаут = 0)");
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"[InactivityManager] Таймаут установлен: {inactivityTimeout}s");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает текущее время таймаута
|
||
/// </summary>
|
||
public float GetInactivityTimeout() => inactivityTimeout;
|
||
|
||
/// <summary>
|
||
/// Получает время с момента последнего ввода
|
||
/// </summary>
|
||
public float GetTimeSinceLastInput() => Time.unscaledTime - lastInputTime;
|
||
}
|
||
}
|
||
|