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

В Phaser контейнеры (Container) позволяют группировать игровые объекты и работать с ними как с единым целым. Но что, если этому целому нужна реалистичная физика? В этом примере мы совместим мощь физического движка 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('platform', 'assets/sprites/platform.png');
        this.load.image('mushroom', 'assets/sprites/mushroom2.png');
    }

    create ()
    {
        const worldWidth = 1600;
        const worldHeight = 1200;

        this.matter.world.setBounds(0, 0, worldWidth, worldHeight);

        const image1 = this.add.image(0, -30, 'mushroom');
        const image2 = this.add.image(-40, 30, 'mushroom');
        const image3 = this.add.image(40, 30, 'mushroom');

        const container = this.add.container(100, 100, [ image1, image2, image3 ]);

        //  A Container has a default size of 0x0, so we need to give it a size before enabling a physics body
        container.setSize(128, 64);

        this.cameras.main.startFollow(container);

        const physicsContainer = this.matter.add.gameObject(container);

        physicsContainer.setFrictionAir(0.001);
        physicsContainer.setBounce(1);

        this.matter.add.image(350, 450, 'platform', null, { isStatic: true }).setScale(6, 0.5).setAngle(10);
    }
}

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

const game = new Phaser.Game(config);

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

В методе preload мы загружаем два изображения: платформу и гриб. Обратите внимание на использование setBaseURL для указания базового пути, что упрощает загрузку ресурсов из удаленного хранилища.

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

Создание составного объекта в контейнере

В create мы сначала задаем размер физического мира больше, чем размер экрана, чтобы объекты могли свободно перемещаться. Затем создаем три отдельных изображения гриба с разными координатами относительно точки (0,0).

const image1 = this.add.image(0, -30, 'mushroom');
const image2 = this.add.image(-40, 30, 'mushroom');
const image3 = this.add.image(40, 30, 'mushroom');

Эти три изображения объединяются в контейнер. Первые два аргумента this.add.container(100, 100, ...) задают мировые координаты самого контейнера.

const container = this.add.container(100, 100, [ image1, image2, image3 ]);

**Важный момент:** у контейнера по умолчанию нулевой размер (0x0). Для корректной работы физического тела ему необходимо задать размер.

container.setSize(128, 64);

Добавление физики и настройка камеры

Превращаем обычный контейнер в физический игровой объект с помощью this.matter.add.gameObject(). Это ключевой метод, который добавляет тело Matter.js к существующему объекту Phaser.

const physicsContainer = this.matter.add.gameObject(container);

Настраиваем свойства физического тела: очень низкое сопротивление воздуха для плавного скольжения и упругость (bounce), равную 1, для идеально упругих столкновений.

physicsContainer.setFrictionAir(0.001);
physicsContainer.setBounce(1);

Чтобы камера автоматически следовала за нашим контейнером, используем метод startFollow.

this.cameras.main.startFollow(container);

Создание статичной платформы

Добавляем в мир статичную платформу, от которой будет отскакивать наш контейнер. Параметр { isStatic: true } делает тело неподвижным. Методами setScale и setAngle мы растягиваем платформу и задаем ей небольшой угол наклона.

this.matter.add.image(350, 450, 'platform', null, { isStatic: true }).setScale(6, 0.5).setAngle(10);

Конфигурация игры и физики

В конфигурации игры критически важно правильно настроить физический плагин Matter. Мы задаем его как систему по умолчанию, включаем отладочную визуалицию (debug: true) и устанавливаем слабую вертикальную гравитацию.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#1b1464',
    parent: 'phaser-example',
    physics: {
        default: 'matter', // Активация Matter.js
        matter: {
            debug: true, // Показывать контуры тел
            gravity: {
                y: 0.3 // Слабая гравитация
            }
        }
    },
    scene: Example
};

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

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