В этой статье, используя различные Joint’s мы реализуем физическое взаимодействие с дверьми, ящиками и прочими подобными объектами в VR.
Краткую информацию об этом вы можете получить в следующем ролике:
🔗 https://youtu.be/T5LZJFg9vWM альтернативная ссылка: 🔗https://rutube.ru/video/7e562e58444b23d899ff0d09a9b32be6/?r=wd
В статье некоторые моменты будут затронуты чуть более глубоко.
Шаг 1. Подготовка моделей
У меня есть 3 модели, которые я создал в Blender – это дверь, кухонный шкаф и комод.

Перед экспортом модели в FBX мы должны убедиться, что у всех объектов сброшен Rotation и Scale, а также, чтобы в дальнейшем упростить работу с Joint, мы должны установить Origin (точка, относительно которой происходят все трансформации) в нужное положение.
Дверь состоит из двух объектов – дверной косяк и сама дверь, объединенная с петлями и ручкой.
В данном случае точка Origin двери (она же Pivot в Unity) должна находиться на одной из петель. Чтобы ее переместить необходимо в Edit Mode выделить грань петли, ближайшую к дверному косяку, нажать Shift+S и выбрать пункт Cursor to Selected, таким образом 3D-курсор переместится к центру этой грани. В Object Mode нажимаем ПКМ и выбираем пункт Set Origin → Origin to 3D Cursor.

У кухонного шкафа есть основание и две дверцы, соответственно у дверцы точка Origin должна находиться как на картинке (здесь нет петель, потому что я схалтурил, в идеале они должны быть, иначе при открытии двери она будет врезаться в основание).

У ящика комода Origin лучше поместить к передней или задней стенке, в моем случае Origin находится по центру передней стенки.

Готовые модели импортируем в Unity.
Шаг 2. Настраиваем дверь
Разберемся сначала с дверью, поскольку ручка у нас объединена с дверью, то на нее нужно накинуть коллайдер, также можно накинуть коллайдеры на дверной косяк (Mesh или несколько Box), а также на саму дверь, вот так они будут выглядеть:

Обратите внимание, что коллайдер двери располагается не по всей площади, поскольку при полном открытии двери он будет сталкиваться с дверным косяком и они будут отталкиваться, в идеале, модель нужно проектировать так, чтобы этого не происходило.
Я уже говорил в видео, что если у двери есть коллайдеры и мы, взяв ее в руку начнем далеко отходить, то возникнет неприятный эффект, можете посмотреть по этому таймкоду – https://rutube.ru/video/7e562e58444b23d899ff0d09a9b32be6/?r=wd&t=75.
Поэтому сразу же напишем скрипт, который это исправляет, назовем его HandleGrabInteractable, вот его содержимое:
using System.Collections;
using System.Collections.Generic;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class HandleXRGrabInteractable : XRGrabInteractable
{
// переопределяем метод, срабатывающий при захвате объекта
protected override void OnSelectEntered(SelectEnterEventArgs args)
{
base.OnSelectEntered(args);
// вызываем корутину, которой передаем Transform руки (interactorObject - это объект который начал взаимодействие с нашим GrabInteractable, это может быть Direct Interactor, или, например, Ray Interactor)
StartCoroutine(CancleGrabWhenHandMove(args.interactorObject.transform.parent));
}
private IEnumerator CancleGrabWhenHandMove(Transform handTransform)
{
while (true)
{
// вычисляем дистанцию между ручкой и рукой
Vector3 distance = this.transform.position - handTransform.position;
// если рука отклонилась на 0.3 метра в любую из сторон
if (distance.magnitude > 0.3f)
{
// включаем и отключаем скрипт, таким образом рука оторвется от ручки
this.enabled = false;
this.enabled = true;
yield break;
}
yield return null;
}
}
}Все пояснения я написал в комментариях.
P.S. я не нашел более элегантного решения для отцепления руки от ручки, кроме как отключать скрипт, у XR Grab Interactable нет такого метода, который бы отсоединял нас от интерактора. Если вы знаете более правильное решение, напишите мне об этом, контакты есть внизу статьи, буду благодарен ❤️.
Накидываем скрипт на ручку двери и делаем следующие настройки:
- в Movement Type ставим Velocity Tracking;
- отключаем Track Rotation, Track Scale и Throw On Detach, поскольку они в данном случае не нужны.
Также накидываем Fixed Joint, чтобы в дальнейшем привязать коллайдер ручки к двери.

