Javascript冲突引擎运行良好,有数百(可能数千)次冲突检查?

Javascript冲突引擎运行良好,有数百(可能数千)次冲突检查?,javascript,performance,collision-detection,Javascript,Performance,Collision Detection,我正在测试一个基于粉末的物理引擎,我很快就遇到了一个问题:在大约150个粒子之后,我的计算机要加载的碰撞检查太多了。我需要一个物理引擎,它能够加载更多的碰撞,可能有数千次,其中一些粒子会进行多次碰撞检查。正在同时检查所有粒子是否与所有其他粒子发生碰撞,它们都是2x2正方形。对更好的碰撞系统有什么建议吗 var ctx=document.getElementByIdc.getContext2d; var粉末={}; var-mouseX=0; var-mouseY=0; var click=fa

我正在测试一个基于粉末的物理引擎,我很快就遇到了一个问题:在大约150个粒子之后,我的计算机要加载的碰撞检查太多了。我需要一个物理引擎,它能够加载更多的碰撞,可能有数千次,其中一些粒子会进行多次碰撞检查。正在同时检查所有粒子是否与所有其他粒子发生碰撞,它们都是2x2正方形。对更好的碰撞系统有什么建议吗

var ctx=document.getElementByIdc.getContext2d; var粉末={}; var-mouseX=0; var-mouseY=0; var click=false; var-select=0; 变量类型={ 0:绿色,, 1:蓝色, 2:布朗,, 3:灰色 } document.onmousemove=函数鼠标{ mouseX=mouse.clientX-document.getElementById'c'.getBoundingClientRect.left; mouseY=mouse.clientY-document.getElementById'c'.getBoundingClientRect.top; }; 函数newx,y,type,id{ 变量温度={ x:x, y:y, 类型:类型, 勾选:假, }; 粉末[id]=温度; }; 函数选择sea,b{ 如果Math.random>0.5{ 归还 }否则{ 返回b } } document.onkeydown=functionevent{ 如果event.keyCode==40{//Down 选择-; }如果event.keyCode==38{//Up,则为else 选择++; }如果event.keyCode==32{//空格,则为else 单击=真; }; 如果选择>3{ 选择=3; }否则 如果选择<1{ 选择=0 }; } document.onkeyup=functionevent{ 如果event.keyCode==32{ 单击=false }; }; var interval=setIntervalfunction{ ctx.clearRect0,0500500; 如果点击{ newpowdMath.roundmouseX/2*2,Math.roundmouseY/2*2,select,Math.random*50; }; 用于粉末中的var键{ var toContinue=false; drawDotpowder[key].x,powder[key].y,类型[powder[key].type] 如果粉末[key],类型==3{ 持续 } 如果粉末[key].onGround==false{ 对于var key2 in粉末{ 如果getDistanceBetweenEntitypowder[key],则粉末[key2]<3{ 如果碰撞检查粉末[key2].x,粉末[key2].y,2,2,粉末[key].x,粉末[key].y+2,2,2{ 粉末[key].onGround=true 如果粉末[key2]。类型==2&&!粉末[key]。已选中{ 粉末[键]。选中=真; 粉末[key].x+=choosechoose2,-2,0; }; }; }; }; }; 如果继续{ 持续 } 如果粉末[key].x>500 | |粉末[key].y>500{ 删除粉末[钥匙]; 持续 } 如果!粉末[钥匙]。继续{ 粉末[key].y+=2; 选中=错误; }如果粉末[key],则为else。类型==1{ 粉末[键].x+=选择2,-2; } 粉末[key].onGround=false; }; }, 0; 函数矩形包含点X1、y1、宽度、高度、x、y{ 如果宽度y2{ 返回true; }; }; getDistanceBetweenEntity=functionentity1,entity2{ var vx=entity1.x-entity2.x; var vy=实体1.y-实体2.y; 返回Math.sqrtvx*vx+vy*vy; };
数组中的第一个值比var i=0慢得多;i
无论如何,您正在执行蛮力对碰撞检测。这永远不会是有效的,你需要一个算法,每一步只计算附近的粒子。基本上,如果发现一对粒子彼此相距较远,则不会在接下来的X步中计算它们,其中X=距离/最大光速。自然,二次复杂度算法不会很好地适用于更多粒子,在这些粒子中,检查每个粒子与其他粒子的碰撞。您希望将每个粒子的搜索时间从线性减少到对数或更好

这里有用的加速结构可以是固定网格、四叉树或K-d树

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

对于栅格,只需检查与正在测试碰撞的粒子位于同一栅格单元中的粒子,如果粒子大小非零,则可能存在多个粒子

四叉树只是这个概念的一个扩展,它带有一个自适应网格,形成了一个层次结构。K-d树与之类似,只是它是一个二叉树,而不是一个在搜索空间的X/Y分区之间循环的四叉树,并且不需要均匀地细分。然而,在这三个分区中,它通常是构造成本最高的

这里需要技巧的部分是,当数据非常动态时(如本例中),您必须能够足够快地构建和更新结构。因此,有时像固定网格这样的简单结构在这里比四叉树工作得更好,因为它更容易更快地构建,即使它提供的空间查询质量较低

在任何情况下,这里的任何一种加速器都应该使您的算法更好地扩展,并且 我建议首先使用固定网格,如果您需要更快的碰撞查询,则使用四叉树,以增加构建和更新加速器的时间


此外,考虑到目前为止程序的性质,粒子仅以直线向下的方式受重力影响,比上述方法更简单的方法是按X位置对粒子进行排序。可以使用基数排序在线性时间内完成此操作。然后,您可以进行二进制搜索,以找到哪些粒子位于X附近的第一位,从而将正在执行的测试数量缩小到对数。就搜索查询而言,这可能比固定网格更糟糕,因为它存在所有粒子可能位于同一X位置的病理情况。固定网格可以同时考虑X和Y,但这是一种快速的方法。

我认为我已经有了类似的方法。如果粒子与相关粒子有一定距离,则不会检查碰撞。它将只检查最大距离为3px的区域或相邻区域。如果GetDistanceBetweenTypowder[key],powder[key2]<3[do collision check],这是一个微观优化,我不确定计算点之间的平方根向量距离是否有助于避免矩形碰撞检查,但您的算法仍然是蛮力成对碰撞检测。如果你甚至必须在每个粒子之间循环,以对抗其他粒子,那么这就是蛮力。在这种情况下,它就像一个冒泡排序——你想先使用一些算法效率更高的东西,比如快速排序,甚至是合并排序,然后进行微优化。否则,粒子越多,速度就会爆炸性地变慢。@Fuzzyzilla:getDistanceBetweenEntity函数的代价就越高,因为它使用sqrt和乘法。对于粗略估计距离来说,只计算曼哈顿距离更便宜:vx+vy。好的,谢谢!我检查了一些结构,但我不明白它们是什么,它们做什么,以及如何使用它们。它们看起来都很复杂,我不太懂编程。如果你想象一个简单的例子,你的屏幕沿着垂直的下潜线分成两半,那么你有一个两个单元格的列表。行左侧的元素可以在一个单元格列表中,中间右侧的元素可以在另一个单元格中。您可以使用两个列表的数组。如果你有这个,那么当你想测试屏幕左侧的粒子,看它是否与其他粒子碰撞时,你只需要检查屏幕左侧的粒子,而不需要检查屏幕右侧的粒子。s/diving/divising。这些数据结构基本上就是这个想法的延伸,屏幕可以由两个以上的单元格组成,数量可以是你想要的。但就在这个uber简单的例子中,你的屏幕被一分为二,分成两个独立的粒子列表,这意味着你可能会将每个粒子平均需要测试的粒子数减半。当然,在这个简单的例子中,如果一个粒子正好位于直线的中心,并横跨屏幕的两侧,然后,您必须检查两个单元列表,即左侧单元和右侧单元,因为粒子将属于这两个单元。但是完全位于直线左侧或右侧的粒子不需要,只需要检查两个列表中的一个。如果你能理解这个基本概念,那么所有这些奇特的数据结构都是在这个基本概念的基础上扩展的。谢谢你的解释!