'use client';
import { useRef } from 'react';
import { CanvasSketch, clamp, randRange } from 'next-sketch';
const PLAYER_W = 36;
interface Block {
x: number;
y: number;
size: number;
}
export default function DodgeGame() {
const state = useRef({
width: 0,
playerX: 0,
pointerX: 0,
blocks: [] as Block[],
spawnTimer: 0,
elapsed: 0,
alive: true,
});
const reset = () => {
const s = state.current;
s.playerX = s.width / 2;
s.pointerX = s.width / 2;
s.blocks = [];
s.spawnTimer = 0;
s.elapsed = 0;
s.alive = true;
};
return (
<CanvasSketch
style={{ width: '100%', height: 400 }}
setup={({ width }) => { state.current.width = width; reset(); }}
onPointerMove={({ x }) => { state.current.pointerX = x; }}
onPointerDown={() => { if (!state.current.alive) reset(); }}
draw={({ ctx, width, height, dt }) => {
const s = state.current;
s.width = width;
ctx.fillStyle = '#0f172a';
ctx.fillRect(0, 0, width, height);
if (s.alive) {
s.playerX = clamp(s.pointerX, PLAYER_W / 2, width - PLAYER_W / 2);
s.elapsed += dt;
const speed = 140 + s.elapsed * 12;
s.spawnTimer -= dt;
if (s.spawnTimer <= 0) {
s.blocks.push({ x: randRange(20, width - 20), y: -20, size: randRange(18, 32) });
s.spawnTimer = Math.max(0.35, 0.9 - s.elapsed * 0.02);
}
for (const b of s.blocks) b.y += speed * dt;
s.blocks = s.blocks.filter((b) => b.y < height + 40);
for (const b of s.blocks) {
const dx = Math.abs(b.x - s.playerX);
const dy = Math.abs(b.y - (height - 30));
if (dx < (b.size + PLAYER_W) / 2 && dy < (b.size + 16) / 2) {
s.alive = false;
}
}
}
ctx.fillStyle = '#ef4444';
for (const b of s.blocks) ctx.fillRect(b.x - b.size / 2, b.y - b.size / 2, b.size, b.size);
ctx.fillStyle = '#6366f1';
ctx.fillRect(s.playerX - PLAYER_W / 2, height - 38, PLAYER_W, 16);
ctx.fillStyle = '#e2e8f0';
ctx.font = '14px sans-serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText(`Score: ${Math.floor(s.elapsed * 10)}`, 12, 12);
if (!s.alive) {
ctx.fillStyle = 'rgba(15, 23, 42, 0.8)';
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = '#e2e8f0';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = '20px sans-serif';
ctx.fillText('Game over — click to restart', width / 2, height / 2);
}
}}
/>
);
}