如何避免JavaScript贪吃蛇游戏中食物生成在蛇身内部?
解决贪吃蛇食物不在蛇身生成的问题
嘿,我发现你的问题出在食物生成的逻辑顺序上!你写的检查思路是对的,但调用时机不对,导致检查根本没生效。让我拆解下问题,再帮你修复代码。
问题根源
你看这段代码:
while(!checkSnakeForFood()) { food = { x : Math.floor(Math.random()*((cvsW/unit)-1)+1)*unit, y : Math.floor(Math.random()*((cvsH/unit)-1)+1)*unit } }
这里的问题是:你在生成新食物之前就调用了checkSnakeForFood(),而这个函数检查的是全局food变量的旧位置(也就是刚被吃掉的那个食物的位置)。等你生成了新食物,却没有再次验证它是否在蛇身上——这就导致新食物还是可能出现在蛇身范围内。
修复方案
我们需要调整逻辑:先生成随机位置,再检查这个位置是否和蛇身重叠,如果重叠就重新生成,直到找到安全位置。最好把食物生成封装成单独函数,逻辑更清晰。
下面是修复后的完整JavaScript代码:
const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); //set canvas dimension equal to css dimension canvas.width = 768; canvas.height = 512; //now put those dimensions into variables const cvsW = canvas.width; const cvsH = canvas.height; //create snake unit const unit = 16; //create snake array let snake = [{x: cvsW/2, y: cvsH/2}]; //delcare global variable to hold users direction let direction; //create food object let food = generateFood(); // 调用生成函数初始化食物 //read user's direction document.addEventListener('keydown', changeDirection); function changeDirection(e) { //set direction if (e.keyCode == 37 && direction != 'right') direction = 'left'; else if (e.keyCode == 38 && direction != 'down') direction = 'up'; else if (e.keyCode == 39 && direction != 'left') direction = 'right'; else if (e.keyCode == 40 && direction != 'up') direction = 'down'; } function draw() { //refresh canvas ctx.clearRect(0, 0, cvsW, cvsH); //draw snake for(let i = 0; i < snake.length; i++) { ctx.fillStyle = 'limegreen'; ctx.fillRect(snake[i].x, snake[i].y, unit, unit); } //grab head position let headX = snake[0].x; let headY = snake[0].y; //posistion food on board ctx.fillStyle = 'red'; ctx.fillRect(food.x, food.y, unit, unit); //send the snake in chosen direction if(direction == 'left') headX -= unit; else if(direction == 'up') headY -= unit; else if(direction == 'right') headX += unit; else if(direction == 'down') headY += unit; //wrap around walls if (headX < 0) headX = cvsW - unit; else if (headX > cvsW - unit) headX = 0; else if (headY < 0) headY = cvsH - unit; else if(headY > cvsH - unit) headY = 0; //create new head let newHead = {x: headX, y: headY} //if snake eats food -do this if(headX == food.x && headY == food.y) { //生成新的安全食物 food = generateFood(); //add 3 units to the snake(你原来写的是30,这里保留你的设置) for (let i = 30; i > 0; i--) { snake.unshift(newHead); } } else { //remove tail snake.pop(); } //add head to snake snake.unshift(newHead); } //run game engine let runGame = setInterval(draw, 40); // 生成安全食物的函数 function generateFood() { let newFood; // 循环生成,直到找到不在蛇身上的位置 while(!newFood || isPositionOnSnake(newFood)) { newFood = { x : Math.floor(Math.random()*((cvsW/unit)-1)+1)*unit, y : Math.floor(Math.random()*((cvsH/unit)-1)+1)*unit } } return newFood; } // 检查某个位置是否在蛇身上的工具函数 function isPositionOnSnake(position) { for(let i = 0; i < snake.length; i++) { if(snake[i].x === position.x && snake[i].y === position.y) { return true; } } return false; }
关键改动说明
- 新增
generateFood()函数:负责生成随机位置,通过isPositionOnSnake()验证位置安全性,直到找到符合要求的位置才返回。 - 替换食物生成逻辑:初始化和进食后的食物生成都调用
generateFood(),确保每次生成的食物都不在蛇身上。 - 拆分检查逻辑为
isPositionOnSnake():让函数职责更单一,后续维护或扩展功能也更方便。
这样修改后,食物就再也不会出现在蛇的身体里了,你可以继续测试游戏速度和蛇的增长效果啦!
内容的提问来源于stack exchange,提问作者Alfred




