首页 > 解决方案 > 防止食物在我的蛇体内生成

问题描述

有人能告诉我怎么做,这样食物就不会在我的蛇里面产生吗?我试过使用一个for循环来检查蛇的每个索引,看看它是否与食物的位置相匹配,但这仍然不起作用。

此外,我加快了游戏的速度以及在进食时添加到蛇中的单位数量,以加快结果并检查该方法是否真的有效。

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 = {
	x : Math.floor(Math.random()*((cvsW/unit)-1)+1)*unit,
	y : Math.floor(Math.random()*((cvsH/unit)-1)+1)*unit
}

//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;

	// //check if snake hit wall
	// if(headX < 0 || headY < 0 || headX > (cvsW-unit) || headY > (cvsH-unit)) {
	// 	clearInterval(runGame);
	// }

	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;

	// for(let i = 0; i < snake.length; i++) {
	// 	if(headX == snake[i].x && headY == snake[i].y) {
	// 		clearInterval(game);
	// 	}
	// }

	//create new head
	let newHead = {x: headX, y: headY}

	//if snake eats food -do this
	if(headX == food.x && headY == food.y) {
		//create new food position
		while(!checkSnakeForFood()) {
		 	food = {
				x : Math.floor(Math.random()*((cvsW/unit)-1)+1)*unit,
				y : Math.floor(Math.random()*((cvsH/unit)-1)+1)*unit
			}
		}
		
		//add 3 units to the snake
		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 checkSnakeForFood() {
	for(let i = 0; i < snake.length; i++) {
		if(snake[i].x === food.x && snake[i].y === food.y) return false;
	}
	return true;
}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Snake Game</title>
	<style>
		body {
			background-color: #333;
		}

		canvas {
			background-color: #4d4d4d;
			margin: auto;
			display: block;
			position: absolute;
			left: 0;
			right: 0;
			top: 0;
			bottom: 0;
			width: 750px;
			height: 500px;		
		}
	</style>
</head>
<body>
	<canvas id="canvas"></canvas>
	<script src="script.js"></script>
</body>
</html>

标签: javascript

解决方案


问题是它checkSnakeForFood()没有像您预期的那样工作,因为newHead在您测试食物头部碰撞时尚未将其添加到蛇体中。

只检查蛇尾并且checkSnakeForFood()例程报告当前食物放置(正下方newHead)不是碰撞,因此while不执行循环体并且食物不移动。这是正在发生的事情的图表:

 0123456789
0+--------- 
1|.........
2|.F######.
3|.......##
    ^^^^^^^
       |
only these segments get
tested for collision

newHead并且food具有相同的位置,[2, 2],用 表示F。他们正在碰撞,这if (headX == food.x && headY == food.y)是真的。但是,当checkSnakeForFood()在下一行调用时#,循环中只考虑尾部元素for (let i = 0; i < snake.length; i++)checkSnakeForFood()然后返回 true 并且while循环永远不会重新定位食物。

您可以通过在尝试调用之前执行unshift以下操作来解决此问题:newHeadcheckSnakeForFood()

  //create new head
  let newHead = {
    x: headX,
    y: headY
  }

  //add head to snake, *before* checking for collision with the food
  snake.unshift(newHead);

  //if snake eats food -do this
  if (headX == food.x && headY == food.y) {
    //create new food position
    while (!checkSnakeForFood()) {  // <-- now this will successfully detect
                                    // that the snake head is touching the food

现在,食物与头部的碰撞将正确记录,并且食物将重新定位,直到它不与蛇的尾巴头部发生碰撞:

 0123456789
0+--------- 
1|.........
2|.F######.
3|.......##
   ^^^^^^^^
       |
all segments are correctly
tested for collision

但是,我建议进行重构以帮助避免此类错误。您可以创建snake具有碰撞和移动的成员函数以及其direction和的属性的对象tail。您的draw功能负担过重,并且比其名称所承诺的要承担更多责任。

分离newHead似乎是一种容易出错的方法;直接在数组上操作头部会省去很多困惑。

同时,这里是更新的代码,让您再次移动(请记住单击Full page片段中的链接以正确玩游戏:

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 = {
  x: Math.floor(Math.random() * ((cvsW / unit) - 1) + 1) * unit,
  y: Math.floor(Math.random() * ((cvsH / unit) - 1) + 1) * unit
}

//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;

  // //check if snake hit wall
  // if(headX < 0 || headY < 0 || headX > (cvsW-unit) || headY > (cvsH-unit)) {
  // 	clearInterval(runGame);
  // }

  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;

  // for(let i = 0; i < snake.length; i++) {
  // 	if(headX == snake[i].x && headY == snake[i].y) {
  // 		clearInterval(game);
  // 	}
  // }

  //create new head
  let newHead = {
    x: headX,
    y: headY
  }

  //add head to snake
  snake.unshift(newHead);

  //if snake eats food -do this
  if (headX == food.x && headY == food.y) {
    //create new food position
    while (!checkSnakeForFood()) {
      food = {
        x: Math.floor(Math.random() * ((cvsW / unit) - 1) + 1) * unit,
        y: Math.floor(Math.random() * ((cvsH / unit) - 1) + 1) * unit
      }
    }

    //add 3 units to the snake
    for (let i = 30; i > 0; i--) {
      snake.unshift(newHead);
    }
  } else {
    //remove tail
    snake.pop();
  }
}

//run game engine
let runGame = setInterval(draw, 40);

function checkSnakeForFood() {
  for (let i = 0; i < snake.length; i++) {
    if (snake[i].x === food.x && snake[i].y === food.y) return false;
  }
  return true;
}
body {
  background-color: #333;
}

canvas {
  background-color: #4d4d4d;
  margin: auto;
  display: block;
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  width: 750px;
  height: 500px;
}
<canvas id="canvas"></canvas>


推荐阅读