蛇游戏:如何检查头部是否与自己的身体发生碰撞

时间:2015-12-26 08:36:32

标签: javascript html algorithm canvas 2d-games

你可能玩过Snake游戏,你必须吃食物才能生长,如果你碰到蛇的身体或某些障碍物,你就会失败。第一部分很简单,但后者似乎无法实现。

我试图进行for循环检查我的蛇形阵列的最后一个元素是否与其他部分发生碰撞。我的情况是这样的:如果我的数组中最后一项的x位置大于任何数组项目' x位置,小于x位置及其宽度,依此类推。那没用。

这是我的代码:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="myCanvas" width="200px" height="200px" style="border:1px solid black"/>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var yPos = 20;
var width = 15;
var variable = 1;
var currentDir = 1;
//var xPos = (width+5)*variable;
var xPos = 20;
var myArr = [{myX:xPos,myY:yPos},{myX:xPos,myY:yPos},{myX:xPos,myY:yPos}];
var downPressed = false;
var upPressed = false;
var leftPressed = false;
var rightPressed = false;
var first = [0,20,40,60,80,100,120,140,160,180];
var firstX = Math.floor(Math.random()*10);
var firstY = Math.floor(Math.random()*10);
var okayed = first[firstX];
var notOkayed = first[firstY];
var maths = myArr[myArr.length-1];
function drawFood() {
ctx.beginPath();
ctx.rect(okayed,notOkayed,15,15);
ctx.fillStyle = "red";
ctx.fill();
ctx.closePath();    
}

function drawRectangle() {

    ctx.clearRect(0,0,200,200);
    drawFood();

    for(var i = 0;i<myArr.length;i++) {
    ctx.beginPath();
    ctx.rect(myArr[i].myX,myArr[i].myY,width,15);
    ctx.fillStyle = "blue";
    ctx.fill();
    ctx.closePath();
    }

    requestAnimationFrame(drawRectangle);
}


setInterval("calledin()",100);
function calledin() {
    var secondX = Math.floor(Math.random()*10);
    var secondY = Math.floor(Math.random()*10);
    var newobj = {myX:myArr[myArr.length-1].myX+20,myY:myArr[myArr.length-1].myY};
    var newobjTwo = {myX:myArr[myArr.length-1].myX,myY:myArr[myArr.length-1].myY+20};
    var newobjLeft = {myX:myArr[myArr.length-1].myX-20,myY:myArr[myArr.length-1].myY};
    var newobjUp = {myX:myArr[myArr.length-1].myX,myY:myArr[myArr.length-1].myY-20};
    var okayNewObj = {myX:myArr[1].myX - 20,myY:myArr[1].myY};


if(myArr[myArr.length-1].myX > 180 || myArr[myArr.length-1].myX < 0 || myArr[myArr.length-1].myY > 180 || myArr[myArr.length-1].myY < 0)
        {alert("Game Over");window.location.reload();}

    if(myArr[myArr.length-1].myX > okayed-5 && myArr[myArr.length-1].myX < okayed+20 && myArr[myArr.length-1].myY < notOkayed+20 &&
    myArr[myArr.length-1].myY > notOkayed-5) {
        okayed = first[secondX];
        notOkayed = first[secondY];
        myArr.unshift(okayNewObj);
    }

    if(currentDir == 1) {
    myArr.push(newobj);
    myArr.shift();}

    if(currentDir == 2) {
        myArr.push(newobjTwo);
        myArr.shift();
    }

    if(currentDir == 4) {
        myArr.push(newobjLeft);
        myArr.shift();
    }


    if(currentDir == 3) {
        myArr.push(newobjUp);
        myArr.shift();
    }

     for(var i = 0;i<myArr.length-2;i++) {
     if(myArr[myArr.length-1].myX > myArr[i].myX &&
     myArr[myArr.length-1].myX < myArr[i].myX + 15 && myArr[myArr.length-1].myY > myArr[i].myY && myArr[myArr.length-1].myY > myArr[i].myY + 15)
     {alert("Game over");window.location.reload();} 
 }

}


function downed(e) {
    if(e.keyCode==40) {if(currentDir != 3) {currentDir = 2;}}
    if(e.keyCode==38) {if(currentDir != 2) {currentDir = 3;}}
    if(e.keyCode==39) {if(currentDir != 4) {currentDir = 1;}}
    if(e.keyCode==37) {if(currentDir != 1) {currentDir = 4;}}

}

function upped(e) {
    if(e.keyCode == 40) {downPressed = false;}
}
document.addEventListener("keydown",downed,false);
document.addEventListener("keyup",upped,false);

drawRectangle();
</script>
</body>
</html>

1 个答案:

答案 0 :(得分:3)

假设蛇由一个名为snake的数组表示,其中头部位于索引snake.length - 1。我们必须将头部的位置与指数0snake.length - 2的身体部位的位置进行比较。

如果蛇头与身体部位发生碰撞,则以下代码将okay设置为false。否则,okay仍为true

var head = snake[snake.length - 1],
    x = head.x,
    y = head.y,
    okay = true;           
for (var i = snake.length - 2; i >= 0; --i) {
  if (snake[i].x == x && snake[i].y == y) {
    okay = false;
    break;
  }
}

下面是我修改代码以澄清游戏逻辑并简化许多计算的片段。

我不是直接使用画布坐标,而是使用虚拟网格单元格的列索引x和行索引y来表示每个位置。这使我们可以通过向xy添加1或-1来计算相邻网格位置。在绘制画布时,我们将虚拟坐标乘以单元格大小。

