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

При создании игр часто возникают ситуации, когда стандартных геометрических форм — прямоугольников и кругов — недостаточно. Нужны статические объекты сложной формы: разрушаемые платформы, астероиды, причудливые препятствия. В этой статье разберем, как создавать сложные полигональные статические тела в Matter.js, используя вершинные пути. Вы научитесь превращать простой SVG-like путь в полноценный физический объект, о который будут отскакивать другие тела.

Версия 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('mountains', 'assets/skies/mountains-tile.png');
        this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    }

    create ()
    {
        this.add.image(0, 600, 'mountains').setOrigin(0, 1);

        this.matter.world.setBounds(0, 0, 800, 600, 32, true, true, false, true);

        const path = '0 307 0 67 8 55 12 53 57 128 86 94 128 136 148 103 190 159 210 135 222 149 248 109 267 133 293 93 321 128 361 75 381 97 439 4 523 117 551 78 563 92 569 93 603 38 637 99 654 53 701 154 729 109 750 140 800 66 800 307';

        //  The direct Matter way:

        // var verts = Phaser.Physics.Matter.Matter.Vertices.fromPath(path);

        // var body = Phaser.Physics.Matter.Matter.Bodies.fromVertices(408, 492, verts, { ignoreGravity: true }, true, 0.01, 10);

        // Phaser.Physics.Matter.Matter.World.add(this.matter.world.localWorld, body);

        //  Or the short-cut version using factory helpers:

        const verts = this.matter.verts.fromPath(path);

        this.matter.add.fromVertices(408, 492, verts, { ignoreGravity: true }, true, 0.01, 10);

        //  Just a repeating timer
        this.time.addEvent({ delay: 250, callback: this.releaseBall, callbackScope: this, repeat: 256 });
    }

    releaseBall ()
    {
        const ball = this.matter.add.image(Phaser.Math.Between(32, 768), -200, 'balls', Phaser.Math.Between(0, 5));

        ball.setCircle();
        ball.setBounce(0.96);
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и настройка физики

Прежде чем создавать сложное тело, нужно настроить мир Matter.js. В примере используется расширенная конфигурация границ мира и загрузка простых ассетов.

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

В методе create() сцены устанавливаются границы физического мира. Ключевой параметр 32 — это толщина стенок. Флаги true, true, false, true определяют, какие границы (левая, правая, верхняя, нижняя) будут созданы.

this.matter.world.setBounds(0, 0, 800, 600, 32, true, true, false, true);

Эта строка создает физические стены по краям игрового поля, кроме верхней (третий флаг — false). Это позволяет объектам падать сверху за пределы экрана.

Магия пути: от строки к вершинам

Сердце примера — строка path. Это набор пар координат x y, разделенных пробелами, описывающих контур будущего тела. Такой формат часто экспортируется из векторных редакторов.

const path = '0 307 0 67 8 55 ... 800 307';

Для превращения этой строки в массив вершин, понятных Matter.js, используется фабричный метод this.matter.verts.fromPath(). Этот метод парсит строку и возвращает массив объектов с координатами.

const verts = this.matter.verts.fromPath(path);

Важно: путь должен описывать выпуклый или вогнутый полигон в правильном порядке (обычно по часовой стрелке). Некорректный путь может привести к ошибкам при создании тела.

Создание статического тела из вершин

Полученный массив вершин передается в фабрику тел this.matter.add.fromVertices(). Это удобная обертка Phaser над нативным API Matter.js.

this.matter.add.fromVertices(408, 492, verts, { ignoreGravity: true }, true, 0.01, 10);

Разберем параметры: 1. 408, 492 — координаты `xиy` в мире, где будет размещен центр тела. 2. verts — массив вершин, полученный на предыдущем шаге. 3. { ignoreGravity: true } — опции тела. ignoreGravity: true делает тело статичным, не подверженным гравитации. 4. true — флаг, разрешающий создание вогнутых тел (если путь описывает такую форму). 5. 0.01decomp порог упрощения пути. Меньшие значения сохраняют больше деталей. 6. 10removeCollinear порог удаления коллинеарных точек (лишних точек на одной прямой).

Альтернативный, более низкоуровневый способ через прямое API Matter.js закомментирован в исходнике. Использование this.matter.add — предпочтительный и лаконичный способ в Phaser.

Динамические объекты и взаимодействие

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

this.time.addEvent({ delay: 250, callback: this.releaseBall, callbackScope: this, repeat: 256 });

Метод releaseBall создает динамическое тело — изображение шарика, которое сразу наделяется физическими свойствами.

releaseBall ()
{
    const ball = this.matter.add.image(Phaser.Math.Between(32, 768), -200, 'balls', Phaser.Math.Between(0, 5));
    ball.setCircle();
    ball.setBounce(0.96);
}

this.matter.add.image создает физическое тело типа Image. setCircle() задает шарику коллизию в форме круга, что обеспечивает реалистичное качение. setBounce(0.96) устанавливает высокий коэффициент упругости, чтобы шарики долго отскакивали от сложного статического тела и стен.

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

Использование this.matter.verts.fromPath() и this.matter.add.fromVertices() открывает путь к созданию сложной, нестандартной статической геометрии в вашей игре. Это мощный инструмент для уровней дизайна. Для экспериментов попробуйте: изменить строку path на свою, сгенерированную в редакторе; сделать тело динамическим, убрав ignoreGravity; настроить трение и упругость статичного тела, чтобы изменить поведение отскакивающих шариков; использовать несколько таких тел для создания разрушаемой сложной конструкции.