[bug] Сквозной пролёт ракет и дронов: непрерывная коллизия с миром вместо отсутствующей/высотной проверки #42

Closed
opened 2026-10-29 16:36:02 +00:00 by chexit · 1 comment
Owner

🤖 Filed by Codex, AI-агент Владимира (chexit).

Проверено на f443f0a94a71eb2d446792f1829146f9563aaf5d. P1 по запросу автора проекта: ракеты и дроны проходят через горы/землю; по наблюдению автора столкновение становится заметно в основном после сбития. Это ломает базовую механику боя: укрытия, траектории, место подрыва и результат перехвата.

Связано с закрытой #34, но не дублирует её: там исправлялась устаревшая высота назначенной цели. Здесь требуется столкновение на всём пути полёта, независимо от выбранной цели.

Что подтверждено кодом

Объект/состояние Текущая проверка мира Ограничение
Перехватчики Missile (включая P1-SUN) Нет; step_missiles получает только Out Прямое изменение позиции, затем проверка сближения с целью/таймаута; мир не участвует
Дроны и крылатые ракеты Cruise/Dive ground.at(d.pos) до движения; после движения сравнивается новая Y со старой высотой Нет проверки пройденного отрезка, координаты XZ не соответствуют новой позиции
Баллистика, включая ветки разделения Орешника Та же старая колонка below + условия назначения/времени жизни Те же пропуски препятствий; условия цели не заменяют столкновение
Сбитые дроны Falling Старая колонка below, гравитация и торможение Это тоже приближение, а не полноценная коллизия
Fx::Debris Высота под конечной позицией после движения Лучше привязка XZ, но нет sweep и настоящей формы блоков

Источники в моде:

  • 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_BLOCKING heightmap. Это одна верхняя высота на XZ, включая жидкости, не список занятых блоков и не collision shapes.

  • В World::motion_blocking на pinned upstream b6c936f отсутствующий загруженный чанк даёт MIN_Y (-64); Ground::at превращает y <= -64 в None.
  • Неизвестная высота в ветках движения фактически означает «не обнаружено препятствие». Ответ -64 кэшируется с обычным timestamp; обновление запрашивается лишь после 200 тиков, и старое значение возвращается до ответа.
  • Виден дальний LOD-рельеф ≠ доступны точные блоки этого рельефа: высотный запрос идёт по world.chunks, а не по картинке/LOD. Для исходного пользовательского случая это обоснованная гипотеза, но состояние его мира/чанков в этой проверке не снималось.
  • Под мостом, навесом или в тоннеле та же верхняя высота не отличает свободный проход от сплошного массива горы. Нельзя объявлять весь объём ниже heightmap твёрдым.
  • Кэш может до обновления не отражать постройку/разрушение препятствия. Локальный 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.

thin_wall: x=7.425000, alive=true, blasts=0
  стена x=[3,4), y=[64,141), полёт на y=100

old_column_tick1: x=7.425000, y=100.000000, alive=true, blasts=0
  стена x=[7,8), ракета уже внутри неё
old_column_tick2: blast=(14.850000,100.000000,0.000000)
  ракета взорвалась уже за стеной, в свободном воздухе

ballistic_thin_wall: x=5.000000, y=99.950000, alive=true, blasts=0
  Искандер пересёк стену x=[2,3)

buried_interceptor: x=90.000000, y=50.000000, count=1, blasts=0
  77Н6 прошёл 90 блоков при заданной поверхности y=64

interceptor_fuse_behind_wall: missile_count=0, kills=1, target_alive=false
  PAC-3 стартует x=0, стена x=[3,4), цель x=8.5 на той же высоте
  мир не проверяется, перехват успешно засчитан за стеной

unknown_columns: x=148.500000, y=50.000000, alive=true, blasts=0
  ответы высот = -64, 20 тиков полёта

bridge_heightmap: removed=true, blast_y=100
  одна высота 141 ложно означает препятствие и в воздухе под мостом y=140

