О чем этот пример
Создание реалистичного космического корабля — ключевой элемент многих игр. В этой статье мы разберём пример из официальной документации 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.atlas('space', 'assets/tests/space/space.png', 'assets/tests/space/space.json');
}
create ()
{
const emitter = this.add.particles(0, 0, 'space', {
frame: 'blue',
speed: {
onEmit: (particle, key, t, value) => this.ship.body.speed
},
lifespan: {
onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 200000
},
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.setFixedRotation();
this.ship.setAngle(270);
this.ship.setFrictionAir(0.05);
this.ship.setMass(30);
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.thrustLeft(0.1);
}
else if (this.cursors.right.isDown)
{
this.ship.thrustRight(0.1);
}
if (this.cursors.up.isDown)
{
this.ship.thrust(0.1);
}
else if (this.cursors.down.isDown)
{
this.ship.thrustBack(0.1);
}
}
}
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);
Настройка физики и загрузка ассетов
Всё начинается с конфигурации игры. Мы указываем, что в качестве физического движка будет использоваться Matter.js, и отключаем гравитацию, чтобы создать эффект невесомости, характерный для космоса.
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
};
В методе preload загружаются необходимые изображения: спрайт корабля и атлас текстур для системы частиц. Важно использовать setBaseURL, чтобы указать корректный путь к удалённым ресурсам из репозитория примеров.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('ship', 'assets/sprites/x2kship.png');
this.load.atlas('space', 'assets/tests/space/space.png', 'assets/tests/space/space.json');
}
Создание физического тела корабля
В методе create происходит основная настройка. Корабль создаётся не как обычный спрайт, а как физическое тело с помощью this.matter.add.image. Это автоматически наделяет его свойствами твёрдого тела.
this.ship = this.matter.add.image(400, 300, 'ship');
Далее тело корабля настраивается с помощью методов Matter.js:
- setFixedRotation() предотвращает вращение тела от воздействия сил, что даёт нам полный контроль над углом поворота.
- setAngle(270) изначально разворачивает корабль носом вверх (так как в Phaser нулевой угол смотрит вправо).
- setFrictionAir(0.05) задаёт сопротивление воздуха (или, в нашем случае, космической среды). Небольшое значение создаёт эффект плавного скольжения и постепенной остановки.
- setMass(30) устанавливает массу тела, которая влияет на его инерцию.
this.ship.setFixedRotation();
this.ship.setAngle(270);
this.ship.setFrictionAir(0.05);
this.ship.setMass(30);
Также важно установить границы мира, чтобы тело корабля не могло их покинуть: this.matter.world.setBounds(0, 0, 800, 600).
Динамическая система частиц для струи
Эффект реактивной струи создаётся с помощью системы частиц (this.add.particles), которая следует за кораблём (emitter.startFollow(this.ship)). Особенность этой системы в том, что её параметры динамически зависят от скорости корабля.
const emitter = this.add.particles(0, 0, 'space', {
frame: 'blue',
speed: {
onEmit: (particle, key, t, value) => this.ship.body.speed
},
lifespan: {
onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 200000
},
alpha: {
onEmit: (particle, key, t, value) => Phaser.Math.Percent(this.ship.body.speed, 0, 300) * 1000
},
scale: { start: 1.0, end: 0 },
blendMode: 'ADD'
});
Ключевые моменты:
- `onEmit`: Это функции обратного вызова, которые вычисляют значение параметра в момент создания каждой новой частицы.
- `this.ship.body.speed`: Текущее значение скорости тела корабля.
- `Phaser.Math.Percent(this.ship.body.speed, 0, 300)`: Преобразует скорость (в диапазоне от 0 до 300) в процентное значение от 0 до 1. Это позволяет связать время жизни (`lifespan`) и прозрачность (`alpha`) частицы со скоростью корабля. Чем быстрее летит корабль, тем длиннее и ярче струя.
- `blendMode: 'ADD'`: Режим наложения 'ADD' делает частицы яркими и светящимися, что идеально подходит для эффекта огня или плазмы.
Управление и применение сил (Thrust)
Логика управления вынесена в метод update, который вызывается каждый кадр. Здесь проверяется состояние клавиш-стрелок и применяются соответствующие силы к телу корабля.
if (this.cursors.left.isDown)
{
this.ship.thrustLeft(0.1);
}
else if (this.cursors.right.isDown)
{
this.ship.thrustRight(0.1);
}
if (this.cursors.up.isDown)
{
this.ship.thrust(0.1);
}
else if (this.cursors.down.isDown)
{
this.ship.thrustBack(0.1);
}
Важно понимать, что методы thrust, thrustBack, thrustLeft и thrustRight применяют силу **относительно текущего угла поворота тела**.
- thrust(0.1): Применяет силу вперёд (в направлении, куда «смотрит» спрайт).
- thrustBack(0.1): Применяет силу назад.
- thrustLeft(0.1) и thrustRight(0.1): Применяют силу влево и вправо относительно направления корабля, что и создаёт эффект поворота на месте или бокового манёвра.
Аргумент 0.1 — это величина силы. Поскольку эти силы применяются каждый кадр, пока зажата клавиша, корабль плавно разгоняется. А благодаря настроенному трению (setFrictionAir) он так же плавно замедляется, когда игрок отпускает клавиши.
Что попробовать дальше
Этот пример — отличная основа для космического симулятора или аркадного шутера. Вы создали не просто управляемый спрайт, а физическое тело, которое ведёт себя предсказуемо и реалистично. Для экспериментов попробуйте: изменить массу корабля и трение, чтобы получить другое ощущение управления; добавить инерционное вращение, убрав setFixedRotation(); создать разные системы частиц для различных типов двигателей или повреждений; или реализовать стрельбу, где снаряды также будут физическими телами, создаваемыми с помощью this.matter.add.image.
