Javascript碰撞引擎运行良好,有数百(可能是数千)碰撞检查?

时间:2015-06-27 05:57:33

标签: javascript performance collision-detection

我正在测试制作基于粉末的物理引擎,我很快就遇到了一个问题:在大约150个粒子后,我的计算机上有太多的碰撞检查。我需要一个物理引擎,它能够加载比这更多的碰撞,可能是成千上万的,其中一些粒子将进行多次碰撞检查。正在检查所有粒子与所有其他粒子的碰撞,它们都是2x2的正方形。有关更好的碰撞系统的任何建议吗?



var ctx = document.getElementById("c").getContext("2d");
var powder = {};
var mouseX = 0;
var mouseY = 0;
var click = false;
var select = 0;
var types = {
  0: "green",
  1: "blue",
  2: "brown",
  3: "grey"
}
document.onmousemove = function(mouse) {
  mouseX = mouse.clientX - document.getElementById('c').getBoundingClientRect().left;
  mouseY = mouse.clientY - document.getElementById('c').getBoundingClientRect().top;
};

function newPowder(x, y, type, id) {
  var temp = {
    x: x,
    y: y,
    type: type,
    checked: false,
  };
  powder[id] = temp;
};

function choose(a, b) {
  if (Math.random() > 0.5) {
    return a
  } else {
    return b
  }
}
document.onkeydown = function(event) {
  if (event.keyCode === 40) { //Down
    select--;
  } else if (event.keyCode === 38) { //Up
    select++;
  } else if (event.keyCode === 32) { //space
    click = true;
  };
  if (select > 3) {
    select = 3;
  } else
  if (select < 1) {
    select = 0
  };
}
document.onkeyup = function(event) {
  if (event.keyCode === 32) {
    click = false
  };
};
var interval = setInterval(function() {
  ctx.clearRect(0, 0, 500, 500);
  if (click) {
    newPowder(Math.round(mouseX / 2) * 2, Math.round(mouseY / 2) * 2, select, Math.random() * 50);
  };
  for (var key in powder) {
    var toContinue = false;
    drawDot(powder[key].x, powder[key].y, types[powder[key].type])
    if (powder[key].type == 3) {
      continue
    }
    if (powder[key].onGround == false) {
      for (var key2 in powder) {
        if (getDistanceBetweenEntity(powder[key], powder[key2]) < 3) {
          if (collisionCheck(powder[key2].x, powder[key2].y, 2, 2, powder[key].x, powder[key].y + 2, 2, 2)) {
            powder[key].onGround = true
            if (powder[key2].type == 2 && !powder[key].checked) {
              powder[key].checked = true;
              powder[key].x += choose(choose(2, -2), 0);
            };
          };
        };
      };
    };
    if (toContinue) {
      continue;
    }
    if (powder[key].x > 500 || powder[key].y > 500) {
      delete powder[key];
      continue;
    }

    if (!powder[key].onGround) {
      powder[key].y += 2;
      checked = false;
    } else if (powder[key].type == 1) {
      powder[key].x += choose(2, -2);
    }
    powder[key].onGround = false;
  };
}, 0);

function rectangleContainsPoint(x1, y1, width, height, x, y) {
  if (width <= 0 || height <= 0) {
    return false;
  }
  return (x >= x1 && x <= x1 + width && y >= y1 && y <= y1 + height);
};

function drawDot(x, y, color) {
  ctx.save();
  ctx.fillStyle = color;
  ctx.fillRect(x, y, 2, 2);
  ctx.restore();
}

function collisionCheck(x1, y1, width1, height1, x2, y2, width2, height2) {
  if (x1 < x2 + width2 && x1 + width1 > x2 && y1 < y2 + height2 && height1 + y1 > y2) {
    return true;
  };
};
getDistanceBetweenEntity = function(entity1, entity2) {
  var vx = entity1.x - entity2.x;
  var vy = entity1.y - entity2.y;
  return Math.sqrt(vx * vx + vy * vy);
};
&#13;
<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <canvas id="c" width="500px" height="500px" style="border:1px solid #000" onclick="click = true"></canvas>
</body>
<script src="script.js" type="text/javascript"></script>

</html>
&#13;
&#13;
&#13;

向上和向下箭头可更改粒子类型。产生粒子的空间。

2 个答案:

答案 0 :(得分:0)

当然,二次复杂度算法不能很好地扩展到更多粒子,您可以检查每个粒子与其他粒子的碰撞。您希望将每个粒子的搜索时间从线性减少到对数或更好。

此处有用的加速结构可以是固定网格或四叉树或K-d树。

不是检查与其他每个粒子的粒子碰撞,而是将粒子放入网格结构或层次结构(四叉树)。

对于网格,只需检查驻留在与要测试碰撞的粒子相同的网格单元格中的粒子(如果粒子的大小非为零,则可能有多个粒子)。

四叉树只是这个概念的一个扩展,带有一个&#34;自适应&#34;形成层次结构的网格。 Kd树是类似的,除了它是一个二叉树而不是一个4-ary树,它在搜索空间的X / Y分区之间循环,并且不必均匀细分(但是,它通常是在这三者中建造最昂贵的。)

这里棘手的部分是,当您的数据非常动态时,就像在这种情况下一样,您必须能够足够快地构建和更新结构。因此,有时像固定网格这样的简单结构可以比四叉树更好地工作,因为即使它提供质量较低的空间查询,它也更容易构建得更快。

在任何情况下,这里的任何类型的加速器都应该使你的算法扩展得更好,我会建议启动器的固定网格,如果你需要更快的碰撞查询以换取增加的时间建立和四叉树更新加速器。

另外考虑到你的程序的性质到目前为止,粒子只是以直线向下的方式受到重力的影响,比上述方法更简单的方法是将粒子按X位置排序(可以使用基数排序来做这是线性时间)。然后,您可以执行二进制搜索,以查找哪些粒子位于X邻域中的第一个,从而将您执行的测试数量缩小到对数。在搜索查询方面,这可能比固定网格更糟糕,因为它具有病态情况,其中所有粒子可能处于相同的X位置(固定网格将同时考虑X和Y),但它是一种快速的方式这样做。

答案 1 :(得分:-1)

First(for in array)比for(var i = 0; i&lt; array.length i ++)慢得多;

无论如何,你正在做蛮力对碰撞检测。这永远不会有效,你需要一个算法只计算&#34;附近&#34;每一步都有颗粒。基本上如果发现一对远离彼此,则不会在接下来的X步骤中计算它们,其中X =距离/ maxSpeed(您世界中的光速)。