Fix ship explosion rendering by passing state through game loop

- Ship explosion now renders correctly when text reaches spaceship
- Pass shipState and shipExplosionTime through game loop callback for immediate updates
- Use refs for ship state to ensure real-time updates in rendering pipeline
- Remove debug console logging
- Ship explosion effect now displays all particles, fragments, and flash animations
This commit is contained in:
2025-11-04 18:51:52 +00:00
parent 3e985efb37
commit 2944d43c4a
2 changed files with 23 additions and 17 deletions
+5 -5
View File
@@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from "react";
import type { GameManager, GameOptions, KeyLogEntry, FallingLetter, GameMode, Laser } from "../types";
import type { GameManager, GameOptions, KeyLogEntry, FallingLetter, GameMode, Laser, ShipState } from "../types";
import { useBoard } from "../useBoard";
import { useCursor } from "../useCursor";
import { useGameStatus } from "../useGameStatus";
@@ -18,12 +18,12 @@ export function useGame(
const gameStatusManager = useGameStatus(undefined, gameMode);
// Wrap renderBoard to include player position and lasers
const renderBoardWrapped = (letters: any = [], lasers: Laser[] = []) => {
boardManager.renderBoard(cursorManager.position(), letters, gameStatusManager.shipState, gameStatusManager.shipExplosionTime, gameModeRef.current, lasers);
const renderBoardWrapped = (letters: any = [], lasers: Laser[] = [], shipState: ShipState = 'normal', shipExplosionTime: number = 0) => {
boardManager.renderBoard(cursorManager.position(), letters, shipState, shipExplosionTime, gameModeRef.current, lasers);
};
// Game loop tick handler
const handleGameLoopTick = (deltaTime: number, letters: FallingLetter[], lasers: Laser[] = []) => {
const handleGameLoopTick = (deltaTime: number, letters: FallingLetter[], lasers: Laser[] = [], shipState: ShipState = 'normal', shipExplosionTime: number = 0) => {
const playerPos = cursorManager.position();
gameStatusManager.updatePlayerX(playerPos.x);
@@ -59,7 +59,7 @@ export function useGame(
cursorManager.moveTowardTarget(deltaTime);
}
renderBoardWrapped(letters, lasers);
renderBoardWrapped(letters, lasers, shipState, shipExplosionTime);
};
// Update gameMode when it changes (without recreating gameStatus state)
+18 -12
View File
@@ -15,10 +15,10 @@ const MARGIN_RIGHT = 50; // Safe margin from right edge
const CANVAS_WIDTH = 800;
const SPACESHIP_Y = 600 - 40; // Position of the ship on canvas
export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: FallingLetter[], lasers: Laser[]) => void, gameMode: GameMode = 'letters') {
export function useGameStatus(_onGameLoopTick?: (deltaTime: number, letters: FallingLetter[], lasers: Laser[]) => void, gameMode: GameMode = 'letters') {
const [gameStatus, setGameStatus] = useState<GameStatus>("waiting");
const [shipState, setShipState] = useState<ShipState>("normal");
const [shipExplosionTime, setShipExplosionTime] = useState(0);
const shipStateRef = useRef<ShipState>("normal");
const shipExplosionTimeRef = useRef<number>(0);
const [score, setScore] = useState(0);
const [correct, setCorrect] = useState(0);
const [missed, setMissed] = useState(0);
@@ -29,7 +29,7 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
const lasersRef = useRef<Laser[]>([]);
const lastSpawnRef = useRef<number>(0);
const playerXRef = useRef<number>(CANVAS_WIDTH / 2);
const onGameLoopTickRef = useRef(onGameLoopTick);
const onGameLoopTickRef = useRef<((deltaTime: number, letters: FallingLetter[], lasers: Laser[], shipState: ShipState, shipExplosionTime: number) => void) | undefined>(undefined);
const gameModeRef = useRef<GameMode>(gameMode);
// Game loop
@@ -100,15 +100,21 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
// Collision detected
letter.state = 'exploding';
letter.stateStartTime = currentTime;
setShipState('exploding');
setShipExplosionTime(performance.now());
// Update refs for immediate rendering
shipStateRef.current = 'exploding';
const explosionTime = performance.now();
shipExplosionTimeRef.current = explosionTime;
setDeaths(prev => prev + 1);
// Clear letters and reset spawn timer after explosion animation
setTimeout(() => {
lettersRef.current = [];
lastSpawnRef.current = performance.now();
setShipState('normal');
shipStateRef.current = 'normal';
shipExplosionTimeRef.current = 0;
}, 500); // Match the 500ms explosion animation
}
}
@@ -122,9 +128,9 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
currentTime - laser.startTime < laser.duration
);
// Call tick callback if provided (now with lasers)
// Call tick callback if provided (now with lasers and ship state)
if (onGameLoopTickRef.current) {
onGameLoopTickRef.current(deltaTime, lettersRef.current, lasersRef.current);
onGameLoopTickRef.current(deltaTime, lettersRef.current, lasersRef.current, shipStateRef.current, shipExplosionTimeRef.current);
}
// Continue loop
@@ -288,7 +294,7 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
playerXRef.current = x;
};
const setGameLoopCallback = (callback: (deltaTime: number, letters: FallingLetter[], lasers: Laser[]) => void) => {
const setGameLoopCallback = (callback: (deltaTime: number, letters: FallingLetter[], lasers: Laser[], shipState: ShipState, shipExplosionTime: number) => void) => {
onGameLoopTickRef.current = callback;
};
@@ -316,8 +322,8 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
startGame,
quitGame,
handleTypedLetter,
shipState,
shipExplosionTime,
shipState: shipStateRef.current, // Return ref value for immediate updates
shipExplosionTime: shipExplosionTimeRef.current, // Return ref value for immediate updates
updatePlayerX,
setGameLoopCallback,
setGameMode,