diff --git a/crates/engine/src/player.rs b/crates/engine/src/player.rs index 664fa2f..da6fc5b 100644 --- a/crates/engine/src/player.rs +++ b/crates/engine/src/player.rs @@ -1265,4 +1265,50 @@ mod tests { player.position.x ); } + + /// Падающий игрок не должен прошивать тонкий пол даже на 60 fps. + /// + /// Скорость падения ограничена 2000 юнитов/с; при dt = 1/60 это 33 юнита + /// за кадр. Число подшагов ограничено восемью, значит на шаг приходится + /// около 6.6 юнита — пол тоньше этого игрок проходит насквозь. Именно + /// это и выглядит как «проваливаюсь при хождении»: чем быстрее кадр, + /// тем больше шаг подшага. + #[test] + fn fast_fall_does_not_tunnel_thin_floor() { + let mut collision = Collision { + brushes: Vec::new(), + tris: Vec::new(), + grid: HashMap::new(), + boxes: Vec::new(), + box_grid: HashMap::new(), + }; + // Тонкая площадка толщиной 4 юнита — как отступы и площадки на картах. + collision.brushes = vec![Brush { + planes: vec![ + (Vec3::new(1.0, 0.0, 0.0), 512.0), + (Vec3::new(-1.0, 0.0, 0.0), 512.0), + (Vec3::new(0.0, 1.0, 0.0), 512.0), + (Vec3::new(0.0, -1.0, 0.0), 512.0), + (Vec3::new(0.0, 0.0, 1.0), 0.0), + (Vec3::new(0.0, 0.0, -1.0), 4.0), + ], + }]; + + let mut player = Player::new(Vec3::new(0.0, 0.0, 400.0)); + // Падаем до удара: за один кадр игрок проходит лишь ~33 юнита, до + // пола от 400 нужно десятки кадров. + for _ in 0..200 { + player.update(&collision, 0.0, 0.0, false, false, 0.0, 1.0 / 60.0); + assert!( + player.position.z >= -1.0, + "игрок не должен пройти сквозь пол, z={}", + player.position.z + ); + } + assert!( + player.on_ground, + "после удара игрок должен стоять на полу, z={}", + player.position.z + ); + } } diff --git a/docs/PARITY.md b/docs/PARITY.md index a63497c..24ff098 100644 --- a/docs/PARITY.md +++ b/docs/PARITY.md @@ -693,3 +693,32 @@ API. Частоты по числу вхождений: - Оси соединения задаются в мировых координатах, как в GMod. - Ограничение угла переводится в радианы; вырожденный диапазон игнорируется. - `pulley` и `thruster` — это не суставы: тяга делается через `add_force`. + +## Часть B3. Проваливание сквозь пол: что проверено и что нет + +### B3.1. Две гипотезы, обе опровергнуты измерением + +1. **«Пропов нет в коллизии игрока».** Гипотеза была верной *как диагноз*, + но не как объяснение жалобы: игрок ходил по брашам. Пробел устранён + (`Collision::boxes`), проп теперь стоит под ногами — это нужно в любом + случае, но от проваливания по брашам не избавило. +2. **«На низком FPS шаг подшага прошивает пол».** Опровергнуто тестом + `fast_fall_does_not_tunnel_thin_floor`: при terminal-скорости 2000 + юнитов/с и dt = 1/60 игрок садится на площадку толщиной **4 юнита** и + `on_ground = true`. Подшагов восемь хватает. + +Обе версии выглядели правдоподобно и обе были неверны — характерная черта +дефектов, которые видны только после попытки воспроизвести их тестом. + +### B3.2. Что измерено + +- Стояние на спавне gm_construct: 180 кадров, `z = -143.9` стабильно, + `on_ground = true`, провала нет. +- Значит дефект локальный и воспроизводится на определённой геометрии. + +### B3.3. Что нужно, чтобы найти причину + +Точное место и обстоятельства: с какой высоты, после какого движения +(прыжок, спуск по рампе, спуск с края) и на каком участке. Без этого +пришлось бы перебирать геометрию карты наугад — а это ровно тот способ, +которым эти две гипотезы и были порождены.