如果用户按箭头键的速度过快,Snake会与自身碰撞

时间:2019-01-21 21:44:07

标签: javascript

我已经完成了我的蛇游戏,但是还有一个我无法修复的错误。例如,如果蛇向左走,则用户可以按上键,然后右键就足够快,这样蛇就永远没有机会向上移动1个单位并导致蛇头自己打开。有什么想法我可以解决这个问题吗?我的大脑告诉我,我需要先使蛇向上移动1个单位,然后再向右转,我只是不知道那是什么样子

//declare global variables
const canvas = document.querySelector('#canvas');
const score = document.querySelector('.score');
const gameOver = document.querySelector('.game-over');

//set canvas context
const ctx = canvas.getContext('2d');

//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;

//load audio files
let eat = new Audio();
let dead = new Audio();
eat.src = 'audio/eat.mp3';
dead.src = 'audio/dead.mp3';

//create snake unit
const unit = 16;

//create points variable 
let points = 0;

//create snake and set starting position
let snake = [{
	x : cvsW/2,
	y : cvsH/2
}]

//create food object and set its position somewhere on board
let food = {
	//Math.floor(Math.random()*cvsW + 1)---number from 1 to 784
	//Math.floor(Math.random()*cvsW/unit + 1)---number from 1 to 79
	//Math.floor(Math.random()*cvsW/unit + 1)*unit---number from 1 to 784(but it's a multiple of unit)
	//Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit---same as above but -1 keeps food inside canvas
	x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
	y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}

//create a variable to store the direction of the snake
let direction;

