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

При создании игр часто возникают ситуации, когда стандартных геометрических форм — прямоугольников и кругов — недостаточно. Нужны статические объекты сложной формы: разрушаемые платформы, астероиды, причудливые препятствия. В этой статье разберем, как создавать сложные полигональные статические тела в 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.01 — decomp порог упрощения пути. Меньшие значения сохраняют больше деталей. 6. 10 — removeCollinear порог удаления коллинеарных точек (лишних точек на одной прямой).

Альтернативный, более низкоуровневый способ через прямое 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; настроить трение и упругость статичного тела, чтобы изменить поведение отскакивающих шариков; использовать несколько таких тел для создания разрушаемой сложной конструкции.