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

Интерактивность — основа вовлечённости в игре. Понимание того, как обрабатывать действия игрока (клики, тапы) — первый шаг к созданию отзывчивого геймплея. В этой статье мы разберём простой, но мощный механизм Phaser — Pointer Events, который позволяет реагировать на нажатия мыши или касания экрана. Вы научитесь получать координаты клика, создавать объекты по ним и в реальном времени отслеживать состояние указателя, что является фундаментом для множества игровых механик: от стрельбы до управления интерфейсом.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    text;

    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.text = this.add.text(10, 10, '', { font: '16px Courier', fill: '#00ff00' });

        this.input.on('pointerdown', function (pointer)
        {

            console.log(this.game.loop.frame, 'down B');

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

        }, this);
    }

    update ()
    {
        const p = this.input.activePointer;

        this.text.setText([
            `x: ${p.x}`,
            `y: ${p.y}`,
            `duration: ${p.getDuration()}`
        ]);
    }
}

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

const game = new Phaser.Game(config);

Настройка сцены и загрузка ассетов

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

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

Ключевой параметр frameWidth и frameHeight указывает Phaser, как нарезать большое изображение на отдельные кадры. В данном случае мы ожидаем 6 кадров (шаров разного цвета), каждый размером 17x17 пикселей.

Обработка события клика (pointerdown)

Вся магия интерактивности происходит в методе create. Мы создаём текстовый объект для отладки и, что важнее, подписываемся на глобальное событие pointerdown через this.input.on. Это событие срабатывает при нажатии кнопки мыши или касании сенсорного экрана в любом месте игрового поля.

this.input.on('pointerdown', function (pointer) {
    console.log(this.game.loop.frame, 'down B');
    this.add.image(pointer.x, pointer.y, 'balls', Phaser.Math.Between(0, 5));
}, this);
Обратите внимание на три важные детали:
1.  **Параметр `pointer`**: он автоматически передаётся в функцию-обработчик и содержит всю информацию о событии, включая координаты `x` и `y`.
2.  **`this.add.image`**: используя координаты из `pointer`, мы создаём новый спрайт (изображение) прямо в точке клика. Ключ `'balls'` указывает на загруженный спрайтшит.
3.  **`Phaser.Math.Between(0, 5)`**: эта функция случайным образом выбирает один из шести кадров (от 0 до 5) из спрайтшита, создавая разноцветные шары.
4.  **Контекст `this`**: последний аргумент `, this` в вызове `on` гарантирует, что внутри функции-обработчика `this` будет ссылаться на текущую сцену (экземпляр `Example`), что позволяет нам вызывать `this.add.image`.

Отслеживание активного указателя в реальном времени

Чтобы дать игроку визуальную обратную связь, полезно показывать состояние указателя. Это делается в методе update, который вызывается на каждом кадре игры.

const p = this.input.activePointer;
this.text.setText([
    `x: ${p.x}`,
    `y: ${p.y}`,
    `duration: ${p.getDuration()}`
]);

Мы получаем ссылку на активный указатель (activePointer) и обновляем текстовое поле: - **p.x / p.y**: текущие координаты указателя. - **p.getDuration()**: время в миллисекундах, прошедшее с момента начала текущего нажатия (если кнопка нажата). Если кнопка отпущена, метод возвращает время, прошедшее с момента последнего отпускания. Это крайне полезно для реализации механик, зависящих от длительности нажатия (например, зарядка выстрела).

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

Финальный шаг — создание и конфигурация экземпляра игры 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-элемента, в который будет встроен canvas игры. - width / height: размер игрового поля. - scene: класс основной сцены, которая будет запущена сразу после инициализации игры.

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

Использование pointerdown и activePointer открывает дверь к созданию базовой интерактивности в вашей игре на Phaser. Вы можете не только создавать объекты, но и начинать перемещение персонажа, активировать способности или выбирать элементы интерфейса. **Идеи для экспериментов:** 1. Измените обработчик pointerdown так, чтобы шар не просто появлялся, а "выстреливал" из центра экрана в точку клика, используя физический движок Arcade Physics (this.physics.add.image и setVelocity). 2. Реализуйте перетаскивание: подпишитесь также на события pointermove и pointerup, чтобы перемещать созданный шар, пока кнопка нажата. 3. Используйте getDuration() для создания механики: чем дольше удерживается клик, тем больше или быстрее создаваемый шар.