Snake游戏:如何更新2D阵列中的位置

时间:2015-08-20 18:00:56

标签: javascript jquery html css graphics

我没有2D阵列的经验所以我开始尝试以便学习。有一件事导致了另一件事,我开始构建经典的蛇游戏。我被困在添加" tail", ie ,在2D数组中放置一个值,然后让它模仿" head&#34的移动的过程中;遍历数组时的值。我尝试过一些东西,但没有什么值得发布,因为它们没有效果。

这是我的小提琴:

https://jsfiddle.net/cshanno/5ht4that/7/

和代码段:



var board = [];
var firstDraw = true;
var movement;
var direction = 'up';
var speed = 300;
var gameinprogress = false;

//Generate the 2d array that is the game board
function buildBoard() {
    for (i = 0; i < 50; i++) { //row
        var arr = [];
        for (x = 0; x < 50; x++) { //column
            if (i === 0 || i === 49) { //if top or bottom
                arr[x] = 1;
            } else {
                if (x === 0 || x === 49) {
                    arr[x] = 1;
                } else if (firstDraw) {
                    if (x === 25 && i === 25) {
                        arr[x] = 2;
                        firstDraw = false;
                    }
                } else {
                    arr[x] = 0;
                }
            }
        }
        board.push(arr);
    }
    drawBoard();
}
//Draw the board from the 2d array
function drawBoard(){
    $('.food').remove();
    for (i = 0; i < 50; i++) {
        for (x = 0; x < 50; x++) {
            if (board[i][x] === 2) {
                $('.board')
                    .append(
                    '<div id="head" class="border player" style="top: ' + 5 * i + 'px;' + 
                    'left: ' + 5 * x + 'px;"></div>');
            }
            if (board[i][x] === 3) {
                $('.board')
                    .append(
                    '<div class="border food" style="top: ' + 5 * i + 'px;' + 'left: ' + 5 * x + 'px;"></div>');
            }
        }
    }
}
//Randomly position food on the board
function drawfood() {
    $('.food').remove();
    row = Math.floor((Math.random() * 48) + 1);
    col = Math.floor((Math.random() * 48) + 1);

    board[row][col] = 3;
}
//Move according to arrow keypress
function move() {
    var nextprop;
    for (i = 1; i < 49; i++) {
        for (x = 1; x < 49; x++) {
            var prop = board[i][x];
            if (prop === 2) {
                switch (direction) {
                    case 'left':
                        nextprop = board[i][x - 1];
                        board[i][x - 1] = 2;
                        break;
                    case 'up':
                        nextprop = board[i - 1][x];
                        board[i - 1][x] = 2;
                        break;
                    case 'right':
                        nextprop = board[i][x + 1];
                        board[i][x + 1] = 2;
                        break;
                    case 'down':
                        nextprop = board[i + 1][x];
                        board[i + 1][x] = 2;
                        break;
                }
                if (nextprop === 1) {
                    reset();
                    $('.board').text('YOU LOSE');
                }
                if (nextprop === 3) {
                    drawfood();
                    if (speed > 100) speed -= 20;
                }
                board[i][x] = 0;
    			$('.player').remove();
                drawBoard();
            }
        }
    }
}
//Reset game board
function reset() {
    gameinprogress = false;
    clearInterval(movement);
    board.length = 0;
    firstDraw = true;
    speed = 300;
    $('.player').remove();
    $('.food').remove();
    buildBoard();
    drawBoard(); 
}

