О чем этот пример
В играх часто требуется реализовать интерактивные элементы интерфейса, такие как кнопки или переключатели, которые можно выбирать и отменять выбор. Этот пример демонстрирует базовый паттерн управления состоянием выбранного объекта с использованием спрайтов и событий ввода Phaser. Понимание этого подхода полезно для создания меню, настроек и любых других интерактивных панелей в вашей игре, где требуется выделение одного элемента из группы.
Версия 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('bg', 'assets/skies/darkstone.png');
this.load.atlas('ui', 'assets/ui/dark-ui.png', 'assets/ui/dark-ui.json');
}
create ()
{
this.add.image(400, 300, 'bg');
this.currentSwitch = null;
// Create a bunch of toggles
for (let i = 0; i < 8; i++)
{
const x = (i < 4) ? 200 : 600;
const y = (i % 4) * 140 + 100;
const sprite = this.add.sprite(x, y, 'ui', 'toggle-off').setInteractive();
sprite.on('pointerdown', (pointer, x, y, event) => {
if (this.currentSwitch)
{
this.currentSwitch.setFrame('toggle-off');
}
if (this.currentSwitch === sprite)
{
this.currentSwitch = null;
}
else
{
this.currentSwitch = sprite;
sprite.setFrame('toggle-on');
}
event.stopPropagation();
});
}
// Clicking anywhere else in the game will deselect the current switch
this.input.on('pointerdown', () => {
if (this.currentSwitch)
{
this.currentSwitch.setFrame('toggle-off');
this.currentSwitch = null;
}
});
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка ресурсов и настройка сцены
В методе preload загружаются необходимые ресурсы: фон и текстуру с атласом для интерфейса. Атлас dark-ui содержит кадры для состояний переключателя (включено/выключено).
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/skies/darkstone.png');
this.load.atlas('ui', 'assets/ui/dark-ui.png', 'assets/ui/dark-ui.json');
}
В create сначала добавляется фон. Ключевая переменная this.currentSwitch инициализируется значением null. Она будет хранить ссылку на текущий выбранный спрайт-переключатель. Это центральная часть паттерна управления состоянием.
Создание группы переключателей
В цикле создаётся 8 интерактивных спрайтов, расположенных в две колонки. Каждому спрайту присваивается начальный кадр toggle-off из атласа ui.
for (let i = 0; i < 8; i++)
{
const x = (i < 4) ? 200 : 600;
const y = (i % 4) * 140 + 100;
const sprite = this.add.sprite(x, y, 'ui', 'toggle-off').setInteractive();
}
Метод setInteractive() делает спрайт чувствительным к событиям ввода (кликам). Без этого обработчик события pointerdown не сработает.
Обработка клика на переключателе
Каждому спрайту назначается обработчик события pointerdown. Его логика управляет выбором и отменой выбора.
sprite.on('pointerdown', (pointer, x, y, event) => {
if (this.currentSwitch)
{
this.currentSwitch.setFrame('toggle-off');
}
if (this.currentSwitch === sprite)
{
this.currentSwitch = null;
}
else
{
this.currentSwitch = sprite;
sprite.setFrame('toggle-on');
}
event.stopPropagation();
});
Алгоритм работы:
1. Если уже есть выбранный переключатель (`this.currentSwitch`), ему возвращается кадр "выключено" с помощью `setFrame('toggle-off')`.
2. Если кликнули по тому же самому спрайту, что уже выбран, происходит отмена выбора: `this.currentSwitch` сбрасывается в `null`.
3. Иначе (клик по новому спрайту) новый спрайт сохраняется в `this.currentSwitch`, и его кадр меняется на `toggle-on`.
4. `event.stopPropagation()` предотвращает всплытие события к обработчику, назначенному на всю сцену, что позволяет избежать немедленной отмены выбора после клика по спрайту.
Глобальная отмена выбора кликом по сцене
Отдельный обработчик события pointerdown назначается на весь Input Manager сцены. Он срабатывает при клике в любом месте игры, кроме самих спрайтов (благодаря stopPropagation).
this.input.on('pointerdown', () => {
if (this.currentSwitch)
{
this.currentSwitch.setFrame('toggle-off');
this.currentSwitch = null;
}
});
Это обеспечивает удобный способ отменить выбор текущего переключателя, кликнув по пустому пространству, что является стандартным поведением для многих интерфейсов.
Что попробовать дальше
Пример демонстрирует элегантный и эффективный паттерн для управления выбором одного объекта из множества с возможностью отмены. Этот подход можно расширить: например, добавить звуковые эффекты при переключении, анимации изменения кадров, хранить не сам спрайт, а его идентификатор, или создать универсальный класс ToggleGroup для управления более сложными состояниями. Попробуйте применить этот принцип к другим типам объектов, например, к юнитам в стратегической игре или к картам в коллекции.
