Константы по таблице GMod и устранены невидимые стены из плоскостей граней

Четыре жалобы из игры, все закрыты измерением.

Константы движения (gmodwiki.com/ConVars_In_Garrysmod, не source-sdk-2013):
  sv_friction         4 -> 8     торможение 250 -> 10 за 0,40 с, путь 26 юнитов
  sv_stopspeed     100 -> 10
  sv_gravity       800 -> 600    прыжок 45,6 -> 60,8 юнита, как в GMod
  sv_airaccelerate  12 -> 10
  sv_maxvelocity   350 -> 3500   падение 585/589/590 -> 600/1200/1800/2400/3000

«В воздухе управление слабее»: в AirAccelerate кап sv_air_max_wishspeed
ограничивает ЦЕЛЬ, а прибавка считается по некапленной wishspeed. Мы
ограничивали и то и другое — прибавка была в 8,3 раза слабее. Замер после
правки: рывок вбок даёт ровно 30,0 юнитов/с, как в Source. Поворот при
полёте вперёд по-прежнему заблокирован (проекция 177 >> капа 30) — это
верное поведение оригинала.

«Высота шага терялась единица»: подъём ровно на sv_stepsime ставит низ
габарита точно на грань ступени, грань считается касанием, и подшаг
блокирует сам себя. Замерено: брались уступы до 17 и застревали на 18.
В оригинале подъём на flStepSize + DIST_EPSILON. После правки — все до 18
включительно, 19 нет.

«Невидимые стены, рампа как параллелипипед»: грань кирпича ограничивает
плоскость, а не кирпич, и останавливает шаг где угодно на её уровне.
Замерено: игрок останавливался за 3,4 юнита до настоящей преграды, блокировала
верхняя грань соседнего кирпича. Правка: при t около нуля проверяется, что
точка касания лежит на самом кирпиче. Замер после правки: преград «из
воздуха» 0 (было 2), упоров за 30 с ходьбы 0.

Проверено и отброшено измерением: кисти не работают как ящик (аудит 2126
кирпичей, 0 расхождений из 129684 точек); displacement не выпадает из сетки
(11776 треугольников, выпало 0); скорость вдоль склона 239-263 против 250.

Пара sv_falldistance/fall_speed НЕ введена осознанно: в таблице cvar'ов GMod
её нет, а формула HL2 при отрицательной velocity[2] даёт замедление падения,
а не разгон. Ставить непроверенные константы значило бы гадать.

228 тестов зелёные, clippy -D warnings чист.
This commit is contained in:
Niko Marmeladkov 2026-10-14 07:52:39 +03:00
parent f34c456034
commit d6c49fd67a
2 changed files with 1004 additions and 28 deletions

File diff suppressed because it is too large Load diff

View file

