首页 > 解决方案 > 蛇头出界

问题描述

我有一个基于用 JavaScript 和画布创建的蛇游戏。

问题是碰撞在块中起作用,但它仅在头部移动到最外面的单元格时才起作用。如何解决这个问题,以使这块头部不会落入框架中?我试图创建一个新头的空块,但它不起作用。

const playground = document.querySelector('canvas');
const ctx = playground.getContext('2d');
const scoreBox = document.querySelector('#scoreBox');
document.addEventListener("keydown", moveSnake);

playground.width = 500;
playground.height = 500;

const gridSize = 20;
const snakeColor = "darkslategray";
const foodColor = "indianred";
const spaceGrid = 2;

let tileCount = playground.width / gridSize;
let snakeVelocityX = 0;
let snakeVelocityY = 0;
let start = true;
let canDeath = false;
let drawSnakeTail = true;
const SnakeLen = [];

const gameFruits = [];
let snakeTail = 3;

function sleep(ms) {
  ms += new Date().getTime();
  while (new Date() < ms) {}
}

function getRandCoord() {
  return Math.floor(Math.random() * tileCount);
}

let snakeX = playground.width / 2 - gridSize / 2;
let snakeY = playground.height / 2 - gridSize / 2;
console.log(snakeX);

function drawGameScene() {
  ctx.fillStyle = "snow";
  ctx.fillRect(0, 0, playground.width, playground.height);
}

function moveSnake(ev) {
  const oldSnakeSpeedX = snakeVelocityX;
  const oldSnakeSpeedY = snakeVelocityY;

  switch (ev.keyCode) {
    case 37:
      snakeVelocityX = -1;
      snakeVelocityY = 0;
      break;
    case 38:
      snakeVelocityX = 0;
      snakeVelocityY = -1;
      break;
    case 39:
      snakeVelocityX = 1;
      snakeVelocityY = 0;
      break;
    case 40:
      snakeVelocityX = 0;
      snakeVelocityY = 1;
      break;

  }
  canDeath = true;
  if ((snakeVelocityX !== 0 && oldSnakeSpeedX !== 0 && snakeVelocityX !== oldSnakeSpeedX) ||
    (snakeVelocityY !== 0 && oldSnakeSpeedY !== 0 && snakeVelocityY !== oldSnakeSpeedY)) {
    snakeVelocityX = oldSnakeSpeedX;
    snakeVelocityY = oldSnakeSpeedY;
  }
}

function drawFruits() {
  for (let i = 0; i < gameFruits.length; i++) {
    const fruit = gameFruits[i];

    ctx.fillStyle = fruit.color;
    ctx.fillRect(fruit.x * gridSize, fruit.y * gridSize, gridSize - 2, gridSize - 2);
  }
}

function SnakeCollisionFoodHandler() {
  for (let i = 0; i < gameFruits.length; i++) {
    const fruit = gameFruits[i];

    if (snakeX === fruit.x && snakeY === fruit.y) {
      snakeTail += fruit.points;

      gameFruits.splice(i, 1);

      spawnFruitTile();

    }
  }
}

function containsObject(obj, list) {
  var i;
  for (i = 0; i < list.length; i++) {
    if (list[i]["x"] == obj["x"] || list[i]["y"] == obj["y"]) {
      return false;
    }
  }
  return true;
}

function spawnFruitTile() {
  let x = getRandCoord();
  y = getRandCoord();
  coors = {
    x,
    y
  };
  if (containsObject(coors, SnakeLen)) {
    if (x === gridSize + 4 || y === gridSize + 4 || x === 0 || y === 0) {
      spawnFruitTile();
    } else {
      gameFruits.push({
        x: x,
        y: y,
        points: 1,
        color: foodColor
      });
    }

  } else {
    spawnFruitTile();
  }

}

function onGameOver() {
  drawSnakeTail = false;
  death_score = snakeTail - 3;
  ctx.font = "20px Arial";
  ctx.fillStyle = '#20201d';
  window.pauseAll = true;
  ctx.fillText("Game over\nScore:" + death_score, (playground.width - 200) / 2, playground.height / 2);
  // alert();
  // location.reload();
}

