项目结构
1 2 3 4 5 6 7 8 9 10
| catbook-react ├── food.js ├── game.js ├── index.html ├── input.js ├── README.md ├── snake.js ├── snakeUtils.js └── style.css
|
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Snake</title> <script src="snake.js"></script> <script src="input.js"></script> <script src="food.js"></script> <script src="snakeUtils.js"></script> <script src="game.js" defer></script> </head> <body> <div id="game-board"></div> </body> </html>
|
代码说明:
- 基本结构:
<head>
中引入了 style.css
来定义样式。
<div id="game-board"></div>
是游戏的主要区域。
- 脚本加载顺序:
snake.js
、input.js
、food.js
和 snakeUtils.js
被同步加载。
game.js
使用了 defer
属性,延迟到 HTML 解析完成后才执行。
为什么使用 defer
属性?
defer
会在 HTML 完全解析后执行脚本,避免阻塞页面的加载。
- 所有带
defer
的脚本会按照它们在 HTML 中的顺序依次执行。
- 如果脚本与 DOM 交互强相关(如
document.getElementById
),defer
能保证脚本运行时 DOM 已加载完成。
使用 defer
的注意点:
- 不建议与
async
混用。
- 适合所有脚本对 DOM 操作的场景,特别是多个脚本间存在依赖关系时。
game.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| const SNAKE_SPEED = 5;
const gameBoard = document.getElementById("game-board"); let isGameOver = false;
const main = () => { update(); draw(); if (isGameOver) { alert("Game Over"); resetGame(); } };
let gameLoop = setInterval(main, 1000 / SNAKE_SPEED);
const update = () => { console.log("Updating"); updateSnake(); updateFood(); isGameOver = checkGameOver(); };
const draw = () => { gameBoard.innerHTML = ""; drawSnake(gameBoard); drawFood(gameBoard); };
const checkGameOver = () => { return snakeOutOfBounds() || snakeIntersectSelf(); };
const resetGame = () => { clearInterval(gameLoop);
resetSnake(); resetDirection();
gameLoop = setInterval(main, 1000 / SNAKE_SPEED); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| let inputDirection = { x: 0, y: 1 };
window.addEventListener("keydown", (event) => { if (event.key === "ArrowUp" && inputDirection.x !== 0) { inputDirection = { x: 0, y: -1 }; } else if (event.key === "ArrowDown" && inputDirection.x !== 0) { inputDirection = { x: 0, y: 1 }; } else if (event.key === "ArrowRight" && inputDirection.y !== 0) { inputDirection = { x: 1, y: 0 }; } else if (event.key === "ArrowLeft" && inputDirection.y !== 0) { inputDirection = { x: -1, y: 0 }; } else if (event.key === "r") { resetGame(); } });
const getInputDirection = () => { return inputDirection; };
const resetDirection = () => { inputDirection = { x: 0, y: 1 }; };
|
// Create a “keydown” event listener
// (https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
// key has been pressed
// (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
// List of key values:
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values