Теперь нам нужно настроить саму дверь. Накидываем на нее Hinge Joint, с помощью которого будем ограничивать вращение двери. Делаем следующие настройки:
- меняем оси так (Axis), чтобы ось вращения стала горизонтальной;
- ставим галочку Use Limits и ограничиваем вращение до 100 градусов;
- также советую включить галочку Use Spring и поставить 5 в Damper.
P.S. Damper регулирует замедление объекта после физического взаимодействия, например, если мы возьмемся за ручку двери и потом ее резко бросим вперед, то при Damping=0 дверь улетит вперед, а при Damping=5 быстро замедлится, поиграйте с этой настройкой и поставьте как вам больше нравится.

Осталось только перенести Rigidbody двери в Fixed Joint ручки.

Шаг 3. Кухонный шкаф
Кратко опишу процесс настройки дверей кухонного шкафа:
- переносим Rigidbody двери в Fixed Joint ручки.
- накидываем коллайдеры на шкаф, двери и ручки;
- на обе ручки накидываем Fixed Joint и Handle XR Grab Interactable, настраиваем следующим образом:
- в Movement Type ставим Velocity Tracking;
- отключаем Track Rotation, Track Scale и Throw On Detach;
- на обе двери накидываем Hinge Joint и настраиваем, как на картинке ниже;
Поскольку у данной модели нет петель, необходимо аккуратно располагать коллайдеры, так, чтобы они никогда не могли войти друг в друга.

Шаг 4. Комод
- отключаем Track Rotation, Track Scale и Throw On Detach.
- накидываем коллайдеры на комод, выдвижной ящик и ручку, здесь нужно быть предельно аккуратными, коллайдеры не должны касаться друг друга;
- на ручку накидываем Fixed Joint и Handle XR Grab Interactable, настраиваем следующим образом:
- в Movement Type ставим Velocity Tracking;
- ставим галочку Use Dynamic Attach, без этого ящик будет магнититься к руке, что будет создавать неприятный эффект;
- y Use Dynamic Attach убираем галочку Match Rotation;
Для ящика комода будем использовать Configurable Joint со следующими настройками:
- блокируем вращение по всем осям — Angular X Motion, Angular Y Motion и Angular Z Motion устанавливаем параметр Locked;
- выбираем локальные координаты и смотрим по какой оси будет двигаться комод, для этой оси ставим параметр Limited (в моем случае Y Motion), а другие оси блокируем (Locked);
- В Linear Limit необходимо указать, на сколько максимум мы можем выдвинуть ящик, чтобы вычислить это значение можем к нужной оси прибавлять примерное значение, обратите внимание, что здесь используются глобальные координаты. В моем случае, получилось 0.35;
- Damping здесь устанавливается для конкретной оси, в моем случае это Y Drive, Position Damper = 20.
Переносим Rigidbody ящика в Fixed Joint ручки.


Бонус. Озвучка при движении ящика/двери
Если вы хотите сделать так, чтобы при открывании дверей или ящика комода был соответствующий звук, то нужно написать небольшой скрипт.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class OpeningSound : MonoBehaviour
{
[SerializeField] private Rigidbody _rb;
[SerializeField] private AudioSource _audioSource;
private void Awake()
{
_rb = GetComponent<Rigidbody>();
_audioSource = GetComponent<AudioSource>();
}
public void CheckOpeningProcess()
{
StartCoroutine(OpeningSoundCoroutine());
}
public IEnumerator OpeningSoundCoroutine()
{
while (true)
{
// если длина вектора скорости > 0.5 т.е. объект движется
if (_rb.velocity.magnitude > 0.5f)
{
_audioSource.Play();
yield return new WaitForSeconds(_audioSource.clip.length);
}
yield return null;
}
}
}
Кидаем скрипт на ручку, а также не забудьте накинуть соответствующий звук. Чтобы скрипт заработал, необходимо вызывать корутину при захвате объекта – First Select Entered и останавливать все корутины при отпускании объекта Select Exited:

Контакты
Если у вас есть какие-либо замечания или полезные дополнения к статье, можете написать мне в VK.
VK – https://vk.com/zabelyaevroman
Youtube – https://www.youtube.com/@vrfordev
Rutube – https://rutube.ru/channel/38871248/


