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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    cursors;
    platforms;
    player;

    preload ()
    {
        // this.load.setBaseURL('https://cdn.phaserfiles.com/v385');
        this.load.image('sky', 'src/games/firstgame/assets/sky.png');
        this.load.image('ground', 'src/games/firstgame/assets/platform.png');
        this.load.image('star', 'src/games/firstgame/assets/star.png');
        this.load.image('bomb', 'src/games/firstgame/assets/bomb.png');
        this.load.spritesheet('dude', 'src/games/firstgame/assets/dude.png', { frameWidth: 32, frameHeight: 48 });
    }

    create ()
    {
        this.add.image(400, 300, 'sky');

        this.platforms = this.physics.add.staticGroup();

        this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();

        this.platforms.create(600, 400, 'ground');
        this.platforms.create(50, 250, 'ground');
        this.platforms.create(750, 220, 'ground');

        this.player = this.physics.add.sprite(100, 450, 'dude');

        this.player.setBounce(0.2);
        this.player.setCollideWorldBounds(true);

        this.anims.create({
            key: 'left',
            frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
            frameRate: 10,
            repeat: -1
        });

        this.anims.create({
            key: 'turn',
            frames: [ { key: 'dude', frame: 4 } ],
            frameRate: 20
        });

        this.anims.create({
            key: 'right',
            frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
            frameRate: 10,
            repeat: -1
        });

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

        this.physics.add.collider(this.player, this.platforms);
    }

    update ()
    {
        if (this.cursors.left.isDown)
        {
            this.player.setVelocityX(-160);

            this.player.anims.play('left', true);
        }
        else if (this.cursors.right.isDown)
        {
            this.player.setVelocityX(160);

            this.player.anims.play('right', true);
        }
        else
        {
            this.player.setVelocityX(0);

            this.player.anims.play('turn');
        }

        if (this.cursors.up.isDown && this.player.body.touching.down)
        {
            this.player.setVelocityY(-330);
        }
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Загрузка ресурсов: изображения и спрайтшит

В методе preload() происходит загрузка всех необходимых для игры ресурсов. Это обязательный этап, чтобы в дальнейшем создавать из них игровые объекты.

Обратите внимание на загрузку спрайтшита для персонажа: мы указываем не только путь к файлу, но и размер одного кадра анимации. Это ключевой момент для последующего создания анимаций.

this.load.image('sky', 'src/games/firstgame/assets/sky.png');
this.load.image('ground', 'src/games/firstgame/assets/platform.png');
this.load.spritesheet('dude', 'src/games/firstgame/assets/dude.png', { frameWidth: 32, frameHeight: 48 });

Создание мира: статические платформы и физический спрайт

Метод create() инициализирует игровой мир. Сначала добавляется фоновое изображение. Затем создается статическая физическая группа this.platforms. Объекты в такой группе по умолчанию неподвижны и с ними могут сталкиваться динамические тела.

Персонаж (player) создается как динамический физический спрайт с помощью this.physics.add.sprite. Ему сразу задаются свойства: упругость (setBounce) и привязка к границам мира (setCollideWorldBounds).

this.platforms = this.physics.add.staticGroup();
this.platforms.create(400, 568, 'ground').setScale(2).refreshBody();

this.player = this.physics.add.sprite(100, 450, 'dude');
this.player.setBounce(0.2);
this.player.setCollideWorldBounds(true);

Анимация персонажа: бег, поворот и покой

Phaser использует систему анимаций, управляемую через this.anims. В этом примере создаются три анимации для персонажа: движение влево, поворот (стойка) и движение вправо.

Анимация 'left' использует кадры с 0 по 3 из спрайтшита 'dude'. Ключевой параметр repeat: -1 заставляет анимацию зацикливаться. Анимация 'turn' состоит всего из одного кадра (кадр №4).

this.anims.create({
    key: 'left',
    frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
    frameRate: 10,
    repeat: -1
});

this.anims.create({
    key: 'turn',
    frames: [ { key: 'dude', frame: 4 } ],
    frameRate: 20
});

Обработка ввода и управление физикой

В create() также настраивается слушатель клавиш-стрелок. Логика управления целиком находится в методе update(), который вызывается на каждом кадре игры.

В зависимости от нажатой клавиши, персонажу задается горизонтальная скорость (setVelocityX) и проигрывается соответствующая анимация (anims.play). Прыжок (setVelocityY) возможен только если в данный момент персонаж касается земли (this.player.body.touching.down). Это простое условие предотвращает "двойной прыжок" в воздухе.

if (this.cursors.left.isDown)
{
    this.player.setVelocityX(-160);
    this.player.anims.play('left', true);
}
else if (this.cursors.right.isDown)
{
    this.player.setVelocityX(160);
    this.player.anims.play('right', true);
}

if (this.cursors.up.isDown && this.player.body.touching.down)
{
    this.player.setVelocityY(-330);
}

Настройка коллизий и конфигурация игры

Одна из самых важных строк кода — это создание коллайдера. Именно он заставляет физические тела персонажа и платформ сталкиваться, а не проходить друг сквозь друга.

Конфигурационный объект игры задает ее базовые параметры: тип рендерера, размер холста и, что самое важное, настройки физического движка Arcade. Здесь определяется сила гравитации по оси Y.

this.physics.add.collider(this.player, this.platforms);

const config = {
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: Example
};

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

Вы разобрали готовый каркас платформера на Phaser. Основные концепции — загрузка ассетов, создание физических тел, анимация и обработка ввода — теперь у вас под рукой. Для экспериментов попробуйте: изменить силу гравитации в конфиге, добавить больше платформ, создать анимацию прыжка или сделать так, чтобы персонаж ускорялся при долгом нажатии клавиши. Это отличная основа для создания своей первой игры.