refactor: LightController + LightSequence, протокол 85XXYY, namespace
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
LightController / LightMode: - Новый LightMode.cs: enum эффектов под протокол 85XXYY (13 эффектов + Off) значения enum = коды команд XX, Off=-1 генерирует просто '85' - Новый LightController.cs: управление подсветкой платы * SetLight(mode, duration) → строит команду 85XXYY, возвращает LightSequence * Blink() / StopBlink() / SetBlinkInterval() — мигание шляпы * StopSequence() / StopAllEffects() — остановка эффектов * Все параметры (hatOn/hatOff, blinkInterval, defaultMode/Duration) в инспекторе - Вложенный класс LightSequence — builder цепочек эффектов: SetLight(A,3).Then(B,3).Then(C,3).Loop() SetLight(A,3).Then(LightMode.Off) SetLight(A,5) — одиночный эффект GameManager: - Добавлено свойство Light (→ Plate.Light) - GameWin → SetLight(FinalWave, 30) - GameOver → SetLight(BlueWave, 30) - NewGame → SetLight(Rainbow, 99) - LoadScene MainMenu → SetLight(Off) + Blink() namespace (добавлены в файлы без namespace): - Editor/MouseWorldPositionOverlay.cs → RR.Common.InspectorOverrides - UI/StatsWindowUIController.cs → RR.Common.UI - Utilities/AdvancedLogging_Test.cs → RR.Common.Utility - Utilities/DebugSettings.cs → RR.Common.Utility
This commit is contained in:
parent
301e4be47d
commit
6b02c76ef3
@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using RR.Game2D.Core.Managers;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace RR.Common.Controls
|
||||
{
|
||||
@ -30,7 +28,6 @@ namespace RR.Common.Controls
|
||||
#region Приватные поля
|
||||
|
||||
private const string REQUEST_INPUT = "91";
|
||||
private const string LIGHTSIGNAL_PREFIX = "80";
|
||||
|
||||
private static int h; // идентификатор хэндла
|
||||
private static readonly object bufferLock = new object(); // Блокировка для синхронизации доступа к COM-порту
|
||||
@ -55,7 +52,11 @@ namespace RR.Common.Controls
|
||||
// ВАЖНО: 0 = кнопка нажата, 1 = кнопка отпущена, поэтому по умолчанию все единицы
|
||||
private static string lastInputBits = "11111111111111111111111111"; // 26 бит по умолчанию
|
||||
|
||||
private Coroutine blinkingCoroutine;
|
||||
// Контроллер подсветки (добавляется автоматически вместе с этим компонентом)
|
||||
private LightController _light;
|
||||
|
||||
/// <summary>Доступ к контроллеру подсветки.</summary>
|
||||
public LightController Light => _light;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -83,11 +84,14 @@ namespace RR.Common.Controls
|
||||
if (!saveMoneyBetweenSessions) PlayerPrefs.SetInt("CoinsCount", 0);
|
||||
CreditsCount = PlayerPrefs.GetInt("CoinsCount", 0);
|
||||
|
||||
// Получаем или создаём LightController
|
||||
_light = GetComponent<LightController>() ?? gameObject.AddComponent<LightController>();
|
||||
|
||||
initPort();
|
||||
|
||||
// Отправляем сигнал запуска игры
|
||||
Send("2171");
|
||||
Blink();
|
||||
_light.Blink();
|
||||
|
||||
// Запускаем асинхронный опрос порта
|
||||
pollCancellationTokenSource = new CancellationTokenSource();
|
||||
@ -146,47 +150,6 @@ namespace RR.Common.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет команду управления подсветкой на периферийную плату.
|
||||
/// </summary>
|
||||
/// <param name="lightMode">Режим подсветки из перечисления LightMode</param>
|
||||
/// <remarks>
|
||||
/// Формирует команду вида "80XXX" где XXX - код режима подсветки.
|
||||
/// Для добавления новых режимов необходимо обновить вложенный метод getLightString + добавить режим в перечисление.
|
||||
/// </remarks>
|
||||
public static void Send(LightMode lightMode)
|
||||
{
|
||||
try
|
||||
{
|
||||
string signal = LIGHTSIGNAL_PREFIX + getLightString(lightMode);
|
||||
Send(signal);
|
||||
|
||||
//ВОТЭТОТ МЕТОД ПОД СВОИ ПОДСВЕТКИ НАДО ПОДГОНЯТЬ
|
||||
string getLightString(LightMode mode) =>
|
||||
|
||||
mode switch
|
||||
{
|
||||
LightMode.Idle1 => "500",
|
||||
LightMode.Idle2 => "507",
|
||||
LightMode.Orange => "501",
|
||||
LightMode.Blue => "502",
|
||||
LightMode.Win => "503",
|
||||
LightMode.Lose => "504",
|
||||
LightMode.Prize => "505",
|
||||
LightMode.Tama => "513",
|
||||
LightMode.Dog => "514",
|
||||
LightMode.Sam => "515",
|
||||
LightMode.Mistress => "516",
|
||||
|
||||
_ => "500"
|
||||
};
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// DebugWindow.Message("Ошибка отправки светового сигнала.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет списание платы за запуск игры.
|
||||
@ -231,22 +194,16 @@ namespace RR.Common.Controls
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Blink()
|
||||
{
|
||||
StopBlink();
|
||||
|
||||
blinkingCoroutine = StartCoroutine(BlinkingCoroutine());
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void StopBlink(bool enableHat = true)
|
||||
{
|
||||
if (blinkingCoroutine != null) StopCoroutine(blinkingCoroutine);
|
||||
blinkingCoroutine = null;
|
||||
Send(enableHat ? "2131" : "2130"); // включаем или выключаем шляпу
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запускает мигание подсветки. Делегируется в <see cref="LightController"/>.
|
||||
/// </summary>
|
||||
public void Blink() => _light.Blink();
|
||||
|
||||
/// <summary>
|
||||
/// Останавливает мигание подсветки. Делегируется в <see cref="LightController"/>.
|
||||
/// </summary>
|
||||
public void StopBlink(bool enableHat = true) => _light.StopBlink(enableHat);
|
||||
|
||||
/// <summary>
|
||||
/// Получаем текущий баланс кредитов
|
||||
/// </summary>
|
||||
@ -651,34 +608,8 @@ namespace RR.Common.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator BlinkingCoroutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
float randomFloat = Random.Range(0f, 1f);
|
||||
|
||||
Send(randomFloat < 0.5f ? "2130" : "2131");
|
||||
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
public enum LightMode
|
||||
{
|
||||
Idle1,
|
||||
Idle2,
|
||||
Orange,
|
||||
Blue,
|
||||
Win,
|
||||
Lose,
|
||||
Prize,
|
||||
Tama,
|
||||
Dog,
|
||||
Sam,
|
||||
Mistress
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
265
Assets/Scripts/Controls/Inputs/ComSystem/LightController.cs
Normal file
265
Assets/Scripts/Controls/Inputs/ComSystem/LightController.cs
Normal file
@ -0,0 +1,265 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace RR.Common.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Управляет подсветкой периферийной платы по протоколу <c>85XXYY</c>:
|
||||
/// <list type="bullet">
|
||||
/// <item><c>XX</c> — код эффекта (<see cref="LightMode"/>)</item>
|
||||
/// <item><c>YY</c> — время в секундах (00–99)</item>
|
||||
/// </list>
|
||||
/// Команда <c>85</c> (без параметров) отключает рендеринг и освобождает память.
|
||||
/// <para>Поддерживает цепочки эффектов через <see cref="LightSequence"/>:</para>
|
||||
/// <code>
|
||||
/// SetLight(LightMode.VerticalWave, 3).Then(LightMode.DiagonalWave, 3).Loop();
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public class LightController : MonoBehaviour
|
||||
{
|
||||
// ───────────────────────── Инспектор ─────────────────────────
|
||||
|
||||
[Header("Команды шляпы")]
|
||||
[Tooltip("COM-команда для включения шляпы")]
|
||||
[SerializeField] private string hatOnCommand = "2131";
|
||||
[Tooltip("COM-команда для выключения шляпы")]
|
||||
[SerializeField] private string hatOffCommand = "2130";
|
||||
|
||||
[Header("Мигание")]
|
||||
[Tooltip("Интервал между переключениями при мигании (секунды)")]
|
||||
[SerializeField] [Range(0.05f, 5f)] private float blinkInterval = 0.5f;
|
||||
[Tooltip("Случайный разброс интервала (±секунды). 0 = строго равномерно")]
|
||||
[SerializeField] [Range(0f, 2f)] private float blinkIntervalVariance;
|
||||
|
||||
[Header("Световые эффекты")]
|
||||
[Tooltip("Эффект по умолчанию при вызове SetLight без аргументов")]
|
||||
[SerializeField] private LightMode defaultLightMode = LightMode.Rainbow;
|
||||
[Tooltip("Время эффекта по умолчанию (секунды, 0–99)")]
|
||||
[SerializeField] [Range(0, 99)] private int defaultDuration = 3;
|
||||
|
||||
// ───────────────────────── Приватное состояние ─────────────────────────
|
||||
|
||||
private Coroutine _blinkingCoroutine;
|
||||
private LightSequence _activeSequence;
|
||||
|
||||
private const string EffectPrefix = "85";
|
||||
|
||||
// ───────────────────────── Свойства ─────────────────────────
|
||||
|
||||
/// <summary>Время эффекта по умолчанию (используется в <see cref="LightSequence.Then(LightMode)"/>).</summary>
|
||||
public int DefaultDuration => defaultDuration;
|
||||
|
||||
// ===========================start: PUBLIC API===========================
|
||||
|
||||
/// <summary>
|
||||
/// Включает эффект по умолчанию и возвращает <see cref="LightSequence"/> для построения цепочки.
|
||||
/// </summary>
|
||||
public LightSequence SetLight() => SetLight(defaultLightMode, defaultDuration);
|
||||
|
||||
/// <summary>
|
||||
/// Включает эффект на время по умолчанию и возвращает <see cref="LightSequence"/> для построения цепочки.
|
||||
/// </summary>
|
||||
public LightSequence SetLight(LightMode mode) => SetLight(mode, defaultDuration);
|
||||
|
||||
/// <summary>
|
||||
/// Включает световой эффект на указанное время и возвращает <see cref="LightSequence"/> для построения цепочки.
|
||||
/// <para>Если вызвать без <c>.Then()</c> / <c>.Loop()</c> — эффект запустится немедленно как одиночный.</para>
|
||||
/// </summary>
|
||||
/// <param name="mode">Эффект подсветки</param>
|
||||
/// <param name="durationSeconds">Время эффекта (0–99 секунд)</param>
|
||||
public LightSequence SetLight(LightMode mode, int durationSeconds)
|
||||
{
|
||||
StopSequence();
|
||||
_activeSequence = new LightSequence(this, mode, durationSeconds);
|
||||
_activeSequence.Play();
|
||||
return _activeSequence;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Останавливает текущую активную последовательность эффектов и отключает рендеринг.
|
||||
/// </summary>
|
||||
public void StopSequence()
|
||||
{
|
||||
_activeSequence?.Stop();
|
||||
_activeSequence = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Останавливает все эффекты и отключает рендеринг платы (<c>85</c>).
|
||||
/// </summary>
|
||||
public void StopAllEffects()
|
||||
{
|
||||
StopSequence();
|
||||
ComWrapper.Send(EffectPrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Запускает мигание шляпы с настроенным интервалом.
|
||||
/// </summary>
|
||||
public void Blink() => Blink(blinkInterval);
|
||||
|
||||
/// <summary>
|
||||
/// Запускает мигание шляпы с явно указанным интервалом.
|
||||
/// </summary>
|
||||
public void Blink(float interval)
|
||||
{
|
||||
StopBlink(enableHat: false);
|
||||
_blinkingCoroutine = StartCoroutine(BlinkingCoroutine(interval));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Меняет интервал мигания на лету; перезапускает мигание, если оно активно.
|
||||
/// </summary>
|
||||
public void SetBlinkInterval(float interval)
|
||||
{
|
||||
blinkInterval = Mathf.Max(0.05f, interval);
|
||||
if (_blinkingCoroutine != null) Blink(blinkInterval);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Останавливает мигание шляпы.
|
||||
/// </summary>
|
||||
/// <param name="enableHat">Оставить шляпу включённой после остановки</param>
|
||||
public void StopBlink(bool enableHat = true)
|
||||
{
|
||||
if (_blinkingCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_blinkingCoroutine);
|
||||
_blinkingCoroutine = null;
|
||||
}
|
||||
|
||||
ComWrapper.Send(enableHat ? hatOnCommand : hatOffCommand);
|
||||
}
|
||||
|
||||
/// <summary>Возвращает <c>true</c>, если мигание шляпы сейчас активно.</summary>
|
||||
public bool IsBlinking => _blinkingCoroutine != null;
|
||||
|
||||
/// <summary>Возвращает <c>true</c>, если последовательность эффектов сейчас активна.</summary>
|
||||
public bool IsSequencePlaying => _activeSequence?.IsPlaying ?? false;
|
||||
|
||||
// ===========================end: PUBLIC API===========================
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет COM-команду для одного шага последовательности.
|
||||
/// Используется из <see cref="LightSequence"/>.
|
||||
/// </summary>
|
||||
public void SendCommand(LightMode mode, int durationSeconds)
|
||||
{
|
||||
ComWrapper.Send(BuildCommand(mode, durationSeconds));
|
||||
}
|
||||
|
||||
// ReSharper disable once IteratorNeverReturns — корутина намеренно бесконечная
|
||||
private IEnumerator BlinkingCoroutine(float interval)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ComWrapper.Send(Random.Range(0f, 1f) < 0.5f ? hatOnCommand : hatOffCommand);
|
||||
|
||||
float wait = blinkIntervalVariance > 0f
|
||||
? Mathf.Max(0.05f, interval + Random.Range(-blinkIntervalVariance, blinkIntervalVariance))
|
||||
: interval;
|
||||
|
||||
yield return new WaitForSeconds(wait);
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildCommand(LightMode mode, int durationSeconds)
|
||||
{
|
||||
if (mode == LightMode.Off)
|
||||
return EffectPrefix;
|
||||
|
||||
int xx = (int)mode;
|
||||
int yy = Mathf.Clamp(durationSeconds, 0, 99);
|
||||
return $"{EffectPrefix}{xx:D2}{yy:D2}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Строитель последовательности световых эффектов.
|
||||
/// Создаётся через <see cref="LightController.SetLight(LightMode,int)"/>.
|
||||
/// <example><code>
|
||||
/// SetLight(LightMode.VerticalWave, 3).Then(LightMode.DiagonalWave, 3).Loop();
|
||||
/// SetLight(LightMode.VerticalWave, 3).Then(LightMode.Off);
|
||||
/// SetLight(LightMode.Rainbow, 5);
|
||||
/// </code></example>
|
||||
/// </summary>
|
||||
public class LightSequence
|
||||
{
|
||||
private readonly struct Step
|
||||
{
|
||||
public readonly LightMode Mode;
|
||||
public readonly int DurationSeconds;
|
||||
public Step(LightMode mode, int durationSeconds) { Mode = mode; DurationSeconds = durationSeconds; }
|
||||
}
|
||||
|
||||
private readonly List<Step> _steps = new();
|
||||
private readonly LightController _controller;
|
||||
private bool _looping;
|
||||
private Coroutine _coroutine;
|
||||
|
||||
internal LightSequence(LightController controller, LightMode firstMode, int firstDuration)
|
||||
{
|
||||
_controller = controller;
|
||||
_steps.Add(new Step(firstMode, firstDuration));
|
||||
}
|
||||
|
||||
/// <summary>Добавляет следующий эффект с временем по умолчанию.</summary>
|
||||
public LightSequence Then(LightMode mode) => Then(mode, _controller.DefaultDuration);
|
||||
|
||||
/// <summary>Добавляет следующий эффект с указанным временем.</summary>
|
||||
public LightSequence Then(LightMode mode, int durationSeconds)
|
||||
{
|
||||
_steps.Add(new Step(mode, durationSeconds));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Зацикливает последовательность. Для остановки — <see cref="LightController.StopSequence"/>.
|
||||
/// </summary>
|
||||
public LightSequence Loop()
|
||||
{
|
||||
_looping = true;
|
||||
Play();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Запускает последовательность один раз.</summary>
|
||||
public LightSequence Play()
|
||||
{
|
||||
Stop();
|
||||
_coroutine = _controller.StartCoroutine(RunSequence());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Останавливает выполнение последовательности.</summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (_coroutine == null) return;
|
||||
_controller.StopCoroutine(_coroutine);
|
||||
_coroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>Возвращает <c>true</c>, если последовательность сейчас выполняется.</summary>
|
||||
public bool IsPlaying => _coroutine != null;
|
||||
|
||||
// ReSharper disable once IteratorNeverReturns — цикл намеренно бесконечный при _looping=true
|
||||
private IEnumerator RunSequence()
|
||||
{
|
||||
do
|
||||
{
|
||||
foreach (Step step in _steps)
|
||||
{
|
||||
_controller.SendCommand(step.Mode, step.DurationSeconds);
|
||||
|
||||
if (step.Mode != LightMode.Off && step.DurationSeconds > 0)
|
||||
yield return new WaitForSeconds(step.DurationSeconds);
|
||||
}
|
||||
}
|
||||
while (_looping);
|
||||
|
||||
_coroutine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b5ae7d3aa86455381891033da97820b
|
||||
timeCreated: 1771504477
|
||||
52
Assets/Scripts/Controls/Inputs/ComSystem/LightMode.cs
Normal file
52
Assets/Scripts/Controls/Inputs/ComSystem/LightMode.cs
Normal file
@ -0,0 +1,52 @@
|
||||
namespace RR.Common.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Эффекты подсветки периферийной платы (протокол <c>85XXYY</c>).
|
||||
/// <c>XX</c> — код эффекта, <c>YY</c> — время в секундах.
|
||||
/// Команда <c>85</c> (без параметров) отключает рендеринг эффектов и освобождает память.
|
||||
/// </summary>
|
||||
public enum LightMode
|
||||
{
|
||||
/// <summary>Отключить рендеринг эффектов (освобождает память). Команда: <c>85</c></summary>
|
||||
Off = -1,
|
||||
|
||||
/// <summary>Горизонтальная волна. XX = 40</summary>
|
||||
HorizontalWave = 40,
|
||||
|
||||
/// <summary>Вертикальная волна. XX = 41</summary>
|
||||
VerticalWave = 41,
|
||||
|
||||
/// <summary>Диагональная волна. XX = 42</summary>
|
||||
DiagonalWave = 42,
|
||||
|
||||
/// <summary>Радуга. XX = 43</summary>
|
||||
Rainbow = 43,
|
||||
|
||||
/// <summary>Волна вверх. XX = 44</summary>
|
||||
WaveUp = 44,
|
||||
|
||||
/// <summary>Вертикальная волна вверх. XX = 45</summary>
|
||||
VerticalWaveUp = 45,
|
||||
|
||||
/// <summary>Конечная волна. XX = 46</summary>
|
||||
FinalWave = 46,
|
||||
|
||||
/// <summary>Синяя волна. XX = 47</summary>
|
||||
BlueWave = 47,
|
||||
|
||||
/// <summary>Диагональная волна 2. XX = 48</summary>
|
||||
DiagonalWave2 = 48,
|
||||
|
||||
/// <summary>Падающие ряды. XX = 49</summary>
|
||||
FallingRows = 49,
|
||||
|
||||
/// <summary>Оранжевая волна. XX = 50</summary>
|
||||
OrangeWave = 50,
|
||||
|
||||
/// <summary>Морская волна. XX = 51</summary>
|
||||
SeaWave = 51,
|
||||
|
||||
/// <summary>Диагональная волна 3. XX = 52</summary>
|
||||
DiagonalWave3 = 52,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d1d29b94c0748148217efb46bcc2816
|
||||
timeCreated: 1771504563
|
||||
@ -4,6 +4,9 @@ using UnityEditor.Overlays;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RR.Common.InspectorOverrides
|
||||
{
|
||||
|
||||
[Overlay(typeof(SceneView), id: "mouse-world-position", displayName: "Mouse World Position")]
|
||||
[Icon("d_Grid.BoxTool")]
|
||||
public class MouseWorldPositionOverlay : Overlay
|
||||
@ -132,4 +135,5 @@ public class MouseWorldPositionOverlay : Overlay
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace RR.Common.InspectorOverrides
|
||||
#endif
|
||||
|
||||
@ -33,6 +33,8 @@ namespace RR.Game2D.Core.Managers
|
||||
|
||||
// Plate control for game
|
||||
public ComWrapper Plate { get; private set; }
|
||||
// Light control for game
|
||||
public LightController Light => Plate != null ? Plate.Light : null;
|
||||
|
||||
// Globals
|
||||
[Header("Globals")]
|
||||
@ -330,6 +332,7 @@ public void TogglePause() => SetPause(!IsPaused);
|
||||
Logger.Log(gameObject.name,$"Game start time = {_gameStartTime}");
|
||||
|
||||
if (Plate) Plate.StopBlink();
|
||||
Light?.SetLight(LightMode.Rainbow, 99);
|
||||
|
||||
// Включаем отслеживание бездействия
|
||||
if (Inactivity != null) Inactivity.EnableInactivityCheck();
|
||||
@ -369,23 +372,27 @@ public void TogglePause() => SetPause(!IsPaused);
|
||||
/// Окончание игры.
|
||||
/// Загружает сцену главного меню
|
||||
/// </summary>
|
||||
private void GameOver() // => LoadScene("MainMenu", false);
|
||||
private void GameOver()
|
||||
{
|
||||
float totalTime = Time.unscaledTime - _gameStartTime;
|
||||
|
||||
// Отключаем отслеживание бездействия
|
||||
if (Inactivity != null) Inactivity.DisableInactivityCheck();
|
||||
|
||||
Light?.SetLight(LightMode.BlueWave, 30);
|
||||
|
||||
GameOverEvent?.Invoke(totalTime);
|
||||
}
|
||||
// --
|
||||
|
||||
private void GameWin() // => LoadScene("MainMenu", false);
|
||||
private void GameWin()
|
||||
{
|
||||
float totalTime = Time.unscaledTime - _gameStartTime;
|
||||
|
||||
// Отключаем отслеживание бездействия
|
||||
if (Inactivity != null) Inactivity.DisableInactivityCheck();
|
||||
|
||||
Light?.SetLight(LightMode.FinalWave, 30);
|
||||
|
||||
GameWonEvent?.Invoke(totalTime);
|
||||
}
|
||||
@ -466,6 +473,7 @@ public void TogglePause() => SetPause(!IsPaused);
|
||||
if (useUI) SceneManager.LoadScene("Level UI", LoadSceneMode.Additive);
|
||||
else
|
||||
{
|
||||
Light?.SetLight(LightMode.Off);
|
||||
Plate.Blink();
|
||||
_shouldSpawnPlayer = false;
|
||||
}
|
||||
|
||||
@ -5,6 +5,9 @@ using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace RR.Common.UI
|
||||
{
|
||||
|
||||
public class StatsWindowUIController : MonoBehaviour
|
||||
{
|
||||
[Header("Управление")]
|
||||
@ -103,3 +106,4 @@ public class StatsWindowUIController : MonoBehaviour
|
||||
onDone?.Invoke();
|
||||
}
|
||||
}
|
||||
} // namespace RR.Common.UI
|
||||
|
||||
@ -1,175 +1,176 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using RR.Common.Utility;
|
||||
|
||||
/// <summary>
|
||||
/// Продвинутый пример использования системы логирования.
|
||||
/// Демонстрирует различные сценарии использования FileLogger и CompositeLogger.
|
||||
/// </summary>
|
||||
public class AdvancedLogging_Test : MonoBehaviour
|
||||
namespace RR.Common.Utility
|
||||
{
|
||||
private CompositeLogger compositeLogger;
|
||||
private FileLogger mainFileLogger;
|
||||
private FileLogger errorFileLogger;
|
||||
|
||||
void Start()
|
||||
{
|
||||
SetupAdvancedLogging();
|
||||
DemonstrateLogging();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настройка продвинутой системы логирования с несколькими выходными файлами
|
||||
/// Продвинутый пример использования системы логирования.
|
||||
/// Демонстрирует различные сценарии использования FileLogger и CompositeLogger.
|
||||
/// </summary>
|
||||
private void SetupAdvancedLogging()
|
||||
public class AdvancedLogging_Test : MonoBehaviour
|
||||
{
|
||||
// Создаём композитный логгер
|
||||
compositeLogger = new CompositeLogger();
|
||||
compositeLogger.Initialize();
|
||||
private CompositeLogger compositeLogger;
|
||||
private FileLogger mainFileLogger;
|
||||
private FileLogger errorFileLogger;
|
||||
|
||||
// Главный файл - все логи
|
||||
mainFileLogger = new FileLogger(
|
||||
System.IO.Path.Combine(Application.persistentDataPath, "full_log.txt"),
|
||||
append: true
|
||||
);
|
||||
mainFileLogger.filterLogType = LogType.Log; // Все типы сообщений
|
||||
|
||||
// Отдельный файл только для ошибок и исключений
|
||||
errorFileLogger = new FileLogger(
|
||||
System.IO.Path.Combine(Application.persistentDataPath, "errors_only.txt"),
|
||||
append: true
|
||||
);
|
||||
errorFileLogger.filterLogType = LogType.Warning; // Только Warning, Error, Exception
|
||||
|
||||
// Добавляем оба логгера
|
||||
compositeLogger.AddLoggers(mainFileLogger, errorFileLogger);
|
||||
|
||||
Debug.Log($"Логирование настроено. Файлы в: {Application.persistentDataPath}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Демонстрация различных типов логирования
|
||||
/// </summary>
|
||||
private void DemonstrateLogging()
|
||||
{
|
||||
// Простые сообщения
|
||||
compositeLogger.Log("=== Демонстрация системы логирования ===");
|
||||
|
||||
// Сообщения с тегами
|
||||
compositeLogger.Log("SYSTEM", "Игра инициализирована");
|
||||
compositeLogger.LogWarning("AUDIO", "Громкость музыки установлена в 0.5");
|
||||
|
||||
// Форматированные сообщения
|
||||
string playerName = "Player1";
|
||||
int score = 1000;
|
||||
compositeLogger.LogFormat(LogType.Log, "Игрок {0} набрал {1} очков", playerName, score);
|
||||
|
||||
// Логирование с контекстом
|
||||
compositeLogger.Log("Объект создан", this.gameObject);
|
||||
|
||||
// Имитация ошибки
|
||||
compositeLogger.LogError("ERROR", "Это тестовая ошибка - будет в обоих файлах!");
|
||||
|
||||
// Имитация исключения
|
||||
try
|
||||
void Start()
|
||||
{
|
||||
throw new InvalidOperationException("Тестовое исключение для демонстрации");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
compositeLogger.LogException(ex, this.gameObject);
|
||||
SetupAdvancedLogging();
|
||||
DemonstrateLogging();
|
||||
}
|
||||
|
||||
// Информация о путях
|
||||
Debug.Log($"Полный лог: {Application.persistentDataPath}/full_log.txt");
|
||||
Debug.Log($"Только ошибки: {Application.persistentDataPath}/errors_only.txt");
|
||||
}
|
||||
/// <summary>
|
||||
/// Настройка продвинутой системы логирования с несколькими выходными файлами
|
||||
/// </summary>
|
||||
private void SetupAdvancedLogging()
|
||||
{
|
||||
// Создаём композитный логгер
|
||||
compositeLogger = new CompositeLogger();
|
||||
compositeLogger.Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Пример метода для логирования игровых событий
|
||||
/// </summary>
|
||||
public void LogGameEvent(string eventName, params object[] parameters)
|
||||
{
|
||||
string paramStr = string.Join(", ", parameters);
|
||||
compositeLogger.Log("GAME_EVENT", $"{eventName}: {paramStr}");
|
||||
}
|
||||
// Главный файл - все логи
|
||||
mainFileLogger = new FileLogger(
|
||||
System.IO.Path.Combine(Application.persistentDataPath, "full_log.txt"),
|
||||
append: true
|
||||
);
|
||||
mainFileLogger.filterLogType = LogType.Log; // Все типы сообщений
|
||||
|
||||
/// <summary>
|
||||
/// Пример метода для логирования производительности
|
||||
/// </summary>
|
||||
public void LogPerformance(string operation, float duration)
|
||||
{
|
||||
if (duration > 0.016f) // Больше 16мс (60 FPS)
|
||||
{
|
||||
compositeLogger.LogWarning("PERFORMANCE", $"{operation} занял {duration * 1000:F2}мс");
|
||||
}
|
||||
else
|
||||
{
|
||||
compositeLogger.Log("PERFORMANCE", $"{operation} выполнен за {duration * 1000:F2}мс");
|
||||
}
|
||||
}
|
||||
// Отдельный файл только для ошибок и исключений
|
||||
errorFileLogger = new FileLogger(
|
||||
System.IO.Path.Combine(Application.persistentDataPath, "errors_only.txt"),
|
||||
append: true
|
||||
);
|
||||
errorFileLogger.filterLogType = LogType.Warning; // Только Warning, Error, Exception
|
||||
|
||||
/// <summary>
|
||||
/// Пример обёртки для измерения времени выполнения
|
||||
/// </summary>
|
||||
public void PerformOperationWithLogging(string operationName, Action operation)
|
||||
{
|
||||
float startTime = Time.realtimeSinceStartup;
|
||||
|
||||
try
|
||||
{
|
||||
operation.Invoke();
|
||||
float duration = Time.realtimeSinceStartup - startTime;
|
||||
LogPerformance(operationName, duration);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
compositeLogger.LogException(ex);
|
||||
compositeLogger.LogError("OPERATION", $"Операция '{operationName}' завершилась с ошибкой");
|
||||
}
|
||||
}
|
||||
// Добавляем оба логгера
|
||||
compositeLogger.AddLoggers(mainFileLogger, errorFileLogger);
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Пример: логируем нажатия клавиш
|
||||
if (Input.GetKeyDown(KeyCode.L))
|
||||
{
|
||||
LogGameEvent("KeyPress", "L", Time.time);
|
||||
Debug.Log($"Логирование настроено. Файлы в: {Application.persistentDataPath}");
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.F))
|
||||
/// <summary>
|
||||
/// Демонстрация различных типов логирования
|
||||
/// </summary>
|
||||
private void DemonstrateLogging()
|
||||
{
|
||||
// Принудительная запись на диск
|
||||
mainFileLogger.Flush();
|
||||
errorFileLogger.Flush();
|
||||
compositeLogger.Log("SYSTEM", "Логи принудительно сохранены на диск");
|
||||
// Простые сообщения
|
||||
compositeLogger.Log("=== Демонстрация системы логирования ===");
|
||||
|
||||
// Сообщения с тегами
|
||||
compositeLogger.Log("SYSTEM", "Игра инициализирована");
|
||||
compositeLogger.LogWarning("AUDIO", "Громкость музыки установлена в 0.5");
|
||||
|
||||
// Форматированные сообщения
|
||||
string playerName = "Player1";
|
||||
int score = 1000;
|
||||
compositeLogger.LogFormat(LogType.Log, "Игрок {0} набрал {1} очков", playerName, score);
|
||||
|
||||
// Логирование с контекстом
|
||||
compositeLogger.Log("Объект создан", this.gameObject);
|
||||
|
||||
// Имитация ошибки
|
||||
compositeLogger.LogError("ERROR", "Это тестовая ошибка - будет в обоих файлах!");
|
||||
|
||||
// Имитация исключения
|
||||
try
|
||||
{
|
||||
throw new InvalidOperationException("Тестовое исключение для демонстрации");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
compositeLogger.LogException(ex, this.gameObject);
|
||||
}
|
||||
|
||||
// Информация о путях
|
||||
Debug.Log($"Полный лог: {Application.persistentDataPath}/full_log.txt");
|
||||
Debug.Log($"Только ошибки: {Application.persistentDataPath}/errors_only.txt");
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.C))
|
||||
/// <summary>
|
||||
/// Пример метода для логирования игровых событий
|
||||
/// </summary>
|
||||
public void LogGameEvent(string eventName, params object[] parameters)
|
||||
{
|
||||
// Очистка логов
|
||||
mainFileLogger.Clear();
|
||||
errorFileLogger.Clear();
|
||||
compositeLogger.Log("SYSTEM", "Файлы логов очищены");
|
||||
string paramStr = string.Join(", ", parameters);
|
||||
compositeLogger.Log("GAME_EVENT", $"{eventName}: {paramStr}");
|
||||
}
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
compositeLogger.Log("SYSTEM", "Приложение завершается...");
|
||||
|
||||
// Важно: освобождаем ресурсы
|
||||
mainFileLogger?.Dispose();
|
||||
errorFileLogger?.Dispose();
|
||||
|
||||
Debug.Log("Файловые логгеры закрыты");
|
||||
}
|
||||
/// <summary>
|
||||
/// Пример метода для логирования производительности
|
||||
/// </summary>
|
||||
public void LogPerformance(string operation, float duration)
|
||||
{
|
||||
if (duration > 0.016f) // Больше 16мс (60 FPS)
|
||||
{
|
||||
compositeLogger.LogWarning("PERFORMANCE", $"{operation} занял {duration * 1000:F2}мс");
|
||||
}
|
||||
else
|
||||
{
|
||||
compositeLogger.Log("PERFORMANCE", $"{operation} выполнен за {duration * 1000:F2}мс");
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// На всякий случай
|
||||
mainFileLogger?.Dispose();
|
||||
errorFileLogger?.Dispose();
|
||||
/// <summary>
|
||||
/// Пример обёртки для измерения времени выполнения
|
||||
/// </summary>
|
||||
public void PerformOperationWithLogging(string operationName, Action operation)
|
||||
{
|
||||
float startTime = Time.realtimeSinceStartup;
|
||||
|
||||
try
|
||||
{
|
||||
operation.Invoke();
|
||||
float duration = Time.realtimeSinceStartup - startTime;
|
||||
LogPerformance(operationName, duration);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
compositeLogger.LogException(ex);
|
||||
compositeLogger.LogError("OPERATION", $"Операция '{operationName}' завершилась с ошибкой");
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Пример: логируем нажатия клавиш
|
||||
if (Input.GetKeyDown(KeyCode.L))
|
||||
{
|
||||
LogGameEvent("KeyPress", "L", Time.time);
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.F))
|
||||
{
|
||||
// Принудительная запись на диск
|
||||
mainFileLogger.Flush();
|
||||
errorFileLogger.Flush();
|
||||
compositeLogger.Log("SYSTEM", "Логи принудительно сохранены на диск");
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.C))
|
||||
{
|
||||
// Очистка логов
|
||||
mainFileLogger.Clear();
|
||||
errorFileLogger.Clear();
|
||||
compositeLogger.Log("SYSTEM", "Файлы логов очищены");
|
||||
}
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
compositeLogger.Log("SYSTEM", "Приложение завершается...");
|
||||
|
||||
// Важно: освобождаем ресурсы
|
||||
mainFileLogger?.Dispose();
|
||||
errorFileLogger?.Dispose();
|
||||
|
||||
Debug.Log("Файловые логгеры закрыты");
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// На всякий случай
|
||||
mainFileLogger?.Dispose();
|
||||
errorFileLogger?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
using RR.Game2D.Core.Managers;
|
||||
|
||||
public class DebugSettings : MonoBehaviour
|
||||
namespace RR.Common.Utility
|
||||
{
|
||||
[SerializeField] private int livesCount = 99;
|
||||
[SerializeField] private int coinsCount;
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
public class DebugSettings : MonoBehaviour
|
||||
{
|
||||
GameManager.Instance.Lives = livesCount;
|
||||
[SerializeField] private int livesCount = 99;
|
||||
[SerializeField] private int coinsCount;
|
||||
|
||||
void Start()
|
||||
{
|
||||
GameManager.Instance.Lives = livesCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.github-glitchenzo.nugetforunity": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity",
|
||||
"com.unity.ai.inference": "2.4.1",
|
||||
"com.unity.ai.inference": "2.5.0",
|
||||
"com.unity.cinemachine": "3.1.5",
|
||||
"com.unity.collab-proxy": "2.10.2",
|
||||
"com.unity.collab-proxy": "2.11.3",
|
||||
"com.unity.feature.2d": "2.0.2",
|
||||
"com.unity.ide.rider": "3.0.39",
|
||||
"com.unity.ide.visualstudio": "2.0.25",
|
||||
"com.unity.inputsystem": "1.17.0",
|
||||
"com.unity.ide.visualstudio": "2.0.26",
|
||||
"com.unity.inputsystem": "1.18.0",
|
||||
"com.unity.multiplayer.center": "1.0.1",
|
||||
"com.unity.render-pipelines.universal": "17.3.0",
|
||||
"com.unity.test-framework": "1.6.0",
|
||||
"com.unity.timeline": "1.8.9",
|
||||
"com.unity.timeline": "1.8.10",
|
||||
"com.unity.ugui": "2.0.0",
|
||||
"com.unity.visualeffectgraph": "17.3.0",
|
||||
"com.unity.visualscripting": "1.9.9",
|
||||
|
||||
@ -8,11 +8,11 @@
|
||||
"hash": "34432703a177c97631a4eb1d908f086037c67969"
|
||||
},
|
||||
"com.unity.2d.animation": {
|
||||
"version": "13.0.2",
|
||||
"version": "13.0.4",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.2d.common": "12.0.1",
|
||||
"com.unity.2d.common": "12.0.2",
|
||||
"com.unity.2d.sprite": "1.0.0",
|
||||
"com.unity.collections": "2.4.3",
|
||||
"com.unity.modules.animation": "1.0.0",
|
||||
@ -34,7 +34,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.2d.common": {
|
||||
"version": "12.0.1",
|
||||
"version": "12.0.2",
|
||||
"depth": 2,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@ -106,22 +106,22 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.2d.tooling": {
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.2d.common": "12.0.1",
|
||||
"com.unity.2d.common": "12.0.2",
|
||||
"com.unity.modules.uielements": "1.0.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ai.inference": {
|
||||
"version": "2.4.1",
|
||||
"version": "2.5.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.burst": "1.8.17",
|
||||
"com.unity.dt.app-ui": "1.3.1",
|
||||
"com.unity.dt.app-ui": "1.3.3",
|
||||
"com.unity.collections": "2.4.3",
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||
"com.unity.modules.imageconversion": "1.0.0"
|
||||
@ -129,7 +129,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.burst": {
|
||||
"version": "1.8.26",
|
||||
"version": "1.8.27",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@ -149,7 +149,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.collab-proxy": {
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.3",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
@ -191,7 +191,7 @@
|
||||
"depth": 0,
|
||||
"source": "builtin",
|
||||
"dependencies": {
|
||||
"com.unity.2d.animation": "13.0.2",
|
||||
"com.unity.2d.animation": "13.0.4",
|
||||
"com.unity.2d.pixel-perfect": "5.1.1",
|
||||
"com.unity.2d.psdimporter": "12.0.1",
|
||||
"com.unity.2d.sprite": "1.0.0",
|
||||
@ -199,7 +199,7 @@
|
||||
"com.unity.2d.tilemap": "1.0.0",
|
||||
"com.unity.2d.tilemap.extras": "6.0.1",
|
||||
"com.unity.2d.aseprite": "3.0.1",
|
||||
"com.unity.2d.tooling": "1.0.0"
|
||||
"com.unity.2d.tooling": "1.0.2"
|
||||
}
|
||||
},
|
||||
"com.unity.ide.rider": {
|
||||
@ -212,16 +212,16 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.visualstudio": {
|
||||
"version": "2.0.25",
|
||||
"version": "2.0.26",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.test-framework": "1.1.31"
|
||||
"com.unity.test-framework": "1.1.33"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.inputsystem": {
|
||||
"version": "1.17.0",
|
||||
"version": "1.18.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@ -345,7 +345,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.timeline": {
|
||||
"version": "1.8.9",
|
||||
"version": "1.8.10",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
m_EditorVersion: 6000.3.2f1
|
||||
m_EditorVersionWithRevision: 6000.3.2f1 (a9779f353c9b)
|
||||
m_EditorVersion: 6000.3.8f1
|
||||
m_EditorVersionWithRevision: 6000.3.8f1 (1c7db571dde0)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user