0

I am trying to develop a phaser3 application with React. I am just setting up the first canvas for the Phaser.Game. Here is my App.js from the create-react-app.

import "./App.css";
import Phaser, { Game } from "phaser";
import PhaserMatterCollisionPlugin from "phaser-matter-collision-plugin";
import { useCallback, useEffect, useState } from "react";

function App() {
  const [game, setGame] = useState(null);
  // Creating game inside a useEffect in order to ensure 1 instance is created
  console.log("before use effect");
  useEffect(() => {
    console.log("Going into useEffect");
    console.log(game);
    if (game) {
      console.log("game detected. stop creation");
      return;
    }
    const phaserGame = new Phaser.Game({
      width: 512,
      height: 412,
      backgroundColor: "#333333",
      type: Phaser.AUTO,
      parent: "survival-game",
      scene: [],
      scale: {
        zoom: 2,
      },
      physics: {
        default: "matter",
        matter: {
          debug: true,
          gravity: { y: 0 },
        },
      },
      plugins: {
        scene: [
          {
            plugin: PhaserMatterCollisionPlugin,
            key: "matterCollision",
            mapping: "matterCollision",
          },
        ],
      },
    });

    setGame(true);
    return;
  }, [game]);
}

export default App;

I used useEffect() with useState in order to prevent multiple game instances, but for some reason I am still getting a duplicate canvas and can see that it is running through the useEffect multiple times. console.log of the react app

isherwood
  • 58,414
  • 16
  • 114
  • 157
  • 1
    Does this answer your question? [React Hooks: useEffect() is called twice even if an empty array is used as an argument](https://stackoverflow.com/questions/60618844/react-hooks-useeffect-is-called-twice-even-if-an-empty-array-is-used-as-an-ar) – Konrad Sep 30 '22 at 15:24
  • You should use `ref` instead of state for that. – Konrad Sep 30 '22 at 15:24
  • For this, you should probably use a ref instead of a state atom. – AKX Sep 30 '22 at 15:26
  • You can also do `return () => phaserGame.destroy()` which would be even better – Konrad Sep 30 '22 at 15:27
  • Does this answer your question? [useEffect is running twice on mount in React](https://stackoverflow.com/questions/72238175/useeffect-is-running-twice-on-mount-in-react) – Youssouf Oumar Nov 23 '22 at 04:06

1 Answers1

2

You should use a ref instead of state for the game object. Here's a small custom hook that sets up a Phaser.Game based on a given configuration:

function usePhaserGame(config) {
  const phaserGameRef = React.useRef(null);
  React.useEffect(() => {
    if (phaserGameRef.current) {
      return;
    }
    phaserGameRef.current = new Game(config);
    return () => {
      phaserGameRef.current.destroy(true);
      phaserGameRef.current = null;
    };
  }, [] /* only run once; config ref elided on purpose */);
  return phaserGameRef.current;
}

const config = {
  width: 512,
  height: 412,
  backgroundColor: '#333333',
  type: Phaser.AUTO,
  parent: 'survival-game',
  scene: [],
  scale: {
    zoom: 2,
  },
  physics: {
    default: 'matter',
    matter: {
      debug: true,
      gravity: {y: 0},
    },
  },
  plugins: {
    scene: [
      {
        plugin: PhaserMatterCollisionPlugin,
        key: 'matterCollision',
        mapping: 'matterCollision',
      },
    ],
  },
};

function App() {
  const game = usePhaserGame(config);
}
AKX
  • 152,115
  • 15
  • 115
  • 172