我用变量替换了大部分文字值。例如,我们可以这样做,而不是将画布尺寸设置为200乘200:

canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;

这使我们可以在一个地方更改numColsnumRows来调整整个游戏网格的大小。所有计算都可以解决,因为它们会评估变量而不是使用文字。

除了箭头键之外,我还改变了键事件处理以识别W-A-S-D键的键码。当游戏嵌入在长网页中时,您可能希望使用WASD键,以便在您播放时页面不会向上和向下滚动。 / p>

&#13;
&#13;
var canvas,
    ctx,
    currentDir,
    startX = 1,
    startY = 1,
    startSnakeLength = 3,
    snake,
    cellSize = 18,
    cellGap = 1,
    foodColor = '#a2302a',
    snakeBodyColor = '#2255a2',
    snakeHeadColor = '#0f266b',
    numRows = 10,
    numCols = 10,
    canvasWidth = numCols * cellSize,
    canvasHeight = numRows * cellSize;

var food = {};

function placeFood() {
  // Find a random location that isn't occupied by the snake.
  var okay = false;
  while (!okay) {
    food.x = Math.floor(Math.random() * numCols);
    food.y = Math.floor(Math.random() * numRows);
    okay = true;
    for (var i = 0; i < snake.length; ++i) {
      if (snake[i].x == food.x && snake[i].y == food.y) {
        okay = false;
        break;
      }
    }
  }
}

function paintCell(x, y, color) {
  ctx.fillStyle = color;
  ctx.fillRect(x * cellSize + cellGap,
               y * cellSize + cellGap,
               cellSize - cellGap,
               cellSize - cellGap);
}

function paintCanvas() {
  ctx.clearRect(0, 0, canvasWidth, canvasHeight);
  paintCell(food.x, food.y, foodColor);
  var head = snake[snake.length - 1];
  paintCell(head.x, head.y, snakeHeadColor);
  for (var i = snake.length - 2; i >= 0; --i) {
    paintCell(snake[i].x, snake[i].y, snakeBodyColor);
  }
}
  
function updateGame() {
  var head = snake[snake.length - 1],
      x = head.x,
      y = head.y;

  // Move the snake.
  var tail = snake.shift();
  switch (currentDir) {
    case 'up': 
      snake.push(head = { x: x, y: y - 1 });
      break;
    case 'right': 
      snake.push(head = { x: x + 1, y: y });
      break;
    case 'down': 
      snake.push(head = { x: x, y: y + 1 });
      break;
    case 'left': 
      snake.push(head = { x: x - 1, y: y });
      break;
  }
  paintCanvas();
  x = head.x;
  y = head.y;

  // Check for wall collision.
  if (x < 0 || x >= numCols || y < 0 || y >= numRows) {
    stopGame('wall collision');
    return;
  }

  // Check for snake head colliding with snake body.
  for (var i = snake.length - 2; i >= 0; --i) {
    if (snake[i].x == x && snake[i].y == y) {
      stopGame('self-collision');
      return;
    }
  }

  // Check for food.
  if (x == food.x && y == food.y) {
    placeFood();
    snake.unshift(tail);
    setMessage(snake.length + ' segments');
  }
}

var dirToKeyCode = {  // Codes for arrow keys and W-A-S-D.
      up: [38, 87],
      right: [39, 68],
      down: [40, 83],
      left: [37, 65]
    },
    keyCodeToDir = {};  // Fill this from dirToKeyCode on page load.

function keyDownHandler(e) {
  var keyCode = e.keyCode;
  if (keyCode in keyCodeToDir) {
    currentDir = keyCodeToDir[keyCode];
  }
}

function setMessage(s) {
  document.getElementById('messageBox').innerHTML = s;
}

function startGame() {
  currentDir = 'right';
  snake = new Array(startSnakeLength);
  snake[snake.length - 1] = { x: startX, y: startY };
  for (var i = snake.length - 2; i >= 0; --i) {
    snake[i] = { x: snake[i + 1].x, y: snake[i + 1].y + 1 };
  }
  placeFood();
  paintCanvas();
  setMessage('');
  gameInterval = setInterval(updateGame, 200);
  startGameButton.disabled = true;
}

function stopGame(message) {
  setMessage(message + '<br> ended with ' + snake.length + ' segments');
  clearInterval(gameInterval);
  startGameButton.disabled = false;
}

var gameInterval,
    startGameButton;

window.onload = function () {
  canvas = document.getElementById('gameCanvas'),
  ctx = canvas.getContext('2d');
  canvas.width = numCols * cellSize;
  canvas.height = numRows * cellSize;
  Object.keys(dirToKeyCode).forEach(function (dir) {
    dirToKeyCode[dir].forEach(function (keyCode) {
      keyCodeToDir[keyCode] = dir;
    })
  });
  document.addEventListener("keydown", keyDownHandler, false);
  startGameButton = document.getElementById('startGameButton');
  startGameButton.onclick = startGame;
}
&#13;
body {
  font-family: sans-serif;
}
#gameCanvas {
  border: 1px solid #000;
  float: left;
  margin-right: 15px;
}
#startGameButton, #messageBox {
  font-size: 16px;
  margin-top: 15px;
}
#messageBox {
  line-height: 24px;
}
&#13;
<canvas id="gameCanvas"></canvas>

<button id="startGameButton">Start game</button>

<div id="messageBox"></div>
&#13;
&#13;
&#13;