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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    }

    create ()
    {
        this.input.on('pointermove', function (pointer)
        {

            if (pointer.isDown)
            {
                this.add.image(pointer.x, pointer.y, 'balls', Phaser.Math.Between(0, 5));
            }

        }, this);
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ассетов

Вся логика в Phaser размещается внутри методов сцены (Scene). На этапе preload мы загружаем ресурсы, необходимые для работы. В данном примере загружается не просто изображение, а спрайтшит (spritesheet) — единая картинка, содержащая несколько кадров (спрайтов).

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
}

Метод setBaseURL задает базовый путь для загрузки. Далее метод load.spritesheet загружает изображение по ключу 'balls'. Третий аргумент — объект конфигурации, который указывает движку, как «нарезать» большую картинку на отдельные кадры размером 17x17 пикселей. После загрузки мы сможем обращаться к каждому из этих кадров по его индексу (от 0 до N).

Обработка ввода и событие pointermove

Основная логика размещения объектов происходит в методе create, который выполняется один раз после загрузки ресурсов. Здесь мы настраиваем взаимодействие с пользователем.

Ключевой элемент — система ввода Phaser (this.input). Мы используем метод on, чтобы подписаться на конкретное событие. Событие 'pointermove' генерируется каждый раз, когда пользователь перемещает указатель (мышь или касание) над игровым холстом.

create ()
{
    this.input.on('pointermove', function (pointer)
    {
        // Логика будет здесь
    }, this);
}

Обратите внимание на третий аргумент this, переданный в метод on. Он задает контекст (значение this) для callback-функции. Без этого контекст внутри функции был бы потерян, и мы не смогли бы вызвать, например, this.add.image.

Условие отрисовки и создание спрайта

Однако нам нужно рисовать не просто при движении мыши, а только когда кнопка мыши нажата. Для этого у объекта pointer, который передается в функцию-обработчик, есть свойство isDown.

if (pointer.isDown)
{
    this.add.image(pointer.x, pointer.y, 'balls', Phaser.Math.Between(0, 5));
}

Если условие выполняется, мы создаем новый спрайт. Метод this.add.image принимает четыре аргумента: 1. Координата X (pointer.x). 2. Координата Y (pointer.y). 3. Ключ текстуры ('balls'). 4. Индекс кадра (frame) из спрайтшита.

Для выбора кадра используется Phaser.Math.Between(0, 5). Эта функция возвращает случайное целое число в заданном диапазоне. Таким образом, при каждом вызове на холсте будет появляться шар одного из шести случайных цветов, что создает простой визуальный эффект.

Конфигурация и запуск игры

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

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

В конфиге мы указываем: - type: Рендерер (Phaser.AUTO выбирает между WebGL и Canvas автоматически). - parent: ID HTML-элемента, в который будет встроен холст игры. - width и height: Размеры игрового поля. - scene: Класс нашей сцены, которая будет запущена сразу.

Создание экземпляра new Phaser.Game(config) инициализирует движок и запускает жизненный цикл сцены.

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

Вы только что реализовали базовую, но полностью рабочую интерактивную механику рисования в Phaser. Этот паттерн — «слушать событие ввода и создавать объекты» — является фундаментальным. Для экспериментов попробуйте изменить спрайтшит на набор разных игровых предметов, добавить физические тела создаваемым объектам с помощью this.physics.add.image, чтобы они падали и сталкивались, или сохранять координаты размещенных объектов для генерации уникальных уровней. Вы также можете изменить событие с 'pointermove' на 'pointerdown', чтобы спрайты появлялись по клику, а не при движении с зажатой кнопкой.