Добавить управление световыми спецэффектами и скорректировать позиции ф初
This commit is contained in:
parent
70c4dbd081
commit
8b22a1e69a
@ -31,7 +31,7 @@ Transform:
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 15.84, y: -5.86, z: 0}
|
||||
m_LocalScale: {x: 9, y: 9, z: 1}
|
||||
m_LocalScale: {x: 7, y: 7, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7212224704880402101}
|
||||
|
||||
@ -230,6 +230,9 @@ namespace CharacterSystem
|
||||
Character newCharacter = GetLoadedCharacter(newIndex);
|
||||
Sequence sequence = DOTween.Sequence();
|
||||
|
||||
if (newIndex <= 4)
|
||||
LightSystem.SpecialEffect(43 + newIndex, 2);
|
||||
|
||||
if (_loadedCharacters.TryGetValue(oldIndex, out LoadedCharacter oldCharacter))
|
||||
{
|
||||
sequence.Append(oldCharacter.Character.visualModule.GetHideSequence());
|
||||
|
||||
@ -71,10 +71,17 @@ public class GameManager : MonoBehaviour
|
||||
|
||||
private const int PvpWinningScore = 5;
|
||||
private const int VictoryNameLength = 10;
|
||||
private const int FirstDebugSpecialEffect = 40;
|
||||
private const int LastDebugSpecialEffect = 49;
|
||||
private const int PveStartSpecialEffect = 48;
|
||||
private const int PvpStartSpecialEffect = 49;
|
||||
private const int SpecialEffectDurationSeconds = 2;
|
||||
private const float SpecialEffectDebugIntervalSeconds = 6f;
|
||||
private bool _isLeavingGameplay;
|
||||
private int _paidEntryAmount;
|
||||
private bool _canRefundEntry;
|
||||
private bool _returnToMenuAfterPrize;
|
||||
private Coroutine _specialEffectsDebugCoroutine;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@ -107,6 +114,10 @@ public class GameManager : MonoBehaviour
|
||||
_canRefundEntry = true;
|
||||
SetMode(mode);
|
||||
_isLeavingGameplay = false;
|
||||
LightSystem.EnableButtonLighting();
|
||||
LightSystem.SpecialEffect(
|
||||
mode == GameMode.pve ? PveStartSpecialEffect : PvpStartSpecialEffect,
|
||||
SpecialEffectDurationSeconds);
|
||||
|
||||
if (mode == GameMode.pvp)
|
||||
{
|
||||
@ -153,12 +164,21 @@ public class GameManager : MonoBehaviour
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.O))
|
||||
ToggleSpecialEffectsDebug();
|
||||
|
||||
if (increaseVsBotTimer)
|
||||
vsBotThinkTime += Time.deltaTime;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_specialEffectsDebugCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_specialEffectsDebugCoroutine);
|
||||
_specialEffectsDebugCoroutine = null;
|
||||
}
|
||||
|
||||
if (timerManager != null && turnManager != null)
|
||||
timerManager.TimerEnded -= turnManager.HandleTimerEnded;
|
||||
|
||||
@ -175,6 +195,7 @@ public class GameManager : MonoBehaviour
|
||||
|
||||
RefundEntryIfAvailable();
|
||||
_isLeavingGameplay = true;
|
||||
LightSystem.DisableButtonLighting();
|
||||
CancelPrizeFlow();
|
||||
VitrinaView.Instance?.Deactivate();
|
||||
ControlsPerformer.Instance.SetControlable(null);
|
||||
@ -409,11 +430,42 @@ public class GameManager : MonoBehaviour
|
||||
|
||||
private void StartRound()
|
||||
{
|
||||
LightSystem.SpecialEffect(41, SpecialEffectDurationSeconds);
|
||||
turnManager.StartGame(gameMode);
|
||||
PrepareRound(Team.Player1);
|
||||
OnRoundStarted?.Invoke();
|
||||
}
|
||||
|
||||
private void ToggleSpecialEffectsDebug()
|
||||
{
|
||||
if (_specialEffectsDebugCoroutine == null)
|
||||
{
|
||||
_specialEffectsDebugCoroutine = StartCoroutine(SpecialEffectsDebugCoroutine());
|
||||
Debug.Log("Отладочный цикл спецэффектов включён.");
|
||||
return;
|
||||
}
|
||||
|
||||
StopCoroutine(_specialEffectsDebugCoroutine);
|
||||
_specialEffectsDebugCoroutine = null;
|
||||
Debug.Log("Отладочный цикл спецэффектов выключен.");
|
||||
}
|
||||
|
||||
private IEnumerator SpecialEffectsDebugCoroutine()
|
||||
{
|
||||
WaitForSecondsRealtime interval = new(SpecialEffectDebugIntervalSeconds);
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (int effectNumber = FirstDebugSpecialEffect;
|
||||
effectNumber <= LastDebugSpecialEffect;
|
||||
effectNumber++)
|
||||
{
|
||||
LightSystem.SpecialEffect(effectNumber, SpecialEffectDurationSeconds);
|
||||
yield return interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Prize> GetAvailablePrizes(IEnumerable<Prize> prizes)
|
||||
{
|
||||
List<Prize> result = new();
|
||||
|
||||
@ -5,6 +5,39 @@ using UnityEngine;
|
||||
public static class LightSystem
|
||||
{
|
||||
private const string LIGHT_COMMAND_PREFIX = "80";
|
||||
private const string ButtonLightingOnCommand = "2171";
|
||||
private const string ButtonLightingOffCommand = "2170";
|
||||
private const string SpecialEffectCommandPrefix = "8560";
|
||||
private const int MinimumSpecialEffect = 40;
|
||||
private const int MaximumSpecialEffect = 49;
|
||||
|
||||
public static void EnableButtonLighting()
|
||||
{
|
||||
ComWrapper.Send(ButtonLightingOnCommand);
|
||||
}
|
||||
|
||||
public static void DisableButtonLighting()
|
||||
{
|
||||
ComWrapper.Send(ButtonLightingOffCommand);
|
||||
}
|
||||
|
||||
public static void SpecialEffect(int effectNumber, int durationSeconds = 2)
|
||||
{
|
||||
if (effectNumber < MinimumSpecialEffect || effectNumber > MaximumSpecialEffect)
|
||||
{
|
||||
Debug.LogError($"Недопустимый номер спецэффекта: {effectNumber}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (durationSeconds < 0 || durationSeconds > 9)
|
||||
{
|
||||
Debug.LogError($"Недопустимая длительность спецэффекта: {durationSeconds}.");
|
||||
return;
|
||||
}
|
||||
|
||||
ComWrapper.Send($"{SpecialEffectCommandPrefix}{effectNumber:D2}{durationSeconds}");
|
||||
}
|
||||
|
||||
public static void Idle()
|
||||
{
|
||||
if (Random.Range(0, 2) == 0)
|
||||
@ -153,4 +186,4 @@ public enum LightMode
|
||||
Dog,
|
||||
Sam,
|
||||
Mistress
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,8 +44,6 @@ public class Board : MonoBehaviour
|
||||
public void ApplyPvpColors()
|
||||
=> SetColorsImmediately(_pvpInnerColor, _pvpOutlineColor, _pvpCellsColor);
|
||||
|
||||
public Vector2 GetMiddleStickedPosition() => new Vector2(GetXPosition(Columns/2), config.stickedYOffset);
|
||||
|
||||
public Vector2 GetStickedPosition(int column) => new Vector2(GetXPosition(column), config.topLeftCell.position.y + config.stickedYOffset);
|
||||
|
||||
public Vector2 GetTargetPosition(int column) => new Vector2(GetXPosition(column), GetYPosition(column));
|
||||
|
||||
@ -361,8 +361,7 @@ public class Bot : BasePlayer, IGamePlayer
|
||||
private void SetInitialChipPosition()
|
||||
{
|
||||
// Начальная позиция – сверху с оффсетом
|
||||
stickedTargetPosition = board.GetMiddleStickedPosition();
|
||||
stickedTargetPosition.x = board.GetStickedPosition(SelectedColumn).x;
|
||||
stickedTargetPosition = board.GetStickedPosition(SelectedColumn);
|
||||
currentChip.transform.position = stickedTargetPosition + Vector2.up * 5f;
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +79,7 @@ public class EndGameHandler : MonoBehaviour
|
||||
{
|
||||
if (winner == Team.Player1)
|
||||
{
|
||||
LightSystem.SpecialEffect(42, 2);
|
||||
MachineCountersService.RecordBotResult(true);
|
||||
EventBus.TriggerEvent(GameEvent.PlayerWin);
|
||||
ShowResultAfterDelay(ShowPveWinResult);
|
||||
|
||||
@ -221,8 +221,7 @@ public class Player : BasePlayer, IGamePlayer
|
||||
|
||||
private void SetInitialChipPosition()
|
||||
{
|
||||
stickedTargetPosition = board.GetMiddleStickedPosition();
|
||||
stickedTargetPosition.x = board.GetStickedPosition(SelectedColumn).x;
|
||||
stickedTargetPosition = board.GetStickedPosition(SelectedColumn);
|
||||
currentChip.transform.position = stickedTargetPosition + Vector2.up * chipYOffset;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
/// - Игрок 1: WASD + Space
|
||||
/// - Игрок 2: Стрелки + RightControl
|
||||
/// - Служебные: 1, 2, 3, F5
|
||||
/// - Датчики хопера: O, P
|
||||
/// - Датчики хопера: H, P (O переключает отладочный цикл спецэффектов)
|
||||
/// </remarks>
|
||||
public class KeyboardInput : InputSource
|
||||
{
|
||||
@ -132,7 +132,7 @@ public class KeyboardInput : InputSource
|
||||
|
||||
InSignal.extra => KeyCode.Alpha3,
|
||||
|
||||
InSignal.hasPrize => KeyCode.O,
|
||||
InSignal.hasPrize => KeyCode.H,
|
||||
InSignal.prizeGiven => KeyCode.P,
|
||||
|
||||
_ => KeyCode.None,
|
||||
@ -166,7 +166,7 @@ public class KeyboardInput : InputSource
|
||||
|
||||
13 => KeyCode.Alpha3, // extra
|
||||
|
||||
14 => KeyCode.O, // hasPrize
|
||||
14 => KeyCode.H, // hasPrize
|
||||
15 => KeyCode.P, // prizeGiven
|
||||
|
||||
_ => KeyCode.None, // невалидный индекс
|
||||
|
||||
@ -149,6 +149,7 @@ namespace PrizeSystem
|
||||
if (success)
|
||||
{
|
||||
PrizeStockService.RecordSuccessfulCountableIssue();
|
||||
LightSystem.SpecialEffect(40, 2);
|
||||
|
||||
//Счетчики
|
||||
IncreasePrizeCounters(Type);
|
||||
|
||||
File diff suppressed because one or more lines are too long
302
CONTEXT_DUMP_2026-09-16.md
Normal file
302
CONTEXT_DUMP_2026-09-16.md
Normal file
@ -0,0 +1,302 @@
|
||||
# Дамп контекста проекта chip-gameV3
|
||||
|
||||
Дата формирования: 2026-09-16
|
||||
Часовой пояс: Europe/Moscow
|
||||
Назначение: восстановление рабочего контекста в новом чате Codex.
|
||||
|
||||
## 1. Быстрый старт для нового чата
|
||||
|
||||
Проект находится в:
|
||||
|
||||
`C:\Users\bondars\UnityProjects\chip-gameV3 WORK`
|
||||
|
||||
Перед любой задачей:
|
||||
|
||||
1. Полностью прочитать `AGENTS.md` в корне проекта.
|
||||
2. Запросить релевантный контекст через MCP-сервер `agentmemory`.
|
||||
3. Проверить `git status --short --branch`, текущую ветку и чужие незакоммиченные изменения.
|
||||
4. Если задача касается Unity, обязательно сначала прочитать состояние и релевантные ресурсы через `unityMCP`.
|
||||
5. После Unity-изменений проверить через `unityMCP` компиляцию, Console, тесты и визуальный результат, когда он применим.
|
||||
6. Не считать Unity-задачу завершённой без этой проверки. Если `unityMCP` недоступен — прямо сообщить пользователю.
|
||||
|
||||
Основной язык общения и сообщений коммитов — русский.
|
||||
|
||||
## 2. Состояние Git на момент дампа
|
||||
|
||||
- Активная ветка: `master`.
|
||||
- HEAD: `70c4dbd081fc602a6fa54ca66f92347667d85442`.
|
||||
- Коммит: `Унифицировать работу с контроллером и улучшить загрузку персонажей`.
|
||||
- `master`, `chip-gameV3-remote/master`, `feature/redesign-sega` и `chip-gameV3-remote/feature/redesign-sega` указывают на `70c4dbd`.
|
||||
- Перед созданием этого файла рабочее дерево было чистым.
|
||||
- Remote: `chip-gameV3-remote` → `http://172.11.0.14:3000/Robotail/chip-gameV3`.
|
||||
- Старое состояние `master` пользователю не требуется; текущая версия уже опубликована в удалённый `master` fast-forward push.
|
||||
- Сам этот дамп создан после синхронизации и на момент создания ещё не закоммичен.
|
||||
|
||||
Последние значимые коммиты:
|
||||
|
||||
```text
|
||||
70c4dbd Унифицировать работу с контроллером и улучшить загрузку персонажей
|
||||
ca42cf5 Усильте авторизацию плат и восстановление COM-соединения
|
||||
ded80f0 Обновить игровые системы и интерфейс
|
||||
826ff57 Доработать игровые режимы, призы и настройки
|
||||
16847fe Обновить игровые системы и удалить устаревший код
|
||||
a656816 Удалить устаревший код и упростить структуру проекта
|
||||
f86eed2 Улучшить PvP-аватары, цвета поля и настройку таймеров
|
||||
16ab9e4 Добавить оплату повторов в PvE и PvP
|
||||
ebc47a8 Доработать PvP и ранний выход из игры
|
||||
```
|
||||
|
||||
## 3. Unity и зависимости
|
||||
|
||||
- Unity: `6000.4.8f1` (`f8b72d3d7343`).
|
||||
- Основная сцена: `Assets/Scenes/GameScene3D.unity`.
|
||||
- Render Pipeline: URP `17.4.0`.
|
||||
- Addressables: `2.9.1`.
|
||||
- uGUI: `2.0.0`.
|
||||
- В проекте используются DOTween, UniTask, Newtonsoft.Json, SQLite, QRCoder и NuGetForUnity.
|
||||
- MCP for Unity подключён пакетом `com.coplaydev.unity-mcp`.
|
||||
|
||||
Последняя подтверждённая Unity-проверка перед дампом:
|
||||
|
||||
- компиляция завершалась без ошибок;
|
||||
- Unity Console: 0 ошибок;
|
||||
- короткий Play Mode smoke test: 0 ошибок;
|
||||
- EditMode: 1/1 существующий тест успешно;
|
||||
- тест независимого чтения COM-осей: `horizontal=-1`, затем `vertical=1`, `success=True`.
|
||||
|
||||
Важно: непосредственно при создании дампа `unityMCP` был недоступен на `http://127.0.0.1:8080/mcp`, поэтому текущее состояние Editor повторно не считано. Это нужно перепроверить в новом чате после запуска/подключения сервера.
|
||||
|
||||
## 4. Обязательные соглашения проекта
|
||||
|
||||
### DOTween
|
||||
|
||||
Новые `Tween` и `Sequence` в этом проекте автоматически не запускаются. После создания обязательно вызывать `PlayForward()` или `Play()`. Если метод только возвращает анимацию, запуск делает вызывающий код.
|
||||
|
||||
### Структура C#-скриптов
|
||||
|
||||
Строгий порядок:
|
||||
|
||||
1. `public event`;
|
||||
2. `public` поля;
|
||||
3. `[SerializeField] private` поля;
|
||||
4. `private` поля;
|
||||
5. свойства;
|
||||
6. Unity-методы в стандартном порядке;
|
||||
7. публичные методы;
|
||||
8. приватные методы.
|
||||
|
||||
Именование:
|
||||
|
||||
- публичные поля — `camelCase`;
|
||||
- публичные события — `PascalCase`;
|
||||
- сериализованные приватные и обычные приватные поля — `_camelCase`;
|
||||
- свойства — `PascalCase`;
|
||||
- параметры — `camelCase`.
|
||||
|
||||
Не добавлять `/// <summary>`. Комментарии использовать только для сложной или неочевидной логики.
|
||||
|
||||
### Работа с файлами и Git
|
||||
|
||||
- Сохранять чужие и несвязанные изменения.
|
||||
- Для ручных правок файлов использовать `apply_patch`.
|
||||
- Не применять `git reset --hard` или `git checkout --` без прямого запроса.
|
||||
- Сообщения коммитов писать только по-русски.
|
||||
|
||||
## 5. Основная архитектура
|
||||
|
||||
Ключевые каталоги `Assets/Scripts`:
|
||||
|
||||
- `Core` — bootstrap и порядок инициализации;
|
||||
- `Common` — `GameManager` и общие сервисы;
|
||||
- `Gameplay` — поле, игроки, боты, ходы, таймеры, level bar, результаты;
|
||||
- `Characters` — PvE-персонажи, определения и Addressables-загрузка;
|
||||
- `Input` — единый ввод, COM, клавиатура, конфиг пинов;
|
||||
- `Payment` — баланс, цены входа и реванша;
|
||||
- `PrizeSystem` и `Vitrina` — призы, TakeOrRisk, QR, витрина, остатки;
|
||||
- `UI` — меню, переходы, модальные панели;
|
||||
- `Settings` — настройки, пароль, тесты, счётчики, остатки;
|
||||
- `Statistics` и `Leaderboard` — статистика и рейтинг;
|
||||
- `Audio` — музыка и SFX;
|
||||
- `Media` — видеофоны и связанная логика.
|
||||
|
||||
Ключевые точки входа:
|
||||
|
||||
- `Assets/Scripts/Core/Bootstrap.cs`
|
||||
- `Assets/Scripts/Common/GameManager.cs`
|
||||
- `Assets/Scripts/Gameplay/TurnManager.cs`
|
||||
- `Assets/Scripts/Gameplay/Player.cs`
|
||||
- `Assets/Scripts/Characters/CharactersController.cs`
|
||||
- `Assets/Scripts/Characters/CharacterDefinition.cs`
|
||||
- `Assets/Scripts/Input/In.cs`
|
||||
- `Assets/Scripts/Input/COMinput.cs`
|
||||
- `Assets/Scripts/Input/ComInputConfiguration.cs`
|
||||
- `Assets/Scripts/Input/ComSystem/ComWrapper.cs`
|
||||
- `Assets/Scripts/Input/ComSystem/ComPortScanner.cs`
|
||||
- `Assets/Scripts/Input/ComSystem/BoardAuthentication.cs`
|
||||
- `Assets/Scripts/Payment/System/PaymentSystem.cs`
|
||||
|
||||
## 6. Актуальная система ввода и COM
|
||||
|
||||
### Физический ввод
|
||||
|
||||
- Контроллер сам присылает строки `INPUT<decimal>` при изменениях входа. Опрос ввода командой не нужен.
|
||||
- Десятичная маска преобразуется в строго 26-битную строку с ведущими нулями и разворотом порядка битов.
|
||||
- Некорректные, нечисловые и выходящие за 26 бит пакеты отклоняются.
|
||||
- `COMinput` дополнительно защищён от пустых и коротких пакетов; прежний `Substring` больше не вызывает `ArgumentOutOfRangeException`.
|
||||
- Логические входы active-low: символ `0` означает нажатое состояние.
|
||||
|
||||
Конфиг управления загружается из:
|
||||
|
||||
`Assets/StreamingAssets/ComInputConfig.json`
|
||||
|
||||
Актуальная конфигурация в репозитории:
|
||||
|
||||
```json
|
||||
{
|
||||
"leftJoystick": {
|
||||
"up": 0,
|
||||
"right": 2,
|
||||
"down": 3,
|
||||
"left": 1,
|
||||
"action": 8
|
||||
},
|
||||
"rightJoystick": {
|
||||
"up": 4,
|
||||
"right": 7,
|
||||
"down": 6,
|
||||
"left": 5,
|
||||
"action": 9
|
||||
},
|
||||
"serviceButton": 10
|
||||
}
|
||||
```
|
||||
|
||||
Не заменять эту схему на первоначально предполагавшиеся последовательные индексы без повторной проверки реального контроллера: текущая схема получена после диагностики фактических сообщений платы.
|
||||
|
||||
### Независимое чтение осей
|
||||
|
||||
`In.GetAxis(..., Discrete)` исторически потреблял события обеих осей, даже если вызывающий код использовал только `.x` или `.y`. Это ломало:
|
||||
|
||||
- перемещение первой фишки до первого хода, потому что `BackToMenuController` съедал горизонталь;
|
||||
- вертикальную прокрутку левой панели меню, потому что `Menu` съедал вертикаль.
|
||||
|
||||
Исправление:
|
||||
|
||||
- добавлены `In.GetHorizontalAxis(...)` и `In.GetVerticalAxis(...)`;
|
||||
- `BackToMenuController`, `Player`, `Menu` и `MenuPanelsScroll` читают только нужную ось;
|
||||
- при `JoystickSide.Both` оба физических события опрашиваются без short-circuit, чтобы второе одновременное нажатие не оставалось в буфере.
|
||||
|
||||
### Режимы управления
|
||||
|
||||
- PvE: первый игрок управляется обоими джойстиками (`Player.ControlSide = JoystickSide.Both`).
|
||||
- PvP: игрок 1 — левый джойстик, игрок 2 — правый.
|
||||
|
||||
### Инициализация и авторизация платы
|
||||
|
||||
- При подключении сначала отправляется команда перезагрузки `00`.
|
||||
- Нужно дождаться сообщения с префиксом `ver_`; оно означает готовность контроллера после перезагрузки.
|
||||
- Затем компьютер отправляет `07` + 32 HEX-символа.
|
||||
- Контроллер отвечает `CHAL` + 32 HEX-символа.
|
||||
- Компьютер отвечает `08` + AES-256 результат, используя существующий обфусцированный ключ в `BoardAuthentication.cs`.
|
||||
- `RAES`, `AUTH_PC_OK` и промежуточные статусы не означают завершение авторизации.
|
||||
- Авторизация завершена только после отдельной точной строки `READY!`.
|
||||
- При длительном отсутствии сообщений выполняется ping, затем переподключение.
|
||||
- При запуске приложения отправляется `856001` для включения подсветки.
|
||||
- При выходе отправляется `856000` для выключения подсветки.
|
||||
- Используется один тип периферийного контроллера: `DeviceType.Controller`.
|
||||
- `PrizeShowcase` остаётся отдельным устройством витрины, а не вторым типом контроллера.
|
||||
|
||||
## 7. Персонажи и Addressables
|
||||
|
||||
- Тяжёлые prefab-персонажи и их видеофоны вынесены в Addressables.
|
||||
- Лёгкие данные хранятся в `CharacterDefinition`/ScriptableObject и доступны без загрузки prefab.
|
||||
- В меню предварительно загружается первый PvE-персонаж — Куб — и его видеофон.
|
||||
- Для текущего PvE-персонажа поддерживается окно соседей, доступных для следующего перехода.
|
||||
- `CharactersController.ShowCharacter` теперь ожидает только целевого персонажа и его видеофон через `PrepareTargetAsync`.
|
||||
- Остаток окна подгружается асинхронно через `PreloadWindowRemainderAsync` и не задерживает начало раунда или переход, если нужный персонаж уже готов.
|
||||
- При смене окна ненужные экземпляры персонажей и video handles освобождаются.
|
||||
- При переходе в PvP PvE-окно приостанавливается; PvP-персонажи 6 и 7 обслуживаются `DualAvatarController`.
|
||||
|
||||
## 8. Игровые режимы и оплата
|
||||
|
||||
- Вход в PvE и PvP требует успешного списания через `PaymentSystem`.
|
||||
- Неуспешная оплата воспроизводит звук и показывает мигающий `NotEnough` в `CoinCounter`.
|
||||
- Реванши PvE и PvP имеют отдельные цены и также выполняют попытку списания.
|
||||
- Ранний выход до первого хода доступен только после подготовки первой фишки.
|
||||
- При раннем выходе первая подготовленная фишка возвращается в пул.
|
||||
- Стоимость входа возвращается на баланс при раннем выходе из PvE/PvP; возврат не попадает в счётчики выручки.
|
||||
- Игра считается запущенной для статистики только после первого фактического хода.
|
||||
|
||||
PvE:
|
||||
|
||||
- учитываются только время размышлений игрока и его ходы;
|
||||
- паузы и время бота не включаются;
|
||||
- результат показывается с задержкой 3 секунды;
|
||||
- `GameplayLevelBar` отражает пройденных/текущего бота, наличие приза и таймер;
|
||||
- на одного бота выделяется 2 минуты, истечение времени считается поражением;
|
||||
- после победы над последним ботом показывается `Victory/NameInput` с русскими буквами и цифрами.
|
||||
|
||||
PvP:
|
||||
|
||||
- раунд показывает `Win PvP`, победителя и кнопки меню/повтора;
|
||||
- матч идёт до 5 очков;
|
||||
- после полной победы повтор сбрасывает очки;
|
||||
- используются персонажи 6 слева и 7 справа;
|
||||
- при входе: `Sleep` → перемещение в кадр → `Appear`;
|
||||
- победителю отправляется `Win`, `Lose` отсутствует;
|
||||
- при выходе персонажи уводятся на скрытые позиции, Animator отключается;
|
||||
- поле окрашивается в три настраиваемых цвета;
|
||||
- на ход даётся 30 секунд; по таймауту текущая фишка автоматически сбрасывается;
|
||||
- подписи таймеров упрощены до `Игрок 1` и `Игрок 2`.
|
||||
|
||||
## 9. Призы, настройки и статистика
|
||||
|
||||
Реализованный функциональный контекст:
|
||||
|
||||
- несколько призов на одного персонажа;
|
||||
- таблица призов в меню использует `IconSmall` визуального модуля персонажа;
|
||||
- модальное окно призов открывается действием на левой панели и закрывается повторным действием или через 20 секунд бездействия;
|
||||
- модальное окно рейтинга показывает последние 100 записей, последнюю запись внизу и ускоренную прокрутку обоими джойстиками;
|
||||
- в меню показывается топ-3 рейтинга;
|
||||
- `TakeOrRisk`: взять приз и выйти либо пропустить и продолжить к более ценному призу;
|
||||
- QR-приз показывает QR-панель с таймаутом 5 минут;
|
||||
- витринный приз сверяет физически доступные ячейки, предлагает выбор и ждёт подтверждение выдачи;
|
||||
- Countable-приз уменьшает остаток только после успешной выдачи; QR и Vitrina не уменьшают этот счётчик;
|
||||
- контроль остатков содержит включатель, добавление, установку количества и ускоренное перелистывание;
|
||||
- надпись об окончании маленьких призов показывается только при включённом контроле и нулевом остатке;
|
||||
- реализованы экранный RGB-тест, тест управления, тест звука;
|
||||
- пароль настроек — комбинация из 6 движений джойстика, хранится отдельным файлом в `persistentDataPath`;
|
||||
- панель счётчиков содержит сбрасываемые и несбрасываемые показатели игр, выручки, призов, времени и результатов по каждому боту/сложности;
|
||||
- статистика побед/поражений ведётся по каждому из 5 ботов и каждой сложности.
|
||||
|
||||
## 10. Звук и визуальные эффекты
|
||||
|
||||
- `SoundManager` использует два `AudioSource`: музыка и SFX.
|
||||
- SFX проигрываются через `PlayOneShot` с передачей `AudioClip` вызывающей стороной.
|
||||
- Лишний пул дочерних `AudioSource` удалён.
|
||||
- Очистка поля проигрывает звук только если поле действительно было непустым.
|
||||
- `WinDots` на победных фишках использует синхронную покадровую анимацию `blik` со скоростью 40 FPS.
|
||||
- Анимированный объект `WinDots` не перемещается по диагонали: локальная позиция `(0, 0)`, scale `1`.
|
||||
- Для Robowolf перед `Appear` должен вызываться триггер `Sleep` как сброс Animator.
|
||||
|
||||
## 11. Известные условия и проверки для следующей задачи
|
||||
|
||||
- Реальный COM-контроллер периодически подключался к `COM3`, но в последней проверке ввода был отключён. Перед аппаратной задачей сначала проверить доступность порта и не считать программную симуляцию полной аппаратной проверкой.
|
||||
- При работе с COM не возвращать активный polling ввода: плата сама присылает `INPUT`.
|
||||
- При работе с Addressables не заставлять старт раунда ждать всё окно соседей.
|
||||
- При работе с дискретным вводом не использовать чтение полной оси там, где нужна только `.x` или `.y`, если рядом есть второй потребитель ввода.
|
||||
- Любые новые DOTween-анимации запускать явно.
|
||||
- Не менять `Assets/StreamingAssets/ComInputConfig.json` без фактической проверки сигналов платы.
|
||||
- Перед следующим изменением повторно проверить `git status`: сам файл дампа делает рабочее дерево изменённым, пока не будет закоммичен или удалён пользователем.
|
||||
|
||||
## 12. Что считать источником истины
|
||||
|
||||
При расхождении этого дампа с проектом приоритет такой:
|
||||
|
||||
1. актуальные файлы и сериализованные данные текущего `master`;
|
||||
2. состояние Unity Editor, прочитанное через `unityMCP`;
|
||||
3. `AGENTS.md`;
|
||||
4. свежий контекст из `agentmemory`;
|
||||
5. этот дамп.
|
||||
|
||||
Этот файл описывает рабочий контекст, но не заменяет чтение кода и проверку сцены перед изменениями.
|
||||
Loading…
x
Reference in New Issue
Block a user