@ -1179,3 +1179,121 @@ for (i=0; i < 3; i++) {
с обнулением оси на 0).
Тесты: `speed_is_capped_at_max_velocity`, `non_finite_velocity_is_zeroed`.
## Часть B8. Свары GMod: константы и «невидимые стены» (сделано)
Четыре жалобы, все — из игры, все закрыты измерением.
### B8.1. «В воздухе управление слабее, чем в оригинальном GMod»
Таблица cvar'ов GMod (gmodwiki.com/ConVars_In_Garrysmod) и код
`AirAccelerate` из CS:GO-версии `gamemovement.cpp` дают разные вещи:
```cpp
wishspd = wishspeed;
if (wishspd > 30) wishspd = 30; // цель ограничена
addspeed = wishspd - currentspeed;
accelspeed = accel * wishspeed * frametime; // а прибавка — по полной
```
Кап `sv_air_max_wishspeed` = 30 ограничивает **цель**, но не величину прибавки.
Мы ограничивали и то и другое, из-за чего прибавка была в 250/30 ≈ 8,3 раза
слабее — это и есть ощущение «вялого управления в воздухе».
Замер после правки на gm_construct: рывок вбок в полёте даёт ровно **30,0**
юнитов/с (кап достигается за один кадр, как в Source). Поворот при полёте
вперёд по-прежнему заблокирован: проекция скорости на желаемое направление 177
>> капа 30, прибавки нет — **это верное поведение оригинала**, а не дефект.
Заодно: `currentspeed` считается по **полной трёхмерной** скорости, а не по
горизонтальной. В `AirMove` вертикаль не обнуляется, поэтому в падении управление
слабее, чем в горизонтальном полёте, — ровно как в Source.
### B8.2. «Трение слабое, останавливаюсь долго»
Константы брались из SDK 2013, а у GMod они переопределены:
| cvar | SDK 2013 | **GMod** | было у нас |
|---|---|---|---|
| `sv_friction` | 4 | **8** | 4 |
| `sv_stopspeed` | 100 | **10** | 100 |
| `sv_gravity` | 800 | **600** | 800 |
| `sv_airaccelerate` | 12 | **10** | 12 |
| `sv_accelerate` | 10 | 10 | 10 |
| `sv_maxvelocity` | 350 | **3500** | 350 |
| `sv_stepsize` | 18 | 18 | 18 |
| `sv_walkable_normal` | 0,7 | 0,7 | 0,7 |
Замер после правки: с 250 юнитов/с до 10 — за **0,40 с**, путь **26 юнитов**.
`sv_maxvelocity` = 3500, а не 350, — отдельная история: это потолок *по осям*, и
при значении 350 вертикальная скорость падения упиралась в него сразу. Отсюда
жалоба «падаю как мешок, без ускорения».
### B8.3. Прыжок и падение
`sv_gravity` = 600 и прыжок 270 дают 270²/1200 = **60,8** юнита высоты — как в
GMod (замерено 60,8). В Source с гравитацией 800 было 45,6.
Падение в пустоте по секундам: **600, 1200, 1800, 2400, 3000** юнитов/с —
равномерный разгон по `g·t` до потолка `sv_maxvelocity`. Прежде было
585, 589, 590 — то есть упиралось в потолок 350 и стояло на месте.
### B8.4. О `sv_falldistance` / `fall_speed`: гипотеза, отброшенная измерением
Поначалу показалось, что «ускорение падения» — это пара cvar'ов HL2
`sv_falldistance` + `fall_speed`. Проверка: в таблице cvar'ов GMod их **нет**, а
формула `velocity[2] -= velocity[2] * fall_speed * frametime` при отрицательной
`velocity[2]` даёт **замедление** падения, а не разгон. Ставить непроверенные
константы значило бы гадать, поэтому пара не вводится: ускорение падения в GMod
даёт постоянная гравитация плюс высокий `sv_maxvelocity`.
### B8.5. «Высота шага терялась единица»
Подшаг поднимает тело ровно на `sv_stepsize` = 18, и тогда низ габарита
становится **точно на грань** ступени. Грань в этот момент считается касанием,
отсечение обнуляет шаг, и подшаг блокирует сам себя.
Замер до правки: игрок перебирал уступы до **17** и застревал на **18**. В
оригинале подъём идёт на `flStepSize + DIST_EPSILON` — не округление, а
необходимость.
После введения `STEP_EPSILON` = 0,1: перебираются все уступы до **18** включительно,
19 — нет. Ровно `sv_stepsize`.
### B8.6. «Невидимые стены, рамка превращается в параллелепипед»
Грань кирпича ограничивает **плоскость**, а не сам кирпич. Стоит телу оказаться
на уровне этой плоскости где угодно — грань останавливает шаг, даже если
кирпича там нет.
Замер на gm_construct: игрок, идущий по полу на `z = -144`, останавливался за
**3,4 юнита** до настоящей преграды — блокировала верхняя грань соседнего
кирпича, до которого было далеко. Такие грани встречаются на каждом стыке
участков, а на наклонных поверхностях образуют блоки, сравнимые с габаритом
целого кирпича, — «рампа превращается в параллелепипед».
Правка: при `t` около нуля (шаг обнуляется полностью) проверяется, что точка
касания лежит на самом кирпиче, а не на продолжении его грани.
Проверка узкая и только для `t < 0,001` намеренно: проверять точку касания для
всех `t` нельзя — при спуске с подшага тело опускается вертикально и в момент
касания ещё находится в стороне от грани по горизонтали. На широкой проверке
игрок переставал перебирать уступы уже высотой 4 юнита (замерено).
Замер после правки: преград «из воздуха» на 1800 шагов ходьбы по gm_construct —
**0** (было 2), и упоров в бордюры за 30 с ходьбы — **0** кадров.
### B8.7. Проверенные гипотезы, оказавшиеся невиновными
Каждая проверялась замером, а не рассуждением, и все оказались в порядке:
- **Кисти кирпичей как ящик.** Синтетическая наклонная грань сходится с
полутночными пространствами точно (`slanted_brush_is_not_treated_as_a_box`).
- **Объём кирпичей на карте.** Аудит всех 2126 кирпичей gm_construct по сетке
4×4×4 точек: **0 расхождений** (129684 проверок).
- **Displacement выпадает из сетки.** `add_displacement_tris` молча выбрасывает
треугольник, размазанный больше чем по 2048 юнитам. На карте: 11776
треугольников, **выпало 0**.
- **Скорость вдоль склона.** Рампа — не ящик: 239–263 юнита/с против 250 на
ровном, при подъёме застреваний 0.