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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    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.physics.add.collider(this.player, this.platforms);
    }

    update ()
    {
    }
}

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 загружает обычные статичные изображения, такие как фон, платформы, звезды и бомбы.

Спрайтшит — это одно изображение, содержащее несколько кадров анимации. Метод this.load.spritesheet загружает его и сразу указывает размер одного кадра (frameWidth и frameHeight), что позволяет Phaser автоматически нарезать его на отдельные фреймы.

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

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

Метод create — это место, где мы конструируем игровой мир. Сначала добавляется фоновое изображение с помощью this.add.image. Важно: этот объект не имеет физического тела и не участвует в столкновениях.

Затем создается группа статических физических тел this.physics.add.staticGroup(). Статические тела неподвижны и не подвержены силам (вроде гравитации), но с ними могут сталкиваться динамические объекты. Это идеально подходит для платформ и земли.

Метод create у группы добавляет новое статическое тело. Метод refreshBody() необходимо вызывать, если вы изменяете размер (setScale) или положение статического тела после его создания, чтобы физический движок корректно пересчитал его границы.

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

Персонаж: физическое тело и движение

Динамический персонаж создается как физический спрайт с помощью this.physics.add.sprite. В отличие от статических платформ, на него воздействует гравитация, заданная в конфигурации игры (y: 300).

Метод setBounce(0.2) задает упругость (коэффициент отскока) при столкновении с другими телами. Значение 0.2 означает, что 20% скорости будет сохранено после удара.

setCollideWorldBounds(true) — критически важный вызов. Он включает столкновения персонажа с границами игрового мира (800x600), не позволяя ему бесконечно падать вниз или уходить за края экрана.

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

Анимация: оживляем спрайт

Phaser использует систему анимаций, управляемую через this.anims.create. Каждая анимация имеет уникальный ключ (key), который используется для её воспроизведения.

Для бега влево и вправо мы используем this.anims.generateFrameNumbers, чтобы автоматически получить массив кадров из спрайтшита по их индексам (например, с 0 по 3). Параметр repeat: -1 заставляет анимацию зацикливаться.

Анимация 'turn' (поворот) состоит всего из одного кадра (кадр №4, где персонаж смотрит в камеру). Её можно использовать, когда игрок стоит на месте.

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

Столкновения: связываем персонажа и платформы

Чтобы физика заработала, необходимо явно указать движку, какие объекты должны сталкиваться друг с другом. За это отвечает коллайдер.

this.physics.add.collider(this.player, this.platforms) регистрирует столкновение между динамическим спрайтом игрока и группой статических платформ. Благодаря этому персонаж будет останавливаться на платформах, а не проваливаться сквозь них под действием гравитации.

Этот метод — основа взаимодействия в платформере. Позже с его помощью можно будет добавить столкновения со звездами, врагами или другими объектами.

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

Конфигурация игры: включаем физический движок

Вся физика в примере работает благодаря правильной конфигурации игры (const config). Ключевой раздел — physics.default: 'arcade'.

Включив аркадную физику (arcade), мы получаем простую и производительную систему для 2D-игр. В её настройках задается вектор гравитации (gravity: { y: 300 }), который заставляет все динамические тела падать вниз.

Параметр debug: false отключает отладочную отрисовку контуров физических тел. На этапе разработки его можно включить, чтобы визуально проверить расстановку коллайдеров.

physics: {
    default: 'arcade',
    arcade: {
        gravity: { y: 300 },
        debug: false
    }
}

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

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