【问题标题】:JavaScript collision detection with objects in a multi-dimensional arrayJavaScript 碰撞检测与多维数组中的对象
【发布时间】:2017-06-18 17:17:10
【问题描述】:

我目前正在使用 p5.js 编写 Pac-man 克隆,但遇到了问题。我创建了一个函数,它通过使用多维数组绘制地图,在 1 所在的位置绘制一个墙块,在 0 所在的位置绘制任何内容。

这很好用,但是我正在努力检测玩家和墙壁之间的碰撞。我尝试使用 for 循环遍历数组,检查 x 和 y 坐标以查看是否存在碰撞,但它根本没有注册。这是我用来检测碰撞的代码:

for(i=0;i<walls.length;i++){
walls[i].draw();

if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){
  console.log('collision')

}

}

我看不出问题出在哪里,因为它似乎在其他程序中也有效。 这在 Draw() 函数中运行,这意味着它每秒循环 30 次。

这是完整的代码,以防问题出在其他地方:

var gridsize = 20;
var walls = [];
var dots = [];
var player;
var score =0;
var maps = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
            [1,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1],
            [1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1],
            [1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1],
            [1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
            [1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1],
            [1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1],
            [1,0,1,0,0,1,0,0,0,0,1,0,0,1,0,1],
            [1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,1],
            [1,0,1,0,0,0,0,2,0,0,0,0,0,1,0,1],
            [1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1],
            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
            [1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1],
            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]];
function setup(){
  createCanvas(320,320);
  frameRate(30);
  createMap();
}
function draw(){
  background(51);

  for(i=0;i<walls.length;i++){
    walls[i].draw();

    if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y 
> walls[i].y && player.y < walls[i].y + gridsize){
      console.log('collision')

    }
  }
  fill('white');
  text('Score: ' + score, 5,10);
  for(i=0;i<dots.length;i++){
    if(player.x == dots[i].x && player.y == dots[i].y && dots[i].collect == 
false){
      dots[i].collect = true;
      score++;
    }
    dots[i].draw();

  }
  player.update();
  player.draw();

}


 function Block(x,y){
  this.x = x;
  this.y = y;

  this.draw = function(){
    fill('black');
    rect(this.x,this.y,gridsize,gridsize);
  }
}

function Dot(x,y){
  this.x = x;
  this.y = y;
  this.collect = false;
  this.draw = function(){
    if(!this.collect){
      fill('yellow');
      ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3);
    }else if(this.collect){
      noFill();
      noStroke();
      ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/3,gridsize/3);
    }
   }


}
function Player(x,y){
  this.x = x;
  this.y = y;

  this.update = function(){
    if(keyIsDown(UP_ARROW) && frameCount%5 == 0){
      player.y -= gridsize;
    }
    if(keyIsDown(DOWN_ARROW) && frameCount%5 == 0){
      player.y += gridsize;
    }
    if(keyIsDown(LEFT_ARROW) && frameCount%5 == 0){
      player.x -= gridsize;
    }
    if(keyIsDown(RIGHT_ARROW) && frameCount%5 == 0){
      player.x += gridsize;
    }
  }

  this.draw = function(){
    fill('blue');
    ellipse(this.x+gridsize/2,this.y+gridsize/2,gridsize/1.2,gridsize/1.2);
  }

}

function createMap(){
  for(i=0;i<maps.length;i++){
    for(j=0;j<maps[i].length;j++){
      if (maps[i][j] == 1){
        walls.push(new Block(j*gridsize,i*gridsize));
      }else if(maps[i][j] == 0){
        dots.push(new Dot(j*gridsize,i*gridsize))
      }else if(maps[i][j] = 2){
        player = new Player(j*gridsize,i*gridsize)
      }

    }
  }
}

我认为问题在于墙壁存储在一个数组中,但是我已经完成了非常相似的程序,其中相同的代码可以工作。

