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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    cursors;
    ship;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('ship', 'assets/sprites/x2kship.png');
        this.load.image('blue', 'assets/particles/blue.png');
    }

    create ()
    {
        const emitter = this.add.particles(0, 0, 'blue', {
            speed: {
                onEmit: (particle, key, t, value) => this.ship.body.speed * 10

            },
            lifespan: {
                onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 40000
            },
            alpha: {
                onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 1000
            },
            scale: { start: 1.0, end: 0 },
            blendMode: 'ADD'
        });

        this.ship = this.matter.add.image(400, 300, 'ship');

        this.ship.setFrictionAir(0.1);
        this.ship.setMass(30);
        this.ship.setFixedRotation();

        emitter.startFollow(this.ship);

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

        this.cursors = this.input.keyboard.createCursorKeys();
    }

    update ()
    {
        if (this.cursors.left.isDown)
        {
            this.ship.setAngularVelocity(-0.1);
        }
        else if (this.cursors.right.isDown)
        {
            this.ship.setAngularVelocity(0.1);
        }

        if (this.cursors.up.isDown)
        {
            this.ship.thrust(0.08);
        }
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и загрузка ассетов

В методе preload мы загружаем два изображения: спрайт корабля (ship) и текстуру для частиц (blue). Важно использовать setBaseURL, чтобы указать базовый путь для загрузки.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('ship', 'assets/sprites/x2kship.png');
    this.load.image('blue', 'assets/particles/blue.png');
}

Этот код выполняется один раз при старте сцены. Корабль и текстура частиц будут готовы к использованию в методе create.

Создание физического тела корабля

В методе create мы создаем физическое тело корабля с помощью движка Matter.js. Ключевой метод — this.matter.add.image, который создает объект с физическим телом.

this.ship = this.matter.add.image(400, 300, 'ship');
this.ship.setFrictionAir(0.1);
this.ship.setMass(30);
this.ship.setFixedRotation();

* setFrictionAir(0.1) устанавливает сопротивление воздуха, что создает эффект инерции и плавного замедления в вакууме. * setMass(30) задает массу тела, влияющую на его движение при приложении силы. * setFixedRotation() фиксирует вращение тела, предотвращая его нежелательное вращение от столкновений. Мы будем управлять им вручную с помощью клавиш.

Также здесь задаются границы игрового мира, чтобы корабль не улетел за его пределы: this.matter.world.setBounds(0, 0, 800, 600).

Управление кораблем с клавиатуры

Логика управления вынесена в метод update, который вызывается на каждом кадре игры. Мы проверяем состояние клавиш-стрелок.

if (this.cursors.left.isDown)
{
    this.ship.setAngularVelocity(-0.1);
}
else if (this.cursors.right.isDown)
{
    this.ship.setAngularVelocity(0.1);
}

if (this.cursors.up.isDown)
{
    this.ship.thrust(0.08);
}

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

Клавиши создаются в create: this.cursors = this.input.keyboard.createCursorKeys().

Динамическая система частиц

Один из самых интересных элементов примера — система частиц выхлопа, параметры которой динамически зависят от скорости корабля.

const emitter = this.add.particles(0, 0, 'blue', {
    speed: {
        onEmit: (particle, key, t, value) => this.ship.body.speed * 10
    },
    lifespan: {
        onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 40000
    },
    alpha: {
        onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 1000
    },
    scale: { start: 1.0, end: 0 },
    blendMode: 'ADD'
});
emitter.startFollow(this.ship);

* onEmit — это специальные коллбэки, которые вызываются в момент создания каждой новой частицы. Они позволяют вычислить значение параметра на основе текущего состояния игры. * this.ship.body.speed — текущая линейная скорость тела корабля. * Phaser.Math.Percent вычисляет процент, который представляет первое значение (скорость) в диапазоне от 0 до 300. * Таким образом, чем быстрее летит корабль, тем быстрее (speed), дольше (lifespan) и заметнее (alpha) будут частицы его выхлопа. blendMode: 'ADD' создает эффект яркого свечения. * startFollow заставляет эмиттер следовать за кораблем.

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

Вся игра инициализируется объектом конфигурации. Ключевой момент — настройка физического движка Matter.js.

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#1b1464',
    parent: 'phaser-example',
    physics: {
        default: 'matter', // Указываем Matter.js как движок по умолчанию
        matter: {
            gravity: {
                x: 0,
                y: 0 // Отключаем гравитацию для космического симулятора
            }
        }
    },
    scene: Example
};

Игра создается с этой конфигурацией: const game = new Phaser.Game(config);.

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

Этот пример демонстрирует мощную синергию между физикой Matter.js и системой частиц Phaser. Вы можете экспериментировать: изменить массу корабля и силу трения для другого "чувства" управления, добавить стрельбу, создавая снаряды через this.matter.add.image, или сделать частицы выхлопа разноцветными, используя несколько эмиттеров с разными текстурами. Попробуйте заменить setFixedRotation() на setDensity и посмотрите, как изменится поведение при столкновениях.