【问题标题】:JavaScript: Collision detectionJavaScript:碰撞检测
【发布时间】:2010-03-13 22:57:39
【问题描述】:

碰撞检测在 JavaScript 中是如何工作的?

我不能使用 jQuery 或 gameQuery - 已经在使用原型 - 所以,我正在寻找一些非常简单的东西。我不是要求完整的解决方案,只是指出我正确的方向。

假设有:

<div id="ball"></div>
and
<div id="someobject0"></div>

现在球正在移动(任何方向)。 "Someobject"(0-X) 已经预先定义好了,其中有 20-60 个像这样随机定位:

#someobject {position: absolute; top: RNDpx; left: RNDpx;}

我可以创建一个带有“someobject(X)”位置的数组,并在“球”移动时测试碰撞......类似于:

for(var c=0; c<objposArray.length; c++){
........ and code to check ball's current position vs all objects one by one....
}

但我想这将是一个“菜鸟”解决方案,而且看起来很慢。 有没有更好的?

【问题讨论】:

    标签: javascript collision-detection


    【解决方案1】:

    这是一个非常简单的边界矩形例程。它期望ab 都是具有xywidthheight 属性的对象:

    function isCollide(a, b) {
        return !(
            ((a.y + a.height) < (b.y)) ||
            (a.y > (b.y + b.height)) ||
            ((a.x + a.width) < b.x) ||
            (a.x > (b.x + b.width))
        );
    }
    

    要查看此功能的实际效果,here's a codepen@MixerOID 慷慨制作。

    【讨论】:

    • 谁能澄清我做错了什么?无论“碰撞”如何,它总是返回 true jsbin.com/akeSeDI/2/edit
    • @KryptoniteDove:不幸的是,您不能在此例程中简单地输入 DOM 元素,您需要某种转换(例如使用 jQuery)。此外,您的 div 可能应该使用 position: absolute 而不是 relative。这是您的版本的一个分支:jsbin.com/akeSeDI/3/edit
    • 我为这个美妙的功能创建了可视化 GUI :) codepen.io/mixal_bl4/pen/qZYWOm 你可以把这个添加到你的答案中:)
    • 这需要 jQuerz,因此不是一个有效的答案。 stackoverflow.com/a/35974082/3580261其实是对的,靠这个功能。
    • @eljefedelrodeodeljefe 我的代码不需要 jQuery。它只需要具有给定属性的对象,您可以使用 jQuery 获得,也可以使用函数中描述的方法。
    【解决方案2】:

    首先要有一个实际的函数来检测你是否在球和物体之间发生了碰撞。

    为了性能起见,最好实现一些粗略的碰撞检测技术,例如bounding rectangles,如果检测到碰撞,如果需要更准确的技术,这样你的函数就会运行得更快一点但使用完全相同的循环。

    另一个有助于提高性能的选项是对您拥有的对象进行一些预处理。例如,您可以将整个区域划分为像通用表格一样的单元格,并存储包含在特定单元格中的适当对象。因此,要检测碰撞,您需要检测球所占据的单元格,从这些单元格中获取对象并使用您的碰撞检测功能。

    要加快速度,您可以实现2d-treequadtreeR-tree

    【讨论】:

      【解决方案3】:

      没有 jQuery 的答案,以 HTML 元素作为参数:

      这是一种更好的方法,可以在元素显示在视口上时检查它们的实际位置,即使它们是绝对的、相对的或已通过转换进行操作:

      function isCollide(a, b) {
          var aRect = a.getBoundingClientRect();
          var bRect = b.getBoundingClientRect();
      
          return !(
              ((aRect.top + aRect.height) < (bRect.top)) ||
              (aRect.top > (bRect.top + bRect.height)) ||
              ((aRect.left + aRect.width) < bRect.left) ||
              (aRect.left > (bRect.left + bRect.width))
          );
      }
      

      【讨论】:

      • 其实它应该是上述问题的唯一答案。最佳答案需要 jQuery
      【解决方案4】:

      你可以试试jquery-collision。完全披露:我刚刚写了这个并发布了它。没找到解决办法,就自己写了。

      它允许你做:

      var hit_list = $("#ball").collision("#someobject0");
      

      这将返回所有与“#ball”重叠的“#someobject0”。

      【讨论】:

      • OP 说他们不能使用 jQuery。不过仍然很好,帮助我解决了我自己的问题。
      • @irwin - 嗯,我在说“不能使用 jquery——使用原型”,我很确定你实际上可以混合它们。不过,老实说,我还没有尝试过。很高兴它对你有所帮助!也试试 jquery-draggable-collision ! :-)
      • @PeterAjtai:不,它在 sourceforge 上,为什么?
      • @eruciform sourceforge 几乎无法使用。如果它在 github 上,我敢肯定很多人会喜欢它
      • @PeterAjtai - 今年夏天我可能会将它们全部移至 github。有一段时间只是零更新,所以没有动力去破坏有效的东西。不过,我会尽快添加内容!
      【解决方案5】:

      Mozilla 对此有一篇很好的文章,代码如下所示。

      2D collision detection

      矩形碰撞

      if (rect1.x < rect2.x + rect2.width &&
         rect1.x + rect1.width > rect2.x &&
         rect1.y < rect2.y + rect2.height &&
         rect1.height + rect1.y > rect2.y) {
          // Collision detected!
      }
      

      圆碰撞

      if (distance < circle1.radius + circle2.radius) {
          // Collision detected!
      }
      

      【讨论】:

        【解决方案6】:

        bcm's answer,此时有 0 票,实际上是一个很好的,被低估的答案。它使用旧的Pythagoras 来检测对象何时比它们的组合边界圆更近。简单的碰撞检测通常使用矩形碰撞检测,如果您的精灵往往是矩形的,这很好。如果它们是圆形(或小于矩形),例如球、小行星或任何其他极端角通常是透明的形状,您可能会发现这个高效的例程是最准确的。

        但为了清楚起见,这里有一个更完整的代码实现版本:

        function doCollide(x1, y1, w1, x2, y2, w2) {
            var xd = x1 - x2;
            var yd = y1 - y2;
            var wt = w2 + w1;
            return (xd * xd + yd * yd <= wt * wt);
        }
        

        其中要传入的参数是两个不同精灵对象的 x,y 和 width 值。

        【讨论】:

          【解决方案7】:

          这是我遇到的一个轻量级解决方案 -

          function E() { // Check collision
              S = X - x;
              D = Y - y;
              F = w + W;
              return (S * S + D * D <= F * F)
          }
          

          大小变量是两个对象,(x坐标,y坐标,w宽度)

          来自here

          【讨论】:

          • 据我所知,这个函数假设对象是正方形的(甚至是圆形的?)。此外,上面赫斯基的代码似乎不太可读,如果这还不够,它似乎也更慢(参见:jsperf.com/simple-collision-detection
          【解决方案8】:
          //Off the cuff, Prototype style. 
          //Note, this is not optimal; there should be some basic partitioning and caching going on. 
          (function () { 
              var elements = []; 
              Element.register = function (element) { 
                  for (var i=0; i<elements.length; i++) { 
                      if (elements[i]==element) break; 
                  } 
                  elements.push(element); 
                  if (arguments.length>1)  
                      for (var i=0; i<arguments.length; i++)  
                          Element.register(arguments[i]); 
              }; 
              Element.collide = function () { 
                  for (var outer=0; outer < elements.length; outer++) { 
                      var e1 = Object.extend( 
                          $(elements[outer]).positionedOffset(), 
                          $(elements[outer]).getDimensions() 
                      ); 
                      for (var inner=outer; inner<elements.length; innter++) { 
                          var e2 = Object.extend( 
                              $(elements[inner]).positionedOffset(), 
                              $(elements[inner]).getDimensions() 
                          ); 
                          if (     
                              (e1.left+e1.width)>=e2.left && e1.left<=(e2.left+e2.width) && 
                              (e1.top+e1.height)>=e2.top && e1.top<=(e2.top+e2.height) 
                          ) { 
                              $(elements[inner]).fire(':collision', {element: $(elements[outer])}); 
                              $(elements[outer]).fire(':collision', {element: $(elements[inner])}); 
                          } 
                      } 
                  } 
              }; 
          })(); 
          
          //Usage: 
          Element.register(myElementA); 
          Element.register(myElementB); 
          $(myElementA).observe(':collision', function (ev) { 
              console.log('Damn, '+ev.memo.element+', that hurt!'); 
          }); 
          //detect collisions every 100ms 
          setInterval(Element.collide, 100);
          

          【讨论】:

            【解决方案9】:

            这是一种简单但效率低下的方法,但是当你不需要太复杂的东西或者你没有很多对象时,它是相当合理的。

            否则有许多不同的算法,但其中大多数实现起来相当复杂。

            例如,您可以使用 divide et impera 方法,在该方法中,根据对象的距离对对象进行分层聚类,并为每个聚类提供一个包含聚类中所有项目的边界框。然后您可以检查哪些集群发生碰撞,并避免检查属于未碰撞/重叠的集群的对象对。

            否则,您可以找出一个通用的空间分区算法,以类似的方式拆分对象,以避免无用的检查。这些算法将碰撞检测分为两个阶段:一个粗略阶段,您可以在其中查看哪些对象可能发生碰撞,而精细一个阶段,您可以有效地检查单个对象。 例如,您可以使用QuadTree(维基百科)来制定一个简单的解决方案...

            看看the Wikipedia page。它可以给你一些提示。

            【讨论】:

              【解决方案10】:

              hittest.js; 检测两个透明 PNG 图像(像素)碰撞。

              Demo and download link

              HTML 代码

              <img id="png-object-1" src="images/object1.png" />
              <img id="png-object-2" src="images/object2.png" />
              

              初始化函数

              var pngObject1Element = document.getElementById( "png-object-1" );
              var pngObject2Element = document.getElementById( "png-object-2" );
              
              var object1HitTest = new HitTest( pngObject1Element );
              

              基本用法

              if( object1HitTest.toObject( pngObject2Element ) ) {
                  // Collision detected
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2023-03-18
                • 1970-01-01
                • 1970-01-01
                • 2016-03-22
                • 2011-07-03
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多