function drawSnake() {
  console.log(SnakeLen);
  if (drawSnakeTail == true) {
    if (start == true) {
      snakeVelocityY = 1;
      snakeVelocityX = 1;
    }
    snakeX += snakeVelocityX;
    snakeY += snakeVelocityY;
    if (snakeX < 0) {
      snakeX = tileCount - 1;
    }
    if (snakeX > tileCount - 1) {
      snakeX = 0;
    }
    if (snakeY < 0) {
      snakeY = tileCount - 1;
    }
    if (snakeY > tileCount - 1) {
      snakeY = 0;
    }
    drawGameScene();

    ctx.fillStyle = snakeColor;

    for (let i = 0; i < SnakeLen.length; i++) {
      const {
        x,
        y
      } = SnakeLen[i];
      ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
      if (start == true) {
        snakeVelocityY = 0;
        snakeVelocityX = 0;
        setTimeout(start = false, 100);
      }
      if (start == false) {
        if (canDeath == true) {
          if ((x === snakeX && y === snakeY) && (snakeX !== 0 || snakeY !== 0)) {
            ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
            setTimeout(onGameOver, 100);
          }
        } //playground.width
        // console.log(snakeX , playground.width - tileCount, gridSize + spaceGrid, x);
        if (snakeX === gridSize + 4 || snakeY === gridSize + 4 || snakeX === 0 || snakeY === 0) { //
          ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
          setTimeout(onGameOver, 100);
        }

      }

    }
  }

  SnakeLen.push({
    x: snakeX,
    y: snakeY
  });


  while (SnakeLen.length > snakeTail) {
    SnakeLen.shift();
  }
}

function drawBorder() {
  ctx.globalCompositeOperation = "source-over";
  ctx.lineWidth = 40;
  ctx.strokeStyle = "#076826";
  ctx.strokeRect(0, 0, playground.width, playground.height);
}

function drawScore() {
  ctx.font = "20px Arial";
  ctx.fillStyle = '#20201d';
  ctx.fillText("Score:" + (snakeTail - 3), 10, 20);
}

function onGameFrame() {
  drawSnake();
  drawFruits();
  SnakeCollisionFoodHandler();
  drawBorder();
  drawScore();
}

(function onGameInit() {
  spawnFruitTile();
  setInterval(onGameFrame, 100);
}());
canvas {
  border: 0.5px solid black;
  color: black;
  display: flex;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-right: -50%;
  transform: translate(-50%, -50%)
}
<div class="container">
  <canvas width="500px" height="500px"></canvas>
</div>

标签: javascripthtmlcanvashtml5-canvas

解决方案


如果您在onGameOver呼叫前后不设置超时,则游戏将在当前位置结束。我猜这是因为 javascript 能够运行另一个循环,因此在事件运行之前绘制了另一个帧:

if (snakeX === gridSize + 4 || snakeY === gridSize + 4 || snakeX === 0 || snakeY === 0) { //
  ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
  onGameOver(); // <-- removed the setTimeout wrap
}

const playground = document.querySelector('canvas');
const ctx = playground.getContext('2d');
const scoreBox = document.querySelector('#scoreBox');
document.addEventListener("keydown", moveSnake);

playground.width = 500;
playground.height = 500;

const gridSize = 20;
const snakeColor = "darkslategray";
const foodColor = "indianred";
const spaceGrid = 2;

let tileCount = playground.width / gridSize;
let snakeVelocityX = 0;
let snakeVelocityY = 0;
let start = true;
let canDeath = false;
let drawSnakeTail = true;
const SnakeLen = [];

const gameFruits = [];
let snakeTail = 3;

function sleep(ms) {
  ms += new Date().getTime();
  while (new Date() < ms) {}
}

function getRandCoord() {
  return Math.floor(Math.random() * tileCount);
}

let snakeX = playground.width / 2 - gridSize / 2;
let snakeY = playground.height / 2 - gridSize / 2;
console.log(snakeX);

function drawGameScene() {
  ctx.fillStyle = "snow";
  ctx.fillRect(0, 0, playground.width, playground.height);
}

function moveSnake(ev) {
  const oldSnakeSpeedX = snakeVelocityX;
  const oldSnakeSpeedY = snakeVelocityY;

  switch (ev.keyCode) {
    case 37:
      snakeVelocityX = -1;
      snakeVelocityY = 0;
      break;
    case 38:
      snakeVelocityX = 0;
      snakeVelocityY = -1;
      break;
    case 39:
      snakeVelocityX = 1;
      snakeVelocityY = 0;
      break;
    case 40:
      snakeVelocityX = 0;
      snakeVelocityY = 1;
      break;

  }
  canDeath = true;
  if ((snakeVelocityX !== 0 && oldSnakeSpeedX !== 0 && snakeVelocityX !== oldSnakeSpeedX) ||
    (snakeVelocityY !== 0 && oldSnakeSpeedY !== 0 && snakeVelocityY !== oldSnakeSpeedY)) {
    snakeVelocityX = oldSnakeSpeedX;
    snakeVelocityY = oldSnakeSpeedY;
  }
}

function drawFruits() {
  for (let i = 0; i < gameFruits.length; i++) {
    const fruit = gameFruits[i];

    ctx.fillStyle = fruit.color;
    ctx.fillRect(fruit.x * gridSize, fruit.y * gridSize, gridSize - 2, gridSize - 2);
  }
}

function SnakeCollisionFoodHandler() {
  for (let i = 0; i < gameFruits.length; i++) {
    const fruit = gameFruits[i];

    if (snakeX === fruit.x && snakeY === fruit.y) {
      snakeTail += fruit.points;

      gameFruits.splice(i, 1);

      spawnFruitTile();

    }
  }
}

function containsObject(obj, list) {
  var i;
  for (i = 0; i < list.length; i++) {
    if (list[i]["x"] == obj["x"] || list[i]["y"] == obj["y"]) {
      return false;
    }
  }
  return true;
}

function spawnFruitTile() {
  let x = getRandCoord();
  y = getRandCoord();
  coors = {
    x,
    y
  };
  if (containsObject(coors, SnakeLen)) {
    if (x === gridSize + 4 || y === gridSize + 4 || x === 0 || y === 0) {
      spawnFruitTile();
    } else {
      gameFruits.push({
        x: x,
        y: y,
        points: 1,
        color: foodColor
      });
    }

  } else {
    spawnFruitTile();
  }

}

function onGameOver() {
  drawSnakeTail = false;
  death_score = snakeTail - 3;
  ctx.font = "20px Arial";
  ctx.fillStyle = '#20201d';
  window.pauseAll = true;
  ctx.fillText("Game over\nScore:" + death_score, (playground.width - 200) / 2, playground.height / 2);
  // alert();
  // location.reload();
}

function drawSnake() {
  console.log(SnakeLen);
  if (drawSnakeTail == true) {
    if (start == true) {
      snakeVelocityY = 1;
      snakeVelocityX = 1;
    }
    snakeX += snakeVelocityX;
    snakeY += snakeVelocityY;
    if (snakeX < 0) {
      snakeX = tileCount - 1;
    }
    if (snakeX > tileCount - 1) {
      snakeX = 0;
    }
    if (snakeY < 0) {
      snakeY = tileCount - 1;
    }
    if (snakeY > tileCount - 1) {
      snakeY = 0;
    }
    drawGameScene();

    ctx.fillStyle = snakeColor;

    for (let i = 0; i < SnakeLen.length; i++) {
      const {
        x,
        y
      } = SnakeLen[i];
      ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
      if (start == true) {
        snakeVelocityY = 0;
        snakeVelocityX = 0;
        setTimeout(start = false, 100);
      }
      if (start == false) {
        if (canDeath == true) {
          if ((x === snakeX && y === snakeY) && (snakeX !== 0 || snakeY !== 0)) {
            ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
            setTimeout(onGameOver, 100);
          }
        } //playground.width
        // console.log(snakeX , playground.width - tileCount, gridSize + spaceGrid, x);
        if (snakeX === gridSize + 4 || snakeY === gridSize + 4 || snakeX === 0 || snakeY === 0) { //
          ctx.fillRect(x * gridSize, y * gridSize, gridSize - spaceGrid, gridSize - spaceGrid);
          onGameOver();
        }
      }

    }
  }

  SnakeLen.push({
    x: snakeX,
    y: snakeY
  });


  while (SnakeLen.length > snakeTail) {
    SnakeLen.shift();
  }
}

function drawBorder() {
  ctx.globalCompositeOperation = "source-over";
  ctx.lineWidth = 40;
  ctx.strokeStyle = "#076826";
  ctx.strokeRect(0, 0, playground.width, playground.height);
}

function drawScore() {
  ctx.font = "20px Arial";
  ctx.fillStyle = '#20201d';
  ctx.fillText("Score:" + (snakeTail - 3), 10, 20);
}

function onGameFrame() {
  drawSnake();
  drawFruits();
  SnakeCollisionFoodHandler();
  drawBorder();
  drawScore();
}

(function onGameInit() {
  spawnFruitTile();
  setInterval(onGameFrame, 100);
}());
canvas {
  border: 0.5px solid black;
  color: black;
  display: flex;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-right: -50%;
  transform: translate(-50%, -50%)
}
<div class="container">
  <canvas width="500px" height="500px"></canvas>
</div>


推荐阅读