+
+ );
+};
+```
+
+### 2. Conditional Rendering
+- Only render `PhaserGame` when modal is open
+- This ensures proper cleanup when modal closes
+
+### 3. Cleanup Considerations
+```typescript
+useEffect(() => {
+ return () => {
+ // Modal cleanup will trigger PhaserGame cleanup
+ // No additional cleanup needed if using conditional rendering
+ };
+}, []);
+```
+
+### 4. Modal-Specific Styling
+```css
+.modal-content {
+ width: 1024px;
+ height: 768px;
+ /* Ensure container matches game dimensions */
+}
+
+#game-container {
+ width: 100%;
+ height: 100%;
+}
+```
+
+## Best Practices
+
+### 1. Memory Management
+- Always destroy Phaser game instance on component unmount
+- Use conditional rendering for modals to ensure cleanup
+- Remove event listeners in useEffect cleanup
+
+### 2. Performance
+- Use `useLayoutEffect` for DOM manipulation
+- Disable SSR for Phaser components
+- Consider lazy loading for large game assets
+
+### 3. Error Handling
+```typescript
+useLayoutEffect(() => {
+ try {
+ if (game.current === null) {
+ game.current = StartGame("game-container");
+ }
+ } catch (error) {
+ console.error("Failed to initialize Phaser game:", error);
+ }
+
+ return () => {
+ try {
+ if (game.current) {
+ game.current.destroy(true);
+ game.current = null;
+ }
+ } catch (error) {
+ console.error("Failed to cleanup Phaser game:", error);
+ }
+ };
+}, []);
+```
+
+### 4. TypeScript Integration
+- Define interfaces for game refs and props
+- Type scene instances for better IntelliSense
+- Use Phaser's built-in TypeScript definitions
+
+## Common Gotchas
+
+1. **SSR Issues**: Always disable SSR for Phaser components
+2. **Container Element**: Ensure the container div exists before game initialization
+3. **Memory Leaks**: Always cleanup game instances and event listeners
+4. **Modal Timing**: Use conditional rendering rather than visibility toggles
+5. **Asset Loading**: Handle loading states and errors gracefully
+
+## Conclusion
+
+This integration pattern provides a robust foundation for embedding Phaser games in React applications. The key is proper lifecycle management, clean separation of concerns, and reliable communication between React and Phaser through the EventBus system.
+
+For modal implementations, the same principles apply with additional consideration for conditional rendering and cleanup timing.
diff --git a/src/App.tsx b/src/App.tsx
index 155a5e5..31a4650 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,9 +1,8 @@
-import { useRef, useState } from 'react';
-import { IRefPhaserGame, PhaserGame } from './PhaserGame';
-import { MainMenu } from './game/scenes/MainMenu';
+import { useRef, useState } from "react";
+import { IRefPhaserGame, PhaserGame } from "./PhaserGame";
+import { MainMenu } from "./game/scenes/MainMenu";
-function App()
-{
+function App() {
// The sprite can only be moved in the MainMenu Scene
const [canMoveSprite, setCanMoveSprite] = useState(true);
@@ -12,53 +11,40 @@ function App()
const [spritePosition, setSpritePosition] = useState({ x: 0, y: 0 });
const changeScene = () => {
-
- if(phaserRef.current)
- {
+ if (phaserRef.current) {
const scene = phaserRef.current.scene as MainMenu;
-
- if (scene)
- {
+
+ if (scene) {
scene.changeScene();
}
}
- }
+ };
const moveSprite = () => {
-
- if(phaserRef.current)
- {
-
+ if (phaserRef.current) {
const scene = phaserRef.current.scene as MainMenu;
- if (scene && scene.scene.key === 'MainMenu')
- {
+ if (scene && scene.scene.key === "MainMenu") {
// Get the update logo position
scene.moveLogo(({ x, y }) => {
-
setSpritePosition({ x, y });
-
});
}
}
-
- }
+ };
const addSprite = () => {
-
- if (phaserRef.current)
- {
+ if (phaserRef.current) {
const scene = phaserRef.current.scene;
- if (scene)
- {
+ if (scene) {
// Add more stars
const x = Phaser.Math.Between(64, scene.scale.width - 64);
const y = Phaser.Math.Between(64, scene.scale.height - 64);
-
+
// `add.sprite` is a Phaser GameObjectFactory method and it returns a Sprite Game Object instance
- const star = scene.add.sprite(x, y, 'star');
-
+ const star = scene.add.sprite(x, y, "star");
+
// ... which you can then act upon. Here we create a Phaser Tween to fade the star sprite in and out.
// You could, of course, do this from within the Phaser Scene code, but this is just an example
// showing that Phaser objects and systems can be acted upon from outside of Phaser itself.
@@ -67,38 +53,47 @@ function App()
duration: 500 + Math.random() * 1000,
alpha: 0,
yoyo: true,
- repeat: -1
+ repeat: -1,
});
}
}
- }
+ };
// Event emitted from the PhaserGame component
const currentScene = (scene: Phaser.Scene) => {
-
- setCanMoveSprite(scene.scene.key !== 'MainMenu');
-
- }
+ setCanMoveSprite(scene.scene.key !== "MainMenu");
+ };
return (