/*
===================
Button Click Events
===================
*/
//Toggle start, stop button on click
$('#start, #stop').click(function () {
    $('#start, #stop').toggle();
});
//Start game (generate food, draw the board, and start movement)
$('#start').click(function () {
    gameinprogress=true;
    drawfood();
	drawBoard();
    movement = setInterval(function () {
        move('up');
    }, speed);
});
//Stop the game (reset the board, remove game lost text if there)
$('#stop').click(function () {
    $('.board').text('');
	reset();
});
//Check which arrow key was pressed, move accordingly
//if (direction === '___') break; implemented to prevent 'cheat' movement
$(document).keyup(function (e) {
    if (!gameinprogress) return;
    var key = e.which;
    switch (key) {
        case 37:
            if (direction === 'left') break;
            direction = 'left';
            clearInterval(movement);
            movement = setInterval(function () {
                move();
            }, speed);
            move();
            break;
        case 38:
            if (direction === 'up') break;
            direction = 'up';
            clearInterval(movement);
            movement = setInterval(function () {
                move();
            }, speed);
            move('up');
            break;
        case 39:
            if (direction === 'right') break;
            direction = 'right';
            clearInterval(movement);
            movement = setInterval(function () {
                move();
            }, speed);
            move();
            break;
        case 40:
            if (direction === 'down') break;
            direction = 'down';
            clearInterval(movement);
            movement = setInterval(function () {
                move('down');
            }, speed);
            move();
            break;
    }
});




