2D游戏中的碰撞检测:圆形与矩形碰撞检测(Javascrip版)
一,原理介绍
这回有点复杂,不过看懂了还是很好理解的。当然,我不敢保证这种算法在任何情况下都会起效果,如果有同学测试时,发现出现错误,请及时联系我。
我们首先来建立一个以圆心为原点的坐标系:
然后要检测碰撞就只有两种情况了。
情况一,矩形全部都在一个象限内,如图:
当然,图中只是举个例子,不一定是只在第二象限,任何一个象限都行,只要是矩形全在该象限。
这种情况比较好解决,首先,我们计算出矩形每个角的坐标,然后用勾股定律依次算出这个角到圆心的距离是否小于或者等于半径。设这个角与圆心横坐标之差为d1,纵坐标之差为d2,半径为r,公式表达如下:
如果有一个角满足要求说明产生碰撞,返回true。
但是有朋友懵了,怎么判断矩形是不是在一个象限内呢?很简单,只要判断这个矩形左上角和右下角是否在同一个象限内就可以了。于是我们得写个函数来实现判断某两个角是否在同一象限。
函数代码如下:
1. function isSameQuadrant(cood,objA,objB){ 2. var coodX = cood.x; 3. var coodY = cood.y; 4. var xoA = objA.x 5. ,yoA = objA.y 6. ,xoB = objB.x 7. ,yoB = objB.y; 8. 9. if(xoA-coodX>0 && xoB-coodX>0){ 10. if((yoA-coodY>0 && yoB-coodY>0) || (yoA-coodY<0 && yoB-coodY<0)){ 11. return true; 12. } 13. return false; 14. }else if(xoA-coodX<0 && xoB-coodX<0){ 15. if((yoA-coodY>0 && yoB-coodY>0) || (yoA-coodY<0 && yoB-coodY<0)){ 16. return true; 17. } 18. return false; 19. }else{ 20. return false; 21. } 22. }
这个函数原本是准备写到lufylegend中LMath静态类中的,参数原本是LPoint对象,但是这里可以用json,因为LPoint里的x,y属性可以写到json里,函数也就同样取得出值了。函数参数介绍:[cood创建的坐标系原点坐标, objA第一个点坐标, objB第二个点坐标] 这几个参数均为json对象,格式为:
{x:点的x坐标, y:点的y坐标}
函数中的代码还是很好理解的,就是判断一下两个点的x坐标都分别减去原点x坐标,看得出的数正负符号是否相同,然后又用同样的办法算出y轴上的符号是否相同,如果都相同就在同一象限。
有了这个函数,剩下得就好办了,直接代入开头给出的公式进行计算即可。
情况二,矩形跨度两个象限或者两个象限以上
这种情况更好办,我们就可以直接把圆看作一个边长为2r正方形,然后用矩形碰撞算法检测正方形和矩形的碰撞,如下图所示:
矩形碰撞的算法是什么呢?很easy,如图:
如果要横向判断碰撞的话,判断(x1-x2)的绝对值是否小于或者等于w1/2+w2/2,如果是则横向则有碰撞。纵向判断是一样的,判断(y1-y2)的绝对值是否小于或等于h1/2+h2/2即可。
有了这些算法,我们就可以实现情况2了。
二,Javascript版算法&测试代码
先上代码吧:
1. function hitTestRectArc(rectObj,arcObj,rectVec,arcR){ 2. var rw = rectObj.getWidth() 3. ,rh = rectObj.getHeight() 4. ,ar = arcObj.getWidth()*0.5 5. ,rx = rectObj.x 6. ,ry = rectObj.y 7. ,ax = arcObj.x 8. ,ay = arcObj.y; 9. 10. if(typeof rectVec != UNDEFINED){ 11. rx += (rw - rectVec[0])*0.5; 12. ry += (rh - rectVec[1])*0.5; 13. rw = rectVec[0]; 14. rh = rectVec[1]; 15. } 16. if(typeof arcR != UNDEFINED){ 17. ax += (ar - arcR); 18. ay += (ar - arcR); 19. ar = arcR; 20. } 21. 22. var rcx = rx+rw*0.5,rcy = ry+rh*0.5; 23. var rltx = rx 24. ,rlty = ry 25. ,rlbx = rx 26. ,rlby = ry+rh 27. ,rrtx = rx+rw 28. ,rrty = ry 29. ,rrbx = rx+rw 30. ,rrby = ry+rh; 31. 32. if( 33. isSameQuadrant( 34. {x:ax,y:ay}, 35. {x:rltx,y:rlty}, 36. {x:rrbx,y:rrby} 37. ) 38. ){ 39. var dX1 = Math.abs(ax-rltx),dY1 = Math.abs(ay-rlty); 40. var dX2 = Math.abs(ax-rlbx),dY2 = Math.abs(ay-rlby); 41. var dX3 = Math.abs(ax-rrtx),dY3 = Math.abs(ay-rrty); 42. var dX4 = Math.abs(ax-rrbx),dY4 = Math.abs(ay-rrby); 43. 44. if( 45. (((dX1*dX1) + (dY1*dY1)) <= (ar*ar)) 46. ||(((dX2*dX2) + (dY2*dY2)) <= (ar*ar)) 47. ||(((dX3*dX3) + (dY3*dY3)) <= (ar*ar)) 48. ||(((dX4*dX4) + (dY4*dY4)) <= (ar*ar)) 49. ){ 50. return true; 51. } 52. return false; 53. }else{ 54. var result = false; 55. var squareX = ax 56. ,squareY = ay 57. ,squareW = ar*2 58. ,squareH = squareW; 59. if( 60. (Math.abs(squareX-rcx) <= (squareW+rw)*0.5) 61. &&(Math.abs(squareY-rcy) <= (squareH+rh)*0.5) 62. ){ 63. result = true; 64. } 65. return result; 66. } 67. }
由于是为lufylegend设计的函数,所以参数为 [ rectObj矩形对象(LSprite或者LShape对象), arcObj圆形对象(LSprite或者LShape对象), rectVec矩形规定大小(可不填), arcR圆形半径(可不填)] 当然,或许些朋友不懂这几行代码:
1. var rw = rectObj.getWidth() 2. ,rh = rectObj.getHeight() 3. ,ar = arcObj.getWidth()*0.5 4. ,rx = rectObj.x 5. ,ry = rectObj.y 6. ,ax = arcObj.x 7. ,ay = arcObj.y;
好吧,我告诉你,这里用到的是lufylegend中LSprite和LShape,这两个类有x、y属性,还有获取宽度和高度的getWidth()和getHeight(),这里看不懂没关系,你知道是取高度和宽度还有x,y坐标的就行了。当然你要深究,那就看看lufylegend.js的API文档吧:http://lufylegend.com/lufylegend/api ,以下测试代码也用到了lufylegend.js,据说这个引擎是个不错的引擎,想了解的同学,去官方网站看看吧:http://lufylegend.com/lufylegend/ 或者看看我的文章,大多数是讲解有关lufylegend开发的。
示例代码:
1. init(50,"mylegend",500,250,main); 2. 3. function main(){ 4. LGlobal.setDebug(true); 5. 6. var back = new LSprite(); 7. back.graphics.drawRect(5,"green",[0,0,LStage.width,LStage.height],true,"lightblue"); 8. addChild(back); 9. 10. var cObj = new LSprite(); 11. cObj.x = 200; 12. cObj.y = 120; 13. cObj.graphics.drawArc(0,"",[0,0,50,0,2*Math.PI],true,"red"); 14. back.addChild(cObj); 15. 16. var rObj = new LSprite(); 17. rObj.x = 250; 18. rObj.y = 70; 19. rObj.alpha = 0.8; 20. rObj.graphics.drawRect(0,"",[0,0,100,100],true,"green"); 21. back.addChild(rObj); 22. 23. trace(hitTestRectArc(rObj,cObj)); 24. 25. back.addEventListener(LMouseEvent.MOUSE_DOWN,function(e){ 26. rObj.x = e.offsetX-rObj.getWidth()*0.5; 27. rObj.y = e.offsetY-rObj.getHeight()*0.5; 28. trace(hitTestRectArc(rObj,cObj)); 29. }); 30. }
测试链接:http://www.cnblogs.com/yorhom/articles/hitTestRectArc.html