//add event to read users input then change direction
document.addEventListener('keydown', (e) => {
	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() {
	//clear canvas and redraw snake 
	ctx.clearRect(0, 0, cvsW, cvsH);
	for(let i = 0; i < snake.length; i++) {
		ctx.fillStyle = 'limegreen';
		ctx.fillRect(snake[i].x-unit/2, snake[i].y-unit/2, unit, unit);
	}
	//draw food
	ctx.fillStyle = 'red';
	ctx.fillRect(food.x-unit/2, food.y-unit/2, unit, unit);

	//grab heads position
	let headX = snake[0].x;
	let headY = snake[0].y;

	//move snake in chosen direction
	if(direction == 'left') headX -= unit;
	else if(direction == 'right') headX += unit;
	else if(direction == 'up') headY -= unit;
	else if(direction == 'down') headY += unit;

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

	//check to see if snake has hit a wall or itself
	if(headX < 0 || headX > cvsW || headY < 0 || headY > cvsH || collision(headX, headY)) {
		dead.play();
		clearInterval(runGame);
		canvas.style.animationName = 'animate';
		setTimeout(function() {gameOver.style.opacity = '1';}, 150);
	}

	//check to see if snakes eaten food
	if(headX === food.x && headY === food.y) {
		eat.play();
		//increase score
		points++;
		score.innerText = points;
		//get new food unit
		getFood();
		//create 3 new units
		for(let i = 3; i > 0; i--) {
			//add those units -without this code snake will not grow 
			snake.unshift(newHead);
		}
	} else {
		//remove tail -without this code snake will keep growing
		snake.pop();
	}
	//add new head position -without this code snake will not move
	snake.unshift(newHead);
}

let runGame = setInterval(draw, 65);

function collision(x, y) {
	for(let i = 1; i < snake.length; i++) {
		if(x == snake[i].x && y == snake[i].y) return true;
	}
	return false;
}

function getFood() {
	food = {
		x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
		y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
	}
	//loop through snake to see if food generates inside snake
	for(let i = 0; i < snake.length; i++) {
		//if so call the function again
		if(food.x == snake[i].x && food.y == snake[i].y) return getFood();
	} 
	//else return new random point
	return food;
}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Snake</title>
	<link href="https://fonts.googleapis.com/css?family=Nova+Square" rel="stylesheet">
	<style>
		body {
			background-color: #333;
		}

		.game-over {
			z-index: 1;
			position: absolute;
			left: 0;
			top: 0;
			right: 0;
			bottom: 0;
			width: 784px;
			height: 528px;
			margin: auto;
			color: red;
			font-family: 'Nova Square';
			font-size: 5rem;
			font-weight: bolder;
			display: flex;
			justify-content: center;
			align-items: center;
			opacity: 0;
			text-shadow: -7px 7px #333;
		}

		#canvas {
			background-color: #4d4d4d;
			display: block;
			margin: auto;
			position: absolute;
			left: 0;
			top: 0;
			right: 0;
			bottom: 0;
			animation-duration: 0.1s;
			animation-fill-mode: forwards;
		}

		@keyframes animate {
			0% {left: 0; right: 0;}
			25% {left: 50px; right: 0;}
			50% {left: 0; right: 50px;}
			75% {left: 50px; right: 0;}
			100% {left: 0; right: 0;}
		}

		.score {
			width: 80px;
			height: 80px;
			margin-left: auto;
			margin-right: auto;
			color: white;
			font-family: 'Nova Square';
			font-size: 4rem;
			display: flex;
			justify-content: center;
			align-items: center;
			margin-top: 50px;
		}
	</style>
</head>
<body>
	<div class="score">0</div>
	<canvas id="canvas" width="784" height="528"></canvas>
	<div class="game-over">Game Over</div>
	<script src="script.js"></script>
</body>
</html>

1 个答案:

答案 0 :(得分:0)

您可以添加一个变量来保存下一个方向,然后从draw更新实际方向,这样它将与实际发生的情况保持同步:

//declare global variables
const canvas = document.querySelector('#canvas');
const score = document.querySelector('.score');
const gameOver = document.querySelector('.game-over');

//set canvas context
const ctx = canvas.getContext('2d');

//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;

//load audio files
let eat = new Audio();
let dead = new Audio();
eat.src = 'audio/eat.mp3';
dead.src = 'audio/dead.mp3';

//create snake unit
const unit = 16;

//create points variable 
let points = 0;

//create snake and set starting position
let snake = [{
	x : cvsW/2,
	y : cvsH/2
}]

//create food object and set its position somewhere on board
let food = {
	//Math.floor(Math.random()*cvsW + 1)---number from 1 to 784
	//Math.floor(Math.random()*cvsW/unit + 1)---number from 1 to 79
	//Math.floor(Math.random()*cvsW/unit + 1)*unit---number from 1 to 784(but it's a multiple of unit)
	//Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit---same as above but -1 keeps food inside canvas
	x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
	y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}

//create a variable to store the direction of the snake
let direction;
let nextDirection

//add event to read users input then change direction
document.addEventListener('keydown', (e) => {
	if(e.keyCode == 37 && direction != 'right') nextDirection = 'left';
	else if (e.keyCode == 38 && direction != 'down') nextDirection = 'up';
	else if (e.keyCode == 39 && direction != 'left') nextDirection = 'right';
	else if (e.keyCode == 40 && direction != 'up') nextDirection = 'down';
})

function draw() {
	//clear canvas and redraw snake 
	ctx.clearRect(0, 0, cvsW, cvsH);
	for(let i = 0; i < snake.length; i++) {
		ctx.fillStyle = 'limegreen';
		ctx.fillRect(snake[i].x-unit/2, snake[i].y-unit/2, unit, unit);
	}
	//draw food
	ctx.fillStyle = 'red';
	ctx.fillRect(food.x-unit/2, food.y-unit/2, unit, unit);

	//grab heads position
	let headX = snake[0].x;
	let headY = snake[0].y;

	//move snake in chosen direction
	if(nextDirection == 'left') headX -= unit;
	else if(nextDirection == 'right') headX += unit;
	else if(nextDirection == 'up') headY -= unit;
	else if(nextDirection == 'down') headY += unit;
	// update the direction the snake is travelling:
	direction = nextDirection;

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

	//check to see if snake has hit a wall or itself
	if(headX < 0 || headX > cvsW || headY < 0 || headY > cvsH || collision(headX, headY)) {
		dead.play();
		clearInterval(runGame);
		canvas.style.animationName = 'animate';
		setTimeout(function() {gameOver.style.opacity = '1';}, 150);
	}

	//check to see if snakes eaten food
	if(headX === food.x && headY === food.y) {
		eat.play();
		//increase score
		points++;
		score.innerText = points;
		//get new food unit
		getFood();
		//create 3 new units
		for(let i = 3; i > 0; i--) {
			//add those units -without this code snake will not grow 
			snake.unshift(newHead);
		}
	} else {
		//remove tail -without this code snake will keep growing
		snake.pop();
	}
	//add new head position -without this code snake will not move
	snake.unshift(newHead);
}

let runGame = setInterval(draw, 65);

function collision(x, y) {
	for(let i = 1; i < snake.length; i++) {
		if(x == snake[i].x && y == snake[i].y) return true;
	}
	return false;
}

function getFood() {
	food = {
		x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
		y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
	}
	//loop through snake to see if food generates inside snake
	for(let i = 0; i < snake.length; i++) {
		//if so call the function again
		if(food.x == snake[i].x && food.y == snake[i].y) return getFood();
	} 
	//else return new random point
	return food;
}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Snake</title>
	<link href="https://fonts.googleapis.com/css?family=Nova+Square" rel="stylesheet">
	<style>
		body {
			background-color: #333;
		}

		.game-over {
			z-index: 1;
			position: absolute;
			left: 0;
			top: 0;
			right: 0;
			bottom: 0;
			width: 784px;
			height: 528px;
			margin: auto;
			color: red;
			font-family: 'Nova Square';
			font-size: 5rem;
			font-weight: bolder;
			display: flex;
			justify-content: center;
			align-items: center;
			opacity: 0;
			text-shadow: -7px 7px #333;
		}

		#canvas {
			background-color: #4d4d4d;
			display: block;
			margin: auto;
			position: absolute;
			left: 0;
			top: 0;
			right: 0;
			bottom: 0;
			animation-duration: 0.1s;
			animation-fill-mode: forwards;
		}

		@keyframes animate {
			0% {left: 0; right: 0;}
			25% {left: 50px; right: 0;}
			50% {left: 0; right: 50px;}
			75% {left: 50px; right: 0;}
			100% {left: 0; right: 0;}
		}

		.score {
			width: 80px;
			height: 80px;
			margin-left: auto;
			margin-right: auto;
			color: white;
			font-family: 'Nova Square';
			font-size: 4rem;
			display: flex;
			justify-content: center;
			align-items: center;
			margin-top: 50px;
		}
	</style>
</head>
<body>
	<div class="score">0</div>
	<canvas id="canvas" width="784" height="528"></canvas>
	<div class="game-over">Game Over</div>
	<script src="script.js"></script>
</body>
</html>