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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    sprite;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
    }

    create ()
    {
        this.sprite = this.add.sprite(400, 300, 'eye');

        this.sprite.setInteractive();

        this.sprite.on('pointerdown', function ()
        {

            this.setTint(0xff0000);

        });

        this.sprite.on('pointerup', function ()
        {

            this.clearTint();

        });

        /*
        this.input.on('gameobjectover', function (pointer, gameObject) {

            gameObject.setTint(0xff0000);

        });

        this.input.on('gameobjectout', function (pointer, gameObject) {

            gameObject.clearTint();

        });
        */
    }

    update ()
    {
        this.sprite.rotation += 0.01;
    }
}

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

const game = new Phaser.Game(config);

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

Вся логика примера содержится в классе сцены Example. В методе preload() мы загружаем одно изображение, которое станет нашим интерактивным спрайтом.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}

Метод setBaseURL() задаёт базовый путь для загрузки ресурсов, что позволяет указывать только относительные пути, как 'assets/pics/...'. Изображение загружается под ключом 'eye' для последующего использования.

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

В методе create() происходит основная настройка. Сначала спрайт создаётся и помещается в центр экрана.

create ()
{
    this.sprite = this.add.sprite(400, 300, 'eye');
    this.sprite.setInteractive();
}

Критически важный шаг — вызов setInteractive() на спрайте. Без этого спрайт не будет генерировать события ввода (pointer events). По умолчанию в качестве хитбокса используется размер текстуры спрайта.

Обработка событий указателя (Pointer Events)

После того как спрайт стал интерактивным, мы можем подписаться на его события. В примере показана реакция на нажатие (pointerdown) и отпускание (pointerup) кнопки мыши.

this.sprite.on('pointerdown', function ()
{
    this.setTint(0xff0000);
});

this.sprite.on('pointerup', function ()
{
    this.clearTint();
});

Внутри функций-обработчиков контекст this ссылается на сам спрайт (this.sprite). setTint(0xff0000) применяет красный оттенок ко всему спрайту. clearTint() — сбрасывает его. Таким образом, спрайт краснеет, пока кнопка мыши зажата над ним.

Альтернатива: глобальные события ввода (закомментировано)

В исходном коде закомментирован альтернативный подход с использованием глобальных событий gameobjectover и gameobjectout.

// this.input.on('gameobjectover', function (pointer, gameObject) {
//     gameObject.setTint(0xff0000);
// });
//
// this.input.on('gameobjectout', function (pointer, gameObject) {
//     gameObject.clearTint();
// });

Эти события генерируются на уровне this.input, а не на конкретном спрайте. Обработчик получает объект gameObject, над которым произошло событие. Этот подход полезен, когда нужно централизованно обрабатывать наведение для множества объектов, но в нашем простом случае прямая подписка на спрайте (this.sprite.on...) более прямолинейна.

Анимация в игровом цикле

Чтобы игра выглядела живой, даже статичные объекты часто анимируются. В методе update(), который вызывается каждый кадр, мы непрерывно вращаем спрайт.

update ()
{
    this.sprite.rotation += 0.01;
}

Изменение свойства rotation спрайта на небольшую величину каждый кадр создаёт плавную анимацию вращения. Это демонстрирует, как интерактивность (реакция на мышь) легко комбинируется с автономной анимацией.

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

Класс сцены должен быть передан в конфигурацию игры Phaser.

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

const game = new Phaser.Game(config);

Объект config определяет основные параметры: использование WebGL, контейнер для отрисовки (parent), размеры холста и главную сцену. Создание экземпляра Phaser.Game с этой конфигурацией запускает весь игровой цикл.

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

Вы научились создавать интерактивный спрайт, который реагирует на клики и плавно вращается. Это базовый, но мощный строительный блок. Для экспериментов попробуйте

  1. Изменить цвет тинта на pointerdown и сделать его другим на pointerover
  2. Использовать setInteractive с другой геометрией хитбокса, например, кругом
  3. Добавить звук при клике, загрузив и воспроизведя аудиофайл в обработчике события