我正在使用JavaScript开发Snake游戏,我希望蛇只能垂直或水平移动,但它会一直沿对角线移动。例如,如果我向上按,它会向上移动,但是如果我向右按,它会沿对角线移动,而不是向右移动。
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const length_width = 15;
let snakeCoord = [
{x:300,y:150},
{x:315,y:150},
{x:330,y:150},
{x:345,y:150},
{x:360,y:150},
{x:375,y:150}
];
function drawSnakePart(snakePart) {
ctx.beginPath();
ctx.fillRect(snakePart.x, snakePart.y, length_width, length_width);
ctx.strokeRect(snakePart.x, snakePart.y, length_width, length_width);
ctx.closePath();
}
function drawSnake() {
snakeCoord.forEach(drawSnakePart);
}
function moveSnake(dx, dy) {
const head = {
x: snakeCoord[0].x + dx,
y: snakeCoord[0].y + dy
};
snakeCoord.unshift(head);
snakeCoord.pop();
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawSnake();
setTimeout(function() {
moveSnake(dx, dy)
}, 100);
}
function keyPress(e) {
let key = e.key;
if (key == "ArrowUp") {
if (snakeCoord[0].y - length_width !== snakeCoord[1].y) {
moveSnake(0, -length_width);
}
} else if (key == "ArrowDown") {
if (snakeCoord[0].y + length_width !== snakeCoord[1].y) {
moveSnake(0, length_width);
}
} else if (key == "ArrowLeft") {
if (snakeCoord[0].x - length_width !== snakeCoord[1].x) {
moveSnake(-length_width, 0);
}
} else if (key == "ArrowRight") {
if (snakeCoord[0].x + length_width !== snakeCoord[1].x) {
moveSnake(length_width, 0);
}
}
}
drawSnake();
document.addEventListener("keyup", keyPress);
<canvas width="500" height="500"></canvas>
答案 0 :(得分:5)
在每次按键上,然后递归地设置新的超时setTimeout(function(){ moveSnake(dx,dy) }, 100);
。最终,越来越多具有争议性的moveSnake
通话。
您应该将超时保存到变量中,并在调用clearTimeout()
之前用moveSnake()
清除它。
答案 1 :(得分:1)
您应该有一个单独的 update 例程,该例程可以更新一帧动画的所有内容,而不是让键盘处理程序调用移动方法来启动自己的计时器循环。您还应该使用requestAnimationFrame
尽可能快地驱动渲染,并让每个渲染请求下一个动画帧。 (请参见提供的链接上的示例。)如果您希望动画慢一点,则可以使用单独的计时器逐步更新场景。 (相信我,总有一天,即使在分步游戏中,您也需要高帧率动画。)
我很无聊,所以我决定对您的代码进行一些更改。
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const length_width = 15;
let snakeCoord = [
{x:300,y:150},
{x:315,y:150},
{x:330,y:150},
{x:345,y:150},
{x:360,y:150},
{x:375,y:150}
];
let snake = {
dir: {dx: -1, dy: 0},
nextDir: [], // buffered direction changes
speed: 5, // steps per second
ratchet: 0
};
function drawSnakePart(snakePart) {
ctx.beginPath();
ctx.fillRect(snakePart.x, snakePart.y, length_width, length_width);
ctx.strokeRect(snakePart.x, snakePart.y, length_width, length_width);
ctx.closePath();
}
function drawSnake() {
snakeCoord.forEach(drawSnakePart);
}
function moveSnake() {
if (snake.nextDir[0]) {
// only change directions if it doesn't result in doubling back on yourself
if (snakeCoord[0].x + snake.nextDir[0].dx * length_width !== snakeCoord[1].x
&& snakeCoord[0].y + snake.nextDir[0].dy * length_width !== snakeCoord[1].y) {
snake.dir = snake.nextDir[0];
}
snake.nextDir.shift(1);
}
const head = {
x: snakeCoord[0].x + snake.dir.dx * length_width,
y: snakeCoord[0].y + snake.dir.dy * length_width
};
snakeCoord.unshift(head);
snakeCoord.pop();
}
function keyPress(e) {
let key = e.key;
if (key == "ArrowUp") {
setDirection(0,-1);
} else if (key == "ArrowDown") {
setDirection(0, 1);
} else if (key == "ArrowLeft") {
setDirection(-1, 0);
} else if (key == "ArrowRight") {
setDirection(1, 0);
}
e.preventDefault();
}
drawSnake();
let lastTime = new Date();
window.requestAnimationFrame(render);
function setDirection(dx, dy) {
snake.nextDir.push({dx, dy}); // overwrite any pending direction changes.
}
function update() {
let now = Date.now();
let elapsed = (now - lastTime) / 1000;
snake.ratchet += elapsed * snake.speed;
while (snake.ratchet >= 1) {
moveSnake();
snake.ratchet -= 1;
}
lastTime = now;
}
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
update();
drawSnake();
window.requestAnimationFrame(render);
}
document.addEventListener("keydown", keyPress);
* {
overflow: hidden
}
<canvas width="500" height="500"></canvas>
使它成为高帧速率渲染循环。采用棘轮机制,使蛇以一定速率不时地离散运动(请参阅snake.speed)。添加了蛇的属性,即蛇的方向。 (请参见snake.dir)。请求方向更改的缓冲按键(请参见snake.nextDir)简化了防止蛇加倍返回自身的逻辑。在每个移动步骤中吃掉一个方向改变。
您仍然需要做蛇自撞。 (假设这是您使用传统的蛇游戏所要达到的目标。)
无论如何,我希望这对您或其他人有帮助。