【问题讨论】:

    标签: javascript arrays canvas collision p5.js


    【解决方案1】:

    PacMan 控件

    检查此类地图的最佳方法是使用玩家的输入。

    玩家必须与墙壁对齐,因此假设玩家的位置是相对于左上角的,并且玩家的宽度和深度是一个地图单位。

    按键输入请求移动方向dxdy 保持一次可能不止一个的方向。如果dxdy 不为0,则首先检查玩家是否与通道对齐,如果是,则检查行进方向是否有块。如果玩家没有排队或被阻挡,则将移动变量设置为 0

    在检查 x 和 y 方向后,如果 dxdy 有一个值,那么这一定是一个有效的移动。

    代码更改

    从主循环中移除玩家碰撞检查代码,并以当前地图为 2D 原图调用玩家更新函数。

    player.update(maps);  // move the player
    

    更改播放器和更新功能

    function Player(x,y){
      this.x = x;
      this.y = y;
      var dx = 0;  // hold current movement
      var dy = 0;
      const speed = 1; // per Frame pixel speed best as an integer (whole number) and evenly divisible into gridSize
    
      // need the map so that must be passed to the update function 
      this.update = function(map){
    
        // assuming keys are held to move up to stop
        dx = 0;  // stop by default
        dy = 0; 
        if (keyIsDown(UP_ARROW))   { dy = -speed }
        if (keyIsDown(DOWN_ARROW)) { dy = speed }
        if (keyIsDown(LEFT_ARROW)) { dx = -speed }
        if (keyIsDown(RIGHT_ARROW)){ dx = speed }
    
        // get player map coords
        var x = Math.floor(this.x / gridSize); // get map coord
        var y = Math.floor(this.y / gridSize); // get map coord
        // the two if statement are best aas function
        // so you can select which one to call first. Depending on the latest
        // new keys down and if the result allows movement in that
        // direction then set the other direction to zero.
        if (dy !== 0) { // is moving up or down?
            if (this.y % gridsize === 0) { // only if lined up
                if (dy > 0){ // is moving down
                     if (map[y + 1][x] === 1) { // down blocked 
                         dy = 0;
                     }
                }else if (map[y - 1][x] === 1) { // up blocked
                     dy = 0;
                }
    
            } else { // block move if not lined up with passage 
                dy = 0;
            }
        }
        if(dx !== 0){ // is moving left or right?
            if (this.x % gridsize === 0) { // only if lined up
                if (dx > 0) { // is moving right 
                     if (map[y][x + 1] === 1) { // right blocked 
                         dx = 0;
                     }
                } else if (map[y][x - 1] === 1) { // left blocked
                     dx = 0;
                }
            } else { // block move if not lined up with passage 
                dx = 0;
            }
         }
         // could have two moves, but can only move left right or up down
         // you need to add some input smarts to pick which one
         // this just favours up down
         if(dy !== 0) { dx = 0 };
    
         // only valid moves will come through the filter above.
         // so move the player.
         this.x += dx;
         this.y += dy;
      }
    

    添加更多智能

    注意我已经改变了玩家移动的方式,我设置了每帧的速度(1 像素),它必须是gridSize 的偶数除数。

    上面的代码是最简单的实现。这种类型的游戏需要一些额外的控制技巧。您应该检查最新键的方向。即,如果向下和向右移动的玩家被按下,那么向右移动应该具有优先权。如果玩家左右移动被按下,那么你应该向左移动,而不是继续向右移动。

    附加功能

    在查看这个问题时,我想可视化地图。作为数组的映射很难创建和修改,而且很难发现错误。作为一组在运行时转换为数组的字符串要容易得多。

    因为我已经完成了转换,所以没有必要浪费它。 maps 与原始数组相同,但现在更易于阅读和更改。

    const maps = [
        "################",
        "#              #",
        "# ## ###### ## #",
        "# ##        ## #",
        "#    ######    #",
        "####        ####",
        "#    ##  ##    #",
        "# #  #    #  # #",
        "# #  #    #  # #",
        "# #  ######  # #",
        "# #    2     # #",
        "# ### #### ### #",
        "#              #",
        "#   ########   #",
        "#              #",
        "################"
    ].map(row => row.split("").map(c => c === "#" ? 1 : c === " " ? 0 : 2));
    

    【讨论】:

    • 哦,太好了,.map 函数然后将其转换为多维数组?
    • @corrigan_sam 没有看到您的评论。我也添加了一个答案,稍微复杂一点,但最适合这种类型的游戏。
    • @corrigan_sam 哦,忘了是的。映射函数转换为您在代码中拥有的相同数组。
    • 干杯人!看起来不错,我想把它扔进去玩一玩
    【解决方案2】:

    当您可以使用基于网格的碰撞检测时,我不太确定您为什么要使用矩形-矩形碰撞检测。你可以直接使用数组。

    但是由于您使用的是矩形-矩形碰撞,这条线看起来有点偏离:

    if(player.x > walls[i].x && player.x < walls[i].x + gridsize && player.y > walls[i].y && player.y < walls[i].y + gridsize){
    

    您正在检查播放器的左边缘是否在墙内以及播放器的上边缘是否在墙内。但是您没有检测到其他边缘。通常你会想做这样的事情:

    if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){
    

    注意if 语句如何检查所有边缘,而不仅仅是顶部和左侧。但就像我说的,你最好只使用网格碰撞检测,因为你已经有了一个墙网格。

    无耻的自我推销:here 是一个碰撞检测教程。它是为 Processing 编写的,但所有内容都应该直接转换为 P5.js。

    【讨论】:

    • 啊,是的,我记得以前犯过那个错误。我会看看你的教程并尝试使用网格,从没想过这一点,尽管现在你已经说过它是如此明显!干杯。
    【解决方案3】:

    如果玩家在此处不是精灵,则此处适合进行直点碰撞检测。

      // point in rect collision detection
        function pointInRect (x, y, rect) {
    
                  return inRange(x, rect.x, rect.x + gridsize) &&
                         inRange(y, rect.y, rect.y + gridsize);
        }
         // check a value is in range or not
        function inRange (value, min, max) {
                  return value >= Math.min(min, max) && value <= Math.max(min, max);
        }
    
         // checking player is hitting the wall or not
         if(pointInRect(player.x,player.y,walls[i].x,walls[i].y))
         {
           console.log('collision')
         }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多