О чем этот пример
Создание правдоподобного космического полета — частая задача при разработке аркадных игр. В отличие от наземного транспорта, космический корабль должен плавно ускоряться, вращаться по инерции и не иметь мгновенной остановки. В этой статье мы разберем пример из официальной коллекции Phaser, который демонстрирует ключевые принципы аркадной физики для космического симулятора. Вы научитесь настраивать демпфирование, силу тяги и телепортацию объектов при выходе за границы мира, что станет отличной основой для вашего шутера или приключенческой игры.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
text;
cursors;
sprite;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bullet', 'assets/games/asteroids/bullets.png');
this.load.image('ship', 'assets/games/asteroids/ship.png');
}
create ()
{
this.sprite = this.physics.add.image(400, 300, 'ship');
this.sprite.setDamping(true);
this.sprite.setDrag(0.99);
this.sprite.setMaxVelocity(200);
this.cursors = this.input.keyboard.createCursorKeys();
this.text = this.add.text(10, 10, '', { font: '16px Courier', fill: '#00ff00' });
}
update ()
{
if (this.cursors.up.isDown)
{
this.physics.velocityFromRotation(this.sprite.rotation, 200, this.sprite.body.acceleration);
}
else
{
this.sprite.setAcceleration(0);
}
if (this.cursors.left.isDown)
{
this.sprite.setAngularVelocity(-300);
}
else if (this.cursors.right.isDown)
{
this.sprite.setAngularVelocity(300);
}
else
{
this.sprite.setAngularVelocity(0);
}
this.text.setText(`Speed: ${this.sprite.body.speed}`);
this.physics.world.wrap(this.sprite, 32);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: { y: 0 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка физического спрайта и его свойств
В методе create() создается основной спрайт корабля и сразу добавляется в физический мир Arcade. Ключевой момент — настройка свойств, имитирующих движение в безвоздушном пространстве.
this.sprite = this.physics.add.image(400, 300, 'ship');
this.sprite.setDamping(true);
this.sprite.setDrag(0.99);
this.sprite.setMaxVelocity(200);
Метод setDamping(true) включает эффект демпфирования (постепенного замедления). В паре с ним работает setDrag(0.99), который устанавливает коэффициент сопротивления движению. Значение 0.99 близко к 1, что создает эффект очень плавного, почти бесконечного скольжения в вакууме. setMaxVelocity(200) ограничивает максимальную скорость корабля, предотвращая его разгон до бесконечности.
Управление ускорением и вращением
Логика управления полностью находится в методе update(). Она обрабатывает нажатия клавиш стрелок и применяет соответствующие силы к физическому телу корабля.
if (this.cursors.up.isDown)
{
this.physics.velocityFromRotation(this.sprite.rotation, 200, this.sprite.body.acceleration);
}
else
{
this.sprite.setAcceleration(0);
}
Здесь используется мощная вспомогательная функция velocityFromRotation. Она вычисляет вектор ускорения (acceleration) на основе текущего угла поворота спрайта (this.sprite.rotation) и заданной величины силы (200). Это означает, что корабль всегда ускоряется туда, куда смотрит его нос, что идеально для космического полета.
if (this.cursors.left.isDown)
{
this.sprite.setAngularVelocity(-300);
}
else if (this.cursors.right.isDown)
{
this.sprite.setAngularVelocity(300);
}
else
{
this.sprite.setAngularVelocity(0);
}
Вращение управляется через установку угловой скорости (angularVelocity). Пока клавиша нажата, корабль вращается с постоянной скоростью. Когда клавиши отпущены, скорость вращения сбрасывается в ноль. В отличие от линейного движения, здесь нет демпфирования на вращение, что является осознанным дизайнерским решением для более отзывчивого управления.
Отслеживание состояния и телепортация на границах
В каждом кадре также обновляется текстовое поле и проверяется положение корабля относительно границ игрового мира.
this.text.setText(`Speed: ${this.sprite.body.speed}`);
this.physics.world.wrap(this.sprite, 32);
Свойство this.sprite.body.speed содержит текущую модуль скорости (скалярное значение), что удобно для вывода в интерфейс.
Метод this.physics.world.wrap() — это «читерский» способ реализации бесконечного пространства. Когда спрайт полностью покидает границы мира (с учетом заданного отступа в 32 пикселя), он мгновенно появляется с противоположной стороны. Это классический прием из игр типа Asteroids, который избавляет от необходимости вручную вычислять координаты и мгновенно перемещать объект.
Конфигурация игрового экземпляра
Весь пример работает благодаря правильной конфигурации физического движка Arcade при создании экземпляра игры (Phaser.Game).
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: { y: 0 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Установка gravity: { y: 0 } отключает гравитацию, что абсолютно необходимо для симуляции невесомости. Параметр debug: false отключает отладочную визуализацию коллайдеров и векторов скорости. Для отладки сложного движения его можно временно включить.
Что попробовать дальше
Разобранный пример предоставляет готовый каркас для управления космическим кораблем с реалистичной инерцией. Экспериментируйте с параметрами: измените setDrag на 0.9 для более «тяжелого» и быстро тормозящего корабля или увеличьте setMaxVelocity для динамичного экшена. Попробуйте добавить стрельбу, создавая пули (physics.add.image) и задавая им скорость с помощью setVelocity в направлении поворота корабля. Для создания поля астероидов используйте группу физических объектов (this.physics.add.group) и метод wrap для каждого из них, чтобы они также циклически появлялись на экране.
