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

При разработке динамических игр с множеством движущихся объектов обработка событий мыши может стать ресурсоёмкой. По умолчанию Phaser постоянно опрашивает (poll) позицию курсора, проверяя пересечение со всеми интерактивными объектами на сцене, даже если мышь неподвижна. Это может приводить к излишней нагрузке, особенно при сложных анимациях. Метод `setPollOnMove` позволяет переключить систему событий в режим «опроса только при движении», что повышает производительность и даёт более предсказуемое поведение в определённых игровых сценариях. В этой статье мы разберём, как работает этот метод на практическом примере с движущимся спрайтом, и объясним, в каких случаях его применение будет наиболее полезно для вашего проекта.

Версия 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 pointer polling only when you move the mouse

        //  This will check the pointer against the interactive objects only when you move the mouse
        //  If you leave the mouse in the path of the sprite and don't touch it,
        //  the sprite will *not* trigger the over/out events as it tweens across the screen.

        this.input.setPollOnMove();

        //  Events

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

            gameObject.setTint(0xff0000);

        });

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

            gameObject.clearTint();

        });

        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 в каждом кадре игры проверяет положение курсора мыши (или касания) и вычисляет его пересечение со всеми объектами, у которых вызван метод setInteractive(). Этот процесс называется опросом (polling). Он гарантирует мгновенную реакцию, но может быть избыточным.

Представьте сцену, где десятки объектов движутся по сложным траекториям. Даже если игрок не двигает мышь, система будет выполнять дорогостоящие проверки на пересечение в каждом кадре для каждого объекта. А если курсор просто покоится на экране, эти вычисления могут быть бесполезны.

// Обычный режим (активен по умолчанию):
// Проверка пересечения курсора со спрайтом происходит в КАЖДОМ кадре.
const sprite = this.add.sprite(100, 300, 'eye').setInteractive();

Решение: setPollOnMove

Метод this.input.setPollOnMove() меняет логику работы системы ввода. После его вызова проверка пересечения курсора с интерактивными объектами будет происходить **только в те кадры, когда позиция курсора изменилась**. Если мышь неподвижна, никаких проверок не выполняется.

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

// Включаем режим опроса только при движении мыши.
// Теперь проверки будут только когда курсор сместится.
this.input.setPollOnMove();

Практический пример и его поведение

Рассмотрим код из примера. Мы создаём спрайт, делаем его интерактивным и запускаем бесконечный tween, который двигает его туда-обратно по горизонтали. Также мы подписываемся на события gameobjectover (наведение) и gameobjectout (уход).

**Ключевое наблюдение:** В режиме setPollOnMove событие gameobjectover сработает **только если вы наведёте курсор на спрайт и пошевелите мышь**. Если вы остановите курсор на пути движения спрайта и не будете его двигать, спрайт, «наезжая» на курсор, не вызовет событие наведения и не окрасится в красный. Это кардинально отличается от поведения по умолчанию.

this.input.on('gameobjectover', (pointer, gameObject) => {
    // Сработает только при наведении И движении мыши.
    gameObject.setTint(0xff0000);
});

this.input.on('gameobjectout', (pointer, gameObject) => {
    // Сработает только при уходе И движении мыши.
    gameObject.clearTint();
});

// Движение спрайта не влияет на события, если мышь неподвижна.
this.tweens.add({
    targets: sprite,
    x: 800,
    yoyo: true,
    repeat: -1,
    duration: 5000
});

Когда использовать этот подход?

1. **Оптимизация UI/меню**: В сложных меню с анимированными элементами, где игрок редко двигает курсор (например, читает текст), этот метод снизит нагрузку. 2. **Стратегические игры или симуляторы**: В играх, где основное взаимодействие — это периодические клики, а не постоянное ведение курсора. 3. **Избегание ложных срабатываний**: Если в вашей игре объекты постоянно двигаются под курсором (как в примере), а вы хотите, чтобы события наведения были исключительно под контролем игрока.

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

Чтобы вернуться к стандартному поведению, используйте метод this.input.setPollAlways().

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

Метод setPollOnMove — это мощный инструмент для тонкой настройки производительности и логики взаимодействия в вашей игре. Он позволяет переложить инициативу генерации событий наведения/ухода на действия игрока, что может быть полезно как для оптимизации, так и для нестандартного геймдизайна. **Идеи для экспериментов:** 1. Создайте сцену с 50-100 движущимися интерактивными объектами и сравните FPS с setPollOnMove и без него, оставив мышь неподвижной. 2. Реализуйте кастомный курсор (например, меч или волшебную палочку), который должен «ударять» объекты только при активном движении мыши игроком. 3. Скомбинируйте setPollOnMove с зонами ввода (input.hitArea), чтобы создать сложное взаимодействие, где объекты реагируют только на целенаправленное движение курсора по определённой траектории.