О чем этот пример
В играх с большими мирами управление камерой — ключевой элемент геймплея. Этот пример демонстрирует, как создать плавно перемещаемую камеру с помощью `SmoothedKeyControl` и совместить её с интерактивными, перетаскиваемыми объектами. Вы научитесь настраивать границы камеры, управлять её ускорением и скоростью, а также реализуете простую механику перетаскивания объектов прямо в мире игры, что полезно для карточных игр, стратегий или редакторов уровней.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
controls;
cards;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
}
create ()
{
// Create a stack of random cards
this.cards = this.add.group();
const frames = this.textures.get('cards').getFrameNames();
for (let i = 0; i < 200; i++)
{
const x = Phaser.Math.Between(0, 2048);
const y = Phaser.Math.Between(0, 1200);
const image = this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames));
image.setInteractive();
image.setScale(Phaser.Math.FloatBetween(0.25, 1.0));
image.setScrollFactor(image.scaleX);
image.setDepth(image.scrollFactorX);
image.setAngle(Phaser.Math.Between(0, 359));
this.input.setDraggable(image);
this.cards.add(image);
}
this.input.on('drag', (pointer, gameObject, dragX, dragY) =>
{
gameObject.x = dragX;
gameObject.y = dragY;
});
const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
acceleration: 0.04,
drag: 0.0005,
maxSpeed: 0.7
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
this.cameras.main.setBounds(0, 0, 2048, 1200);
}
update (time, delta)
{
this.controls.update(delta);
Phaser.Actions.Rotate(this.cards.getChildren(), 0.01);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1024,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка и создание объектов
В методе preload загружается атлас спрайтов с картами. В create создаётся группа this.cards для удобного управления множеством объектов.
Затем в цикле генерируется 200 карт со случайными параметрами: позиция, кадр из атласа, масштаб, угол поворота. Ключевые настройки — setScrollFactor и setDepth.
image.setScale(Phaser.Math.FloatBetween(0.25, 1.0));
image.setScrollFactor(image.scaleX);
image.setDepth(image.scrollFactorX);
setScrollFactor определяет, как объект движется относительно камеры. Значение, равное масштабу, создаёт параллакс-эффект: крупные карты (масштаб ~1.0) движутся почти с камерой, мелкие (масштаб ~0.25) — медленнее, создавая ощущение глубины. setDepth использует тот же фактор для корректного порядка отрисовки.
Каждая карта делается интерактивной и перетаскиваемой с помощью setInteractive и setDraggable.
Механика перетаскивания объектов
Для обработки перетаскивания используется событие drag объекта this.input. Оно срабатывает при перемещении зажатого указателя (мыши или касания).
this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
gameObject.x = dragX;
gameObject.y = dragY;
});
В колбэке gameObject — это перетаскиваемый объект (карта), а dragX и dragY — его новые мировые координаты, автоматически рассчитанные движком с учётом камеры. Просто присваивая эти значения, мы перемещаем карту. Это базовая, но мощная механика для взаимодействия с игровыми объектами.
Настройка плавного управления камерой
Управление камерой реализовано через класс Phaser.Cameras.Controls.SmoothedKeyControl. Сначала создаётся объект конфигурации, куда передаются ключи-стрелки и настройки камеры.
const controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
acceleration: 0.04,
drag: 0.0005,
maxSpeed: 0.7
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
Здесь acceleration — ускорение камеры при нажатии клавиши, drag — сила замедления при отпускании, а maxSpeed — ограничение максимальной скорости. Такой подход даёт инерционное, плавное движение, а не мгновенные рывки.
Важно установить границы мира для камеры, чтобы она не выходила за пределы уровня:
this.cameras.main.setBounds(0, 0, 2048, 1200);
Обновление состояния в update
В методе update происходит две вещи: обновление управления камерой и анимация карт.
this.controls.update(delta);
Phaser.Actions.Rotate(this.cards.getChildren(), 0.01);
Метод this.controls.update(delta) необходимо вызывать каждый кадр, передавая дельту времени для плавного и независимого от частоты кадров движения камеры.
Phaser.Actions.Rotate — это хелпер для групповых операций. Он поворачивает все карты в группе на 0.01 радиана за кадр, создавая постоянное, едва заметное вращение, которое оживляет сцену. Вращение происходит в мировых координатах, независимо от камеры и параллакса.
Что попробовать дальше
Пример объединяет несколько важных техник: плавное инерционное управление камерой, параллакс-эффект через scrollFactor, перетаскивание объектов и фоновую анимацию. Для экспериментов попробуйте изменить параметры acceleration и drag у камеры, чтобы добиться другого 'ощущения' управления. Добавьте зум камеры через this.cameras.main.zoom или реализуйте сортировку глубины при перетаскивании, меняя depth у активной карты. Можно также заменить вращение карт на их покачивание или пульсацию, используя Phaser.Actions.
