Unity, физика, опыты с Joint

Любая игра погружает нас в нереальный мир. Герои и предметы в сцене могут взаимодействовать по своим законам. Иногда это совсем не похоже на то, с чем мы сталкиваемся в жизни. Герои проходят сквозь стены, перемещают огромные тяжести, выживают падая с огромной высоты и т.п. «Глюки» или «фичи» — как посмотреть. Но наш мозг мыслит аналогиями с реальной жизнью и ему проще, когда на неё всё похоже.

В игровых движках многие физические возможности уже встроены. Если бы не разработчики, то пришлось бы в каждой игре писать для эмуляции этих процессов «море» кода. В Unity для реализации физических процессов чаще всего используются Rigidbody и различные коллайдеры. Чтобы придать игре большей «атмосферности», хочется иногда добавить качающийся на ветру фонарь или вывеску. В Unity можно задать некоторые варианты взаимодействия предметов, связав их с помощью Joint между собой. Их существует 5 типов для разных случаев взаимодействия. Подробно назначения всех параметров описаны в документации по Unity, например, для версии 2021 docs.unity3d.com. Но намного интереснее разобраться как это работает. Давайте проведём некоторые опыты с Joint.

В такой сцене будем делать опыты с Joint

Для работы создадим простую сцену: Plane в качестве пола и 4 стены из вытянутых и повернутых кубов чтобы персонаж оставался внутри. Два вытянутых куба в качестве опор псевдо качелей. Из вытянутых кубов по-тоньше дверная коробка. Ещё 2 тонких маленьких вытянутых кубика — опоры для шлагбаума. Для взаимодействия с этим всем богатством добавим нашего персонажа — Capsule. Раскрасим это всё в разные цвета. В результате у меня получилось вот так, см. рис. 1:

рис. 1 Сцена для экспериментов.

Дверь как простая калитка

Если у Вас есть готовая модель красивой двери, то можете использовать её. Но у меня никакой модели двери нет, поэтому сделаю её из кубика. Изменим размеры, как на рис. 2:

рис. 2 Размеры двери.

Чтобы дверь была похожа на настоящую, её размеры должны быть чуть меньше дверной коробки. Далее создадим пустой объект, в котором создадим ещё один пустой объект и назовём его Anchor. И уже в этот Anchor поместим дверь из куба. Переименуем верхний пустой объект в Door. Должна получиться структура как на рис. 3:

рис. 3 Дверь
рис. 4 Элементы

К родительскому объекту Door добавим элементы Rigidbody, Box Collider и Hinge Joint (рис. 4). Добавим новый слой Door и назначим его главному и дочерним объектам Door. В разделе Physics настроек проекта удалим галочку на пересечении слоёв Door и Default см. рис. 5:

рис. 5 Слои

В результате физический движок не будет просчитывать столкновения (взаимодействия) объектов этих слоёв. Иначе один коллайдер будет выталкивать из себя второй или дверь не сможет открываться как задумано взаимодействуя с дверной коробкой. Осталось настроить Hinge Joint так, как показано на рис. 6:

рис. 6 Hinge Joint

Настройка оси вращения

Чтобы дверь вращалась вокруг вертикальной оси, ставим 1 в поле Y для Axis. X и Z сбрасываем в 0. Якоря привязки (Anchor) — точки, вокруг которых должно происходить вращение, для нашей двери расположены справа. Anchor Х = 0.5 относительно центра объекта. Connected Anchor — позиция в мировых координатах сцены. Положение якоря на объекте отмечено белой стрелкой, которую не очень хорошо видно, см. рис. 7 для куба без наложенного материала. Галочку Auto оставим, чтобы эта позиция вычислялась автоматически.

рис. 7 Якорь — точка вращения.

Раздел пружины(Spring) не трогаем. Можно соединить, например, две сферы и они будут приближаться/удаляться как два шарика на пружине в детской игрушке, останавливаясь со временем в целевой точке. Раздел «Motor» можно использовать, например, чтобы сделать вращающийся стеклянный турникет (как дверь в вокзал или ТЦ) с нужной скоростью постоянно (Free Spin = True). Установим лимиты в градусах на открывание двери. Что получилось увидим в итоговом видео.

Слабое подобие качелей

Представим качели как платформу, жестко связанную с осью и вращающеюся вокруг неё в определённых пределах. В качестве оси используем цилиндр, с которым позже и свяжем качели. Чтобы он был похож на ось, повернём его на 90 градусов по X и Y, поднимем, уменьшим диаметр и увеличим высоту. В результате параметры цилиндра на рис. 8:

рис. 8 Элементы оси качелей.

Чтобы создать сами качели будем действовать по аналогии с созданием двери. Создадим пустой объект и назовём его Swing, в нем создадим пустой объект Anchor и уже в него добавим 3 вытянутых куба, чтобы это было похоже на качели. Структура качелей показана на рис. 9:

рис.9 Структура качелей

