123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- import { useEffect, useRef } from "react";
- import styles from "./GuessPane.module.css";
- const tileSize = 15;
- const tiles = 40;
- const rawSize = tileSize * tiles;
- const drawTile = (ctx, x, y, color) => {
- ctx.fillStyle = color;
- ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
- };
- const snakeGame = () => {
- const snakeHead = { x: null, y: null, vx: null, vy: null };
- let snakeTail = [];
- const apple = { x: null, y: null };
- const randomApple = () => {
- apple.x = Math.floor(Math.random() * tiles);
- apple.y = Math.floor(Math.random() * tiles);
- };
- const reset = () => {
- snakeHead.x = Math.floor(tiles / 2);
- snakeHead.y = snakeHead.x;
- snakeHead.vx = 0;
- snakeHead.vy = -1;
- snakeTail = [{ ...snakeHead }];
- randomApple();
- };
- reset();
- const onKey = ({ code }) => {
- switch (code) {
- case "ArrowUp":
- snakeHead.vx = 0;
- snakeHead.vy = -1;
- break;
- case "ArrowDown":
- snakeHead.vx = 0;
- snakeHead.vy = 1;
- break;
- case "ArrowLeft":
- snakeHead.vx = -1;
- snakeHead.vy = 0;
- break;
- case "ArrowRight":
- snakeHead.vx = 1;
- snakeHead.vy = 0;
- break;
- default:
- break;
- }
- };
- let interval;
- const start = (ctx, finishScore, onFinish) => {
- document.addEventListener("keydown", onKey);
- interval = setInterval(() => {
- snakeHead.x += snakeHead.vx;
- snakeHead.y += snakeHead.vy;
- if (
- snakeHead.x < 0 ||
- snakeHead.x >= tiles ||
- snakeHead.y < 0 ||
- snakeHead.y >= tiles ||
- snakeTail.find(({ x, y }) => x === snakeHead.x && y === snakeHead.y)
- ) {
- reset();
- }
- snakeTail.push({ ...snakeHead });
- if (apple.x === snakeHead.x && apple.y === snakeHead.y) {
- randomApple();
- } else {
- snakeTail.shift();
- }
- const finished = snakeTail.length >= finishScore;
- ctx.fillStyle = "black";
- ctx.fillRect(0, 0, rawSize, rawSize);
- snakeTail.forEach(({ x, y }) => {
- drawTile(ctx, x, y, "green");
- });
- if (!finished) {
- drawTile(ctx, apple.x, apple.y, "red");
- }
- ctx.font = "24px serif";
- ctx.fillStyle = "#fff";
- ctx.textAlign = "left";
- ctx.textBaseline = "top";
- ctx.fillText(`${snakeTail.length}/${finishScore}`, tileSize, tileSize);
- if (finished) {
- clearInterval(interval);
- interval = null;
- ctx.font = "48px serif";
- ctx.textAlign = "center";
- ctx.textBaseline = "middle";
- ctx.fillText("Done!", rawSize / 2, rawSize / 2);
- setTimeout(onFinish, 1000);
- }
- }, 50);
- };
- const stop = () => {
- document.removeEventListener("keydown", onKey);
- if (interval) {
- clearInterval(interval);
- }
- };
- return { start, stop, onKey };
- };
- const GunGame = ({ points, onFinish }) => {
- const ref = useRef();
- useEffect(() => {
- const { start, stop } = snakeGame();
- start(ref.current?.getContext("2d"), 10, onFinish);
- return () => {
- stop();
- };
- }, [onFinish]);
- return (
- <div className={styles.gungame}>
- <div className={styles.gungame_panel}>
- <div className={styles.gungame_label}>
- <div>Not good enough! That guess only got {points} points!</div>
- <div>Finish this snake game to try again!</div>
- </div>
- <canvas
- className={styles.gungame_canvas}
- ref={ref}
- width={rawSize}
- height={rawSize}
- />
- </div>
- </div>
- );
- };
- export default GunGame;
|