falling_flat_land: removed=true, blast_y=64.052250
  сбитый дрон обнаруживает известную ровную землю

Проверка PAC-3 вызывает именно step_missiles с неподвижной живой целью; координаты стен/высоты заданы отдельно. Отсутствие параметра мира у метода и есть проверяемая причина, по которой даже известная преграда не может остановить ракету.

Отсюда уточнение наблюдения «только после сбития»: у обычных дронов проверка высоты тоже есть. Падающий объект тормозит/опускается, чаще остаётся в известных колонках, поэтому грубая проверка заметнее. При этом и Falling всё ещё способен пропустить стену между точками.

Минимальный fixture для переноса в sim/regress.rs

Сейчас этот тест падает, поскольку ракета остаётся жива:

#[test]
fn fast_drone_must_not_cross_a_one_block_wall() {
    let mut sim = Sim::new(1);
    sim.damage = false;
    let mut ground = Ground::default();
    ground.answer(
        (-10..=1200)
            .map(|x| (x, 0, if x == 3 { 141 } else { 64 }))
            .collect(),
        0,
    );
    sim.add_drone(
        DroneKind::Zircon,
        DVec3::new(0.0, 100.0, 0.0),
        DVec3::X,
        DVec3::new(1000.0, 100.0, 0.0),
    );
    sim.drones[0].flight = Flight::Dive;
    sim.tick = 1;
    sim.step_drones(&mut ground, None, &mut Out::default());
    assert!(sim.drones.is_empty(), "crossed the wall in one tick");
}

После появления 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, а результаты — относиться к нужному миру/поколению объекта/тику.

  • Общий путь для Cruise, Dive, Ballistic, разделившихся боеприпасов, перехватчиков, Falling. Небольшие декоративные обломки можно включить вторым этапом, не блокируя исправление боевой физики.
  • Минимум — segment traversal/DDA по voxel grid с narrow phase по реальной форме блока. Для заметных габаритов — swept sphere/capsule или консервативный sweep по расширенным collision AABB; один луч центра может пропускать касание крылом.
  • В Horizon уже есть обход voxel и slab-пересечения в Game::clip_ray, а также SceneCollisions/LookupCollisions и BlockCollisionRegistry::collision_shape_with_context в collisions.rs. Использовать собственные механизмы форка, но не переносить player picking вслепую: он использует OUTLINE shapes и свою политику жидкостей, а здесь нужна выбранная projectile-collision политика.
  • Отдельно определить правила для воды/жидкостей, листвы и неполных блоков. Heightmap MOTION_BLOCKING не является готовой спецификацией этих правил.
  • Взрыв/удаление/дебрис — в точке первого контакта, с корректной нормалью/небольшим безопасным смещением эффектов; не после вылета за препятствие.

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. Порядок событий и игровая логика

  • Сравнивать время первого world hit с временем срабатывания взрывателя/сближения с движущейся целью. Стена, достигнутая раньше, не должна разрешать успешный перехват за ней.
  • На столкновении перехватчика освободить engaged/назначение живой цели; корректно обновить камеры, эффекты и повтор. Отличать world collision от неудачного броска pk и истечения ресурса.
  • На столкновении дрона с горой не засчитывать автоматически «долетел до объекта/прорвался». Сейчас in_ground ведёт к hits += 1, EventKind::Hit и learn(sector, false); после включения настоящей коллизии это массово исказит отчёты и обучение маршрутов. Нужна отдельная причина/событие удара о препятствие и корректный учёт реально нанесённого урона.
  • Учесть столкновение в boost/cold-launch и возле пусковой. Возможные исключения для своего корпуса должны быть узкими; «первые N тиков игнорировать весь мир» снова позволит стрелять сквозь стены/потолок.
  • Коллизия обязательна независимо от выбора shader off/on/hd; визуальная интерполяция не должна продолжать модель сквозь препятствие после подтверждённого удара.
  • Избегание рельефа/новое 3D-планирование — отдельная задача поверх корректных столкновений. Сейчас cruise-lookahead 20 блоков и маршруты по риску ПВО не гарантируют проходимость. Не исправлять это телепортацией наверх или отключением коллизии на маршруте.

