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

При создании игр часто возникают ситуации, когда интерактивные объекты движутся под курсором мыши, а не наоборот. Стандартный подход Phaser обрабатывает события `pointerover` и `pointerout` только при движении курсора, что может привести к "пропаданию" взаимодействия с анимированными элементами. В этой статье мы разберем метод `setPollAlways()`, который заставляет систему ввода непрерывно проверять пересечения, обеспечивая плавную и предсказуемую реакцию на движущиеся спрайты, что критически важно для динамичных интерфейсов и геймплея.

Версия 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.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
    }

    create ()
    {
        const sprite = this.add.sprite(100, 300, 'eye').setInteractive();

        //  Enable continuous polling

        //  This will check the interactive objects even if you don't move the mouse
        //  So you can leave it in the path of the sprite and it will still fire
        //  over and out events while tweening across the screen, even if you don't
        //  move the mouse.

        this.input.setPollAlways();

        //  Events

        this.input.on('gameobjectover', (pointer, gameObject) =>
        {

            gameObject.setTint(0xff0000);

        });

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

            gameObject.clearTint();

        });

        this.input.on('pointerdown', () =>
        {
            console.log('down');
        });

        this.tweens.add({

            targets: sprite,
            x: 800,
            yoyo: true,
            repeat: -1,
            duration: 5000

        });
    }
}

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

const game = new Phaser.Game(config);

Проблема стандартного опроса ввода

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

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

Решение: метод setPollAlways()

Класс Phaser.Input.InputManager предоставляет метод setPollAlways(). Его вызов переключает систему в режим непрерывного опроса.

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

this.input.setPollAlways();

Вызов этого метода обычно размещают в методе create() сцены, после создания интерактивных объектов, но до определения обработчиков событий.

Настройка сцены и обработка событий

Рассмотрим ключевые части примера. Сначала создается спрайт и делается интерактивным с помощью setInteractive().

const sprite = this.add.sprite(100, 300, 'eye').setInteractive();

Затем настраиваются обработчики для нужных событий. Событие 'gameobjectover' срабатывает, когда курсор (или палец на тачскрине) входит в область спрайта. В обработчике мы устанавливаем красный оттенок (tint).

this.input.on('gameobjectover', (pointer, gameObject) => {
    gameObject.setTint(0xff0000);
});

Событие 'gameobjectout' срабатывает при выходе курсора за границы объекта. Здесь оттенок сбрасывается.

this.input.on('gameobjectout', (pointer, gameObject) => {
    gameObject.clearTint();
});

Для демонстрации также добавлен простой обработчик клика ('pointerdown').

Анимация и итоговый результат

Чтобы продемонстрировать работу setPollAlways(), к спрайту применяется бесконечный tween, который двигает его по горизонтали туда-обратно.

this.tweens.add({
    targets: sprite,
    x: 800,
    yoyo: true,
    repeat: -1,
    duration: 5000
});

Без вызова setPollAlways() спрайт, двигаясь под курсором, не вызывал бы события over/out. С этим методом, даже если оставить мышь неподвижной на пути движения спрайта, он будет последовательно окрашиваться в красный цвет при наведении и возвращаться к исходному виду при отведении, создавая четкую визуальную связь с игроком.

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

Метод this.input.setPollAlways() — это мощный инструмент для обеспечения надежного взаимодействия в динамичных сценах Phaser, где объекты движутся независимо от курсора. Он особенно полезен для игр с автоматически движущимися элементами интерфейса, пазлов с перемещающимися фишками или любых ситуаций, где интерактивность не должна зависеть от активности мыши игрока. **Идеи для экспериментов:** 1. Сравните производительность сцены с включенным и выключенным setPollAlways при большом количестве интерактивных объектов. 2. Реализуйте сложный интерфейс с плавно выезжающими меню, которые должны реагировать на наведение. 3. Создадите мини-игру, где игрок должен кликать на движущиеся мишени, и убедитесь, что наведение работает безупречно.