Danro/TECHDOC.md
Danil Iarosh 79b66b2d17
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat: add controller disconnect/reconnect handling, MAC identification, lighting pause/resume system
- Рефакторинг системы подсветки: вместо длительности в секундах теперь
  используется количество циклов (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`: переписана документация
  на актуальную структуру проекта и текущие возможности игры.
2026-03-04 13:55:08 +03:00

521 lines
23 KiB
Markdown
Raw Permalink 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.

# TECHDOC
## Назначение документа
Этот файл описывает технические решения проекта `Danro` на уровне runtime-логики, архитектуры и эксплуатационных ограничений. В отличие от `README.md`, здесь собраны детали, важные для разработки, сопровождения и дальнейшего расширения проекта.
## 1. Общая техническая картина
Проект представляет собой 2D-платформер на Unity с двумя режимами эксплуатации:
- обычный игровой режим с клавиатурным управлением;
- аркадный режим с внешним COM-устройством, кредитами, балансом, световыми эффектами и опциональной призовой механикой.
Архитектурно игра построена вокруг одного постоянного `GameManager`, набора глобальных сервисов из prefab `Globals`, сценовой компонентной логики и отдельной подсистемы настроек.
## 2. Стек и зависимости
### Движок и язык
- `Unity 6000.3.8f1`
- `C#`
### Основные Unity-пакеты
По `Packages/manifest.json` проект использует:
- `com.unity.inputsystem`
- `com.unity.ugui`
- `com.unity.cinemachine`
- `com.unity.render-pipelines.universal`
- `com.unity.visualeffectgraph`
- `com.unity.feature.2d`
- `com.unity.test-framework`
Дополнительно подключен `NuGetForUnity`, а также `com.unity.ai.inference`, но ключевой gameplay-код завязан в первую очередь на стандартные Unity-подсистемы.
## 3. Сцены и структура запуска
### Активные сцены в Build Settings
Сейчас включены:
- `Assets/Scenes/MainMenu.unity`
- `Assets/Scenes/Level UI.unity`
- `Assets/Scenes/Levels/1-1.unity`
- `Assets/Scenes/Levels/1-2.unity`
- `Assets/Scenes/Levels/1-3.unity`
Это означает, что фактическая игровая цепочка на текущий момент состоит из трех игровых уровней. Старые сцены в `Assets/Scenes/Levels/Old` в билд не включены.
### Принцип загрузки
`GameManager.LoadScene()` использует такую схему:
- основная сцена загружается как active scene;
- при `useUI = true` отдельно догружается `Level UI` в additive-режиме;
- для сцен без игрового UI (например, меню) игрок не спавнится.
### Схема именования уровней
Переход между уровнями завязан на шаблон имени сцены `world-stage`, например:
- `1-1`
- `1-2`
- `1-3`
`GameManager.NextLevel()` инкрементирует `Stage`, а при переполнении переходит к следующему `World`. Проверка наличия следующей сцены выполняется через `SceneManager.sceneCountInBuildSettings`. Если следующей сцены нет, игра считается завершенной.
Это важное ограничение: любое изменение схемы названий уровней потребует правки логики переходов.
## 4. Точка входа и жизненный цикл
### `GameManager`
Главный runtime-координатор находится в `Assets/Scripts/Managers/GameManager.cs`.
Ключевые особенности:
- создается автоматически через `RuntimeInitializeOnLoadMethod`;
- живет между сценами через `DontDestroyOnLoad`;
- хранит глобальное состояние игровой сессии;
- поднимает базовые подсистемы при старте приложения.
### Что инициализирует `GameManager`
В `Awake()` он:
- создает и настраивает `CompositeLogger` с файловым логгером в `game.log`;
- создает `AudioManager`;
- загружает `Resources/Prefabs/Global/Globals`;
- получает ссылки на `SettingsManager`, `PaymentManager`, `BalanceManager`, `ComSystemManager`;
- создает `InactivityManager`;
- подцепляет `LightController` из COM-подсистемы;
- подписывается на события настроек и оплаты.
### Runtime-события `GameManager`
`GameManager` публикует верхнеуровневые события:
- `PauseStateChanged`
- `PlayerSpawned`
- `CoinsChanged`
- `MaxLivesChanged`
- `LivesChanged`
- `GameOverEvent`
- `GameWonEvent`
Эти события используются UI-слоем и другими системами для синхронизации без жесткой связности.
## 5. Глобальные сервисы
### `Globals` prefab
Проект зависит от prefab `Resources/Prefabs/Global/Globals`. Это контейнер долгоживущих компонентов, которые должны быть доступны независимо от текущей сцены.
Если структура этого prefab изменится, инициализация игры может сломаться, потому что `GameManager` ожидает получить из него конкретные компоненты.
### `AudioManager`
Отвечает за:
- запуск и паузу музыки;
- управление SFX;
- реакцию на глобальную паузу.
`GameManager.SetPause()` меняет `Time.timeScale` и вызывает `Audio.SmoothPauseMusic(pause)`.
### `InactivityManager`
Используется для аркадного режима:
- включается при старте игры;
- выключается при победе или поражении;
- предназначен для реакции на бездействие игрока и возврата в безопасное состояние.
### Логирование
Используется `CompositeLogger`, который пишет в `game.log` в корне проекта. Это отдельная эксплуатационная подсистема, полезная при отладке инсталляции и поведения железа.
## 6. Игровой цикл
### Старт новой игры
`GameManager.NewGame()` выполняет:
1. Проверку режима `free game`.
2. Проверку достаточности баланса для старта.
3. Расчет числа стартовых жизней.
4. Списание текущего баланса.
5. Сброс монет и статистики.
6. Включение игрового светового режима.
7. Включение контроля бездействия.
8. Загрузку уровня `1-1`.
### Расчет жизней
Текущая логика такая:
- первая оплата дает `StartingLives`;
- каждая дополнительная оплата добавляет `LivesIncrement`;
- итоговое число жизней рассчитывается от накопленного баланса и стоимости импульса оплаты.
Таким образом, жизни завязаны не только на gameplay, но и на экономику аркадной установки.
### Поражение
При поражении:
- вычисляется полное время сессии;
- отключается проверка бездействия;
- включается специальный световой сценарий;
- вызывается `GameOverEvent`.
Дальше UI показывает `LoseBannerController`, где игрок может:
- продолжить игру, если хватает баланса;
- дождаться завершения таймера и вернуться в меню.
### Победа
При победе:
- вычисляется время сессии;
- отключается контроль бездействия;
- запускается финальный световой режим;
- вызывается `GameWonEvent`.
Победный экран показывает итоговое время и, при наличии данных, QR-коды призовой системы.
## 7. Спавн, смерть и чекпоинты
### Первый спавн
После загрузки игровой сцены `GameManager` ищет `PlayerSpawn`:
- если точка найдена, берет ее позицию и направление;
- если нет, использует запасную позицию `(0, 1.3, 0)`.
Игрок создается из prefab `Resources/Prefabs/Player_New`, если ссылка не задана в инспекторе.
### Респавн
`GameManager.RespawnPlayer()`:
- уменьшает число жизней;
- при нуле жизней завершает сессию;
- ищет активный чекпоинт через `CheckpointSystem`;
- телепортирует игрока или создает его заново;
- сбрасывает скорость `Rigidbody2D`;
- восстанавливает здоровье через `Health.ResetHealth()`.
### Что не сбрасывается
В текущей реализации при респавне:
- уровень не перезагружается;
- монеты не обнуляются.
Это заметное отличие от старой модели `ResetLevel()`, которая помечена как устаревшая.
## 8. Игровые сущности и сцена
### Основные gameplay-компоненты
Ключевая сценовая логика лежит в `Assets/Scripts/Behaviour` и `Assets/Scripts/Entities`.
Реализованы:
- враги (`EnemyController`);
- игрок (`PlayerController`);
- здоровье (`Health`);
- завершение уровня (`LevelFinish`);
- система монет и статистики (`ScoreSystem`);
- сообщения и сценарные последовательности (`MessageController`, `Sequencer`);
- снаряды игрока и врагов;
- VFX-пулы и окружение.
### Интерактивные объекты
В `Assets/Scripts/Entities/Props` и подкаталогах находятся:
- монеты и другие коллектаблы;
- ловушки;
- рычаги;
- прозрачный передний план;
- платформы разных типов.
Поддерживаются:
- `MovingPlatform`
- `ShiftingPlatform`
- `TrajectoryPlatform`
- `TriggeringPlatform`
- `HidingTriggeringPlatform`
- `VanishingPlatform`
Это означает, что уровень собирается из компонентов с локальной логикой, а не из одного большого контроллера.
## 9. Система ввода
### Абстракция ввода
Ввод изолирован в `Assets/Scripts/Controls`.
Центральная идея:
- игровой код не должен знать, пришел сигнал с клавиатуры или с внешней платы;
- источник ввода скрыт за фасадом `In.cs`.
### Поддерживаемые источники
В проекте присутствуют:
- `KeyboardInput`
- `COMInput`
- адаптер `ComInputToInputSystemAdapter` для интеграции с `Unity Input System`
Это дает два параллельных слоя совместимости:
- gameplay-код может работать с абстракцией собственного ввода;
- UI и часть действий могут работать через стандартные `InputActionReference`.
### COM-подсистема
В `Assets/Scripts/Controls/Inputs/ComSystem` собраны:
- подключение COM-устройств;
- handshake и маршрутизация;
- работа с кредитами;
- управление подсветкой;
- поддержка призовой стойки.
Ключевые классы:
- `ComSystemManager`
- `ComHub`
- `ComDeviceClient`
- `LightController`
- `PrizeStandController`
- `PrizeSystem`
Это один из самых чувствительных участков проекта: он завязан на внешнее железо и требует особенно аккуратных изменений.
## 10. Платежная модель, кредиты и баланс
### `PaymentManager`
`PaymentManager` хранит и синхронизирует число кредитов.
Он:
- реагирует на события COM-подсистемы;
- умеет добавлять кредиты вручную для тестирования;
- поддерживает free play;
- уведомляет другие системы через `UnityEvent`.
Ключевые события:
- `OnCreditsChanged`
- `OnCreditAdded`
- `OnCreditRemoved`
### `BalanceManager`
`BalanceManager` является более высоким уровнем экономики.
Он:
- хранит числовой баланс;
- начисляет его за импульсы оплаты;
- списывает баланс за старт и продолжение;
- кеширует значения из настроек;
- синхронизирует количество кредитов с COM-системой.
Важный нюанс: в текущем `GameManager.NewGame()` при старте списывается весь накопленный баланс, а не только фиксированная стоимость старта. Это сознательно влияет на число выдаваемых жизней и на модель одной игровой сессии.
### Free Play
Если в настройках включен `payment.freeGame`:
- старт игры доступен без баланса;
- продолжение доступно без списаний;
- часть UI-ограничений отключается.
## 11. Настройки
### Новая система настроек
Подсистема находится в `Assets/Scripts/Settings`.
Она состоит из:
- `SettingsDatabase` - схема настроек;
- `SettingsManager` - загрузка, хранение и события;
- `SettingsApplier` - применение значений к runtime;
- `SettingsUIGenerator` и binder-классов - построение UI.
### Типы настроек
В коде поддерживаются:
- `Bool`
- `Int`
- `Float`
- `String`
- `Button`
- `OptionPicker`
Это позволяет хранить не только значения, но и описывать кнопочные действия и enum-подобные селекторы в одной общей схеме.
### Хранение и валидация
Система:
- использует схему из `SettingsDatabase`;
- валидирует значения через `Sanitize`;
- приводит значения к диапазонам;
- сохраняет настройки в JSON в `persistentDataPath`.
### Практическое применение
По текущему коду настройки влияют как минимум на:
- стартовые жизни;
- дополнительные жизни;
- free play;
- стоимость импульса оплаты;
- стоимость старта;
- стоимость продолжения;
- номер поддержки;
- описание суперприза;
- номер телефона для призовой системы.
Это уже не декоративный UI, а реально рабочая конфигурационная подсистема.
## 12. Пользовательский интерфейс
### Основные UI-экраны
В `Assets/Scripts/UI` реализованы:
- `MainMenu`
- `PauseMenu`
- `SettingsUI`
- `LevelSelectModal`
- `GameEndUIController`
- `WinBannerController`
- `LoseBannerController`
### Что делает UI
На текущий момент UI умеет:
- блокировать старт игры при недостаточном балансе;
- показывать платежные подсказки;
- открывать настройки;
- собирать список уровней из `Build Settings`;
- показывать время сессии на экранах победы/поражения;
- разрешать продолжение только при достаточном балансе;
- динамически создавать QR-коды на победном экране.
### Статистика
Есть два отдельных режима статистики:
- `StatsWindowUIController` - простой просмотр;
- `AdminStatsWindowUIController` - просмотр плюс сброс обнуляемых счетчиков.
Статистика хранится через `PlayerPrefs`.
## 13. Призовая система и QR-коды
### `PrizeSystemManager`
Эта подсистема:
- читает параметры из настроек;
- готовит номер телефона;
- генерирует QR-коды заранее;
- может инициировать выдачу приза через hopper.
### Отображение QR
Актуальная логика показа QR-кодов перенесена в `WinBannerController`:
- при победе он получает `Texture2D[]` из `PrizeSystemManager`;
- создает `Image`-элементы динамически;
- очищает их при закрытии окна.
`QRCodeUIController` помечен как устаревший и использовать его больше не нужно.
## 14. Система ресурсов и зависимости от `Resources`
Проект по-прежнему опирается на `Resources.Load` для критичных runtime-объектов:
- `Prefabs/Global/Globals`
- `Prefabs/Player_New`
- `Audio/Master`
Это удобно для ранней инициализации, но создает ограничения:
- переименование ассетов требует обновления строковых путей;
- ошибки проявляются только в runtime;
- часть связности остается строковой, а не ссылочной.
## 15. Ключевые технические риски
### 1. Жесткая зависимость от `Globals`
Если из prefab убрать или переименовать нужный компонент, `GameManager` потеряет доступ к базовым сервисам.
### 2. Завязка на соглашения по именам сцен
Переход между уровнями работает только для формата `world-stage`.
### 3. Связь с внешним железом
Любые изменения вокруг COM-контуров, кредита, подсветки и призовой стойки нужно тестировать отдельно от обычного desktop-режима.
### 4. Смешение аркадной экономики и gameplay
Количество жизней напрямую зависит от баланса и стоимости импульсов, поэтому изменения в экономике автоматически меняют сложность игры.
### 5. Частичное хранение данных в `PlayerPrefs`
Баланс, статистика и часть служебного состояния могут сохраняться между сессиями. Это нужно учитывать при тестах и при подготовке демо-сборок.
## 16. Практические правила для изменений
При изменениях в проекте стоит проверять:
1. Загружается ли `Globals` и поднимаются ли `SettingsManager`, `BalanceManager`, `PaymentManager`.
2. Не сломалась ли загрузка `MainMenu`, `Level UI` и уровней `1-1`..`1-3`.
3. Работает ли старт новой игры при обычном и `free play` режимах.
4. Корректно ли доступны респавн, чекпоинты и завершение уровня.
5. Не нарушился ли сценарий продолжения после поражения.
6. Не перестали ли отображаться QR-коды на победе.
7. Не нарушилась ли совместимость клавиатуры и COM-ввода.
## 17. Карта основных директорий
- `Assets/Scripts/Managers` - глобальные менеджеры и orchestration.
- `Assets/Scripts/Settings` - схема, хранение и UI настроек.
- `Assets/Scripts/Controls` - ввод и интеграция с COM-оборудованием.
- `Assets/Scripts/Behaviour` - игровая логика на сценах.
- `Assets/Scripts/Entities` - интерактивные сущности и платформы.
- `Assets/Scripts/UI` - меню, модальные окна, баннеры и служебные панели.
- `Assets/Scripts/Utilities` - логирование, утилиты и вспомогательный код.
## 18. Итог
Текущее техническое решение проекта можно описать так:
- gameplay сделан как набор независимых Unity-компонентов;
- orchestration централизован в `GameManager`;
- аркадные функции вынесены в отдельные менеджеры;
- конфигурация постепенно переводится в самостоятельную систему настроек;
- проект уже ориентирован не только на домашний запуск, но и на работу в аппаратной инсталляции.
Если `README.md` отвечает на вопрос "что умеет игра", то этот документ отвечает на вопрос "как именно это собрано и где это менять".