278
game.js
Normal file
278
game.js
Normal file
@@ -0,0 +1,278 @@
|
||||
// 游戏配置
|
||||
const CONFIG = {
|
||||
gridSize: 20, // 网格大小
|
||||
canvasSize: 400, // 画布大小
|
||||
easySpeed: 150, // 简单模式速度
|
||||
mediumSpeed: 100, // 中等模式速度
|
||||
hardSpeed: 50 // 困难模式速度
|
||||
};
|
||||
|
||||
// 游戏状态
|
||||
let snake = [];
|
||||
let food = {};
|
||||
let direction = 'right';
|
||||
let nextDirection = 'right';
|
||||
let score = 0;
|
||||
let gameLoop = null;
|
||||
let isPaused = false;
|
||||
let isGameRunning = false;
|
||||
|
||||
// DOM 元素
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const scoreElement = document.getElementById('score');
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const pauseBtn = document.getElementById('pauseBtn');
|
||||
const resetBtn = document.getElementById('resetBtn');
|
||||
const difficultySelect = document.getElementById('difficulty');
|
||||
|
||||
// 初始化游戏
|
||||
function initGame() {
|
||||
snake = [
|
||||
{ x: 5, y: 10 },
|
||||
{ x: 4, y: 10 },
|
||||
{ x: 3, y: 10 }
|
||||
];
|
||||
direction = 'right';
|
||||
nextDirection = 'right';
|
||||
score = 0;
|
||||
isPaused = false;
|
||||
isGameRunning = false;
|
||||
updateScore();
|
||||
spawnFood();
|
||||
draw();
|
||||
}
|
||||
|
||||
// 生成食物
|
||||
function spawnFood() {
|
||||
const maxPos = CONFIG.canvasSize / CONFIG.gridSize;
|
||||
let newFood;
|
||||
|
||||
do {
|
||||
newFood = {
|
||||
x: Math.floor(Math.random() * maxPos),
|
||||
y: Math.floor(Math.random() * maxPos)
|
||||
};
|
||||
} while (snake.some(segment => segment.x === newFood.x && segment.y === newFood.y));
|
||||
|
||||
food = newFood;
|
||||
}
|
||||
|
||||
// 更新游戏状态
|
||||
function update() {
|
||||
if (isPaused) return;
|
||||
|
||||
direction = nextDirection;
|
||||
|
||||
// 计算蛇头新位置
|
||||
const head = { ...snake[0] };
|
||||
|
||||
switch (direction) {
|
||||
case 'up':
|
||||
head.y -= 1;
|
||||
break;
|
||||
case 'down':
|
||||
head.y += 1;
|
||||
break;
|
||||
case 'left':
|
||||
head.x -= 1;
|
||||
break;
|
||||
case 'right':
|
||||
head.x += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// 检查碰撞
|
||||
if (checkCollision(head)) {
|
||||
gameOver();
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加蛇头
|
||||
snake.unshift(head);
|
||||
|
||||
// 检查是否吃到食物
|
||||
if (head.x === food.x && head.y === food.y) {
|
||||
score += 10;
|
||||
updateScore();
|
||||
spawnFood();
|
||||
} else {
|
||||
snake.pop();
|
||||
}
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
// 检查碰撞
|
||||
function checkCollision(head) {
|
||||
const maxPos = CONFIG.canvasSize / CONFIG.gridSize;
|
||||
|
||||
// 撞墙检测
|
||||
if (head.x < 0 || head.x >= maxPos || head.y < 0 || head.y >= maxPos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 撞自己检测
|
||||
return snake.some(segment => segment.x === head.x && segment.y === head.y);
|
||||
}
|
||||
|
||||
// 绘制游戏
|
||||
function draw() {
|
||||
// 清空画布
|
||||
ctx.fillStyle = '#2c3e50';
|
||||
ctx.fillRect(0, 0, CONFIG.canvasSize, CONFIG.canvasSize);
|
||||
|
||||
// 绘制蛇
|
||||
snake.forEach((segment, index) => {
|
||||
const gradient = ctx.createRadialGradient(
|
||||
segment.x * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
segment.y * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
0,
|
||||
segment.x * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
segment.y * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
CONFIG.gridSize
|
||||
);
|
||||
|
||||
if (index === 0) {
|
||||
gradient.addColorStop(0, '#2ecc71');
|
||||
gradient.addColorStop(1, '#27ae60');
|
||||
} else {
|
||||
gradient.addColorStop(0, '#82e0aa');
|
||||
gradient.addColorStop(1, '#58d68d');
|
||||
}
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(
|
||||
segment.x * CONFIG.gridSize + 1,
|
||||
segment.y * CONFIG.gridSize + 1,
|
||||
CONFIG.gridSize - 2,
|
||||
CONFIG.gridSize - 2
|
||||
);
|
||||
});
|
||||
|
||||
// 绘制食物
|
||||
const foodGradient = ctx.createRadialGradient(
|
||||
food.x * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
food.y * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
0,
|
||||
food.x * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
food.y * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
CONFIG.gridSize
|
||||
);
|
||||
foodGradient.addColorStop(0, '#e74c3c');
|
||||
foodGradient.addColorStop(1, '#c0392b');
|
||||
|
||||
ctx.fillStyle = foodGradient;
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
food.x * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
food.y * CONFIG.gridSize + CONFIG.gridSize / 2,
|
||||
CONFIG.gridSize / 2 - 2,
|
||||
0,
|
||||
Math.PI * 2
|
||||
);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// 更新分数
|
||||
function updateScore() {
|
||||
scoreElement.textContent = score;
|
||||
}
|
||||
|
||||
// 游戏结束
|
||||
function gameOver() {
|
||||
stopGame();
|
||||
alert(`游戏结束!得分:${score}\n点击"重置"重新开始`);
|
||||
}
|
||||
|
||||
// 开始游戏
|
||||
function startGame() {
|
||||
if (isGameRunning) return;
|
||||
|
||||
initGame();
|
||||
isGameRunning = true;
|
||||
startBtn.disabled = true;
|
||||
pauseBtn.disabled = false;
|
||||
difficultySelect.disabled = true;
|
||||
|
||||
const speed = getGameSpeed();
|
||||
gameLoop = setInterval(update, speed);
|
||||
}
|
||||
|
||||
// 停止游戏
|
||||
function stopGame() {
|
||||
if (gameLoop) {
|
||||
clearInterval(gameLoop);
|
||||
gameLoop = null;
|
||||
}
|
||||
isGameRunning = false;
|
||||
startBtn.disabled = false;
|
||||
pauseBtn.disabled = true;
|
||||
pauseBtn.textContent = '暂停';
|
||||
difficultySelect.disabled = false;
|
||||
}
|
||||
|
||||
// 暂停/继续游戏
|
||||
function togglePause() {
|
||||
if (!isGameRunning) return;
|
||||
|
||||
isPaused = !isPaused;
|
||||
|
||||
if (isPaused) {
|
||||
pauseBtn.textContent = '继续';
|
||||
} else {
|
||||
pauseBtn.textContent = '暂停';
|
||||
}
|
||||
}
|
||||
|
||||
// 重置游戏
|
||||
function resetGame() {
|
||||
stopGame();
|
||||
initGame();
|
||||
}
|
||||
|
||||
// 获取游戏速度
|
||||
function getGameSpeed() {
|
||||
const difficulty = difficultySelect.value;
|
||||
|
||||
switch (difficulty) {
|
||||
case 'easy':
|
||||
return CONFIG.easySpeed;
|
||||
case 'medium':
|
||||
return CONFIG.mediumSpeed;
|
||||
case 'hard':
|
||||
return CONFIG.hardSpeed;
|
||||
default:
|
||||
return CONFIG.mediumSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘控制
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const key = e.key;
|
||||
|
||||
if (key === 'ArrowUp' && direction !== 'down') {
|
||||
nextDirection = 'up';
|
||||
e.preventDefault();
|
||||
} else if (key === 'ArrowDown' && direction !== 'up') {
|
||||
nextDirection = 'down';
|
||||
e.preventDefault();
|
||||
} else if (key === 'ArrowLeft' && direction !== 'right') {
|
||||
nextDirection = 'left';
|
||||
e.preventDefault();
|
||||
} else if (key === 'ArrowRight' && direction !== 'left') {
|
||||
nextDirection = 'right';
|
||||
e.preventDefault();
|
||||
} else if (key === ' ' && isGameRunning) {
|
||||
togglePause();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// 按钮事件
|
||||
startBtn.addEventListener('click', startGame);
|
||||
pauseBtn.addEventListener('click', togglePause);
|
||||
resetBtn.addEventListener('click', resetGame);
|
||||
|
||||
// 初始化
|
||||
initGame();
|
||||
Reference in New Issue
Block a user