Модель полёта (scripts/flight/)
Реализует FR-1…FR-10. Логика — в RefCounted-классах (тестируются headless), нода Glider —
тонкая обёртка: время, ввод, визуал. Исследование массы пилота и источники поляр —
research/pilot_mass.md.
| Файл | Что |
|---|---|
flight_model.gd — FlightModel | полёт: силы, тангаж, крен, срыв, касание земли, телеметрия |
wing_polar.gd — WingPolar | поляра крыла CL→CD из точек (V, снижение) |
ground_run.gd — GroundRun | стоит / идёт / разбег, отрыв, ошибки разбега |
landing_flare.gd — LandingFlare | выравнивание, «подвешивание», касание, пробежка |
landing_judge.gd — LandingJudge | оценка посадки |
flight_telemetry.gd — FlightTelemetry | заполнение Telemetry |
load_meter.gd — LoadMeter | перегрузка n = L/W (FlightModel.load), сглаживание 0,15 с |
glider.gd — Glider (scenes/glider/glider.tscn) | нода: шаг физики, интерполяция, сигналы |
glider_visual.gd — GliderVisual (scenes/glider/glider_visual.tscn) | крыло + пилот, маркеры, заглушки |
Модель
Точечная масса с ориентацией. Состояние: положение, скорость относительно земли, курс, крен,
угловая скорость крена, тангаж киля θ, угол атаки α, флаг срыва. Шаг — полу-неявный Эйлер
с фиксированным dt (120 Гц из sim.json).
Силы. Подъёмная сила ⊥ воздушной скорости, наклонена на крен; сопротивление вдоль потока; вес. q = ½ρV²S, L = q·CL(α), D = q·CD(CL).
Поляра → CL/CD. Точки поляры (км/ч, м/с) при эталонной массе переводятся в безразмерную таблицу CL→CD (CL = 2Mg·cosγ/(ρSV²), CD = CL·tgγ). В спокойном воздухе модель сходится точно к поляре. За быстрым краем — парабола CD0 + k·CL².
Трапеция (FR-4). Трапеция задаёт угол атаки: pitch 0 — трим, −1 — скорость «полностью на себя», +1 — «полностью от себя» (ниже сваливания). Клавиши в полёте — раскладка дельтапланериста: W/↑ и стик вперёд — от себя (нос вверх, торможение), S/↓ — на себя (разгон); инверсия тангажа в настройках — «как в самолёте». Скорость между ними линейна по отклонению; значения — при эталонной массе. Крыло выходит на заданный угол атаки с постоянной времени
pitch_time_constant_s(инерция). Раскачка скорость↔высота (фугоида) гасится добавкой к углу атаки ∝ dV/dt (flight.json → phugoid_damping, реальное крыло устойчиво по скорости).Масса и высота (FR-3). Раз трапеция задаёт CL, все скорости и снижение ∝ √(M/ρ), качество неизменно; постоянные времени по крену/тангажу ∝ √(M/M_эт). Плотность ρ(h) = 1,225·e^(−h/10400).
Крен (FR-5). Два режима управления креном (
controls.json → roll_control_mode, пункт настроек «Управление креном»); смыслControlInput.rollв полёте задаётControlInput.weight_shift.- «Как раньше» (
rate, по умолчанию). Смещение веса задаёт угловую скорость крена (roll_rate_max_dps, множитель (V/V_трим)^0,5), выход на неё — с постоянной времениroll_time_constant_s. Без управления крен держится. Переложение 45→45 ≈ 2,3–2,7 с. - «Смещение веса» (
weight_shift).roll— положение пилота поперёк трапеции (−1..+1, 0 — центр, как в прямолинейном полёте). Техника как в жизни: качнулся вправо — крыло кренится и поворачивает; встал в центр — крыло само выравнивается и летит прямо в новом направлении. Параметры —wing.weight_shift: угловая скорость, к которой крыло выходит с постоянной времениroll_time_constant_s, p = u·roll_rate_max_dps·k(V) − (roll_stability_per_s+roll_stability_steep_per_s·(φ/45°)²)·φ (+ разница потоков на концах, FR-8). k(V) — эффективность смещения: ниже трима (V/V_трим)^roll_speed_exponent(крыло вялое), выше — (V_трим/V)^roll_heavy_exponent(на скорости тяжелее). Держишь смещение — установившийся крен ∝ смещению, спирали нет: при полном смещении в вираже ≈ 41° учебное / 45° мачтовое (Laminar) / 44° спортивное. Из центра после 2 с полного смещения крыло выравнивается (|φ| < 3°) за ≈ 2,0 / 2,6 / 3,3 с: устойчивость у учебного сильная, у спортивного на малом крене почти нейтральна. Кручеroll_overbank_deg(70 / 65 / 60°, смещением не достать — болтанка в облаке, сваливание на крыло) устойчивость за 10° сходит на нет — спираль, выход только смещением в обратную сторону. Переложение 45→45 полным смещением ≈ 2,2 с.roll_input_for_bank(φ, V)— смещение для крена φ. - Ввод (
controls.json → keyboard). Как раньше: A/D ведут ручку крена (roll_rate_per_s/roll_return_per_s). Смещение веса: A/D смещают вес до упора заroll_shift_time_s, отпустил — пружиной в центр (roll_center_time_s), у центра плавная нейтральroll_neutral_zone. X (center) в обоих режимах — «в центр»: трапеция плавно в нейтраль (center_time_s), крыло в горизонт (как раньше — ручкой против крена,center_rate_bank_deg). Мышь в режиме «трапеция» и стик — пропорционально: ход = ручка. Автопилот (Game.autopilot) и боты тестов всегда в режиме «как раньше». - Вираж координированный (без скольжения): курс идёт по воздушной скорости, радиус = V²/(g·tgφ), в вираже V ×√n, снижение ×n^1,5.
- «Как раньше» (
Сваливание (FR-6). α > α_крит → срыв. За
lift_loss_time_sобтекание переходит к сорванному (динамический срыв): парус работает как пластина поперёк потока, CL = CN·sinα·cosα, CD = CN·sin²α (post_stall_cn= 2, как купол парашюта). Черезnose_drop_delay_sнос сам опускается наnose_drop_deg, крыло разгоняется и выходит из срыва; если держать трапецию от себя — срывы повторяются. В крене >wing_drop_bank_deg— сваливание на крыло: опущенная консоль продолжает падать, управление креном ослаблено.Воздух (FR-7, FR-8).
air_fn(pos)опрашивается в центре и на концах крыла (0,8 полуразмаха). Путевая = воздушная + ветер, снос получается сам. Разница вертикальных потоков на концах даёт угловую скорость крена (w_лев − w_прав)/размах — крыло кренит от термика на его краю. Порывы меняют воздушную скорость и угол атаки (толчки, «болтанка»). Резкий боковой ветер разворачивает нос в поток.Земля (FR-9, контракты С1 v2 / К3 v3 —
docs/contracts/control-fix.md). Фазы:standing→walking(input.walk, поворот на месте — input.turn, скорость шага падает на крутом склоне) →running(input.run: сила ног F·(1 − v/v_ног) линейно падает к пределу ног v_ног =run.speed_max_ms·(1 +unload_speed_bonus·разгрузка); это предел ног (частота и длина шага), а не скорости пилота: быстрее него ноги тормозят той же линейной силой, но только через долю веса на ногах (N/W), а склон и тяга крыла разгоняют и выше — под крутую горку (круче угла планирования) в штиль разгружённого крылом пилота «несёт», отрыв короче (CF-1: раньше скорость обрезалась по v_ног, и в штиль на высоком старте крылья с Vmin выше ≈ 30 км/ч не взлетали — «спорт», combat —weak_runчерез 66 м; теперь отрыв за 13–15 м на старте по умолчанию). Границы: спотыкание и падение на бегу быстрее ног не моделируются. Курс на бегу — дугой от крена крыла. Во всех фазах pitch — нос крыла (угол атаки киляlaunch.alpha_neutral_deg + pitch·alpha_range_deg, 0 — угол разбега крыла), roll — заданный крен «руки пилота» (±ground_bank.command_max_deg). Рука держит крыло с ограниченной силой, боковой ветер его кренит — раздел «Крен на земле». Отрыв — когда вертикальная составляющая подъёмной силы ≥ веса (и крыло не сорвано). Срыв взлёта один —wingtip: конец консоли, опущенной креном (полуразмах от киля на высотеtakeoff.wing_height_m), на земле — крыло опрокинуто (и стоя тоже; на ровном это крен ≈ asin(h/полуразмах) ≈ 17–21°). Нос высоко (крыло за срывом), нос низко (крыло давит вниз), попутный ветер (мала воздушная скорость) — не отдельные срывы: подъёмной силы не хватает, отрыва просто нет. Срывов и переходов по времени нет (К3 v3): отрыв — только L ≥ W от воздушной скорости (бег + ветер), без бега и ветра крыло не отрывается, в штиль пилот бежит, сколько нужно, бросил бежать — снова стоит (tests/flight/test_liftoff_physics.gd). Текст —GroundRun.failure_text(reason).Выравнивание (FR-10,
LandingFlare,flight.json → flare). Нижеheight_m(4 м над ногами) трапеция «от себя» сильнееpush_thresholdставит киль на тангаж доpitch_deg(50°) к горизонту заpitch_time_s; нос при срыве не опускается. Парус на закритике тормозит (нормальная сила ×cn_factor, кратковременный рост CL ×dynamic_cl_factor), крыло подскакивает, и с верхней точки «подвешивается»: опускает пилота на ноги не быстрееhang_sink_ms(0,7 м/с), пока не опустится наhang_drop_m(1,6 м) — дальше динамический эффект кончается и крыло падает (феноменологическая модель: вихрь динамического срыва, экран земли, пилот встаёт под крыло). Правильно: ноги ~0,5 м над землёй, при ~1,2·V_св — от себя до упора: касание ~1,2 м/с вертикально, путевая ≤ 1,2 м/с, пилот встаёт на ноги (пробежкаrunout_decel_ms2, < 1,5 м). На 1 м выше — всё ещё мягкая (~1,3 м/с); на 3 м выше — «плюх» ~6,4 м/с, жёсткая. Во встречный ветер выравнивают позже (при ~0,9·V_св) — путевая почти 0; полное раннее выравнивание в ветер тащит крыло назад.Посадка (FR-10). Касание: скорость раскладывается по нормали к склону («вертикальная») и вдоль него («горизонтальная»). Пороги по ответу пилотов («с 5 м/с посадка уже жёсткая, при правильной посадке скорость ≈ 0»): мягкая — вертикальная < 5 м/с, горизонтальная ≤ 3 м/с, крен ≤ 15°; жёсткая — вертикальная 5…7 м/с или горизонтальная 3…12 м/с (на триме без выравнивания ~10 м/с); авария — вертикальная > 7 м/с (вдвое больше энергии удара), горизонтальная
12 м/с или крен > 35°. После мягкой/жёсткой посадки пилот добегает и может идти или снова разбегаться. После отрыва пилот ещё на ногах (держит стойки, бежит), пока ступни не поднялись над землёй выше
takeoff.upright_clear_m(1 м — длина ноги): в это время угол атаки задают руки, как на разбеге (launch.alpha_neutral_deg + pitch·alpha_range_deg), а касание — снова шаги разбега (GroundRun.resume, отрыв опять при L ≥ W), не посадка. Выше — пилот в подвеске, трим по трапеции, касание — посадка. Геометрия вместо прежней «форы» 1,5 с (К3 v3).Перегрузка.
FlightModel.load.load_factor— n = подъёмная сила / вес, сглаженная (flight.json → load_factor.filter_s); естьload_raw,load_max,load_min. Если в Telemetry есть полеload_factor, оно заполняется. Поломка крыла при превышении (FR-14c) отложена.
Допущения
- Нет скольжения и рыскания как отдельной степени свободы; нет момента от вращения по курсу.
- Тангаж — кинематический (первый порядок), без моделирования момента трапеции и центровки.
- Нет экранного эффекта у земли; на земле пилот и крыло — одна точка (кроме крена крыла на
плечах — «Крен на земле»). Выравнивание —
феноменологическое (
cn_factor,dynamic_cl_factor): маятник пилота не моделируется. - Поляры синтезированы по паспортным ориентирам (производители не публикуют полных поляр).
- FR-1 внутренне противоречив (качество 15 @ 42 ⇒ снижение 0,78 < «мин.» 0,9); принято мин. снижение 0,82 @ 33–36, качество 15 @ 45 км/ч (questions.md).
Параметры
configs/wings/<id>.json— площадь, размах, масса крыла, диапазон и эталон массы пилота, поляра (точки), скорости трима/«на себя»/«от себя», наклон CL(α), крен, сваливание, разбег, визуал.reference— ориентиры поляры для тестов.configs/pilot.json— масса пилота, ходьба, разбег, визуал пилота.configs/flight.json— плотность воздуха, инерция от массы, демпфирование фугоиды, опрос воздуха, разбег и ошибки, крен крыла на земле, оценка посадки, визуал.
Крылья
Девять крыльев с реальными прототипами (план и источники — docs/archive/plan/wings-lineup.md). Группы —
configs/wing_groups.json (порядок в меню «Полёт…»), модели в группе — по reference.best_glide,
затем wind_max_ms (WingCatalog, scripts/game/wing_catalog.gd):
| Группа | id: прототип | Качество | Ветер до, м/с |
|---|---|---|---|
| soviet — Советские 1980-х | slavutich_ut: Славутич-УТ; apogee: «Апогей» (Мысенко); atlas: «Атлас» | 6,2 / 7 / 8,4 | 7 / 10 / 8 |
| trainer — Учебные однообшивочные | target: Aeros Target 16; training: WW Falcon 170 | 7,3 / 9 | 8 / 8 |
| kingpost — Мачтовые двухобшивочные | magic: Airwave Magic IV 166; laminar: Icaro Laminar Easy 14 | 10,6 / 13,2 | 10 / 10 |
| topless — Безмачтовые | sport: Moyes Litespeed RS 4; combat: Aeros Combat GT 13.2 | 15 / 16 | 12 / 12 |
Поля крыла для меню (у каждого _doc): group, wind_max_ms (комфортный ветер на старте для
опытного пилота — подсказка, не запрет), prototype, era, kingpost, double_surface_pct
(0 — однообшивочное). Управляемость задаётся прежними параметрами: «Апогей» — самый устойчивый и
лёгкий в крене, мягкое сваливание (слова пилота). Wills Wing Sport 2 (kingpost) убран
28.09.2026.
Тесты: tests/flight/test_wings.gd, tests/game/test_wing_catalog.gd; поляра и сваливание —
для всех крыльев из Config.list_configs("wings").
API
# FlightModel (RefCounted)
signal took_off
signal landed(result: Dictionary) # {grade: "soft"|"hard"|"crash", vertical_speed_ms,
# horizontal_speed_ms, bank_deg, position, flight_time_s}
signal takeoff_failed(reason: String)
var telemetry: Telemetry
var mode: FlightModel.Mode # GROUND, AIR, LANDED, FAILED
func setup(wing: Dictionary, pilot: Dictionary, flight_override := {}) -> void
func reset_on_ground(pos: Vector3, heading_deg: float) -> void
func reset_in_air(pos: Vector3, heading_deg: float, airspeed_ms := 0.0, wind := Vector3.ZERO) -> void
func step(dt: float, input: ControlInput, air_fn: Callable, ground_fn: Callable) -> void
func phase() -> String # standing/walking/running/flying/landed/failed
func trim_speed() -> float; func stall_speed() -> float; func steady_glide(v) -> Vector2
static func clamp_pilot_mass(wing: Dictionary, mass_kg: float) -> float
# Glider (Node3D, scenes/glider/glider.tscn); начало координат — ноги пилота
signal telemetry_updated(t: Telemetry) # каждый шаг физики, один и тот же объект
signal landed(result: Dictionary)
signal takeoff_failed(reason: String)
signal took_off
@export var wing_id := "sport"; @export var pilot_mass_kg := 0.0; @export var auto_start
var control: ControlInput
func setup(wing_name: String, mass_kg := 0.0) -> void # масса ограничивается диапазоном крыла
func set_input(c: ControlInput) -> void
func set_air_fn(f: Callable) -> void # f(pos: Vector3) -> Vector3
func set_ground_fn(f: Callable) -> void # f(x: float, z: float) -> float
func reset_on_ground(pos: Vector3, heading_deg: float) -> void
func reset_in_air(pos: Vector3, heading_deg: float, airspeed_ms := 0.0) -> void
func step(dt: float) -> void # шаг физики; _physics_process зовёт его сам —
# интегратор может выключить physics_process и шагать сам
func get_telemetry() -> Telemetry
func phase() -> String
func get_marker(name: String) -> Node3D # PilotHead, InstrumentMount, BaseBar, HangPoint, WingTipL/RВизуал и контракт моделей
Glider создаёт сцену flight.json → visual.scene (scenes/glider/glider_visual.tscn,
скрипт GliderVisual) и вызывает build(wing, pilot, visual_cfg). Нода Visual стоит в начале
координат планера (ноги пилота). Оси моделей: 1 ед. = 1 м, вперёд −Z, вверх +Y (glTF «+Y up»).
Крыло — wings/<id>.json → visual.visual_model (например res://assets/models/glider_sport.glb).
Начало координат = точка подвески. Ноды по имени:
Sail— парус (меш),ControlFrame— трапеция;- маркеры-пустышки:
HangPoint(подвеска, ≈ начало координат),BaseBar(центр ручки),InstrumentMount(крепление прибора, +Z маркера — нормаль экрана, смотрит на пилота),WingTipL,WingTipR. Модель ставится так, чтоHangPointоказывается на высотеvisual.hang_height_mнад ногами.
Пилот — pilot.json → visual.visual_model (res://assets/models/pilot.glb). Начало координат —
карабин (крепится в HangPoint), тело лёжа ниже и чуть позади, голова в −Z; пустышка Head —
точка глаз. Модель висит под нодой обёртки Pilot, которая сдвигается по крену (±pilot_shift_m)
и тангажу (±pilot_bar_m). На земле обёртка ставит пилота вертикально (поворот лежачей модели на
90° вокруг центра тела pilot.json → visual.body_*). Маркер PilotHead обёртки следует за Head
(get_marker("PilotHead"), GliderVisual.get_head_transform()) — для кабинной камеры.
Парус — после загрузки крыла SailMaterial.apply(Sail, "<имя модели>") (шейдер агента models,
параметры — configs/sail.json), каждый кадр GliderVisual.set_flight(airspeed, stall, turbulence):
stall = FlightModel.stall_amount() (доля сорванного потока 0..1), turbulence = средний разброс
мгновенной перегрузки FlightModel.load.jitter / sail.json → turbulence_full_g (0,5 g): в спокойном
воздухе ~0, в термике ~0,1–0,3, в облачной болтанке — до 1.
Нет файла модели — предупреждение в лог и заглушка из примитивов с теми же именами нод.
Тесты
godot --headless --path . res://tests/run_tests.tscn -- --filter=flight — тесты: поляра и трим
каждого крыла, ориентиры FR-1/FR-2, √M и неизменное качество, инерция, переложение 45→45 (2,3 / 2,3 /
2,7 с), удержание крена; режим «смещение веса» (test_roll_weight_shift): крен от смещения,
выравнивание в центре (учебное быстрее спортивного), крен без спирали, 45→45, клавиша X;
радиус и снижение в вираже, сваливание (нос вниз, потеря высоты,
на крыло, выход), поток +2 м/с, снос и путевая, несимметричный подъём, порыв, разбег со встречным /
попутным / боковым ветром, нос высоко/низко, слабый разбег, ходьба и склон, оценка посадки, выравнивание (штиль / на 1 м и 3 м выше / встречный ветер), пробежка, склон,
нода Glider.
Таблица поляры
godot --headless --path . res://tests/flight/polar_table.tscn — прогон модели в спокойном воздухе
(плотность уровня моря) при разных положениях трапеции, для мин./эталонной/макс. массы пилота.
«срыв» — трапеция за сваливанием, крыло парашютирует (значения усреднены по циклам срыва).
Учебное (training), пилот 64 кг, полная масса 87 кг: сваливание 22.3 км/ч, трим 28.5 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 33.3 | 4.77 | 1.9 срыв
+0.75 | 28.8 | 2.95 | 2.7
+0.50 | 23.3 | 1.10 | 5.9
+0.25 | 25.9 | 0.94 | 7.7
+0.00 | 28.4 | 0.93 | 8.4
-0.10 | 31.1 | 0.98 | 8.8
-0.20 | 33.7 | 1.05 | 9.0
-0.30 | 36.4 | 1.13 | 8.9
-0.45 | 40.3 | 1.32 | 8.5
-0.60 | 44.3 | 1.57 | 7.8
-0.80 | 49.5 | 2.02 | 6.8
-1.00 | 54.7 | 2.57 | 5.9
Учебное (training), пилот 80 кг, полная масса 103 кг: сваливание 24.3 км/ч, трим 31.0 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 36.2 | 5.19 | 1.9 срыв
+0.75 | 31.0 | 1.43 | 6.0
+0.50 | 25.3 | 1.20 | 5.9
+0.25 | 28.1 | 1.02 | 7.7
+0.00 | 30.9 | 1.02 | 8.4
-0.10 | 33.8 | 1.07 | 8.8
-0.20 | 36.7 | 1.14 | 9.0
-0.30 | 39.6 | 1.23 | 8.9
-0.45 | 43.9 | 1.43 | 8.5
-0.60 | 48.2 | 1.71 | 7.8
-0.80 | 53.9 | 2.20 | 6.8
-1.00 | 59.6 | 2.80 | 5.9
Учебное (training), пилот 100 кг, полная масса 123 кг: сваливание 26.5 км/ч, трим 33.9 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 39.6 | 5.67 | 1.9 срыв
+0.75 | 32.1 | 2.40 | 3.7 срыв
+0.50 | 27.7 | 1.31 | 5.9
+0.25 | 30.7 | 1.11 | 7.7
+0.00 | 33.8 | 1.11 | 8.4
-0.10 | 36.9 | 1.17 | 8.8
-0.20 | 40.1 | 1.24 | 9.0
-0.30 | 43.2 | 1.35 | 8.9
-0.45 | 48.0 | 1.56 | 8.5
-0.60 | 52.7 | 1.87 | 7.8
-0.80 | 58.9 | 2.40 | 6.8
-1.00 | 65.1 | 3.06 | 5.9
Мачтовое (laminar), пилот 65 кг, полная масса 93 кг: сваливание 24.2 км/ч, трим 32.2 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 30.6 | 2.94 | 2.9 срыв
+0.75 | 29.1 | 1.07 | 7.6 срыв
+0.50 | 26.1 | 1.10 | 6.6
+0.25 | 29.1 | 0.92 | 8.8
+0.00 | 32.1 | 0.85 | 10.5
-0.10 | 36.7 | 0.84 | 12.2
-0.20 | 41.3 | 0.88 | 13.0
-0.30 | 45.9 | 0.98 | 13.0
-0.45 | 52.8 | 1.28 | 11.5
-0.60 | 59.6 | 1.72 | 9.6
-0.80 | 68.7 | 2.48 | 7.7
-1.00 | 77.8 | 3.45 | 6.3
Мачтовое (laminar), пилот 82 кг, полная масса 110 кг: сваливание 26.3 км/ч, трим 35.0 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 33.6 | 3.33 | 2.8 срыв
+0.75 | 32.6 | 2.41 | 3.8
+0.50 | 28.3 | 1.20 | 6.6
+0.25 | 31.6 | 1.00 | 8.8
+0.00 | 34.9 | 0.92 | 10.5
-0.10 | 39.9 | 0.91 | 12.2
-0.20 | 44.9 | 0.96 | 13.0
-0.30 | 49.9 | 1.07 | 13.0
-0.45 | 57.4 | 1.39 | 11.5
-0.60 | 64.8 | 1.87 | 9.6
-0.80 | 74.8 | 2.70 | 7.7
-1.00 | 84.6 | 3.79 | 6.2
Мачтовое (laminar), пилот 95 кг, полная масса 123 кг: сваливание 27.8 км/ч, трим 37.0 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 37.7 | 3.97 | 2.6 срыв
+0.75 | 34.0 | 1.82 | 5.2
+0.50 | 30.0 | 1.27 | 6.6
+0.25 | 33.5 | 1.06 | 8.8
+0.00 | 36.9 | 0.98 | 10.5
-0.10 | 42.2 | 0.96 | 12.2
-0.20 | 47.5 | 1.01 | 13.0
-0.30 | 52.8 | 1.13 | 13.0
-0.45 | 60.7 | 1.47 | 11.5
-0.60 | 68.5 | 1.97 | 9.7
-0.80 | 79.2 | 2.87 | 7.7
-1.00 | 89.3 | 4.03 | 6.2
Спортивное безмачтовое (sport), пилот 70 кг, полная масса 104 кг: сваливание 25.4 км/ч, трим 33.7 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 32.5 | 2.88 | 3.1
+0.75 | 32.4 | 2.28 | 3.9
+0.50 | 27.5 | 0.86 | 8.9
+0.25 | 30.6 | 0.77 | 11.0
+0.00 | 33.6 | 0.77 | 12.2
-0.10 | 39.1 | 0.78 | 14.0
-0.20 | 44.6 | 0.83 | 14.9
-0.30 | 50.1 | 0.99 | 14.1
-0.45 | 58.4 | 1.37 | 11.9
-0.60 | 66.5 | 1.92 | 9.6
-0.80 | 77.6 | 3.02 | 7.1
-1.00 | 88.0 | 4.30 | 5.7
Спортивное безмачтовое (sport), пилот 85 кг, полная масса 119 кг: сваливание 27.2 км/ч, трим 36.0 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 34.5 | 2.88 | 3.3 срыв
+0.75 | 33.7 | 1.51 | 6.2
+0.50 | 29.4 | 0.92 | 8.9
+0.25 | 32.7 | 0.83 | 11.0
+0.00 | 35.9 | 0.82 | 12.2
-0.10 | 41.8 | 0.83 | 14.0
-0.20 | 47.7 | 0.89 | 14.9
-0.30 | 53.6 | 1.05 | 14.1
-0.45 | 62.4 | 1.46 | 11.9
-0.60 | 71.2 | 2.04 | 9.7
-0.80 | 83.0 | 3.26 | 7.1
-1.00 | 93.8 | 4.59 | 5.7
Спортивное безмачтовое (sport), пилот 105 кг, полная масса 139 кг: сваливание 29.4 км/ч, трим 38.9 км/ч
трапеция | V км/ч | снижение м/с | качество
+1.00 | 37.3 | 4.67 | 2.2
+0.75 | 36.4 | 1.66 | 6.1
+0.50 | 31.8 | 0.99 | 8.9
+0.25 | 35.3 | 0.89 | 11.0
+0.00 | 38.8 | 0.89 | 12.2
-0.10 | 45.2 | 0.90 | 14.0
-0.20 | 51.6 | 0.96 | 14.9
-0.30 | 58.0 | 1.14 | 14.1
-0.45 | 67.4 | 1.57 | 11.9
-0.60 | 77.1 | 2.21 | 9.7
-0.80 | 89.6 | 3.57 | 7.0
-1.00 | 100.9 | 4.91 | 5.7Крен на земле (SF-3, GroundRun._ground_bank, flight.json → ground_bank)
Пилот стоит или бежит с крылом на плечах; крыло поворачивается по крену вокруг оси киля у плеч.
Крен FlightModel.bank — относительно горизонта, не склона: на косом склоне крыло ровно по
горизонту, верхняя консоль ближе к земле (так и в жизни). Крен ведёт рука пилота к заданному
φ_зад = input.roll · command_max_deg (К3 v3; без ввода — горизонт); поворот стоя (input.turn)
крыло не кренит. Уравнение (полунеявный Эйлер, шаг физики):
I·φ̈ = M_скольж + M_несимм + M_веса + M_центроб + M_пилота − D·φ̇
- I — инерция по крену, кг·м²: конструкция
span_mass_fraction(0,75) массы крыла по размаху как стержень, m·b²/12 (киль, мачта, трапеция — у оси), плюс присоединённая масса воздуха пластины π/4·ρ·c² на метр размаха (c = S/b): π/48·ρ·c²·b³. «Спорт» (34 кг, 10,4 м, 14,1 м²): 230 + 165 ≈ 400 кг·м²; остальные крылья — таблица ниже. - M_скольж — поперечная устойчивость от скольжения. Стреловидное крыло при скольжении β:
наветренная консоль видит поток ближе к нормали к передней кромке, её подъёмная сила больше.
Полосовая теория (подъёмная сила ∝ квадрату нормальной к кромке скорости, эллиптическая
нагрузка, ЦД полукрыла на 4/(3π) полуразмаха): C_l = −0,42·tgΛ·C_L·½sin2β; стреловидность
передней кромки по геометрии крыльев ≈ 15–18° ⇒ |C_lβ| =
slip_roll_per_cl· C_L = 0,12·C_L. Тогда M = ½ρ·S·b·|C_lβ|·u·s, где u — встречная, s — боковая составляющие потока на крыло (V²·½sin2β = u·s): при чисто боковом ветре стоя (u = 0) момента нет, момент растёт со встречной скоростью — на бегу. C_L — по текущему углу атаки (обтекаемая часть, до C_L max): нос ниже — крыло спокойнее в боковой ветер (C_L меньше), это и есть техника старта в боковик. Ветер слева (воздух движется вправо) поднимает левую консоль — крен вправо (+). - M_несимм — несимметрия вертикального ветра по размаху (как в воздухе, FR-8): воздух
опрашивается на концах (
air_sampling.tip_fractionполуразмаха) на высоте крыла; M = D·air_roll_gain·(w_лев − w_прав)/b — в воздухе тот же момент даёт установившуюся угловую скорость (w_лев − w_прав)/b. - D — аэродемпфирование по крену: C_lp = −CLα/8 (эллиптическая нагрузка, CLα из крыла), D = ¼·ρ·V·S·b²·|C_lp| (V — воздушная скорость). «Спорт» при 4,5 м/с ≈ 1200 Н·м·с.
- M_веса = m_крыла·g·h·sinφ, h =
wing_cg_above_axis_m(0,2 м: ЦТ крыла чуть выше киля на плечах) — опрокидывающий: чем больше крен, тем сильнее. - M_центроб = −m_крыла·v·ω·h·cosφ — центробежная сила на ЦТ крыла на дуге разбега (наружу); при a = 2 м/с² ≈ 14 Н·м, крен ≈ 0,2°.
- M_пилота — «рука пилота»: регулятор к заданному крену φ_зад «пропорционально отклонению +
демпфирование», M = −I/τ²·(φ − φ_зад) − 2I/τ·φ̇ (
GroundRun.pilot_moment(m, φ − φ_зад, …)), жёсткость I/τ², демпфирование 2I/τ (критическое), τ =pilot_response_s= 0,3 с (реакция на наклон груза в руках 0,15–0,3 с), ограниченныйpilot_moment_max_nm·N/W. M_max = 200 Н·м — две оценки сходятся: руки на стойках ≈ ±0,35 м от киля, по ≈ 300 Н вверх/вниз: 2·300·0,35 ≈ 210; опора — пилот с крылом не может передать на землю момент больше W·d, не опрокинувшись через ребро стоп: (85+30)·9,81·0,18 ≈ 200 (полуширина стойки ≈ 0,15 м плюс смещение тела). Второе объясняет множитель N/W = clamp(1 − L_верт/W, 0, 1) — доля веса на ногах (GroundRun.feet_load): стоит — полная сила, крыло несёт половину веса — половина, на отрыве (L_верт = W) — ноль, крен свободен. Предел достигается при отклонении от φ_зад ≈ 2–3°: стоя в штиль рука доводит крыло до φ_зад (против веса крыла: m·g·h·sin15° ≈ 18 Н·м ≪ M_max), в сильный встречный — только пока хватает M_max·N/W. - Заданный крен φ_зад,
command_max_deg= 15°: кисти на стойках у плеч ≈ ±0,35 м от киля; с крылом на плечах одну руку можно поднять, другую опустить на ≈ 0,1 м — 0,2 м на базе 0,7 м: atan(0,2/0,7) ≈ 16°. Взято 15° — меньше крена касания консоли ровной земли (≈ 17–21°): полный ход руки сам консоль на землю не кладёт, ветер или дуга — могут. Ввод с клавиатуры ведёт ручку к ±1 так же, как в полёте (тот же ход и возврат), поэтому на отрывеrollнепрерывен. - Опрокидывание: если |M_ветра| > M_max·N/W, рука «проскальзывает» — крен растёт (избыток
момента против аэродемпфирования, дальше помогает вес крыла); опущенная консоль коснулась земли
(на ровном — крен ≈ asin(1,5/полуразмах) ≈ 17°) — срыв
wingtip(итог в интерфейсе, без подсказок). Проверяется и стоя. - Переход в полёт: предел руки уходит в ноль вместе с N/W, поэтому к отрыву крыло уже
свободно;
bankиroll_rateна отрыве непрерывны, в воздухе их подхватывает прежняя модель крена (_update_roll, удержания нет). Боковой ветер на отрыве поднимает наветренную консоль (слабый боковик 2,5 м/с — крен на отрыве 6–10°, 10°/с) — в воздухе скольжения нет, и крен дальше гасится моделью полёта. - Поворот (
_turn, К3 v3): стоя и шагом — на месте от input.turn,pilot.walk.turn_rate_dps(45°/с), крыло не кренится. На бегу — дугой от крена крыла (input.turn не используется). Пилот бежит по дуге заданного рукой крена φ_зад — наклоняется в поворот вместе с крылом на плечах: a_зад = g·tgφ_зад. Боковая составляющая подъёмной силы крыла при крене φ тянет вбок: a_кр = (1 − N/W)·g·sinφ (L ≈ L_верт = (1 − N/W)·W). Разницу дают ноги трением, но не больше, чем позволяет равновесие наклонённого бегуна с весом N на ногах: N/W·a_max. Итого a = a_кр + clamp(a_зад − a_кр, ±N/W·a_max), |a| ≤ a_max =pilot.run.turn_accel_max_ms2= 2 м/с² (наклон тела с ≈ 30 кг крыла на плечах на бегу ≈ 10–12°: g·tg11° ≈ 2; сцепление на траве μ ≈ 0,5 допускает ≈ 5 — не оно ограничивает). ω = a/v, но не больше поворота на месте. Пока ноги нагружены, курс — от φ_зад (предел a_max при φ_зад ≈ 11,5°, ≈ ¾ хода руки), R_min = v²/a_max: 18 м на 6 м/с, 29 м на 7,7 м/с. К отрыву (N → 0) курс — от крена крыла, a = g·sinφ — тот же вираж g·tgφ/v, что в воздухе, без излома. Без ввода крена (φ_зад = 0) — бег прямо; крен от бокового ветра ноги перебарывают, пока нагружены, а почти разгруженного пилота крыло уводит в дугу по ветру — крен растёт быстрее (боковик сильнее). Пилот исправляет креном руки: у крупных крыльев, долго бегущих почти разгруженными (ww_cross_country, ww_ultra_sport), в боковик 2,5 м/с при встречном 4 без поправки крен к отрыву переходит 20° (срывwingtip), с поправкой — взлёт с креном 13–18°. Порог бокового ветра стоя (M_ветра = M_max·N/W; бисекция по самой модели, как вtest_side_gust_threshold): боковая составляющая, м/с, при которой рука перестаёт держать, пилот лицом во встречный ветер. Нос нейтрально (α ≈ 16–17°, C_L у C_L max) / трапеция наполовину на себя (нос на 10° ниже):
| Крыло | I, кг·м² | встречный 2 м/с | 4 м/с | 6 м/с |
|---|---|---|---|---|
| training | 305 | 3,7 / 6,0 | 1,8 / 3,5 | 0,7 / 2,0 |
| apogee | 356 | 3,5 / 5,7 | 1,7 / 3,3 | 0,6 / 1,8 |
| sport | 396 | 3,8 / 6,0 | 1,9 / 3,4 | 0,9 / 2,0 |
| combat | 379 | 4,1 / 6,4 | 2,1 / 3,7 | 1,0 / 2,2 |
(остальные — в пределах тех же чисел; atlas при нейтральном носе срывается по носу.) Порог падает
со встречным ветром вдвойне: момент ∝ u·s, а крыло разгружает ноги (N/W) — в сильный ветер
надо держать нос ниже и стоять точно против ветра. Выше порога крен растёт медленно: избыток
момента против аэродемпфирования (порыв ×1,2 — опрокидывание за ≈ 10 с, ×2 — за ≈ 3,5 с);
на 20 % ниже — крен ≈ 1,6°. На бегу рука ослабевает с разгрузкой, и в сильный боковик крыло
отрывается с креном (6 м/с сбоку при встречном 1: «спорт» 17°, combat 22°, apogee 8°) — срыв
wingtip на разбеге получается, только если консоль успеет коснуться земли до отрыва.
Чего модель не умеет: ноги пилота неподвижны относительно крыла (нет шага в сторону, подсаживания
под опущенную консоль); наклон крыла в ветер — только рукой до ±15°; поза тела не моделируется — только предел
момента; C_lβ — полосовая теория стреловидности без анэдра и выгиба паруса и за пределами малых β
(½sin2β); порывы по размаху — только вертикальные; утомление рук нет; при опрокидывании крыло не
касается земли физически — только срыв wingtip по касанию. Следствие модели (находка, не подгонка): при
нейтральном носе порог бокового ветра стоя быстро падает со встречным — при встречном 6 м/с всего
0,6–1,0 м/с сбоку (≈ 5–10° от оси ветра), с носом на 10° ниже ≈ 2 м/с: момент ∝ C_L·u·s, а крыло
уже несёт половину веса и рука слабеет вдвое. Лечится техникой (нос ниже, точно против ветра),
а не параметрами; если пилоты скажут, что в 6 м/с крыло держится заметно легче, — первые кандидаты
на уточнение C_lβ (анэдр, выгиб паруса) и M_max.
Тесты — tests/flight/test_ground_bank.gd (условия SF-3), test_takeoff.gd::test_crosswind_*, test_liftoff_physics.gd.
Поза крыла на земле (SF-4, зазор крыла над рельефом)
Поза планера на земле — Telemetry.basis = from_euler(θ, −курс, −крен) с началом в ступнях
(FlightModel.position), карабин на visual.hang_height_m выше (К4, не менялось). Замер —
tools/flight/wing_clearance.gd: вершины реальной модели крыла (консоли, нос/хвост киля,
трапеция) в этой позе, зазор до рельефа по вертикали; таблица по фазам и стартам —
tools/flight/wing_clearance_run.tscn, данные — docs/archive/plan/sf4_wing_clearance/.
Почему крыло уходило в землю (по замерам, разложение на кандидатов):
- Крен от A/D — главная причина на ровном: A/D стоя кренили крыло до −17,9°, консоль в землю на 0,20 м (combat); на склоне 20° стоя с A пилот разворачивается поперёк склона — до −2,2 м. Исправлено SF-3: A/D на земле — только курс, крен держит «рука пилота» (раздел «Крен на земле»).
- Тангаж стоя от горизонта: пока потока нет, угол атаки считался от горизонта, и на склоне 20°
вниз нос стоял на 20° выше, чем на первом шаге разбега; с трапецией от себя концы консолей
уходили в склон на 0,56 м. Теперь опорное направление без потока — вдоль склона по курсу
(плавный переход к потоку до
min_airspeed_ms), а первый шаг послеreset_on_groundсразу ставит установившийся тангаж (без переходного наклона от горизонта). - Не причины: поворот базиса вокруг ступней (против поворота вокруг карабина — зазор того же порядка), высота подвески и сглаживание позы.
Зазор после исправлений (штиль, крен 0, все крылья; tests/flight/test_wing_clearance.gd):
ровно и склон 20° вниз по курсу — стоит (трапеция нейтрально и до упора), шагом, разбег, A/D стоя
и на бегу по ровному — ≥ 0,35 м (порог теста 0,3 м).
Границы модели:
- Рельеф рисуется треугольниками по узлам сетки (25 м), ступни стоят на билинейной высоте —
видимая земля под крылом может быть до ≈ 0,3 м выше расчётной (замер
clear_renderedна стартах локаций). Рельеф не трогаем, пока на стартах локаций при крене 0 зазор по видимой земле ≥ 0 (таблица ниже). - Косой склон и поворот поперёк склона — крыло ровно по горизонту, верхняя консоль у склона
или в нём (косой 15°: от −0,8 до +0,06 м; стоя с A на склоне 20° поперёк — до −0,3 м, на бегу
по дуге поперёк — до −0,57 м): так и в жизни консоль чиркает по склону; столкновения крыла с
землёй на земле нет — срыв
wingtip, только если консоль опущена креном. - Тело пилота стоя вертикально визуально:
GliderVisual._ground_poseснимает тангаж крыла поворотом тела вокруг ступней (иначе пилот «сидит», отклонившись назад на 16–37° вместе с крылом); руки тянутся к стойкам (IK) как получится; физическая поза (К4) не меняется.
Старт в сильный ветер (F01)
Нос на земле без ввода — угол разбега крыла launch.alpha_neutral_deg (16–17°, CF-3; прежняя
надбавка игрока run_nose_neutral = 0,3 — 22–23° — у «Славутича» с α_срыва 21,4° была за срывом:
нос высоко, отрыва нет). Нос пилот ведёт сам на полный ход (±20° угла атаки). В сильный ветер
нос выше нейтрали близок к срыву: в затишье, когда у склона дует снизу, поток разворачивается и
угол атаки уходит за срыв (крыло сорвано — отрыва нет). Правильная техника, как в жизни: при ветре ≥ 3,5 м/с
держать нос ниже — угол атаки ≈ 18°; в штиль — чуть выше нейтрали (≈ 19–20°), чтобы крыло
несло на малой скорости. Автопилот (scripts/game/autopilot.gd) так и делает; тест —
tests/flight/test_strong_wind_launch.gd.