О чем этот пример
Правильная обработка событий ввода — основа отзывчивого и интуитивного геймплея. Этот пример наглядно демонстрирует работу двух важных событий: `pointerover` (курсор наведён на объект) и `gameout` (курсор покинул пределы игрового холста). Понимание их взаимодействия позволяет создавать сложную визуальную обратную связь для игрока, например, подсвечивать интерактивные элементы при наведении и менять их состояние, когда игрок убрал фокус с окна браузера. Мы разберём код и увидим, как избежать потенциальных багов в логике взаимодействия.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
function create ()
{
var sprite = this.add.sprite(800, 300, 'eye').setInteractive();
sprite.on('pointerover', function (event) {
this.setTint(0xff0000);
});
sprite.on('pointerout', function (event) {
this.clearTint();
});
this.input.on('gameout', () => {
sprite.setTint(0x00ff00);
});
}
Структура сцены и загрузка ресурсов
В Phaser вся игровая логика организована в сценах (Scenes). В данном примере сцена содержит две стандартные функции: preload и create. В preload мы загружаем изображение, которое будем использовать в качестве интерактивного спрайта.
function preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
Метод this.load.setBaseURL() задаёт базовый URL для всех последующих загрузок, что удобно для указания общей части пути. Метод this.load.image() регистрирует загрузку изображения и присваивает ему ключ 'eye' для дальнейшего использования.
Создание интерактивного спрайта
В функции create происходит инициализация игровых объектов. Ключевой шаг — создание спрайта и немедленное назначение ему интерактивности.
function create ()
{
var sprite = this.add.sprite(800, 300, 'eye').setInteractive();
}
Метод this.add.sprite() создаёт новый спрайт с указанными координатами X, Y и ключом текстуры. Вызов .setInteractive() без аргументов делает весь спрайт чувствительной областью для событий ввода (по умолчанию используется его прямоугольный контур — bounding box). Без этого вызова события pointerover и pointerout не будут срабатывать.
Обработка наведения курсора (pointerover/out)
Когда спрайт интерактивен, на него можно подписаться для обработки событий ввода. Событие pointerover генерируется, когда указатель (мышь, палец) входит в область спрайта. pointerout — когда покидает её.
sprite.on('pointerover', function (event) {
this.setTint(0xff0000);
});
sprite.on('pointerout', function (event) {
this.clearTint();
});
Внутри функций-обработчиков контекст this ссылается на сам спрайт (sprite). Метод .setTint(0xff0000) применяет красный оттенок ко всему спрайту. Метод .clearTint() — убирает его. Это классический паттерн для визуальной обратной связи при наведении.
Обработка ухода курсора с игрового холста (gameout)
Событие gameout — это событие глобального менеджера ввода (this.input). Оно срабатывает, когда указатель покидает область всего игрового холста Phaser, а не отдельного спрайта. Это полезно для сброса состояния игры при потере фокуса.
this.input.on('gameout', () => {
sprite.setTint(0x00ff00);
});
Обратите внимание, что здесь используется стрелочная функция. Внутри неё sprite доступна из замыкания внешней функции create. В этом обработчике спрайту назначается зелёный оттенок (0x00ff00). Важный нюанс: если курсор ушёл с холста, находясь над спрайтом, то событие pointerout на спрайте НЕ срабатывает. Однако при возврате курсора на холст (но уже не на спрайт) сразу сработает pointerout.
Потенциальный конфликт логики
В текущей реализации есть особенность, которую нужно учитывать. Представьте последовательность:
1. Курсор наведён на спрайт — он красный.
2. Курсор быстро перемещён за пределы игрового окна — срабатывает gameout, спрайт становится зелёным.
3. Курсор возвращается обратно на холст, но мимо спрайта.
В момент (3) сработает событие pointerout на спрайте (так как он был последним объектом под указателем до ухода), и его зелёный оттенок будет сброшен. Это может быть неочевидным поведением. Для согласованной логики часто требуется дополнительная проверка состояний.
// Пример возможного улучшения
let cursorOverSprite = false;
sprite.on('pointerover', function () {
cursorOverSprite = true;
this.setTint(0xff0000);
});
sprite.on('pointerout', function () {
cursorOverSprite = false;
this.clearTint();
});
this.input.on('gameout', () => {
sprite.setTint(0x00ff00);
});
this.input.on('gameover', () => {
// При возврате на холст, если курсор не над спрайтом, убрать зелёный tint
if (!cursorOverSprite) {
sprite.clearTint();
}
});
Что попробовать дальше
События pointerover/pointerout и gameout решают разные задачи: локальное взаимодействие с объектом и глобальный уход фокуса с игры. Их совместное использование требует внимания к сценариям, когда курсор покидает холст, находясь над интерактивным элементом. Для экспериментов попробуйте
- Добавить звук при наведении
- Использовать
gameoutдля паузы игрового процесса - Сделать спрайт перетаскиваемым и изменить логику tint при drag-событиях
