如何阻止蛇对角运动?

时间:2018-12-28 04:23:35

标签: javascript html css html5 html5-canvas

我正在使用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>

2 个答案:

答案 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)简化了防止蛇加倍返回自身的逻辑。在每个移动步骤中吃掉一个方向改变。

您仍然需要做蛇自撞。 (假设这是您使用传统的蛇游戏所要达到的目标。)

无论如何,我希望这对您或其他人有帮助。