第一版
<!-- 一個簡易的貪吃蛇專案 -->
<!-- CSS:
調整畫布大小,確保畫布在視口中居中。
設置遊戲區域的背景顏色和其他視覺效果。
HTML:
建立一個畫布元素用來顯示遊戲。
增加一些按鈕或訊息區來控制遊戲和顯示分數。
JavaScript:
透過 JavaScript 來控制遊戲邏輯,包括蛇的移動、吃食物和碰撞檢測。 -->
<!DOCTYPE html>
<html>
<head>
<title>貪吃蛇</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #db9f9f;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="game-canvas" width="400" height="400"></canvas>
<div id="score-board">
<p>Score: <span id="score">0</span></p>
<button id="start-btn">Start</button>
</div>
<script>
// 設定遊戲參數
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const cellSize = 20;
const gridSize = 20;
const speed = 10;
const snakeLength = 3;
const foodCount = 1;
const scoreBoard = document.getElementById('score-board');
const score = document.getElementById('score');
const startBtn = document.getElementById('start-btn');
let gameOver = false;
let scoreCount = 0;
let snake = [];
let food = [];
let direction = 'right';
// 設定遊戲初始狀態
function init() {
gameOver = false;
scoreCount = 0;
snake = [];
food = [];
direction = 'right';
for (let i = 0; i < snakeLength; i++) {
snake.push({ x: i * gridSize, y: 0 });
}
placeFood();
draw();
}
// 產生食物
function placeFood() {
let x = Math.floor(Math.random() * (width - cellSize) / gridSize) * gridSize;
let y = Math.floor(Math.random() * (height - cellSize) / gridSize) * gridSize;
food = [{ x: x, y: y }];
}
// 繪製遊戲畫面
function draw() {
ctx.clearRect(0, 0, width, height);
// 繪製格子
for (let x = 0; x < width; x += gridSize) {
for (let y = 0; y < height; y += gridSize) {
ctx.fillStyle = (x + y) % 2 === 0? '#fff' : '#ddd';
ctx.fillRect(x, y, gridSize);
}
}
// 繪製蛇
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0? '#000' : '#fff';
ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
}
// 繪製食物
ctx.fillStyle = '#f00';
ctx.fillRect(food[0].x, food[0].y, cellSize);
// 繪製分數
score.textContent = scoreCount;
}
// 移動蛇
function moveSnake() {
// 取得蛇頭的位置
let head = { x: snake[0].x, y: snake[0].y };
// 根據方向移動蛇頭
switch (direction) {
case 'up':
head.y -= gridSize;
break;
case 'down':
head.y += gridSize;
break;
case 'left':
head.x -= gridSize;
break;
case 'right':
head.x += gridSize;
break;
}
// 判斷蛇是否吃到食物
if (head.x === food[0].x && head.y === food[0].y) {
scoreCount++;
placeFood();
} else {
// 移動蛇身
snake.pop();
}
snake.unshift(head);
}
// 控制蛇移動
function controlSnake() {
if (gameOver) {
return;
}
// 取得蛇頭的位置
let head = snake[0];
// 判斷蛇是否碰撞到邊界
if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height) {
gameOver = true;
scoreBoard.style.display = 'none';
alert('Game Over!');
return;
}
// 判斷蛇是否吃到自己
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
gameOver = true;
scoreBoard.style.display = 'none';
alert('Game Over!');
return;
}
}
// 移動蛇
moveSnake();
draw();
// 判斷蛇是否吃到食物
if (head.x === food[0].x && head.y === food[0].y) {
scoreCount++;
placeFood();
}
// 設定下一個移動方向
switch (direction) {
case 'up':
direction = 'left';
break;
case 'left':
direction = 'down';
break;
case 'down':
direction = 'right';
break;
case 'right':
direction = 'up';
break;
}
// 設定遊戲速度
setTimeout(controlSnake, speed);
}
// 開始遊戲
startBtn.addEventListener('click', function () {
scoreBoard.style.display = 'block';
init();
controlSnake();
startBtn.style.display = 'none';
});
</script>
</body>
第二版(補注釋)
<!DOCTYPE html>
<html>
<head>
<title>貪吃蛇</title>
<style>
body {
/* 設置邊距和填充為0 */
margin: 0;
padding: 0;
/* 設置背景顏色 */
background-color: #db9f9f;
/* 使用flex布局,使內容居中 */
display: flex;
align-items: center;
justify-content: center;
/* 設置高度為視窗高度 */
height: 100vh;
}
</style>
</head>
<body>
<!-- 定義遊戲畫布,設置寬高為400像素 -->
<canvas id="game-canvas" width="400" height="400"></canvas>
<!-- 分數顯示區域,包含分數顯示和開始按鈕 -->
<div id="score-board">
<p>Score: <span id="score">0</span></p>
<button id="start-btn">Start</button>
</div>
<script>
// 設定遊戲參數
// 獲取畫布元素和繪圖上下文
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
// 獲取畫布的寬度和高度
const width = canvas.width;
const height = canvas.height;
// 設置單元格大小和網格大小
const cellSize = 20;
const gridSize = 20;
// 設置遊戲速度(毫秒)
const speed = 100;
// 設置初始蛇的長度
const snakeLength = 3;
// 設置食物的數量
const foodCount = 1;
// 獲取計分板和分數顯示元素
const scoreBoard = document.getElementById('score-board');
const score = document.getElementById('score');
// 獲取開始按鈕元素
const startBtn = document.getElementById('start-btn');
// 遊戲是否結束的標誌
let gameOver = false;
// 分數計數
let scoreCount = 0;
// 蛇的數組
let snake = [];
// 食物的數組
let food = [];
// 蛇的移動方向
let direction = 'right';
// 遊戲循環的間隔ID
let intervalId = null;
// 設定遊戲初始狀態
// 初始化遊戲狀態的函數
function init() {
gameOver = false; // 設置遊戲是否結束的標誌
scoreCount = 0; // 初始化得分
snake = []; // 初始化蛇的身體
food = []; // 初始化食物的位置
direction = 'right'; // 初始化蛇的移動方向
for (let i = 0; i < snakeLength; i++) {
snake.push({ x: i * gridSize, y: 0 }); // 初始化蛇的身體位置
}
placeFood(); // 放置食物
draw(); // 繪製遊戲畫面
}
// 產生食物
// 放置食物的函數
// 在遊戲區域內隨機生成食物的位置
function placeFood() {
let x = Math.floor(Math.random() * (width / gridSize)) * gridSize;
let y = Math.floor(Math.random() * (height / gridSize)) * gridSize;
food = [{ x: x, y: y }];
}
// 繪製遊戲畫面
// 繪製函數,用於清除畫布上的內容
function draw() {
ctx.clearRect(0, 0, width, height);
ctx.clearRect(0, 0, width, height);
// 繪製格子
// 繪製棋盤格的函數
// 使用雙重循環遍歷每個網格單元,根據其位置決定填充顏色
// 如果 (x + y) 是偶數,則填充白色,否則填充淺灰色
for (let x = 0; x < width; x += gridSize) {
for (let y = 0; y < height; y += gridSize) {
ctx.fillStyle = (x + y) % 2 === 0 ? '#fff' : '#ddd';
ctx.fillRect(x, y, gridSize, gridSize);
}
}
// 繪製蛇
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? '#000' : '#fff';
ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
}
// 繪製食物
ctx.fillStyle = '#f00';
ctx.fillRect(food[0].x, food[0].y, cellSize, cellSize);
// 繪製分數
score.textContent = scoreCount;
}
// 移動蛇
function moveSnake() {
// 取得蛇頭的位置
let head = { x: snake[0].x, y: snake[0].y };
// 根據方向移動蛇頭
switch (direction) {
case 'up':
head.y -= gridSize;
break;
case 'down':
head.y += gridSize;
break;
case 'left':
head.x -= gridSize;
break;
case 'right':
head.x += gridSize;
break;
}
// 判斷蛇是否吃到食物
if (head.x === food[0].x && head.y === food[0].y) {
scoreCount++;
placeFood();
} else {
// 移動蛇身
snake.pop();
}
snake.unshift(head);
}
// 控制蛇移動
function controlSnake() {
if (gameOver) {
clearInterval(intervalId);
return;
}
moveSnake();
draw();
let head = snake[0];
// 判斷蛇是否碰撞到邊界或自己
if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height || snake.slice(1).some(s => s.x === head.x && s.y === head.y)) {
gameOver = true;
scoreBoard.style.display = 'none';
alert('Game Over!');
}
}
// 開始遊戲
startBtn.addEventListener('click', function () {
scoreBoard.style.display = 'block';
init();
intervalId = setInterval(controlSnake, speed);
});
// 監聽鍵盤事件來改變方向
document.addEventListener('keydown', function (event) {
switch (event.key) {
case 'ArrowUp':
if (direction !== 'down') direction = 'up';
break;
case 'ArrowDown':
if (direction !== 'up') direction = 'down';
break;
case 'ArrowLeft':
if (direction !== 'right') direction = 'left';
break;
case 'ArrowRight':
if (direction !== 'left') direction = 'right';
break;
}
});
</script>
</body>
</html>
第三版
<!DOCTYPE html>
<html>
<head>
<title>貪吃蛇</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #db9f9f;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="game-canvas" width="400" height="400"></canvas>
<div id="score-board">
<p>Score: <span id="score">0</span></p>
<button id="start-btn">Start</button>
</div>
<script>
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const cellSize = 20;
const gridSize = 20;
const speed = 100;
const snakeLength = 3;
const foodCount = 1;
const scoreBoard = document.getElementById('score-board');
const score = document.getElementById('score');
const startBtn = document.getElementById('start-btn');
let gameOver = false;
let scoreCount = 0;
let snake = [];
let food = [];
let direction = 'right';
let intervalId = null;
function init() {
gameOver = false;
scoreCount = 0;
snake = [];
food = [];
direction = 'right';
// 將蛇初始化為不重疊
for (let i = 0; i < snakeLength; i++) {
snake.push({ x: gridSize * (snakeLength - i - 1), y: 0 });
}
placeFood();
draw();
}
function placeFood() {
let x = Math.floor(Math.random() * (width / gridSize)) * gridSize;
let y = Math.floor(Math.random() * (height / gridSize)) * gridSize;
food = [{ x: x, y: y }];
}
function draw() {
ctx.clearRect(0, 0, width, height);
for (let x = 0; x < width; x += gridSize) {
for (let y = 0; y < height; y += gridSize) {
ctx.fillStyle = (x + y) % 2 === 0 ? '#fff' : '#ddd';
ctx.fillRect(x, y, gridSize, gridSize);
}
}
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? '#000' : '#fff';
ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
}
ctx.fillStyle = '#f00';
ctx.fillRect(food[0].x, food[0].y, cellSize, cellSize);
score.textContent = scoreCount;
}
function moveSnake() {
let head = { x: snake[0].x, y: snake[0].y };
switch (direction) {
case 'up':
head.y -= gridSize;
break;
case 'down':
head.y += gridSize;
break;
case 'left':
head.x -= gridSize;
break;
case 'right':
head.x += gridSize;
break;
}
if (head.x === food[0].x && head.y === food[0].y) {
scoreCount++;
placeFood();
addBody();
} else {
snake.pop();
}
snake.unshift(head);
}
function controlSnake() {
if (gameOver) {
clearInterval(intervalId);
return;
}
moveSnake();
draw();
let head = snake[0];
if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height || snake.slice(1).some(s => s.x === head.x && s.y === head.y)) {
gameOver = true;
scoreBoard.style.display = 'none';
alert('Game Over!');
}
}
//當蛇吃到食物時,身長會增加一格
function addBody() {
// 在蛇尾部新增一格,與蛇尾部的最後一格位置相同
let tail = snake[snake.length - 1];
snake.push({ x: tail.x, y: tail.y });
}
startBtn.addEventListener('click', function () {
scoreBoard.style.display = 'block';
init();
intervalId = setInterval(controlSnake, speed);
});
document.addEventListener('keydown', function (event) {
switch (event.key) {
case 'ArrowUp':
if (direction !== 'down') direction = 'up';
break;
case 'ArrowDown':
if (direction !== 'up') direction = 'down';
break;
case 'ArrowLeft':
if (direction !== 'right') direction = 'left';
break;
case 'ArrowRight':
if (direction !== 'left') direction = 'right';
break;
}
});
</script>
</body>
</html>
最終失敗版
<!DOCTYPE html>
<html>
<head>
<title>貪吃蛇</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #db9f9f;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="game-canvas" width="400" height="400"></canvas>
<div id="score-board">
<p>Score: <span id="score">0</span></p>
<button id="start-btn">Start</button>
</div>
<script>
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const cellSize = 20;
const gridSize = 20;
const speed = 100;
const snakeLength = 3;
const scoreBoard = document.getElementById('score-board');
const score = document.getElementById('score');
const startBtn = document.getElementById('start-btn');
let gameOver = false;
let scoreCount = 0;
let snake = [];
let food = [];
let direction = 'right';
let intervalId = null;
function init() {
gameOver = false;
scoreCount = 0;
snake = [];
food = [];
direction = 'right';
for (let i = 0; i < snakeLength; i++) {
snake.push({ x: gridSize * (snakeLength - i - 1), y: 0 });
}
placeFood();
draw();
}
function placeFood() {
let x, y, isFoodOnSnake;
do {
x = Math.floor(Math.random() * (width / gridSize)) * gridSize;
y = Math.floor(Math.random() * (height / gridSize)) * gridSize;
isFoodOnSnake = snake.some(s => s.x === x && s.y === y);
} while (isFoodOnSnake);
food = [{ x: x, y: y }];
}
function draw() {
ctx.clearRect(0, 0, width, height);
for (let x = 0; x < width; x += gridSize) {
for (let y = 0; y < height; y += gridSize) {
ctx.fillStyle = (x + y) % 2 === 0 ? '#fff' : '#ddd';
ctx.fillRect(x, y, gridSize, gridSize);
}
}
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i === 0 ? '#000' : '#fff';
ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
}
ctx.fillStyle = '#f00';
ctx.fillRect(food[0].x, food[0].y, cellSize, cellSize);
score.textContent = scoreCount;
}
function moveSnake() {
let head = { x: snake[0].x, y: snake[0].y };
switch (direction) {
case 'up':
head.y -= gridSize;
break;
case 'down':
head.y += gridSize;
break;
case 'left':
head.x -= gridSize;
break;
case 'right':
head.x += gridSize;
break;
}
if (head.x === food[0].x && head.y === food[0].y) {
console.log('Food eaten!'); // Debug message
scoreCount++;
placeFood();
addBody();
} else {
snake.pop(); // Remove tail
}
snake.unshift(head); // Add new head
console.log('Snake:', snake); // Debug message
}
function controlSnake() {
if (gameOver) {
clearInterval(intervalId);
return;
}
moveSnake();
draw();
let head = snake[0];
if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height || snake.slice(1).some(s => s.x === head.x && s.y === head.y)) {
gameOver = true;
scoreBoard.style.display = 'none';
alert('Game Over!');
}
}
function addBody() {
let tail = snake[snake.length - 1];
snake.push({ x: tail.x, y: tail.y });
console.log('Body added! Snake:', snake); // Debug message
}
startBtn.addEventListener('click', function () {
scoreBoard.style.display = 'block';
init();
intervalId = setInterval(controlSnake, speed);
});
document.addEventListener('keydown', function (event) {
switch (event.key) {
case 'ArrowUp':
if (direction !== 'down') direction = 'up';
break;
case 'ArrowDown':
if (direction !== 'up') direction = 'down';
break;
case 'ArrowLeft':
if (direction !== 'right') direction = 'left';
break;
case 'ArrowRight':
if (direction !== 'left') direction = 'right';
break;
}
});
</script>
</body>
</html>