[bug] Сквозной пролёт ракет и дронов: непрерывная коллизия с миром вместо отсутствующей/высотной проверки #42
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
🤖 Filed by Codex, AI-агент Владимира (chexit).
Проверено на
f443f0a94a71eb2d446792f1829146f9563aaf5d. P1 по запросу автора проекта: ракеты и дроны проходят через горы/землю; по наблюдению автора столкновение становится заметно в основном после сбития. Это ломает базовую механику боя: укрытия, траектории, место подрыва и результат перехвата.Связано с закрытой #34, но не дублирует её: там исправлялась устаревшая высота назначенной цели. Здесь требуется столкновение на всём пути полёта, независимо от выбранной цели.
Что подтверждено кодом
Missile(включая P1-SUN)step_missilesполучает толькоOutCruise/Diveground.at(d.pos)до движения; после движения сравнивается новая Y со старой высотойbelow+ условия назначения/времени жизниFallingbelow, гравитация и торможениеFx::DebrisИсточники в моде:
sim/mod.rs:Ground::at(222–239),step(1363–1377),step_drones(1388–1558),step_missiles(2064–2110),step_fx(2189+).lib.rs: чтение предыдущих ответов высот →sim.step→ отправка новых запросов, 1062–1070.sight.rs: радар использует приближённый горизонт по heightmap; это не коллизия летящего объекта.update_sightтакже запрашивает конечные колонки дронов, но не проверяет пройденный отрезок.Модели из
render.set-models/set-models-by-idсами по себе не становятся физическими сущностями мира. Добавление/смена модели не решит проблему.Почему «проверить новую точку» недостаточно
Симуляция идёт 20 Гц. Например, Циркон в Dive проходит
5.5 × 1.35 = 7.425блока за тик, перехватчик 77Н6 — до 9 блоков/тик. Баллистическая скорость дополнительно зависит от вертикальной составляющей. Стену толщиной один блок можно целиком пересечь между двумя свободными конечными точками. Нужно искать первое столкновение по отрезку перемещения, а не только занятость точки после шага.Ограничения данных о мире
В патче 1
render.query-groundотвечает черезGame::rain_height, то есть поMOTION_BLOCKINGheightmap. Это одна верхняя высота на XZ, включая жидкости, не список занятых блоков и не collision shapes.World::motion_blockingна pinned upstreamb6c936fотсутствующий загруженный чанк даётMIN_Y(-64);Ground::atпревращаетy <= -64вNone.-64кэшируется с обычным timestamp; обновление запрашивается лишь после 200 тиков, и старое значение возвращается до ответа.world.chunks, а не по картинке/LOD. Для исходного пользовательского случая это обоснованная гипотеза, но состояние его мира/чанков в этой проверке не снималось.ground.forgetвызывается при собственных разрушительных взрывах, но не решает все изменения мира и асинхронность применения TNT.Текущий
game.raycast(distance)тоже нельзя просто вызвать от ракеты: API привязан к взгляду игрока, а не к произвольным(from,to). Произвольного sweep/block-collision запроса в просмотренном WIT нет.Исполненные воспроизведения
Проверки выполнены нативным Rust-стендом на копии исходного
sim/*.rs, без изменения алгоритмов движения. Добавлены только fixtures/вызовы приватных методов; UI-форматтерpanel::distanceзаменён заглушкой форматирования, не участвующей в физике. Зависимость — тот жеglam 0.30. Игра/GPU не запускались. Эти проверки характеризуют существующий дефект, а не показывают исправление.Для стен высоты всех колонок заранее заполнены: пропуск нельзя объяснить задержкой host API.
Проверка PAC-3 вызывает именно
step_missilesс неподвижной живой целью; координаты стен/высоты заданы отдельно. Отсутствие параметра мира у метода и есть проверяемая причина, по которой даже известная преграда не может остановить ракету.Отсюда уточнение наблюдения «только после сбития»: у обычных дронов проверка высоты тоже есть. Падающий объект тормозит/опускается, чаще остаётся в известных колонках, поэтому грубая проверка заметнее. При этом и
Fallingвсё ещё способен пропустить стену между точками.Минимальный fixture для переноса в
sim/regress.rsСейчас этот тест падает, поскольку ракета остаётся жива:
После появления 3D collision provider заменить heightmap fixture на реальные занятые voxel/AABB; дополнительно проверить точку первого контакта около
x=3, а не взрыв на конечномx=7.425.Предлагаемое направление реализации
1. Общий запрос столкновения для всех движущихся боевых объектов
Отделить наведение от фиксации перемещения: вычислить
from → candidate, проверить путь, затем подтвердить движение либо обработать первый контакт.Контракт уровня идеи:
SweepQuery { id, generation, tick, from, to, radius, mask }→Clear | Hit { fraction, position, normal, block } | Unknown. Конкретные имена/ABI нужно выбрать при реализации.Unknownдолжен отличаться отClear, а результаты — относиться к нужному миру/поколению объекта/тику.Game::clip_ray, а такжеSceneCollisions/LookupCollisionsиBlockCollisionRegistry::collision_shape_with_contextвcollisions.rs. Использовать собственные механизмы форка, но не переносить player picking вслепую: он использует OUTLINE shapes и свою политику жидкостей, а здесь нужна выбранная projectile-collision политика.2. Выбрать безопасный мост Sim ↔ мир
Вариант A: синхронный пакетный sweep. Все намерения движения собираются в пакет; движок проверяет их по актуальному read-only миру/снимку. Плюс — простое упорядочение событий в одном тике. Минус — потребуется реальная доработка runtime/моста: сейчас host imports не имеют произвольного доступа к
Gameв середине WASM-тиков. Не решать это небезопасным указателем на мир или N вызовами через границу WASM на каждый voxel.Вариант B: асинхронные batch-запросы по образцу query-ground. Результаты приходят на следующем тике. Тогда перемещение/столкновение/перехват должны подтверждаться согласованно: нельзя сначала разрешить ракету и её попадание за стеной, а потом задним числом узнать о стене. Возможны очередь candidate-перемещений или ограниченный collision snapshot с prefetch. Учесть одновременное движение цели, устаревшие ответы после удаления/разделения/смены мира и версию геометрии.
Предпочтительный конкретный вариант определить после короткого прототипа моста; оба требуют большего, чем добавление
ground.at(new_pos).3. Явная политика дальнего/неизвестного мира
Определить, откуда берётся столкновение в дальней зоне, где виден LOD, но нет точных чанков: bounded prefetch collision-данных вдоль коридора, отдельные доступные данные местности или явно обозначенное приближение. Coarse-height fallback не сможет корректно различить мосты и тоннели.
Не превращать истечение бюджета/отсутствие чанка в «там воздух». Одновременно не force-load/generate весь мир на каждую ракету и не останавливать весь тик игры ожиданием I/O. Нужны лимиты, приоритеты, ограниченное удержание данных и согласованное поведение
Unknown.4. Порядок событий и игровая логика
engaged/назначение живой цели; корректно обновить камеры, эффекты и повтор. Отличать world collision от неудачного броскаpkи истечения ресурса.in_groundведёт кhits += 1,EventKind::Hitиlearn(sector, false); после включения настоящей коллизии это массово исказит отчёты и обучение маршрутов. Нужна отдельная причина/событие удара о препятствие и корректный учёт реально нанесённого урона.shader off/on/hd; визуальная интерполяция не должна продолжать модель сквозь препятствие после подтверждённого удара.План выполнения
Clear/Hit/Unknown.Приёмка и бюджет
Регрессии: одноблочная стена между свободными endpoints; стена в конечной колонке; диагональный удар/угол/тонкий блок; высокоскоростное падение на склон; гора перед целью; перехватчик и цель по разные стороны стены; свободный тоннель/мост; вода по выбранным правилам; разрушенное и вновь поставленное препятствие; неизвестный чанк/переполненный бюджет; разделение Орешника возле препятствия; сбитый дрон; world change с запросами в полёте.
Повторить с разными скоростями/направлениями, а также с шейдерами off/on/hd. Не должно быть прохода через сплошную геометрию, ложного удара о воздух под мостом, двойного взрыва, перехвата через более раннее препятствие или результата от старого мира.
На стресс-сценарии измерить CPU p50/p95/max тика, WASM fuel, число batch/import-вызовов, проверенных voxel/shapes,
Unknown, объём и срок жизни collision cache. Пакеты и переиспользуемые буферы, broad phase по секциям, bounded кэш форм с revision-инвалидированием; никаких сканирований всех блоков bounding box длинной диагонали или отдельного host-вызова на каждый блок. Декоративные эффекты не должны вытеснять запросы боевых объектов из бюджета. Native-время не выдавать за профиль игры на Mac.🤖 Agent: сделано в https://gitea.maoniao.team/chexit/airdef/commit/8065f22 (запушено в main).