К родительскому объекту Swing добавим элементы Rigidbody, Box Collider и Hinge Joint (рис. 10). У дочерних элементов удалим их Box Collider, а вот в родительском установим размеры так, чтобы он охватывал все качели целиком — такая задумка. Настроим Hinge Joint, см. рис. 10. В поле Connected Body перетащим нашу ось вращения Cylinder.

Настройка якорей

Если Вы не понимаете, какими цифрами задать позицию якоря и вокруг какой оси должно быть вращение, то можно поставить ненулевые значения полей мотора или пружины и запустить игру. В результате объект куда-то переместится или даже будет двигаться вокруг якоря, позиция которого помечена бежевой стрелочкой. Можно подобрать ось и позицию якоря, меняя цифры в соответствующих полях (Anchor и Axis). Кстати, будет весело, так как объект может «разнести» всю сцену при изменении значений.

Есть ещё один интересный момент. Ось просто лежит на опорах-кубах. Когда качели качаются, то физический движок просчитывает смещение оси со своего места, особенно если герой их толкает не посредине. В результате через некоторое время вся конструкция слетает с опор и падает по законам физики вниз, чего нам не нужно. Чтобы ось стояла на месте, добавим вокруг неё 2 Box Collider вверху опор, см. рис. 11:

рис. 11 Ограничители оси

Аналогично можно поступить, если хочется спрятать ось внутрь опор. Если коллайдеры расположены один (или его часть) внутри другого, то внешний выталкивает внутренний. Можно уменьшить размер основного коллайдера внутри меша и добавить ещё несколько коллайдеров вокруг воображаемого отверстия для оси. Тогда ось будет зафиксирована этими коллайдерами. Что получилось смотрим в итоговом видео.

Просто шлагбаум

Также как с дверью и качелями, во-первых, шлагбаум начнём делать с создания пустого объекта SchB. Во-вторых, в нем создаём пустой объект Anchor, и в-третьих уже в нём создаём Cylinder для визуального отображения шлагбаума. Чтобы коллайдер шлагбаума не взаимодействовал с соседним коллайдером дверной коробки уменьшим его по высоте до 1.9 (или нужно назначить слой, отличный от default). Якорь шлагбаума должен быть расположен около дверной коробки. Поскольку цилиндр повернут по оси Z на 90 градусов, вращение шлагбаума будет вокруг оси Z. В итоге значения параметров элементов и их структура приведены на рис. 12:

рис. 12 Структура и элементы шлагбаума

Чтобы шлагбаум своим концом не падал на землю, установим слева опору. Перемещать шлагбаум вверх/вниз будем толкая его столбиком SchT. Чтобы им управлять из кода, добавим на него скрипт с кодом — листинг ниже:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SchTControl : MonoBehaviour
{
    [SerializeField] float speed = 1f;
    [SerializeField] float upDelay = 0.5f;
    [SerializeField] float minY = -1f, maxY = 2f;

    float timer;
    Vector3 pos;
    bool bUP = true;
    // Start is called before the first frame update
    void Start()
    {
        timer = upDelay;
        bUP = true;
    }

    // Update is called once per frame
    void Update()
    {
        if (timer > 0) timer -= Time.deltaTime;
        else
        {
            timer = upDelay;
            pos = transform.position;
            if (bUP)
            {
                if (pos.y < maxY)
                {
                    pos.y += 0.05f * speed * Time.deltaTime;
                    transform.position = pos;
                }
                else bUP = false;
            }
            else
            {
                if (pos.y > minY)
                {
                    pos.y -= 0.05f * speed * Time.deltaTime;
                    transform.position = pos;
                }
                else bUP = true;
            }
        }
    }
}

По таймеру столбик двигаем вверх/вниз с задаваемой скоростью в указываемых пределах. Шлагбаум подымается потому что коллайдер столбика не должен в него входить. Если столбик начинает опускаться, то шлагбаум опускается вниз под действием силы тяжести.

Смотрим, что получилось

Чтобы придать жизни нашей сцене, добавим персонажу скрипт для управления его перемещением. Код в листинге ниже:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    [SerializeField] float speed;
    Rigidbody _rb;

    Vector3 _movement
    {
        get
        {
            return new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        _rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
    }

    private void FixedUpdate()
    {
        //_rb.AddForce(_movement * speed, ForceMode.Impulse);
        transform.Translate(_movement * speed * Time.fixedDeltaTime);
    }
}

А теперь давайте посмотрим как это всё работает. Наш герой Capsule достаточно бодро бегает по сцене. Если шлагбаум поднят, то он может пройти под ним. А если он внизу, то герой в него упирается и пройти не может. Герой толкает качели, поднимая их. После того как он их проходит, они под действием силы тяжести падают вниз. После прохода героя дверь не закрывается, потому что мы не использовали пружинистость соединения. На мой взгляд, всё выглядит достаточно естественно в соответсвии с законами физики.

Capsule и Hinde Joint в действии

Надеюсь, что мои опыты с Joint помогли кому-то понять как их использовать и что от них ждать. В ходе написания статьи появились ещё идеи и если смогу их реализовать, то продолжу рассказ про опыты с Joint в следующей статье Unity, физика, работа с Joint и не только.

Обновлено: 13.11.2023 — 16:19

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *