【问题标题】:Snake head goes out of bounds蛇头出界
【发布时间】:2021-08-11 11:31:03
【问题描述】:

我有一个基于 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>

【问题讨论】:

    标签: javascript html canvas html5-canvas


    【解决方案1】:

    如果您不围绕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>

    【讨论】:

      猜你喜欢
      • 2015-02-05
      • 2015-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-15
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多