План выполнения

  • Зафиксировать failing fixtures для ракет ПВО, Cruise/Dive, баллистики и Falling.
  • Прототип collision provider/моста, настоящий sweep и Clear/Hit/Unknown.
  • Подключить боевые объекты, приоритет world hit над более поздним перехватом, корректную точку удара и освобождение назначений.
  • Определить обработку дальних/неизвестных данных и инвалидирование при изменении блоков/смене мира.
  • Отделить столкновение с препятствием от успешного прилёта в отчётах/повторах/обучении.
  • Проверить производительность и интеграционные сцены; затем при необходимости доработать obstacle avoidance.

Приёмка и бюджет

Регрессии: одноблочная стена между свободными 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.

🤖 Filed by Codex, AI-агент Владимира (chexit). Проверено на `f443f0a94a71eb2d446792f1829146f9563aaf5d`. **P1 по запросу автора проекта:** ракеты и дроны проходят через горы/землю; по наблюдению автора столкновение становится заметно в основном после сбития. Это ломает базовую механику боя: укрытия, траектории, место подрыва и результат перехвата. Связано с закрытой #34, но не дублирует её: там исправлялась устаревшая высота назначенной цели. Здесь требуется столкновение **на всём пути полёта**, независимо от выбранной цели. ## Что подтверждено кодом | Объект/состояние | Текущая проверка мира | Ограничение | |---|---|---| | Перехватчики `Missile` (включая P1-SUN) | Нет; `step_missiles` получает только `Out` | Прямое изменение позиции, затем проверка сближения с целью/таймаута; мир не участвует | | Дроны и крылатые ракеты `Cruise`/`Dive` | `ground.at(d.pos)` до движения; после движения сравнивается новая Y со старой высотой | Нет проверки пройденного отрезка, координаты XZ не соответствуют новой позиции | | Баллистика, включая ветки разделения Орешника | Та же старая колонка `below` + условия назначения/времени жизни | Те же пропуски препятствий; условия цели не заменяют столкновение | | Сбитые дроны `Falling` | Старая колонка `below`, гравитация и торможение | Это тоже приближение, а не полноценная коллизия | | `Fx::Debris` | Высота под конечной позицией после движения | Лучше привязка XZ, но нет sweep и настоящей формы блоков | Источники в моде: - [`sim/mod.rs`](https://gitea.maoniao.team/chexit/airdef/src/commit/f443f0a94a71eb2d446792f1829146f9563aaf5d/airdef-mod/src/sim/mod.rs): `Ground::at` (222–239), `step` (1363–1377), `step_drones` (1388–1558), `step_missiles` (2064–2110), `step_fx` (2189+). - [`lib.rs`](https://gitea.maoniao.team/chexit/airdef/src/commit/f443f0a94a71eb2d446792f1829146f9563aaf5d/airdef-mod/src/lib.rs): чтение предыдущих ответов высот → `sim.step` → отправка новых запросов, 1062–1070. - [`sight.rs`](https://gitea.maoniao.team/chexit/airdef/src/commit/f443f0a94a71eb2d446792f1829146f9563aaf5d/airdef-mod/src/sim/sight.rs): радар использует приближённый горизонт по heightmap; это не коллизия летящего объекта. `update_sight` также запрашивает конечные колонки дронов, но не проверяет пройденный отрезок. Модели из `render.set-models`/`set-models-by-id` сами по себе не становятся физическими сущностями мира. Добавление/смена модели не решит проблему. ### Почему «проверить новую точку» недостаточно Симуляция идёт 20 Гц. Например, Циркон в Dive проходит `5.5 × 1.35 = 7.425` блока за тик, перехватчик 77Н6 — до 9 блоков/тик. Баллистическая скорость дополнительно зависит от вертикальной составляющей. Стену толщиной один блок можно целиком пересечь между двумя свободными конечными точками. Нужно искать **первое столкновение по отрезку перемещения**, а не только занятость точки после шага. ### Ограничения данных о мире В [патче 1](https://gitea.maoniao.team/chexit/airdef/src/commit/f443f0a94a71eb2d446792f1829146f9563aaf5d/horizon-patches/0001-Mod-API-AirDef-fork-world-models-client-particles-so.patch) `render.query-ground` отвечает через `Game::rain_height`, то есть по `MOTION_BLOCKING` heightmap. Это одна верхняя высота на XZ, включая жидкости, **не список занятых блоков и не collision shapes**. - В `World::motion_blocking` на pinned upstream `b6c936f` отсутствующий загруженный чанк даёт `MIN_Y` (`-64`); `Ground::at` превращает `y <= -64` в `None`. - Неизвестная высота в ветках движения фактически означает «не обнаружено препятствие». Ответ `-64` кэшируется с обычным timestamp; обновление запрашивается лишь после 200 тиков, и старое значение возвращается до ответа. - Виден дальний LOD-рельеф ≠ доступны точные блоки этого рельефа: высотный запрос идёт по `world.chunks`, а не по картинке/LOD. Для исходного пользовательского случая это **обоснованная гипотеза**, но состояние его мира/чанков в этой проверке не снималось. - Под мостом, навесом или в тоннеле та же верхняя высота не отличает свободный проход от сплошного массива горы. Нельзя объявлять весь объём ниже heightmap твёрдым. - Кэш может до обновления не отражать постройку/разрушение препятствия. Локальный `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. ```text thin_wall: x=7.425000, alive=true, blasts=0 стена x=[3,4), y=[64,141), полёт на y=100 old_column_tick1: x=7.425000, y=100.000000, alive=true, blasts=0 стена x=[7,8), ракета уже внутри неё old_column_tick2: blast=(14.850000,100.000000,0.000000) ракета взорвалась уже за стеной, в свободном воздухе ballistic_thin_wall: x=5.000000, y=99.950000, alive=true, blasts=0 Искандер пересёк стену x=[2,3) buried_interceptor: x=90.000000, y=50.000000, count=1, blasts=0 77Н6 прошёл 90 блоков при заданной поверхности y=64 interceptor_fuse_behind_wall: missile_count=0, kills=1, target_alive=false PAC-3 стартует x=0, стена x=[3,4), цель x=8.5 на той же высоте мир не проверяется, перехват успешно засчитан за стеной unknown_columns: x=148.500000, y=50.000000, alive=true, blasts=0 ответы высот = -64, 20 тиков полёта bridge_heightmap: removed=true, blast_y=100 одна высота 141 ложно означает препятствие и в воздухе под мостом y=140 falling_flat_land: removed=true, blast_y=64.052250 сбитый дрон обнаруживает известную ровную землю ``` Проверка PAC-3 вызывает именно `step_missiles` с неподвижной живой целью; координаты стен/высоты заданы отдельно. Отсутствие параметра мира у метода и есть проверяемая причина, по которой даже известная преграда не может остановить ракету. Отсюда уточнение наблюдения «только после сбития»: у обычных дронов проверка высоты тоже есть. Падающий объект тормозит/опускается, чаще остаётся в известных колонках, поэтому грубая проверка заметнее. При этом и `Falling` всё ещё способен пропустить стену между точками. ### Минимальный fixture для переноса в `sim/regress.rs` Сейчас этот тест **падает**, поскольку ракета остаётся жива: ```rust #[test] fn fast_drone_must_not_cross_a_one_block_wall() { let mut sim = Sim::new(1); sim.damage = false; let mut ground = Ground::default(); ground.answer( (-10..=1200) .map(|x| (x, 0, if x == 3 { 141 } else { 64 })) .collect(), 0, ); sim.add_drone( DroneKind::Zircon, DVec3::new(0.0, 100.0, 0.0), DVec3::X, DVec3::new(1000.0, 100.0, 0.0), ); sim.drones[0].flight = Flight::Dive; sim.tick = 1; sim.step_drones(&mut ground, None, &mut Out::default()); assert!(sim.drones.is_empty(), "crossed the wall in one tick"); } ``` После появления 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`, а результаты — относиться к нужному миру/поколению объекта/тику. - Общий путь для Cruise, Dive, Ballistic, разделившихся боеприпасов, перехватчиков, Falling. Небольшие декоративные обломки можно включить вторым этапом, не блокируя исправление боевой физики. - Минимум — segment traversal/DDA по voxel grid с narrow phase по реальной форме блока. Для заметных габаритов — swept sphere/capsule или консервативный sweep по расширенным collision AABB; один луч центра может пропускать касание крылом. - В Horizon уже есть обход voxel и slab-пересечения в `Game::clip_ray`, а также `SceneCollisions`/`LookupCollisions` и `BlockCollisionRegistry::collision_shape_with_context` в `collisions.rs`. Использовать собственные механизмы форка, но **не переносить player picking вслепую**: он использует OUTLINE shapes и свою политику жидкостей, а здесь нужна выбранная projectile-collision политика. - Отдельно определить правила для воды/жидкостей, листвы и неполных блоков. Heightmap MOTION_BLOCKING не является готовой спецификацией этих правил. - Взрыв/удаление/дебрис — в точке первого контакта, с корректной нормалью/небольшим безопасным смещением эффектов; не после вылета за препятствие. ### 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. Порядок событий и игровая логика - Сравнивать время первого world hit с временем срабатывания взрывателя/сближения с движущейся целью. Стена, достигнутая раньше, не должна разрешать успешный перехват за ней. - На столкновении перехватчика освободить `engaged`/назначение живой цели; корректно обновить камеры, эффекты и повтор. Отличать world collision от неудачного броска `pk` и истечения ресурса. - На столкновении дрона с горой не засчитывать автоматически «долетел до объекта/прорвался». Сейчас `in_ground` ведёт к `hits += 1`, `EventKind::Hit` и `learn(sector, false)`; после включения настоящей коллизии это массово исказит отчёты и обучение маршрутов. Нужна отдельная причина/событие удара о препятствие и корректный учёт реально нанесённого урона. - Учесть столкновение в boost/cold-launch и возле пусковой. Возможные исключения для своего корпуса должны быть узкими; «первые N тиков игнорировать весь мир» снова позволит стрелять сквозь стены/потолок. - Коллизия обязательна независимо от выбора `shader off/on/hd`; визуальная интерполяция не должна продолжать модель сквозь препятствие после подтверждённого удара. - Избегание рельефа/новое 3D-планирование — отдельная задача поверх корректных столкновений. Сейчас cruise-lookahead 20 блоков и маршруты по риску ПВО не гарантируют проходимость. Не исправлять это телепортацией наверх или отключением коллизии на маршруте. ## План выполнения - [ ] Зафиксировать failing fixtures для ракет ПВО, Cruise/Dive, баллистики и Falling. - [ ] Прототип collision provider/моста, настоящий sweep и `Clear/Hit/Unknown`. - [ ] Подключить боевые объекты, приоритет world hit над более поздним перехватом, корректную точку удара и освобождение назначений. - [ ] Определить обработку дальних/неизвестных данных и инвалидирование при изменении блоков/смене мира. - [ ] Отделить столкновение с препятствием от успешного прилёта в отчётах/повторах/обучении. - [ ] Проверить производительность и интеграционные сцены; затем при необходимости доработать obstacle avoidance. ## Приёмка и бюджет Регрессии: одноблочная стена между свободными 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.
chexit added the priority:P1area:simulationtype:bug labels 2026-10-29 16:36:02 +00:00
Author
Owner

🤖 Agent: сделано в https://gitea.maoniao.team/chexit/airdef/commit/8065f22 (запушено в main).

🤖 Agent: сделано в https://gitea.maoniao.team/chexit/airdef/commit/8065f22 (запушено в main).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chexit/airdef#42