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

При создании классических аркадных игр, таких как "Asteroids" или космические симуляторы, часто нужно, чтобы объекты, вылетая за одну границу экрана, появлялись с противоположной. Этот эффект, известный как "wrap-around" или "toroidal space", можно легко реализовать в Phaser с физическим движком Matter.js. В этой статье мы разберем пример, который демонстрирует, как задать границы телепортации для физических тел, создавая бесконечное игровое пространство в ограниченной области.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        //  Let's create a bunch of random shaped objects and add them to the world
        for (let i = 0; i < 48; i++)
        {
            const x = Phaser.Math.Between(100, 700);
            const y = Phaser.Math.Between(100, 500);

            const wrapBounds = {
                min: {
                    x: 0,
                    y: 0
                },
                max: {
                    x: 800,
                    y: 600
                }
            };

            if (Math.random() < 0.7)
            {
                const sides = Phaser.Math.Between(3, 14);
                const radius = Phaser.Math.Between(8, 50);

                this.matter.add.polygon(x, y, sides, radius, { restitution: 0.9, wrapBounds: wrapBounds });
            }
            else
            {
                const width = Phaser.Math.Between(16, 128);
                const height = Phaser.Math.Between(8, 64);

                this.matter.add.rectangle(x, y, width, height, { restitution: 0.9, wrapBounds: wrapBounds });
            }
        }

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

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

const game = new Phaser.Game(config);

Настройка физики Matter.js

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

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

Создание объектов с границами телепортации

Вся магия происходит в методе create сцены. Мы создаем 48 случайных объектов. Для каждого объекта определяется область (wrapBounds), за пределами которой тело будет "заворачиваться". Область задается объектом с полями min и max, содержащими координаты левого верхнего и правого нижнего углов. В примере эти границы совпадают с размерами игрового холста (800x600).

const wrapBounds = {
    min: { x: 0, y: 0 },
    max: { x: 800, y: 600 }
};

Этот объект wrapBounds затем передается как свойство в опциях физического тела.

Генерация случайных многоугольников и прямоугольников

Код использует генератор случайных чисел, чтобы с вероятностью 70% создать многоугольник, а в остальных 30% — прямоугольник. Это добавляет визуальное разнообразие.

Для создания многоугольника используется фабричный метод this.matter.add.polygon(x, y, sides, radius, options). Параметр sides определяет количество сторон, а radius — расстояние от центра до вершины. Оба значения генерируются случайно.

const sides = Phaser.Math.Between(3, 14);
const radius = Phaser.Math.Between(8, 50);
this.matter.add.polygon(x, y, sides, radius, { restitution: 0.9, wrapBounds: wrapBounds });

Для прямоугольника используется метод this.matter.add.rectangle(x, y, width, height, options). Его ширина и высота также генерируются случайно.

const width = Phaser.Math.Between(16, 128);
const height = Phaser.Math.Between(8, 64);
this.matter.add.rectangle(x, y, width, height, { restitution: 0.9, wrapBounds: wrapBounds });

Обратите внимание, что оба метода принимают объект опций, где мы задаем высокий коэффициент упругости (restitution: 0.9) для энергичных отскоков и наши заранее определенные wrapBounds.

Интерактивность с помощью мыши

Чтобы можно было взаимодействовать с созданными объектами, в конце метода create добавляется пружинный указатель мыши.

this.matter.add.mouseSpring();

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

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

Свойство wrapBounds в Matter.js — это мощный и простой инструмент для создания иллюзии бесконечного или замкнутого мира. Вы можете экспериментировать: задайте границы телепортации меньше размеров холста, чтобы создать "буферную зону", где объекты невидимы во время телепортации. Или реализуйте логику, при которой объект, вылетев за левую границу, появляется справа, но на 50 пикселей ниже, создавая эффект наклонного пространства. Это открывает двери для создания уникальных игровых механик.