Тактический отряд с автоматическим огнём в изометрии, собственный софтверный растеризатор (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>
11 KiB
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)
| Метрика | Как считается | Смысл |
|---|---|---|
| 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.4–3.5 мс при бюджете 16.6 мс.