JavaScript贪吃蛇游戏重启后保留上次按键输入问题求助
贪吃蛇游戏重启后自动移动的问题修复
我是JavaScript新手,开发贪吃蛇游戏时遇到一个问题:点击画布上的重启按钮后,蛇会自动沿游戏结束前最后一次按键的方向移动,但首次启动游戏时需要按下任意按键才开始移动。这个问题是在修复快速按键导致蛇反向撞自身的事件监听问题后出现的,相关代码如下:
HTML代码
<!-- the snake game section --> <div class="snake-game" data-hover="Press any key to start"> <div class="score"> Score: <p id="score-number">0</p> </div> <canvas id="game-board"> </canvas> </div>
JavaScript代码
const gameBoard = document.getElementById("game-board"); const context = gameBoard.getContext("2d"); let rows = 20; let cols = 20; let gridBoard = 20; // per row 30 grids let gameOver = false; let snakeSize = 1; // initializing snake's body size let x = new Array(900); let y = new Array(900); gameBoard.height = rows * gridBoard; gameBoard.width = cols * gridBoard; for (let i = 0; i < snakeSize; i++){ x[i] = 40 - i*20 y[i] = 40 //the head of the snake } // to come up with a random position for the apple let eatenApple = true; let appleX; let appleY; let score = 0; let scoreNumber = document.getElementById("score-number"); const path = new Path2D(); // for the reset button function mainGame(){ //when the snake eats the apple and grows eatApple(); //movement validation and check the collision movementValidation(); // keep record of head&body in canvas for movement move(); // or change the snake position //to display the snake's head, body, and the apple display(); setTimeout(mainGame, 150); } function display(){ context.clearRect(0,0, gameBoard.width, gameBoard.height); if(gameOver){ isGameOver(); } else { //apple if(eatenApple){ appleRandomPlace(); eatenApple = false; } //the apple's head context.beginPath() context.fillStyle = "red"; context.fillRect(appleX, appleY, gridBoard / 1, gridBoard / 1); context.closePath(); for (let i = 0; i < snakeSize; i++){ //the snake's head if(i === 0){ const snake_head = new Path2D(); context.fillStyle = "#86626E"; snake_head.rect(x[0], y[0], gridBoard / 1, gridBoard / 1); context.fill(snake_head); } else { //the snake's body const snake_body = new Path2D(); context.fillStyle = "#201335"; context.strokeStyle = "#fff"; context.lineWidth = 3; snake_body.rect(x[i], y[i], gridBoard / 1, gridBoard / 1); context.fill(snake_body); context.stroke(snake_body); } } } } function isGameOver(){ //game over text context.fillStyle = "black"; context.textBaseline = "middle"; context.textAlign = "center"; context.font = "35px Adonay"; context.fillText("game over", gameBoard.width / 2, gameBoard.height / 2); // the reset button and text path.rect(150,240,100,40); //x, y, width, height path.closePath(); context.fillStyle = "black"; context.textBaseline = "middle"; context.textAlign = "center"; context.font = "20px Adonay"; context.strokeStyle = "#0B1D51"; context.lineWidth = 1; context.fillText("restart", 200, 261, 111, 40); context.stroke(path); } function eatApple(){ if(x[0] === appleX && y[0] === appleY){ snakeSize++ eatenApple = true; score += 1; scoreNumber.innerText = score; } } function appleRandomPlace(){ appleX = Math.floor(Math.random() * (cols - 5)) * gridBoard + 20; appleY = Math.floor(Math.random() * (rows - 5)) * gridBoard + 20; } let direction; let nextDirection; document.addEventListener("keydown", function(event){ if(event.keyCode === 38 && direction !== "down") { nextDirection = "up"; } else if(event.keyCode === 40 && direction !== "up"){ nextDirection = "down"; } else if(event.keyCode === 37 && direction !== "right"){ nextDirection = "left"; } else if(event.keyCode === 39 && direction !== "left"){ nextDirection = "right"; } }); function movementValidation(){ // snake's collision with the walls if(y[0] >= gameBoard.height / 1.1) { gameOver = true; } else if(y[0] < 1) { gameOver = true; } else if(x[0] >= gameBoard.width / 1.1) { gameOver = true; } else if(x[0] < 1) { gameOver = true; } //x[0] and y[0] represent the head of the snake // snake eats its own body for (let i = 1; i < snakeSize; i+=1){ if(x[0] === x[i] && y[0] === y[i]){ gameOver = true; } } } function move(){ // for snake's body for (let i = snakeSize; i > 0; i--){ x[i] = x[(i-1)]; y[i] = y[(i-1)]; } // for snake's head if(nextDirection === "left"){ x[0] -= gridBoard; } else if(nextDirection === "right"){ x[0] += gridBoard; } else if(nextDirection === "up"){ y[0] -= gridBoard; } else if(nextDirection === "down"){ y[0] += gridBoard; } direction = nextDirection; } function buttonReset(gameBoard, event){ const rect = gameBoard.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; return {x:x, y:y}; } document.addEventListener("click", function (e){ const x_y = buttonReset(gameBoard, e); if(context.isPointInPath(path, x_y.x, x_y.y)){ gameOver = false; snakeSize = 1; // initializing snake's body size x = new Array(900); y = new Array(900); gameBoard.height = rows * gridBoard; gameBoard.width = cols * gridBoard; for (let i = 0; i < snakeSize; i++){ x[i] = 40 - i*10 y[i] = 40 //the head of the snake } eatenApple = false; appleX; appleY; score = 0; scoreNumber.innerText = score; direction; nextDirection; display(); move(); eatApple(); movementValidation(); } }, false); mainGame();
问题原因与修复方案
原因
重启游戏时,direction和nextDirection变量未被重置,仍保留上一局的方向值,导致蛇重启后直接按之前的方向移动;同时重启代码中蛇的初始位置计算有误,且苹果生成逻辑存在问题。
修复步骤
- 重置方向变量:在重启按钮的点击事件中,显式将
direction和nextDirection设为undefined,清除上一局的方向记录,确保重启后必须按键才会移动。 - 修正蛇初始位置:将重启时蛇的x坐标计算改为和首次启动一致的
40 - i*20,避免位置偏移。 - 修复苹果生成逻辑:重启时将
eatenApple设为true,确保重新生成苹果。 - 优化move函数:添加判断,只有当
nextDirection存在时才移动蛇头,增强鲁棒性。
修改后的重启事件处理函数:
document.addEventListener("click", function (e){ const x_y = buttonReset(gameBoard, e); if(context.isPointInPath(path, x_y.x, x_y.y)){ gameOver = false; snakeSize = 1; x = new Array(900); y = new Array(900); // 修正初始位置,和首次启动保持一致 for (let i = 0; i < snakeSize; i++){ x[i] = 40 - i*20; y[i] = 40; } eatenApple = true; // 重启后重新生成苹果 score = 0; scoreNumber.innerText = score; // 关键:重置方向变量 direction = undefined; nextDirection = undefined; display(); } }, false);
修改后的move函数:
function move(){ // 蛇身跟随移动 for (let i = snakeSize; i > 0; i--){ x[i] = x[(i-1)]; y[i] = y[(i-1)]; } // 只有存在方向指令时才移动蛇头 if(nextDirection){ if(nextDirection === "left"){ x[0] -= gridBoard; } else if(nextDirection === "right"){ x[0] += gridBoard; } else if(nextDirection === "up"){ y[0] -= gridBoard; } else if(nextDirection === "down"){ y[0] += gridBoard; } direction = nextDirection; } }
额外优化
删除重启代码中重复设置gameBoard.height和gameBoard.width的语句,这部分初始化时已完成,无需重复执行。
内容的提问来源于stack exchange,提问作者meii18
相关产品推荐
相关产品推荐

