Учебные материалы для школы программирования. Часть 11

Spoiler

На сегодняшнем занятии мы познакомимся с физикой на джоинтах движка BOX 2D, на примере создания персонажа, похожего на главного героя Gish или Slime Laboratory.

Порядок выполнения

Создадим новый 2д проект и импортируем в него приложенный ассет. Ассет содержит все необходимые ресурсы, включая спрайты для уровня, скрипты и меш для персонажа.

Для начала, создадим новую сцену и поместим в неё 10 сфер с радиусом 0.5, таким образом, чтобы получилась "ромашка":

Установим на каждую сферу Rigidbody2D массой 0.5 и CircleCollider2D. Центральная сфера имеет массу 0.05 и drag = 1 и не имеет коллайдера.

Добавим между соседними сферами по одному SpringJoint2D. Обратите внимание на параметр Frequency - это частота опроса пружины, чем частота выше, тем пружина будет сильнее возвращаться на своё место. Damping - демпфирование пружины (предотвращение колебаний, смягчающий эффект).

Таким образом, мы не даём нашей заготовке разлетаться на куски или, наоборот, излишне сжиматься. Тем не менее, форму она держать не будет.

Для придания формы используем среднюю точку. От каждой, из 9-и окружающих её сфер, создадим ещё по одному джоинту.

Данная конструкция позволяет нам держать форму, при этом, она может сработать некорректно, если при сильном ударе центр вылетит за пределы круга. Более того, данная модель не учитывает поверхностное натяжение между удалёнными точками. Для этого, создадим систему из джоинтов по такому принципу:

Итоговая система джоинтов должна выглядеть примерно так:

Камеру повесим на центральный объект и протестируем, как себя ведёт наша физическая модель. Изменяя её параметры, можно добиться разной вязкости и плотности.

Теперь придадим "лизуну" очертания. Для этого в ассет приложена 3д-модель со скелетом, каждая из 9-и костей которого управляет по одному вертексу по периметру.
Для неё написан скрипт Goo, который управляет привязкой и отвечает за перемещение нашего главного героя.

Закинем модель на сцену и назначим ей скрипт. Конфигурируем его таким образом. На сцене создадим уровень из спрайтов и назначим ему коллайдеры.

Разберём немного сам скрипт:

using? ?System?.?Collections?;
using? ?System?.?Collections?.?Generic?;
using? ?UnityEngine?;
 
public?? class ??Goo?: ?MonoBehaviour ?{
    public ??ConstantForce2D ?ForceObject; ?// центральная точка как объект приложения силы
    public?? float? maxForce ?=?? 4f?; ?// сила передвижения ?public??Transform?[] bones; ?// массив костей
    ?public ??Transform?[] go; ?// массив сфер
    ?public?? float? sphereRadius; ?// радиус сферы, можно брать автоматически, но в данном случае выставляем вручную
    public?? Transform? center; ?// центральная точка как трансформ
    ConstantForce2D?[] frc; ?// все объекты приложения силы ?
 
    // Use this for initialization
    void ??Start?() {
        frc?? = ??new??ConstantForce2D?[?9?]; ?// инициализируем
        // находим все объекты приложения силы
        for ?(?int ?i ?= ?0?; ?i? <? 9?; ?i?++?) ?{ 
            frc?[?i?] ?=??go?[?i?].?GetComponent?<?ConstantForce2D?>();
        }
    }
    // Update is called once per frame 
    void ??Update?() {
        for ?(?int ?i ?= ?0?; ?i ?< ?9?; ?i?++?) {
             ?// выставляем кости по точкам с небольшим смещение
            bones?[?i?].?position? ?=? ?go?[?i?].?position? ?+? (?go?[?i?].?position? ?- center?.?position?).?normalized?? * ??sphereRadius? / ?2f?;
            // добавляем всем точкам силу по горизонтали
            frc?[?i?].?force =? ?new??Vector2?(?Input?.?GetAxis?(?"Horizontal"?) ?* ??maxForce?, 0f?); ?
        }
        // и центральной точке тоже
        ForceObject?.?force = ??new??Vector2?(?Input?.?GetAxis?(?"Horizontal"?) ?* maxForce?, ?0f?);
        ?// нажали пробел
        if? (?Input?.?GetKeyDown?(?KeyCode?.?Space?)) {
            foreach?(?Transform?rig ?in??go?) {
                rig?.?GetComponent?<?Rigidbody2D?>().?velocity? ?+=? ?new Vector2?(?0?,? 6f?); ?// прыжок
            }
        }
    }
}

После отключения MeshRenderer у сфер, можно увидеть, что всё адекватно работает. Для полноты картины, фон и камеру прикрепляем к центральному объекту. У риджитбади центрального объекта нужно запретить вращение по Z. Можно добавить, на свое усмотрение, пару глаз.

На серые объекты вешаем скрипт RandomColor и выставим ему палитру.

using? ?System?.?Collections?;
using? ?System?.?Collections?.?Generic?;
using? ?UnityEngine?;
 
public ??class ??RandomColor?: ?MonoBehaviour {
    SpriteRenderer ?rndr; 
    ?public ??Color32?[] colors;
    
    // Use this for initialization
    void ??Start?() {
        rndr? ?=? ?GetComponent?<?SpriteRenderer?>(); 
        ?rndr?.?color ??= ??colors?[?Random?.?Range?(?0?, ?colors?.?Length?)];
    }
}

Теперь при старте уровень приобретёт цвет.

На этом, сборка нашего проекта завершена!