О чем этот пример
При разработке игр часто возникает необходимость создавать невидимые интерактивные зоны — триггеры, кнопки-ловушки или области с особым поведением. Однако в Phaser по умолчанию интерактивность отключается для объектов с нулевой прозрачностью (`alpha: 0`). В этой статье мы разберем, как использовать свойство `alwaysEnabled` для обхода этого ограничения и создания гибких интерактивных систем, независимых от визуального отображения объектов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
create: create
}
};
var game = new Phaser.Game(config);
function create()
{
// Test 1
/*
var invisibleRect = this.add.rectangle(400, 300, 300, 300, 0x00ff00);
var rect = this.add.rectangle(400, 300, 256, 256, 0x0000ff);
var text = this.add.text(128, 128, "0").setFontSize(28);
var count = 0;
invisibleRect.name = 'invis';
invisibleRect.setInteractive();
invisibleRect.input.alwaysEnabled = true;
invisibleRect.on('pointerdown', function () {
count++;
text.text = count.toString();
});
rect.name = 'rect';
rect.setInteractive();
invisibleRect.alpha = 0;
// invisibleRect.setDepth(0);
rect.setDepth(-1);
*/
// Test 2
var invisibleRect = this.add.rectangle(400, 300, 300, 300, 0x00ff00);
var rect = this.add.rectangle(400, 300, 256, 256, 0x0000ff).setAlpha(0.5);
var rect2 = this.add.rectangle(350, 300, 256, 256, 0xff0000).setAlpha(0.5);
var text = this.add.text(128, 128, "0").setFontSize(28);
var count = 0;
invisibleRect.name = 'invis';
invisibleRect.setInteractive();
invisibleRect.input.alwaysEnabled = true;
invisibleRect.on('pointerdown', function () {
count++;
text.text = count.toString();
});
rect.name = 'rect1';
rect.setInteractive();
rect2.name = "rect2"
rect2.setInteractive();
invisibleRect.alpha = 0;
// invisibleRect.alpha = 0.3;
invisibleRect.setDepth(3);
rect.setDepth(2);
rect2.setDepth(1);
console.log(this.input);
}
Проблема: невидимый объект не реагирует на клики
По умолчанию в Phaser объекты с alpha: 0 (полностью прозрачные) теряют интерактивность, даже если на них установлено setInteractive(). Это логичное поведение для визуальных элементов, но оно становится проблемой, когда нужно создать невидимую «горячую зону». Например, для скрытой кнопки-активатора или области, которая должна срабатывать при наведении курсора.
Рассмотрим базовый пример без alwaysEnabled:
var invisibleRect = this.add.rectangle(400, 300, 300, 300, 0x00ff00);
invisibleRect.setInteractive();
invisibleRect.alpha = 0; // Интерактивность теряется!
Зеленый прямоугольник после установки alpha: 0 перестанет откликаться на события ввода, хотя setInteractive() был вызван.
Решение: включаем alwaysEnabled
Свойство alwaysEnabled — это флаг, который принудительно сохраняет интерактивность объекта, независимо от его визуального состояния. Оно находится в объекте input интерактивного игрового объекта.
Ключевой код решения:
invisibleRect.setInteractive();
invisibleRect.input.alwaysEnabled = true; // Принудительно включаем интерактивность
invisibleRect.alpha = 0; // Теперь объект невидим, но кликабелен
После установки alwaysEnabled: true объект будет обрабатывать события указателя (pointerdown, pointerover и др.), даже если он полностью прозрачен. Это свойство следует устанавливать после вызова setInteractive(), так как объект input создается именно в этот момент.
Практический пример: кликабельный невидимый слой
В исходном коде примера реализована сцена с тремя прямоугольниками разной глубины (depth) и прозрачности. Невидимый зеленый прямоугольник (invisibleRect) расположен поверх всех и обрабатывает клики благодаря alwaysEnabled.
Полный код создания сцены:
function create() {
var invisibleRect = this.add.rectangle(400, 300, 300, 300, 0x00ff00);
var rect = this.add.rectangle(400, 300, 256, 256, 0x0000ff).setAlpha(0.5);
var rect2 = this.add.rectangle(350, 300, 256, 256, 0xff0000).setAlpha(0.5);
var text = this.add.text(128, 128, "0").setFontSize(28);
var count = 0;
invisibleRect.name = 'invis';
invisibleRect.setInteractive();
invisibleRect.input.alwaysEnabled = true; // Ключевая строка
invisibleRect.on('pointerdown', function () {
count++;
text.text = count.toString(); // Счетчик увеличивается при клике
});
rect.name = 'rect1';
rect.setInteractive(); // Кликабелен, но перекрыт невидимым слоем
rect2.name = "rect2";
rect2.setInteractive();
invisibleRect.alpha = 0; // Полная прозрачность
invisibleRect.setDepth(3); // Самый верхний слой
rect.setDepth(2);
rect2.setDepth(1);
}
При клике в области пересечения прямоугольников сработает событие pointerdown только у invisibleRect, так как он имеет наибольшую глубину (depth: 3) и интерактивность у него принудительно включена. Синий (rect) и красный (rect2) прямоугольники, несмотря на свою интерактивность, будут перекрыты невидимым слоем.
Взаимодействие с глубиной (Depth) и порядком отрисовки
Свойство alwaysEnabled не отменяет стандартную систему управления порядком отрисовки и обработки ввода в Phaser. Объекты с большим значением depth отображаются поверх других и имеют приоритет в обработке событий.
В примере установлены следующие глубины:
invisibleRect.setDepth(3);
rect.setDepth(2);
rect2.setDepth(1);
Это означает:
1. invisibleRect — верхний слой, его события обрабатываются в первую очередь.
2. Даже если rect и rect2 видимы и интерактивны, клик по ним «перехватывается» невидимым прямоугольником выше.
3. Если бы alwaysEnabled было false, invisibleRect игнорировал бы ввод, и клик обрабатывали бы нижележащие прямоугольники.
Сочетание depth и alwaysEnabled позволяет создавать сложные многослойные интерфейсы, где визуально задние элементы могут быть логически активными, а передние — служить невидимыми масками или триггерами.
Использование и ограничения
Основное применение alwaysEnabled — создание невидимых интерактивных областей:
* **Секретные зоны** на карте, активируемые кликом.
* **Невидимые кнопки** или триггеры для пазлов.
* **Области с особым поведением** (например, увеличение урона в определенной зоне).
* **Слои-маски**, которые должны блокировать ввод для объектов под ними, оставаясь невидимыми.
Важное ограничение: alwaysEnabled работает только с объектами, у которых есть компонент Input, т.е. после вызова setInteractive(). Также помните, что это свойство влияет только на интерактивность самого объекта, но не на его детей в контейнере.
Проверить, включена ли принудительная интерактивность, можно через консоль, как это сделано в примере:
console.log(this.input); // Изучаем менеджер ввода
// Или для конкретного объекта:
console.log(invisibleRect.input.alwaysEnabled);
Что попробовать дальше
Свойство alwaysEnabled — это мощный инструмент для тонкого управления интерактивностью в Phaser, позволяющий отделить логику обработки ввода от визуального представления объекта. Вы можете экспериментировать: создавайте сложные системы триггеров, меняйте depth у объектов динамически, чтобы управлять приоритетом кликов, или комбинируйте невидимые области с системами физики (enableBody) для создания нестандартных игровых механик. Попробуйте применить этот подход для создания скрытых тестовых панелей или интерактивного фона, не мешающего основному UI.
