Ленточка на тросе трапеции («ниточка», yaw string)
Просьба пилота: ленточка, привязанная к переднему тросу трапеции (сначала висела на боковом; перевешена по просьбе пользователя, wire: "front"). На старте показывает направление и
силу ветра, в полёте — поток воздуха у крыла (скольжение, рыскание). Подсказок нет: ленточка — просто
предмет в мире, как у настоящих пилотов.
Решения
- Обе стороны (
sides: ["left","right"]): видно и при взгляде влево, и вправо; при скольжении они отклоняются вместе, разница видна сразу. Одну можно оставить в конфиге. - Боковой трос (
wire: "side"), узел вalong_wire_m= 0,3 м от угла трапеции. Из кабины (FOV 60°) лента видна при взгляде вбок на 50–60° (на старте — вбок-вниз на 40°), как у настоящего пилота: периферия и быстрый взгляд.wire: "front"— на передний трос (к носу), ближе к центру поля зрения. - Конфиг —
configs/instruments.json → telltale: ленточка — простейший прибор (показывает поток), аsail.json— только параметры шейдера паруса. - Цвет — насыщенный красный (слова пилота), лёгкое свечение (флуоресцентная ткань видна в тени).
Узел — из геометрии крыла, без координат в коде
Telltale.find_anchor(wing, side, cfg):
- Пустышки
TelltaleL/TelltaleRв модели крыла, если есть (будущие модели могут задать узел явно, ось +Y пустышки — вдоль троса). - Иначе — поверхность с материалом
Wireу мешаControlFrame(контракт docs/guide/models.md): угол трапеции — нижние точки тросов этой стороны; боковой трос — к самой дальней по размаху точке (узел поперечины на передней кромке), передний — к самой передней точке у киля (нос). Узел = угол +along_wire_mвдоль троса. Работает для всех крыльев изbuild_gliders.py. Нет тросов (заглушка без модели) — предупреждение в лог, ленточки нет.
Физика (TelltaleModel, RefCounted, headless)
- Поток у узла:
w = ветер(точка узла) − (скорость планера + вращение), ветер —Atmosphere.air_velocity_at(порывы, термики, склоновый поток — всё, что в нём есть); вращение — по смене ориентации планера за шаг (airflow_at:(B·r − B_prev·r)/dt). - Цепочка из
segmentsзвеньев; равновесие звена —ŵ·(|w|/v45)² + вниз(сопротивление ткани против веса;lift_45_ms— поток, при котором лента на 45°). Без потока висит вниз. - Подстройка экспоненциальная (безусловно устойчива): темп
follow_k·|w|/длина звена + pendulum_rate. Хвост тянется за предыдущим звеном (chain_coupling), трепет — бегущая синусоида с амплитудой отflutter_start_msдоflutter_full_ms, частотаflutter_hz_per_ms·|w|(не вышеflutter_max_hz). - Цена: 1 запрос ветра на ленточку за шаг физики, 6 пар вершин ImmediateMesh за кадр.
Подключение
GliderVisual.build→Telltale.build_on(wing)(нодыTelltaleL/R— дети крыла, едут с тряской крыла).Glider.step→visual.step_telltales(dt, prev_basis, cur_xform, model.velocity, air_fn).- До первого шага ленточка скрыта: у ботов (
BotGlider) шагов нет — ленточек не видно.
Ограничение модели полёта
FlightModel летит без скольжения: курс каждый шаг ставится по воздушной скорости в центре крыла.
Поэтому в полёте лента показывает только то, что даёт вращение (ввод в крен: узел ~0,7 м над ногами,
~1–2°) и разницу ветра в точке узла и в центре (болтанка) — в ровном вираже она строго назад. Появится
скольжение в модели полёта (обратное рыскание при вводе в крен, боковой порыв) — лента покажет его
сама, код ленточки менять не нужно.
Проверка
tests/instruments/test_telltale.gd: встречный и боковой ветер на месте, скольжение 12° в полёте (угол ленты в осях планера = углу потока ±3°), провис при 1 м/с, без потока — вниз, вращение, узел на тросе у каждого крыла (симметрично, над штангой), планер на старте в боковом ветре.- Кадры:
tools/shots/telltale_shot.tscn(камера у крыла--eye,--set-wind=<км/ч>,<откуда>для бокового ветра на старте; без--eye— кадр камеры игры с--camera=cockpit --look).
Проверено пилотом (28.09.2026): в вираже лента смотрит строго назад, бокового сноса в полёте нет — это правильно (крыло летит носом в поток). Скольжение в лётную модель ради ленты не добавлять.