О чем этот пример
В мобильных играх поддержка мультитача — это не просто возможность, а необходимость. Часто нужно понимать, каким именно пальцем игрок взаимодействует с объектом. В этой статье разберем пример, который показывает, как в Phaser 3 добавить несколько указателей и отслеживать, на какие объекты наведен каждый из них. Это полезно для сложных интерфейсов, меню с несколькими активными зонами или игр, где действия выполняются разными пальцами одновременно.
Версия 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.setPath('assets/input');
this.load.image([ 'p', 'h', 'a', 's', 'e', 'r' ]);
}
create ()
{
this.input.addPointer(6);
const p = this.add.image(0, 0, 'p').setInteractive();
const h = this.add.image(0, 0, 'h').setInteractive();
const a = this.add.image(0, 0, 'a').setInteractive();
const s = this.add.image(0, 0, 's').setInteractive();
const e = this.add.image(0, 0, 'e').setInteractive();
const r = this.add.image(0, 0, 'r').setInteractive();
Phaser.Actions.GridAlign([ p, h, a, s, e, r ], {
width: 6,
cellWidth: 132,
cellHeight: 200,
x: 68,
y: 300
});
const text = this.add.text(10, 10, `${Phaser.VERSION} + v3`, { font: '16px Courier', fill: '#000000' });
this.input.on('pointermove', function (pointer)
{
text.setText(this.input._over[pointer.id].length);
}, this);
this.input.on('gameobjectover', (pointer, gameObject) =>
{
gameObject.setTint(0x00ff00, 0x00ff00, 0xff0000, 0xff0000).setTintMode(Phaser.TintModes.FILL);
});
this.input.on('gameobjectout', (pointer, gameObject) =>
{
gameObject.clearTint();
});
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#efefef',
scene: Example
};
const game = new Phaser.Game(config);
Добавление дополнительных указателей
По умолчанию Phaser создает два указателя: один для мыши, второй для первого касания. Для поддержки нескольких одновременных касаний нужно явно добавить новые указатели. Это делается в методе create сцены.
this.input.addPointer(6);
Этот вызов добавляет в систему ввода 6 дополнительных указателей. Теперь общее количество доступных указателей (включая два стандартных) станет 8. Это позволяет отслеживать до 8 одновременных касаний на сенсорном экране.
Создание и расположение интерактивных объектов
В примере создается 6 спрайтов-букв, каждый из которых помечается как интерактивный. Без вызова .setInteractive() объект не будет реагировать на события ввода.
const p = this.add.image(0, 0, 'p').setInteractive();
// ... и так для букв h, a, s, e, r
Чтобы красиво расположить объекты на экране, используется утилита Phaser.Actions.GridAlign. Она принимает массив объектов и конфигурацию сетки.
Phaser.Actions.GridAlign([ p, h, a, s, e, r ], {
width: 6,
cellWidth: 132,
cellHeight: 200,
x: 68,
y: 300
});
Параметры width: 6 и количество объектов (6) задают сетку 1x6. Объекты будут выровнены в один ряд с заданными отступами между ячейками.
Отслеживание количества объектов под указателем
Ключевая задача примера — показать, сколько интерактивных объектов в данный момент находится под каждым указателем (пальцем). Для этого слушает событие pointermove.
this.input.on('pointermove', function (pointer) {
text.setText(this.input._over[pointer.id].length);
}, this);
При каждом движении указателя (или пальца) текст на экране обновляется. this.input._over — это внутренний массив Phaser, где для каждого pointer.id хранится список игровых объектов, над которыми в данный момент находится этот указатель. Мы берем длину этого списка и отображаем ее. Это полезно для отладки и понимания, как система ввода видит пересечения.
Визуальная обратная связь: события over и out
Чтобы игрок видел, на какой объект он навел палец, используются события gameobjectover и gameobjectout. Они срабатывают, когда указатель входит в зону объекта и выходит из нее.
При наведении объект окрашивается в два цвета с помощью setTint. Режим Phaser.TintModes.FILL означает, что тинт полностью заменяет исходные цвета текстуры.
this.input.on('gameobjectover', (pointer, gameObject) => {
gameObject.setTint(0x00ff00, 0x00ff00, 0xff0000, 0xff0000).setTintMode(Phaser.TintModes.FILL);
});
Цвета задаются для каждого угла спрайта (верхний левый, верхний правый, нижний левый, нижний правый). В примере верхняя половина становится зеленой (0x00ff00), а нижняя — красной (0xff0000).
При уходе указателя тинт сбрасывается.
this.input.on('gameobjectout', (pointer, gameObject) => {
gameObject.clearTint();
});
Что попробовать дальше
Пример демонстрирует базовый, но мощный паттерн для работы с мультитачем в Phaser. Вы можете расширить его: например, привязать разные действия к разным пальцам или реализовать сложный интерфейс с перетаскиванием нескольких объектов одновременно. Попробуйте изменить логику окрашивания в зависимости от pointer.id или добавить обработку жестов, комбинируя данные с нескольких указателей.
