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

Современные веб-игры часто требуют интерактивных интерфейсов, таких как формы ввода или панели меню. Phaser предоставляет мощный инструмент для интеграции обычного HTML-кода в игровую сцену, позволяя использовать привычные веб-формы прямо поверх игрового мира. В этой статье мы разберем пример, который показывает, как совместить интерактивные игровые объекты, такие как перетаскиваемые карты, с HTML-формой для ввода имени, используя модуль `DOM` и систему ввода 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.html('nameform', 'assets/text/nameform.html');
        this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
    }

    create ()
    {
        //  Create a stack of random cards

        const frames = this.textures.get('cards').getFrameNames();

        let x = 100;
        let y = 100;

        for (let i = 0; i < 64; i++)
        {
            const image = this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setInteractive({ draggable: true });

            x += 4;
            y += 4;
        }

        this.input.on('dragstart', function (pointer, gameObject)
        {

            this.children.bringToTop(gameObject);

        }, this);

        this.input.on('drag', (pointer, gameObject, dragX, dragY) =>
        {

            gameObject.x = dragX;
            gameObject.y = dragY;

        });

        const text = this.add.text(300, 10, 'Please enter your name', { color: 'white', fontSize: '20px '});

        const element = this.add.dom(400, 0).createFromCache('nameform');

        element.addListener('click');

        element.on('click', function (event)
        {

            if (event.target.name === 'playButton')
            {
                const inputText = this.getChildByName('nameField');

                //  Have they entered anything?
                if (inputText.value !== '')
                {
                    //  Turn off the click events
                    this.removeListener('click');

                    //  Hide the login element
                    this.setVisible(false);

                    //  Populate the text with whatever they typed in
                    text.setText(`Welcome ${inputText.value}`);
                }
                else
                {
                    //  Flash the prompt
                    this.scene.tweens.add({
                        targets: text,
                        alpha: 0.2,
                        duration: 250,
                        ease: 'Power3',
                        yoyo: true
                    });
                }
            }

        });
     
        this.tweens.add({
            targets: element,
            y: 300,
            duration: 3000,
            ease: 'Power3'
        });

    }
}

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

const game = new Phaser.Game(config);

Загрузка ресурсов: HTML и атлас

В методе preload загружаются два ключевых ресурса. HTML-файл с формой загружается как ассет с ключом 'nameform'. Это позволяет кэшировать и повторно использовать HTML-код. Атлас с картами загружается для создания визуальных игровых объектов. Обратите внимание на использование setBaseURL для указания базового пути к ресурсам.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.html('nameform', 'assets/text/nameform.html');
this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');

Создание стопки перетаскиваемых карт

В методе create сначала создается стопка из 64 карт. Каждая карта — это изображение (Image Game Object), использующее случайный кадр из атласа 'cards'. Ключевой момент — вызов метода setInteractive({ draggable: true }) для каждого изображения. Это регистрирует объект в системе ввода Phaser и активирует его перетаскивание.

const frames = this.textures.get('cards').getFrameNames();
let x = 100;
let y = 100;
for (let i = 0; i < 64; i++) {
    const image = this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setInteractive({ draggable: true });
    x += 4;
    y += 4;
}

Затем настраиваются обработчики событий перетаскивания. Обработчик 'dragstart' использует this.children.bringToTop(gameObject), чтобы перетаскиваемая карта отображалась поверх остальных. Обработчик 'drag' обновляет координаты карты в реальном времени, следуя за указателем.

this.input.on('dragstart', function (pointer, gameObject) {
    this.children.bringToTop(gameObject);
}, this);

this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
    gameObject.x = dragX;
    gameObject.y = dragY;
});

Добавление и анимация HTML-формы

Для работы с HTML необходимо включить поддержку DOM в конфигурации игры, установив dom.createContainer: true. В сцене для добавления HTML используется метод this.add.dom(). Созданный элемент позиционируется относительно сцены и может быть анимирован стандартными твинами Phaser.

const element = this.add.dom(400, 0).createFromCache('nameform');
this.tweens.add({
    targets: element,
    y: 300,
    duration: 3000,
    ease: 'Power3'
});

Обработка событий DOM-элемента

DOM-элемент может обрабатывать стандартные события браузера, такие как клик. Сначала добавляется слушатель для события 'click', а затем навешивается обработчик. Внутри обработчика проверяется, была ли нажата кнопка с именем 'playButton'. Если да, то происходит проверка поля ввода. При успешном вводе форма скрывается, а в текстовый объект сцены выводится приветствие. Если поле пустое, запускается твин, мигающий текстом-подсказкой.

element.addListener('click');
element.on('click', function (event) {
    if (event.target.name === 'playButton') {
        const inputText = this.getChildByName('nameField');
        if (inputText.value !== '') {
            this.removeListener('click');
            this.setVisible(false);
            text.setText(`Welcome ${inputText.value}`);
        } else {
            this.scene.tweens.add({
                targets: text,
                alpha: 0.2,
                duration: 250,
                ease: 'Power3',
                yoyo: true
            });
        }
    }
});

Конфигурация игры с поддержкой DOM

Для корректной работы DOM-элементов в игре необходимо явно включить эту опцию в конфигурации, создав контейнер. Без этого DOM-объекты не будут добавлены на страницу.

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#222288',
    dom: {
        createContainer: true // Ключевая настройка
    },
    scene: Example
};

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

Пример демонстрирует, как Phaser стирает границы между игровым миром и веб-интерфейсом. DOM-элементы можно анимировать, позиционировать и наделять интерактивностью, а игровые объекты — легко перетаскивать. Для экспериментов попробуйте

  1. Создать сложную форму с несколькими полями и кнопками
  2. Использовать другие события DOM, такие как input или submit
  3. Реализовать перетаскивание DOM-элементов по сцене с помощью той же системы drag