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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

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

        this.matter.add.mouseSpring();

        let x = 100;
        let y = 300;
        let prevBody;

        for (let i = 0; i < 8; i++)
        {
            const body = this.matter.add.circle(x, y, 24);

            if (i > 0)
            {
                const s = this.matter.add.spring(body, prevBody, 48, 0.9, { damping: 0.9, angularStiffness: 0.9 });
            }

            prevBody = body;
            x += 64;
        }
    }

    update ()
    {
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#1b1464',
    parent: 'phaser-example',
    physics: {
        default: 'matter',
        matter: {
            enableSleeping: true,
            debug: {

                showAxes: false,
                showAngleIndicator: true,
                angleColor: 0xe81153,

                showBroadphase: false,
                broadphaseColor: 0xffb400,

                showBounds: false,
                boundsColor: 0xffffff,

                showVelocity: true,
                velocityColor: 0x00aeef,

                showCollisions: true,
                collisionColor: 0xf5950c,

                showSeparations: false,
                separationColor: 0xffa500,

                showBody: true,
                showStaticBody: true,
                showInternalEdges: true,

                renderFill: false,
                renderLine: true,

                fillColor: 0x106909,
                fillOpacity: 1,
                lineColor: 0x28de19,
                lineOpacity: 1,
                lineThickness: 1,

                staticFillColor: 0x0d177b,
                staticLineColor: 0x1327e4,

                showSleeping: true,
                staticBodySleepOpacity: 1,
                sleepFillColor: 0x464646,
                sleepLineColor: 0x999a99,

                showSensors: true,
                sensorFillColor: 0x0d177b,
                sensorLineColor: 0x1327e4,

                showPositions: true,
                positionSize: 4,
                positionColor: 0xe042da,

                showJoint: true,
                jointColor: 0xe0e042,
                jointLineOpacity: 1,
                jointLineThickness: 2,

                pinSize: 4,
                pinColor: 0x42e0e0,

                springColor: 0xe042e0,

                anchorColor: 0xefefef,
                anchorSize: 4,

                showConvexHulls: true,
                hullColor: 0xd703d0
            }
        }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка: настройка физики

Вся магия начинается с конфигурации игры. Мы используем движок Matter.js (default: 'matter'), который позволяет работать с продвинутой физикой твердых тел. Ключевая особенность — включение детального отладочного режима. В примере он настроен так, чтобы подсвечивать скорости, столкновения и сами тела, что невероятно полезно при отладке физических взаимодействий.

const config = {
    type: Phaser.AUTO,
    physics: {
        default: 'matter',
        matter: {
            enableSleeping: true,
            debug: {
                showVelocity: true,
                showCollisions: true,
                showBody: true,
                // ... остальные настройки визуализации
            }
        }
    },
    scene: Example
};

Сцена: отключаем гравитацию и добавляем управление

В методе create() сцены мы подготавливаем мир. Строка this.matter.world.disableGravity() отключает глобальную гравитацию. Это нужно, чтобы наша цепочка не падала вниз и мы могли наблюдать за ее поведением в изоляции.

Следующая важная строка — this.matter.add.mouseSpring(). Она создает пружинное соединение между курсором мыши и любым телом, за которое вы перетащите. Это позволяет интерактивно дергать и растягивать нашу змейку.

create ()
{
    this.matter.world.disableGravity();
    this.matter.add.mouseSpring();
    // ... создание цепочки
}

Создание цепочки тел

Сердце примера — цикл, создающий восемь круглых тел. Мы используем this.matter.add.circle(x, y, 24) для добавления каждого нового тела (шара) с радиусом 24 пикселя. Координата X увеличивается на 64 пикселя с каждой итерацией, выстраивая тела в линию.

let x = 100;
let y = 300;
let prevBody;

for (let i = 0; i < 8; i++)
{
    const body = this.matter.add.circle(x, y, 24);
    // ... создание пружины
    prevBody = body;
    x += 64;
}

Связывание тел пружинными соединениями

Чтобы тела не разлетелись, их нужно соединить. Начиная со второго тела (i > 0), мы связываем текущее тело с предыдущим с помощью this.matter.add.spring(). Эта функция создает пружинное соединение (джоинт) между двумя физическими телами.

Параметры соединения: - body, prevBody: Соединяемые тела. - 48: Длина покоя пружины. Если тела находятся ближе или дальше этого расстояния, пружина будет стремиться вернуть их на место. - 0.9: Жесткость пружины (stiffness). Определяет, насколько сильно пружина сопротивляется сжатию или растяжению. - { damping: 0.9, angularStiffness: 0.9 }: Демпфирование гасит колебания, делая движение более плавным и предотвращая "дребезг". angularStiffness влияет на жесткость при угловом (вращательном) смещении.

if (i > 0)
{
    const s = this.matter.add.spring(body, prevBody, 48, 0.9, { damping: 0.9, angularStiffness: 0.9 });
}

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

Вы создали базовую модель мягкого, сегментированного тела, реагирующего на физику и взаимодействие с мышью. Это основа для множества игровых объектов: цепей, щупалец, хвостов драконов или даже реалистичных волос. Для экспериментов попробуйте изменить параметры пружин (жесткость, демпфирование), добавить больше сегментов, включить гравитацию (this.matter.world.setGravity(0, 1)) или прикрепить первый сегмент к статичной точке опоры с помощью this.matter.add.constraint().