squad-proto/docs/06-debug-and-tuning.md
z.kirill 7880f275bd Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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 13:00:49 +03:00

11 KiB
Raw Blame History

06 — Оверлей, метрики, тюнинг

Оверлей

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.

Метрика Как считается Смысл
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).

Как читать цифры

  • FIRE UPTIME считает «нет живой цели в радиусе» как простой. Поэтому на переходах между комнатами и после зачистки кучки метрика честно падает — это не дефект ИИ. Критерий приёмки 11.8 относится к стоячему бою рядом с мишенями.
  • SETTLE TIME по той же причине растёт, если отряд остановился там, где стрелять не по чему. Сценарий --accept меряет его в правильной обстановке.

Runtime-тюнинг

Все числа живут в src/tuning.h в структуре Tuning. Магических констант по коду нет — если нужно новое число, оно добавляется туда.

Часть параметров вынесена в таблицу TUNABLES и крутится на ходу:

  • [ / ] — выбрать параметр, текущее имя и значение видно в оверлее;
  • - / = — изменить с шагом параметра (с зажатием работает автоповтор).

Те же параметры можно задать из командной строки без пересборки:

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):

Параметр На что влияет
meleeReach / meleeArc размер сектора удара; шире → чаще задевает нескольких, но и своих
meleeWindup / meleeStrike / meleeRecover ритм удара: читаемость замаха против скорострельности
meleeInterval / meleeDamage DPS клинка
meleeLunge / meleeKnock вес удара: рывок бойца и отдача мишени
meleeCohesion СВОЙ, больший круг клинка вокруг диска
meleeAllyMargin / meleePredict строгость дисциплины сектора; выше → меньше оборванных взмахов и меньше взмахов вообще
meleeReserve насколько широко клинок отжимает своих со своей дуги
meleeShake отдача камеры на попадание лезвием

Хоррор-часть (подробности — 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 мс.