Встраивание игр Phaser3 в функциональные компоненты React 18 с помощью useEffects

Встраивание игр Phaser3 в функциональные компоненты React 18 с помощью useEffects

5 марта 2023 г.

Вот шаблон для встраивания игр Phaser3 в компоненты React.

В самой простой реализации вы можете использовать этот шаблон для добавления игр Phaser3 и обращаться с ними как с GIF-анимацией на стероидах.

В самом сложном случае я покажу вам, как запускать игры Phaser3 внутри (и из других) компонентов React, перехватывать события, происходящие внутри игры, и запускать функции в React. Эти события могут обновлять уведомления о статусе в баннере веб-сайта или отправлять данные во внутренний API вашего веб-сайта, чтобы вести постоянную запись прогресса игрока.

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

Предпосылки

  • Знакомство с React.
  • Знакомство с Phaser3.
  • Знакомство с JavaScript.
  • Склонность нажимать кнопки «Мне нравится этот пост».

Основной шаблон

Как и было обещано, вот основной шаблон, который мы будем использовать:

import Phaser from "phaser"
import React, { useEffect, useState } from "react"
import happyFace from "./happy-face.png"

class HappyFaceScene extends Phaser.Scene {
  preload() {
    // A convention of mine to avoid `this` confusion with sub classes is to 
    // give it a better reference name at the top of every function.
    let scene = this
    scene.load.image("happyFace", happyFace)
  }
  create(data) {
    let scene = this
    // Add your game objects and all that other stuff. It's just a simple image.
    scene.add.image(0, 0, "happyFace").setOrigin(0, 0).setDisplaySize(480, 142)
    // Finally trigger an event so that the scene is now visible. This is optional 
    // but useful if you want to transition your game's appearance.
    scene.game.events.emit("putOnAHappyFace", true)
  }
  update() {
    let scene = this
    let { lives, progress } = scene
    // For example, monitor the number of lives and exit when 
    if (!lives) {
      // Save the progress.
      game.registry.merge(progress)
      // Trigger the game end.
      scene.game.events.emit("putOnAHappyFace", false)
    }
  }
}

export const Phaser3GameComponent = () => {
  const [isReady, setReady] = useState(false);
   useEffect(() => {
     // Nothing really special here... Your phaser3 config should work just fine.  
     let config = {
       height: 320,
       width: 480,
       physics: { default: "arcade" },
       scale: {
         // Except this should match the ID of your component host element.
         parent: "phaser-game",
         mode: Phaser.Scale.FIT,
         autoCenter: Phaser.Scale.CENTER_HORIZONTALLY,
       },
       transparent: true,
       type: Phaser.AUTO,
     }
     let game = new Phaser.Game(config)
     // Triggered when game is fully visible.
     game.events.on('putOnAHappyFace', setReady)
     // Add your scene/s here (or in `scene` key of `config`).
     game.scene.add("HappyFaceScene", HappyFaceScene, true)
     // If you don't do this, you get duplicates of the canvas piling up
     // everytime this component renders. 
     return () => {
       setReady(false)
       game.destroy(true)
     }
   // You must have an empty array here otherwise the game restarts every time
   // the component renders.
   }, []) 
  // Return the host element where Phaser3 will append the canvas.
  return  <div id="phaser-game" className={isReady ? "visible" : "invisible"} />
}progress

Вы видите, насколько это просто. React делает то, для чего создан: он реагирует на происходящее в игре.

Оценка

Помимо того, что React обычно перехватывает события, вызванные игрой, его также можно подключить к настройке обратных вызовов Phaser.

В этом дополнительном фрагменте, который вы должны добавить в часть конфигурации Phaser Game приведенного выше шаблона, мы будем загружать предыдущий прогресс пользователя в «реестр» игры Phaser3 с помощью функции merge. Мы также можем отслеживать изменения в

      ...
      physics: { default: "arcade" },
      callbacks: {
        preBoot: (game) => {
          game.registry.merge(userProgress)
          game.registry.events.on('changedata', function(parent, key, value, previousValue) => {
            updateUserProgress(key, value)
          })
        },
      },
      ...

Где userProgress выходит из хранилища, а updateUserProgress изменяет изменения обратно в хранилище.

Это показывает, насколько легко встраивать игры Phaser (либо для отображения неинтерактивной анимации, либо для воспроизведения) в React, а также передавать данные туда и обратно.

Модальный

Наконец, использование div в качестве контейнера идеально подходит, если вы хотите встроить игру в страницу. В реализации, над которой я работаю для клиента, игра отображается в полноэкранном режиме с использованием модального окна Bootstrap-React следующим образом:

  return (
    <Modal show={isReady} backdrop="static" fullscreen={true} keyboard={false}>
      <Modal.Body>{isReady && <div id="PhaserGame" />}</Modal.Body>
    </Modal>
  )

С этим простым изменением модальное окно начальной загрузки будет всплывать при загрузке игры и исчезать, когда оно закончится.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE