- Рефакторинг системы подсветки: вместо длительности в секундах теперь используется количество циклов (float). Добавлена таблица длительности циклов эффектов `LightCycleDurations` для пересчёта циклов в секунды. Префикс команд подсветки изменён с `85` на `86`. - `LightSequence`: добавлены `Pause()` / `Resume()` с запоминанием текущего шага последовательности; возобновление продолжает с сохранённой позиции. - `LightController`: добавлены `PauseAll()` / `ResumeAll()` для приостановки и восстановления подсветки и мигания шляпы при отключении/переподключении платы. Свойства `IsSequencePaused`, `IsBlinkPaused`. - `ComDeviceClient`: после успешной авторизации отправляются команды `890003` и `8600`. Добавлен запрос MAC-адреса через команду `06` (ответ — 12 hex-символов без префикса). Новое событие `ConnectionLost` при начале KeepAlive-реконнекта. - `ComHub`: очередь `_disconnectedQueue` с типом `DeviceDisconnectMessage` (DeviceId + Role + Mac) — сохраняет идентификатор устройства до его удаления из словаря. Подписка на `ConnectionLost`. Добавлен `GetDeviceMac()`. - `ComSystemManager`: при инициализации, если плата не обнаружена, подсветка сразу ставится на паузу. При отвале дефолтного устройства — `PauseAll()`, при переподключении — сравнение по MAC и роли через `IsSameDevice()`: та же плата → `ResumeAll()`, другая → `Blink()` с нуля. Добавлены событие `OnDeviceDisconnected` и метод `GetDeviceMac()`. - `COMInput`: при событии `OnDeviceDisconnected` сбрасываются все состояния кнопок через `ResetAllButtons()`, генерируя `buttonUp` для зажатых кнопок. Устраняет баг залипания управления после переподключения платы. - `GameManager`: расширена последовательность световых эффектов в меню (добавлены `DiagonalWave3`, `WaveUp`, `OrangeWave`); вызовы `SetLight` для game over/won переведены на float-циклы. - Обновлены `ARCHITECTURE.md` и `README.md`: переписана документация на актуальную структуру проекта и текущие возможности игры.
14 KiB
Архитектура проекта "Mario-like platformer"
Обзор
Проект представляет собой 2D-платформер на Unity. Архитектура построена вокруг одного постоянного GameManager, набора сервисных менеджеров в Globals, компонентного игрового поведения на сценах и отдельной системы ввода, которая может работать как от клавиатуры, так и от COM-устройств.
Код в основном разделен по доменным папкам внутри Assets/Scripts, а логика сцены собирается из независимых MonoBehaviour-компонентов.
Технологический стек
- Unity (2D-проект);
- C#;
- Unity Input System;
- Unity UI (
com.unity.ugui); - Cinemachine;
- Universal Render Pipeline;
- Visual Effect Graph;
PlayerPrefsдля части runtime-данных;- JSON через
JsonUtilityдля сохранения настроек.
Актуальная структура проекта
Assets/Scripts/Abstract
Базовые перечисления и интерфейсы, на которых держатся общие контракты игрового кода:
Enums.cs;Interfaces.cs.
Assets/Scripts/Managers
Глобальные менеджеры и сервисы верхнего уровня:
GameManager- центральная точка управления сессией;AudioManager- музыка, пауза и SFX;PaymentManager- кредиты и логика оплаты;BalanceManager- баланс для запуска/продолжения игры;InactivityManager- реакция на бездействие;GlobalsManager- инициализация общих зависимостей;PrizeSystemManager- управление призовой системой.
Assets/Scripts/Settings
Новая система настроек, отделенная от игрового цикла:
Core/SettingsManager- хранение значений, загрузка, сохранение, рассылка событий;Core/SettingsDatabase- схема настроек и значения по умолчанию;Core/SettingsApplier- применение значений к рантайм-системам;UI/*- генерация и связывание интерфейса настроек.
Assets/Scripts/Controls
Подсистема ввода и интеграции с внешним оборудованием:
Inputs/In.cs- фасад поверх выбранного источника ввода;Inputs/InputSource*- абстракции и реализации источников;Inputs/KeyboardInput.cs- клавиатурное управление;Inputs/COMInput.cs- чтение данных с COM-контроллера;Inputs/ComSystem/*- подключение устройств, handshake, освещение, призовые стойки и маршрутизация сообщений;ComInputToInputSystemAdapter.cs- адаптер в сторону Unity Input System.
Assets/Scripts/Behaviour
Основная игровая логика, размещаемая на объектах сцен:
Controllers/*- игрок, враги, сценовая музыка;Camera/*- слежение и ограничение поведения камеры;Checkpoints/*- точки возрождения и текущий активный чекпоинт;Projectiles/*иThrowProjectiles/*- снаряды и логика стрельбы;Sequence/*- сценарные последовательности действий;Health,LevelFinish,ScoreSystem,MessageController,Parallax,LightDimmer, пулы VFX и окружения.
Assets/Scripts/Entities
Интерактивные сущности уровня:
- платформы (
MovingPlatform,ShiftingPlatform,TrajectoryPlatform,VanishingPlatform,TriggeringPlatform,HidingTriggeringPlatform); - собираемые объекты (
CoinCollectible,LeafCollectible,BatCollectible); - ловушки и рычаги (
Trap,Lever,DeactivatingLever); - прочие физические объекты (
JumpPad2D,Rotator,Oscillator,LightActionsController).
Assets/Scripts/UI
Пользовательский интерфейс игры и служебные окна:
- главное меню, пауза, выбор уровня, финальные баннеры;
- верхняя панель статуса, отображение кредитов и баланса;
- модальная система (
UI/Modal); - текстовый ввод (
UI/TextInput); - служебные окна статистики и QR-кодов.
Дополнительные домены
Audio- обвязка звуковых эффектов и сценовых аудио-наборов;Animations- обработчики animation events;FX- запуск визуальных эффектов;ScriptableObjects- конфигурация врагов, SFX и sequence actions;Utilities- логирование, утилиты, атрибуты, QR-коды, форматирование и отладочные настройки;Editor- кастомные инспекторы и drawers.
Ключевые runtime-компоненты
GameManager
GameManager создается автоматически через RuntimeInitializeOnLoadMethod, живет между сценами и является главным координационным узлом:
- инициализирует логгер;
- поднимает
AudioManager; - загружает prefab
Resources/Prefabs/Global/Globals; - получает ссылки на
SettingsManager,PaymentManager,BalanceManager,ComSystemManager; - управляет паузой, жизнями, монетами, переходами между уровнями и спавном игрока;
- публикует основные события игры (
PauseStateChanged,PlayerSpawned,CoinsChanged,LivesChanged,GameOverEvent,GameWonEvent).
Globals
Глобальный prefab из Resources/Prefabs/Global/Globals выступает контейнером для постоянных компонентов, которые должны существовать независимо от текущей сцены. Через него GameManager подцепляет сервисы настроек, оплаты и COM-системы.
SettingsManager
Новая подсистема настроек работает независимо от старой игровой логики и хранит значения в persistentDataPath/settings.json:
- читает схему из
SettingsDatabase; - приводит входные значения к допустимому диапазону через
Sanitize; - применяет изменения сразу через
SettingsApplier; - сообщает об изменениях через
OnSettingsLoaded,OnSettingsSaved,OnSettingChanged.
ComSystemManager
Подсистема внешних устройств отвечает за:
- обнаружение одного или нескольких COM-устройств;
- handshake и идентификацию роли устройства;
- прием монет и обновление кредитов;
- маршрутизацию входных битов от контроллера;
- управление подсветкой через
LightController; - подключение специализированных обработчиков, например призовой стойки.
Потоки управления
Запуск приложения
- Unity вызывает автоматическую инициализацию
GameManager. GameManager.Awake()создает логгер, аудио и поднимаетGlobals.- Из
Globalsполучаются ссылки на настройки, оплату, баланс и COM-систему. - После загрузки сцены уровня
GameManagerпытается заспавнить игрока вPlayerSpawn.
Старт игровой сессии
GameManager.NewGame()проверяет режим бесплатной игры и доступный баланс.- Баланс конвертируется в стартовое число жизней.
- Сбрасываются монеты и статистика (
ScoreSystem). - Загружается сцена первого уровня и аддитивно сцена
Level UI.
Смерть и возрождение
- Игровой объект получает урон через
Health. - При необходимости вызывается
GameManager.RespawnPlayer(). - Менеджер уменьшает число жизней.
- Если жизни есть, игрок переносится к активному чекпоинту или стартовой точке.
- Если жизни закончились, вызывается событие окончания игры.
Переход между уровнями
LevelFinishили другая игровая логика вызываетGameManager.NextLevel().GameManagerвычисляет следующую сцену по схемеworld-stage.- Если сцена существует в build settings, она загружается.
- Если следующей сцены нет, игра считается пройденной.
Архитектурные принципы
Компонентная модель Unity
Игровая логика вынесена в независимые MonoBehaviour, которые комбинируются на prefab-ах и объектах сцен. Большинство сущностей собирается из небольших компонентов вместо глубокой иерархии наследования.
Централизованное orchestration + событийная связь
GameManager координирует верхнеуровневый поток игры, но отдельные подсистемы общаются через события, а не через жесткие прямые вызовы между всеми слоями.
Конфигурация через ScriptableObject
Настройки, конфигурации врагов, аудио-наборы и сценарные действия вынесены в ресурсы, чтобы часть поведения менялась без переписывания кода.
Разделение по доменам
Код разделен не по техническим слоям MVC, а по игровым областям ответственности: Managers, Behaviour, Controls, UI, Entities, Settings.
Фасад над вводом
In.cs скрывает конкретный источник сигналов от остального игрового кода. Благодаря этому один и тот же gameplay-код может работать и с клавиатурой, и с COM-платой.
Сцены и ресурсы
Сцены
В проекте явно присутствуют:
MainMenu;Level UI;- игровые сцены уровня (например,
1-1,99-99) по шаблонуworld-stage.
Resources
Критичные runtime-ресурсы, которые подгружаются из кода:
Prefabs/Global/Globals;Prefabs/Player_New(или альтернативный player prefab);Audio/Master;SettingsDatabase.
Это означает, что проект по-прежнему опирается на Resources.Load для части базовой инициализации.
Пространства имен
Основные пространства имен в текущем коде:
RR.Game2D.Core.Managers;RR.Common.Settings;RR.Common.Controls;RR.Common.Behaviour;RR.Game2D.Behaviour;RR.Game2D.Entities;RR.Common.Utility.
Что важно учитывать при изменениях
GameManagerостается точкой входа и зависит от структурыGlobals; изменения в prefab-е могут ломать инициализацию всей игры.- Схема имен сцен (
1-1,1-2и т.д.) встроена в логику переходов между уровнями. - Настройки частично влияют на базовый gameplay (
startLives,additionalLives, параметры оплаты). - COM-подсистема тесно связана с аркадным режимом: кредиты, свет, призовые устройства и внешний ввод.
- Для части критичных объектов используются пути в
Resources, поэтому переименование или перенос этих ассетов требует обновления строковых путей в коде.