squad-proto/docs/06-debug-and-tuning.md

151 lines
11 KiB
Markdown
Raw Normal View History

Прототип боевого ядра: отряд, хоррор-слой, ближний бой Тактический отряд с автоматическим огнём в изометрии, собственный софтверный растеризатор (raylib только окно, ввод и финальный блит). Ядро (спек, раздел 6): - LaneClear: проверка линии огня по своим, стенам и дальности; - решатель огневых позиций с гистерезисом и коммитом; - резервирование линий огня и расступание из чужих секторов; - пули-снаряды, дружественный урон физически возможен. Хоррор-слой (render-only): свет и туман войны с памятью карты, светящиеся трассеры, кровь, виньетка, зерно, напряжение. Ближний бой: сектор удара, три фазы, связка из трёх ударов, своя дисциплина «не бить сквозь своего». Проверка: squad_proto.exe --accept прогоняет критерии приёмки раздела 11 плюс блок M7 по ближнему бою — все PASS, FRIENDLY_HITS = 0 за 3 минуты боя. Сборка: cmake -S . -B build && cmake --build build --config Release raylib 6.0 подтягивается через FetchContent. Документация — CLAUDE.md как оглавление, содержание в docs/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
# 06 — Оверлей, метрики, тюнинг
## Оверлей
[src/debug/overlay.cpp](../src/debug/overlay.cpp). Рисуется примитивами raylib **поверх**
уже отмасштабированного кадра. Это единственное исключение из «вся сцена софтверная»,
и оно dev-only: `F1` его убирает.
| Клавиша | Что показывает |
|---|---|
| `F1` | оверлей целиком |
| `F2` | линии огня |
| `F3` | кандидаты последнего прогона решателя |
| `F4` | метрики |
| `F5` | выключить хоррор-слой целиком (свет, туман, пост) |
| `F6` | показать диски обзора |
Всегда (при включённом `F1`): якорь отряда, **два** круга когезии (синий —
стрелков, оранжевый — клинков), слоты строя, подпись над каждым агентом
`индекс, класс (R/M), состояние (A/H), id цели` и у клинка фаза удара
(`wind` / `HIT` / `rec`), выбранная огневая позиция `postPos` кружком.
**Сектор клинка**: кольцо досягаемости и две границы дуги, цвет по фазе —
жёлто-оранжевый на замахе, белый на ударе, серый на восстановлении. В покое цвет
говорит, почему не бьём: жёлтый — свой в дуге, синий — просто ждёт.
**Цвета линий огня (`F2`)**:
| Цвет | Значение |
|---|---|
| зелёный | линия чиста, агент стреляет |
| жёлтый | цель есть, линию перекрыл свой |
| красный | линию перекрыла стена |
**Кандидаты (`F3`)**: точками, цвет по оценке (синий низкая → белый высокая),
отбракованные — тусклым красным, выбранный обведён белым кругом.
Показывается последний прогон решателя, поэтому у агентов, у которых линия есть,
там пусто — решатель для них не запускался, так и задумано.
В ручном режиме дополнительно рисуется вектор прицела, границы конуса ±20°
и обводка назначенной цели.
## Метрики (`F4`)
[src/debug/metrics.h](../src/debug/metrics.h).
| Метрика | Как считается | Смысл |
|---|---|---|
| **FRIENDLY_HITS** | накопительный счётчик попаданий пуль по своим | главная метрика корректности, должна быть 0 |
| **FIRE UPTIME** | доля шагов с валидной линией за окно 10 с; среднее — **только по стрелкам** | насколько отряд «в деле» |
| **MELEE reach** | доля шагов с целью в зоне удара, среднее по клинкам | то же самое для ближнего боя |
| `swings / hits / aborted` | взмахов начато, попаданий лезвием, взмахов оборвано своим в дуге | `aborted` — видимая цена скученности, не баг |
| **SETTLE TIME** | от перехода в `HOLDING` до момента, когда ≥4 агента «в бою» | как быстро расходится веером |
| fps, `sim ms/frame` | время симуляции за кадр | бюджет 16.6 мс |
«В бою» для стрелка — валидная линия, для клинка — выбранная цель. Смешивать
доли времени двух разных занятий в одно среднее бессмысленно, поэтому
`FIRE UPTIME` и `MELEE reach` считаются раздельно: у клинка линии огня не бывает
по определению, и его ноль занижал бы метрику стрельбы.
Реализация `FIRE UPTIME` — кольцевой буфер на 600 сэмплов на агента с
инкрементальными счётчиками, пересчёт O(1) за шаг.
**Журнал попаданий по своим**: каждое попадание пишется в `Metrics::hitLog`
(время, стрелок, жертва, координаты, состояние жертвы). Без него `FRIENDLY_HITS != 0`
нечем объяснить — именно журнал показал, что жертва всегда была `ADVANCING`,
и вывел на настоящую причину (см. [08-decisions.md](08-decisions.md)).
### Как читать цифры
- `FIRE UPTIME` считает «нет живой цели в радиусе» как простой. Поэтому на
переходах между комнатами и после зачистки кучки метрика честно падает — это не
дефект ИИ. Критерий приёмки 11.8 относится к **стоячему бою рядом с мишенями**.
- `SETTLE TIME` по той же причине растёт, если отряд остановился там, где стрелять
не по чему. Сценарий `--accept` меряет его в правильной обстановке.
## Runtime-тюнинг
Все числа живут в [src/tuning.h](../src/tuning.h) в структуре `Tuning`.
Магических констант по коду нет — если нужно новое число, оно добавляется туда.
Часть параметров вынесена в таблицу `TUNABLES` и крутится на ходу:
- `[` / `]` — выбрать параметр, текущее имя и значение видно в оверлее;
- `-` / `=` — изменить с шагом параметра (с зажатием работает автоповтор).
Те же параметры можно задать из командной строки без пересборки:
```bash
squad_proto.exe --headless 300 --set safetyMargin=0.25 --set laneReserve=2.0
```
Список параметров смотри прямо в `TUNABLES` — там имя, поле, шаг и границы.
Самые содержательные для ощущения:
| Параметр | На что влияет |
|---|---|
| `safetyMargin` | запас к «телу в линии»; шире → реже стреляют, но безопаснее |
| `laneReserve` | во сколько раз резерв линии шире порога блокировки; отвечает за «текучесть» |
| `wLaneAvoid` | насколько нагло агент выдавливается из чужого сектора |
| `hysteresis` | насколько лучше должна быть новая позиция, чтобы агент переехал |
| `commitTime` | сколько держится решение; меньше → живее и дёрганее |
| `sDistCost` / `sCohesionCost` | готовность отбегать далеко ради выстрела |
| `postLeash` | жёсткая граница огневой позиции от диска; ниже — отряд плотнее |
| `anchorSpeed` / `anchorLeash` | отзывчивость диска и насколько он может оторваться от отряда |
Ближний бой (подробности — [11-melee.md](11-melee.md)):
| Параметр | На что влияет |
|---|---|
| `meleeReach` / `meleeArc` | размер сектора удара; шире → чаще задевает нескольких, но и своих |
| `meleeWindup` / `meleeStrike` / `meleeRecover` | ритм удара: читаемость замаха против скорострельности |
| `meleeInterval` / `meleeDamage` | DPS клинка |
| `meleeLunge` / `meleeKnock` | вес удара: рывок бойца и отдача мишени |
| `meleeCohesion` | СВОЙ, больший круг клинка вокруг диска |
| `meleeAllyMargin` / `meleePredict` | строгость дисциплины сектора; выше → меньше оборванных взмахов и меньше взмахов вообще |
| `meleeReserve` | насколько широко клинок отжимает своих со своей дуги |
| `meleeShake` | отдача камеры на попадание лезвием |
Хоррор-часть (подробности — [10-atmosphere.md](10-atmosphere.md)):
| Параметр | На что влияет |
|---|---|
| `sightRadius` / `sightCone` | как далеко и как широко видит отряд |
| `sightPersonal` | круговой ореол вокруг агента, спасает от клаустрофобии |
| `fogMemory` | насколько тускло помнится уже увиденное |
| `fogReveal` | при каком свете показывается тело мишени |
| `lightGamma` / `lightGain` | форма затухания света; **перепекают таблицу рампы** |
| `flashPower` | насколько вспышка выстрела освещает окружение |
| `fxIntensity` / `tracerLength` | яркость и длина трассеров |
| `grainTense` / `vigDepthTense` | зерно и виньетка на пике напряжения |
| `dreadRadius` | с какой дистанции мишень начинает пугать |
| `shakePerShot` | отдача камеры |
`F5` — аварийный тумблер: мгновенно возвращает прежний кадр без света и тумана.
Полезен, когда непонятно, баг это или просто темно.
## Dev-тумблеры ядра
`--no-solver` и `--no-laneavoid` выключают решатель и расступание. Это A/B-проверка
ценности ядра, цифры за 60 с на одном маршруте:
| Конфигурация | FRIENDLY_HITS | FIRE UPTIME |
|---|---|---|
| ядро выключено | 1 | 13.6 % |
| ядро включено | 0 | 88 % (на стоянке у мишеней) |
`sim`/`render` в оверлее показывают время шага симуляции и время кадра рендера
раздельно: 2 мкс против 1.43.5 мс при бюджете 16.6 мс.