Как остановить движение змеи по диагонали? - PullRequest
0 голосов
/ 28 декабря 2018

Я работаю над игрой Snake на JavaScript и хочу, чтобы змея двигалась только вертикально или горизонтально, но она продолжает двигаться по диагонали.Например, если я нажимаю вверх, он движется вверх, но затем, если я нажимаю вправо, он будет двигаться по диагонали, а не только вправо.

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 голосов
/ 28 декабря 2018

Вместо того, чтобы обработчик клавиатуры вызывал метод перемещения, который запускает свой собственный цикл таймера, у вас должна быть одна подпрограмма 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) Упрощенная логика предотвращения дублирования змеи на себя.Съешь одну смену направления за шаг.

Тебе все еще нужно сделать самоконтроль змей.(Предполагая, что это то, что вы делаете, с традиционной игрой в змею.)

В любом случае, я надеюсь, что это поможет вам или кому-то еще.

0 голосов
/ 28 декабря 2018

При каждом нажатии клавиши, а затем рекурсивно вы устанавливаете новое время ожидания setTimeout(function(){ moveSnake(dx,dy) }, 100);.В итоге вы получаете все большее количество противоречивых вызовов moveSnake.

Вы должны сохранить тайм-аут в переменной и очистить его с помощью clearTimeout() при нажатии клавиши перед вызовом moveSnake().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...