/// Main
buildBoard();
drawBoard();
&#13;
.container {
    text-align:center;
    width:95%;
    height:95%;
    position:absolute;
    background-color:lightgray;
}
.board {
    position:relative;
    height:250px;
    width:250px;
    margin: 0 auto;
    border: 5px  black solid;
}
.border {
    box-sizing: border-box;
    position:absolute;
    display:inline-block;
    border: 1px solid black;
    width:5px;
    height:5px;
    margin:0;
}
.player {
    background-color: lightgreen;
}
.food {
    background-color: orange;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='container'>
    <div class='board'></div>
    <button id='start' type='button'>Start</button>
    <button id='stop' type='button' hidden>Stop</button>
</div>
&#13;
&#13;
&#13;

游戏网格可能如下所示:

var arr = [
    [0,0,0,0,0]
    [0,0,3,3,0]
    [0,0,0,0,0]
]

如果我将前3个值移动到左侧,那么它将在数组中的位置[1] [1],那么我将如何进行检查,并将第二个3值移动到位置[1] [2 ]?请记住,&#39; 3&#39;价值观会不断增长。

2 个答案:

答案 0 :(得分:2)

我建议你把蛇分成几个部分,每个部分都跟踪它在游戏网格中的位置。您可以使用对象表示每个部分。整条蛇由一系列这样的物体代表。

每个部分必须至少知道两件事:

  • 我目前在网格中的位置是什么?
  • 我要关注哪个部分?

当你想要更新蛇时,你从头部开始,这是蛇的第一部分。它没有跟随任何其他部分。接下来你看看头部的追随者,然后看那个部分的追随者,依此类推,直到你到达尾巴。

对于每个部分,您想回答以下问题:

  • 我的下一个职位是什么?

对于头部,下一个位置由用户的按键(或缺少按键)确定。对于其他每个部分,我们必须考虑其领导者会发生什么,这是它所遵循的部分:

  • 如果我的领导人没有搬到任何地方,我也不会动。
  • 如果我的领导人搬到新职位,我将转到目前的职位。

在分析整条蛇之前,避免更新每个部分的位置非常重要。您必须在每个更新周期中执行两次传递:

  1. 第一遍:从头部开始按顺序考虑每个部分,并确定其下一个位置是什么。

  2. 第二遍:将每个部分的位置更新为您在第一轮中计算的新职位。

  3. 下面是一个蛇的简单实现,它响应用户输入而移动。我认为你对Game.moveSnake函数最感兴趣,它演示了更新蛇的两遍方法。

    &#13;
    &#13;
    var Game = {
      numRows: 12,
      numCols: 32,
      numSections: 10,
      cellSize: 13
    };
    
    Game.refreshDisplay = function () {
      var canvas = Game.canvas,
          context = Game.context,
          cellSize = Game.cellSize;
      context.clearRect(0, 0, canvas.width, canvas.height);
      var snake = Game.snake,
          rgbHead = [70, 161, 52],
          rgb = rgbHead.slice(),
          numSections = Game.numSections;
      context.lineWidth = cellSize / 2;
      context.strokeStyle = '#acc9b2';
      for (var i = 1; i < numSections; ++i) {
        var section = snake[i],
            x = cellSize / 2 + section.c * cellSize,
            y = cellSize / 2 + section.r * cellSize;
        for (var j = 0; j < 3; j += 2) {
          rgb[j] += Math.round((255 - rgb[j]) / 12);
        }
        context.fillStyle = 'rgb(' + rgb.join(', ') + ')';
        context.beginPath();
        context.arc(x, y, cellSize / 3, 0, 2 * Math.PI);
        context.stroke();
        context.fill();
      }
      var head = snake[0],
          x = cellSize / 2 + head.c * cellSize,
          y = cellSize / 2 + head.r * cellSize;
      context.fillStyle = 'rgb(' + rgbHead.join(', ') + ')';
      context.beginPath();
      context.arc(x, y, cellSize / 1.6, 0, 2 * Math.PI);
      context.fill();
    };
    
    // The head wants to move from position (r, c) to position (r + dr, c + dc).
    Game.moveSnake = function (dr, dc) {
      // Prevent the head from moving out of bounds.
      var snake = Game.snake,
          head = snake[0],
          numRows = Game.numRows, numCols = Game.numCols,
          nextR = head.r + dr, nextC = head.c + dc;
      if (nextR < 0 || nextR >= numRows || nextC < 0 || nextC >= numCols) {
        return;
      }
      // First pass: calculate each section's next position.
      head.nextR = nextR;
      head.nextC = nextC;
      var numSections = Game.numSections;
      for (var i = 1; i < numSections; ++i) {
        var section = snake[i],
            leader = section.leader;
        section.nextR = leader.r;
        section.nextC = leader.c;
        // Prevent the head from running over another snake section.
        if (head.nextR == section.nextR && head.nextC == section.nextC) {
          return;
        }
      }
      // Second pass: update each section's position.
      var grid = Game.grid,
          tail = snake[numSections - 1];
      for (var i = 0; i < numSections; ++i) {
        var section = snake[i];
        section.r = section.nextR;
        section.c = section.nextC;
      }
      Game.refreshDisplay();
    };
    
    window.onload = function () {
      // Prepare the game canvas.
      var container = Game.container = document.getElementById('grid'),
          canvas = Game.canvas = document.createElement('canvas'),
          context = Game.context = canvas.getContext('2d'),
          numRows = Game.numRows,
          numCols = Game.numCols,
          cellSize = Game.cellSize;
      canvas.width = numCols * cellSize;
      canvas.height = numRows * cellSize;
      container.appendChild(canvas);
      var numSections = Game.numSections,
          snake = Game.snake = new Array(snake);
      // Initialize the head.
      snake[0] = {
        r: Math.floor(numRows / 2),
        c: Math.floor(numCols / 2)
      };
      // Make each successive section follow the previous one.
      for (var i = 1; i < numSections; ++i) {
        var leader = snake[i - 1];
        snake[i] = {
          leader: leader,
          r: leader.r,
          c: leader.c - 1
        };
      }
      $(window).keydown(function (event) {
        var code = event.which;
        if (code == 37 || code == 65) {
          Game.moveSnake(0, -1);
        } else if (code == 39 || code == 68) {
          Game.moveSnake(0, 1);
        } else if (code == 38 || code == 87) {
          Game.moveSnake(-1, 0);
        } else if (code == 40 || code == 83) {
          Game.moveSnake(1, 0);
        }
      });
      Game.refreshDisplay();
    };
    &#13;
    #instructions {
      font-family: sans-serif;
      font-size: 14px;
      color: #666;
    }
    #grid {
      display: inline-block;
      border: 2px solid #eee;
      background: #ffe;
    }
    &#13;
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div id="instructions">Click here, then press W-A-S-D or arrow keys to move the snake.</div>
    
    <div id="grid"></div>
    &#13;
    &#13;
    &#13;

答案 1 :(得分:0)

你必须以某种方式代表蛇。例如。 [{X:1,Y:1},{X:2,Y:1},{X:2,Y:2}]。当蛇移动时,使用推/移或不移位/弹出阵列方法添加新的头部位置并可选择移除尾部位置。
我认为在2d地图数组中另外代表蛇不是一个好主意,它没有带来胜利。