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

Современные игры и приложения часто требуют поддержки нескольких одновременных касаний — для управления несколькими персонажами, совместного взаимодействия или реализации сложных жестов. Встроенная система ввода 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.setPath('assets/input');
        this.load.image([ 'p', 'h', 'a', 's', 'e', 'r' ]);
    }

    create ()
    {
        this.input.addPointer(5);

        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
        });

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

            gameObject.setTint(0xffff00, 0xffff00, 0xff0000, 0xff0000).setTintMode(Phaser.TintModes.FILL);

        });

        this.input.on('gameobjectup', (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);

Подготовка ассетов и добавление указателей

Первым делом в методе preload мы загружаем шесть изображений букв, составляющих слово 'phaser'. Обратите внимание на использование load.image с массивом названий — это компактный способ загрузить несколько файлов за один вызов.

Ключевой момент для мультитача происходит в create. Система ввода Phaser (this.input) по умолчанию имеет один активный указатель. Метод addPointer добавляет новые указатели в пул. В нашем примере мы добавляем пять, получая в сумме шесть (один исходный + пять новых). Это позволяет одновременно отслеживать до шести независимых касаний на экране.

this.input.addPointer(5);

Далее мы создаём шесть объектов Image, по одному на каждую букву, и сразу делаем их интерактивными с помощью .setInteractive(). Это необходимо, чтобы объекты могли генерировать события ввода.

Автоматическое выравнивание объектов

Вручную расставлять шесть изображений по координатам неудобно. Phaser предоставляет удобный хелпер Phaser.Actions.GridAlign для автоматического размещения массива объектов в виртуальную сетку.

Мы передаём массив наших изображений и объект конфигурации. Параметр width: 6 указывает, что все объекты должны быть размещены в одну строку. cellWidth и cellHeight задают размер ячейки для каждого объекта, а `xиy` — стартовую точку для всей сетки.

Phaser.Actions.GridAlign([ p, h, a, s, e, r ], {
    width: 6,
    cellWidth: 132,
    cellHeight: 200,
    x: 68,
    y: 300
});

В результате все шесть букв аккуратно выстраиваются в горизонтальный ряд на заданной высоте.

Обработка событий нажатия и отпускания

Теперь нужно «оживить» буквы, чтобы они реагировали на касания. Мы подписываемся на два события системы ввода: gameobjectdown (касание началось или нажата кнопка мыши на объекте) и gameobjectup (касание завершилось или кнопка мыши отпущена).

В обработчике gameobjectdown объект, на котором произошло событие, получает tint (оттенок) с помощью setTint. Цвета передаются четырьмя аргументами, соответствующими углам объекта (top-left, top-right, bottom-left, bottom-right), что создаёт градиентный эффект. Важный момент — мы также устанавливаем режим tint через setTintMode(Phaser.TintModes.FILL). Без этого, в зависимости от текстуры, tint может не применяться или применяться некорректно.

this.input.on('gameobjectdown', (pointer, gameObject) => {
    gameObject.setTint(0xffff00, 0xffff00, 0xff0000, 0xff0000).setTintMode(Phaser.TintModes.FILL);
});

В обработчике gameobjectup tint просто сбрасывается, возвращая объекту исходный вид.

this.input.on('gameobjectup', (pointer, gameObject) => {
    gameObject.clearTint();
});

Поскольку мы добавили несколько указателей, эти события будут корректно срабатывать для каждого касания независимо, позволяя одновременно «нажимать» несколько букв разными пальцами.

Конфигурация и запуск игры

Сцена готова, осталось настроить и запустить экземпляр игры Phaser. Конфигурационный объект задаёт базовые параметры: тип рендерера (Phaser.AUTO выбирает WebGL или Canvas автоматически), родительский HTML-элемент, размеры холста, цвет фона и класс нашей сцены.

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#efefef',
    scene: Example
};

const game = new Phaser.Game(config);

После создания экземпляра Phaser.Game сцена автоматически проходит через свои жизненные циклы (preload, create, update), и на экране появляется интерактивная мультитач-панель.

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

Всего в несколько строк кода мы превратили статичные изображения в мультитач-интерактивную панель. Основной секрет — вызов this.input.addPointer() для расширения пула указателей. Этот механизм открывает двери для множества экспериментов: попробуйте добавить логику перетаскивания объектов с помощью событий drag, измените реакцию объектов в зависимости от индекса pointer.id (чтобы различать пальцы), или создайте сложные жесты, отслеживая движение нескольких указателей одновременно. Такая техника отлично подходит для казуальных игр, образовательных приложений и интерактивных инсталляций на сенсорных экранах.