Программирование логики захвата и метания предметов в VR
Взять предмет рукой в VR и швырнуть его в стену — одна из самых базовых и одновременно одна из самых технически неочевидных механик. Когда она работает хорошо, игрок не замечает ничего. Когда плохо — предмет телепортируется в руку с задержкой, дрожит при удержании, летит не туда или проваливается сквозь пол.
Почему стандартный Rigidbody + Attach не работает как ожидается
Первый инстинкт — при захвате переместить объект в позицию контроллера и сделать его child. Это работает 5 секунд. Потом выясняется:
Rigidbody с isKinematic = false под контроллером-родителем ведёт себя непредсказуемо: физический движок не знает, что объект двигается кинематически, и продолжает вычислять коллизии со старым velocity. Объект проходит сквозь поверхности при быстром движении руки.
С isKinematic = true объект перестаёт участвовать в физике вообще: он игнорирует коллизии, не толкает другие объекты. Можно размахивать мечом сквозь всё.
Правильное решение в XR Interaction Toolkit — XRGrabInteractable с Movement Type = VelocityTracking. В этом режиме объект не телепортируется к руке — к нему применяется velocity, которая двигает Rigidbody к target позиции. Физика остаётся активной. Объект сталкивается с препятствиями при перемещении рукой. Параметры Track Position Strength и Track Rotation Strength определяют «жёсткость» слежения — высокие значения (20–30) дают плотное прилипание к руке, низкие (5–10) — мягкое, с лагом, как будто держишь тяжёлый шар.
Метание: откуда берётся velocity
После release нужно передать объекту скорость, соответствующую движению руки. Казалось бы, просто rigidBody.velocity = controllerVelocity. Проблемы:
Velocity контроллера в XR Node — это мгновенное значение на момент release. Если игрок резко остановил руку перед броском (что делают многие), velocity = 0 и объект падает вертикально вниз. Реальный физический бросок использует пиковую скорость в фазе броска, не финальную.
Velocity smoothing: XRGrabInteractable собирает историю позиций контроллера за последние N кадров (по умолчанию — Velocity Smoothing буфер 5–10 фреймов). При release вычисляется средняя velocity по буферу. Это имитирует физическую инерцию. В версии XR Interaction Toolkit 2.3+ параметр Throw Velocity Scale дополнительно масштабирует финальную velocity — значение 1.5–2.0 даёт ощущение более «весомого» броска.
Angular velocity для вращения. Если не передать angular velocity при броске — объект летит без вращения, что неестественно. rigidBody.angularVelocity = controllerAngularVelocity * throwAngularVelocityScale.
Специфика для Quest и мобильного VR
На Quest controller tracking работает с задержкой 20–30 мс (Prediction компенсирует частично). Velocity из InputDevice.TryGetFeatureValue(CommonUsages.deviceVelocity) уже предсказана Meta Runtime — использовать именно её, не вычислять вручную по дельте позиций.
Haptic feedback при захвате — обязателен для quality of life. XRBaseController.SendHapticImpulse(0.7f, 0.05f) при начале grab, SendHapticImpulse(0.3f, 0.02f) при release. Без вибрации VR-захват ощущается пластиковым.
Отдельная тема — two-handed grab: оружие, которое можно держать двумя руками. XR Interaction Toolkit 2.x предоставляет XRGrabInteractable с несколькими Attach Points и логикой dominant/secondary hand. Для оружия нужна кастомная логика: основная рука определяет позицию, вторая — добавляет ротацию вдоль оси ствола.
Сроки: базовая grab/throw механика через XR Interaction Toolkit — 2–4 рабочих дня; кастомная two-handed система с haptics и velocity tuning — 1–2 недели. Стоимость определяется после анализа требований проекта.





