О чем этот пример

Физические связи, или констрейнты, — это мощный инструмент для создания сложных взаимодействий между телами в вашей игре. Используя их, вы можете моделировать пружины, веревки, шарниры и другие соединения, которые оживляют игровой мир. В этой статье мы разберем, как создать простое соединение между двумя телами в Phaser с помощью плагина Matter.js, и посмотрим на разные способы его добавления.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('ball', 'assets/sprites/shinyball.png');
    }

    create ()
    {
        this.matter.world.setBounds();

        //  Our two bodies which will be connected by a constraint (aka a Joint or a Spring)

        const ballA = this.matter.add.image(420, 100, 'ball', null, { shape: 'circle', friction: 0.005, restitution: 0.6 });
        const ballB = this.matter.add.image(400, 200, 'ball', null, { shape: 'circle', friction: 0.005, restitution: 0.6 });

        //  You can create a constraint between the two bodies using a Factory function.
        //  The value 100 is the resting length and 0.2 is the stiffness of the constraint.

        this.matter.add.constraint(ballA, ballB, 100, 0.2);

        //  To help those of you more used to the Box2D syntax you can use
        //  add.joint or add.spring instead (with the exact same parameters)

        // this.matter.add.spring(ballA, ballB, 100, 0.2);
        // this.matter.add.joint(ballA, ballB, 100, 0.2);

        //  Or you can create a native Matter constraint:

        // var constraint = Phaser.Physics.Matter.Matter.Constraint.create({
        //     bodyA: ballA.body,
        //     bodyB: ballB.body,
        //     length: 100,
        //     stiffness: 0.2
        // });

        //  Which you then have to add to the world yourself:

        // this.matter.world.add(constraint);

        this.matter.add.mouseSpring();
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#1b1464',
    parent: 'phaser-example',
    physics: {
        default: 'matter'
    },
    scene: Example
};

const game = new Phaser.Game(config);

Настройка сцены и загрузка ассетов

В начале кода мы объявляем класс сцены Example. В методе preload() загружается спрайт мяча. Обратите внимание на использование this.load.setBaseURL — это устанавливает базовый URL для всех последующих загрузок, что удобно для примеров.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('ball', 'assets/sprites/shinyball.png');
}

Создание физического мира происходит в create(). Строка this.matter.world.setBounds() устанавливает границы мира, чтобы тела не улетали за пределы экрана.

Создание физических тел

Далее мы создаем два физических тела — ballA и ballB. Важно: используется именно this.matter.add.image, а не обычный this.add.image. Этот метод создает игровой объект (Image), который также является физическим телом Matter.js.

const ballA = this.matter.add.image(420, 100, 'ball', null, { shape: 'circle', friction: 0.005, restitution: 0.6 });
const ballB = this.matter.add.image(400, 200, 'ball', null, { shape: 'circle', friction: 0.005, restitution: 0.6 });

Третий аргумент — ключ текстуры, четвертый — кадр анимации (у нас null). Пятый аргумент — это конфигурация тела Matter.js. Мы задаем форму 'circle', низкое трение (friction) и упругость (restitution). Эти параметры влияют на то, как тела будут отскакивать и скользить.

Добавление связи (Constraint) между телами

Самый простой способ соединить два тела — использовать фабричный метод this.matter.add.constraint. Он принимает два тела, длину связи в состоянии покоя и ее жесткость.

this.matter.add.constraint(ballA, ballB, 100, 0.2);

- **Длина 100**: Это расстояние в пикселях, к которому будет стремиться связь. Если тела раздвинуть или сдвинуть, связь будет пытаться вернуть их на это расстояние. - **Жесткость 0.2**: Значение от 0 до 1, определяющее, насколько упруга связь. Чем ближе к 1, тем сильнее связь сопротивляется растяжению и сжатию, делая соединение более жестким, как стержень. При значении 0.2 связь будет вести себя больше как мягкая пружина или веревка.

Для удобства разработчиков, знакомых с Box2D, Phaser предоставляет синонимы: this.matter.add.spring и this.matter.add.joint. Они делают то же самое.

Прямая работа с нативным API Matter.js

Иногда может потребоваться более тонкий контроль. В этом случае можно создать констрейнт напрямую через нативный API Matter.js, который встроен в Phaser.

var constraint = Phaser.Physics.Matter.Matter.Constraint.create({
    bodyA: ballA.body,
    bodyB: ballB.body,
    length: 100,
    stiffness: 0.2
});

Обратите внимание: здесь мы обращаемся к свойству .body объектов ballA и ballB. Это внутреннее физическое тело Matter, которое создается при вызове this.matter.add.image.

Созданный объект constraint нужно вручную добавить в мир:

this.matter.world.add(constraint);

Этот подход дает доступ ко всем параметрам констрейнта, описанным в документации Matter.js, но он более многословен.

Интерактивность: мышиная пружина

В конце примера добавляется полезный инструмент для отладки и прототипирования — this.matter.add.mouseSpring().

this.matter.add.mouseSpring();

Эта функция создает пружинную связь между указателем мыши и физическим миром. Зажав и потянув любое физическое тело на сцене, вы можете его перемещать, вращать и наблюдать, как на это реагирует созданная нами связь между мячами. Это отличный способ быстро протестировать поведение физической системы.

Что попробовать дальше

Констрейнты в Matter.js открывают путь к созданию сложных составных объектов: маятников, цепей, тряпичных кукол или подвижных механизмов. Для экспериментов попробуйте изменить параметры жесткости и длины, создайте цепочку из нескольких связанных тел или используйте разные формы (например, прямоугольники). Комбинируя связи с другими силами и столкновениями, вы сможете наполнить свою игру по-настоящему динамичной физикой.