本篇虽然起源于国外一网站的教程,但绝非单纯翻译,包括文中图片也是自己所截,所以为本人和闪吧所有,如有转载,请通知本人,谢谢 数学建模 - 两球的高级碰撞检测 碰撞对于游戏来说是少不了的环节,碰撞的可能性也很多,圆形碰撞是其中一种,也是最经典之一.很多游 戏,比如桌球就是单纯的判断球和球之间的碰撞(假设无边界).下面我就讲一些球于球之间的碰撞(思路来 自一个游戏开发网站) 假设两个球中一个是不动的,并且两个球的MC的注册点都在中心 要判断球有没有碰到,只要算出球A和球B间的距离是否大于球A和球B的半径和
var disX=ballB._x-ballA._x; // 球A和球B在x轴上的差距 var disy=ballB._y-ballA._y; // 球A和球B在y轴上的差距 var diss=Math.sqrt(disX*disX+disY*disY); // 球A和球B的距离 var sum_radii=ballA.radii+ballB.radii; // 球A和球B的半径之和 if(diss<=sum_radii){ // 判断距离和半径之和的大小 trace(两球碰到了); }
上面就是判断碰撞的基本条件,我们可以稍微换一下,把Math.sqrt去掉,直接使用平方来做,也是一样的, 因为sqrt实在是浪费资源 // 红色为修改部分
var disX=ballB._x-ballA._x; // 球A和球B在x轴上的差距 var disy=ballB._y-ballA._y; // 球A和球B在y轴上的差距 var diss=disX*disX+disY*disY; // 球A和球B的距离 var sum_radii=ballA.radii+ballB.radii; // 球A和球B的半径之和 if(diss<=(sum_radii*sun_radii) ){ // 判断距离和半径之和的大小 trace(两球碰到了); }
ok,一般这么判断就很够用了,但是你会发现这么做很不好,因为会出现速度过大,而导致球A盖在球B的上 面,更可怕的是,球A一次性串过了球B 所以单单这么判断,是不够的,那么我们换一个想法...是不是可以根据球的速度(注意:速度是矢量,是有 方向的)来判断呢??比如球A的速度指在球B的直径范围内,球A就会碰到球B...不过马上一想这样也是不完 善的,因为出出现这种情况 screen.width-333)this.width=screen.width-333 border=0> 那是不是通过多做几条线呢??我看也是不行的,比如球B很小的情况下 screen.width-333)this.width=screen.width-333 border=0> 单看上面两点..但是是不够的,但是我们放在一起讨论就不一样了..现在我定义一个函数 detectCollision();他里面有4个方法来排除不可能碰撞的情况,并且是逐步缩小范围,如果这4个情况都 不成立,那么就是撞到了. 第一种不碰撞可能检测 : 判断移动速率(注意:速率是标量,没有方向只有大小)是否大于球表面距离 如图 screen.width-333)this.width=screen.width-333 border=0> 如果速率v小于两球表面距离,那么就说明这次检测中,两球没撞到,这个太好理解了吧^^ 第二种不碰撞可能检测 : 判断速度方向的夹角是否是钝角 如图 screen.width-333)this.width=screen.width-333 border=0> v''''''''''''''''的速度夹角为钝角,显然不可能碰到,但是图中好像不用钝角也有可能不会碰到..呃..那是因为红球远 了点,如果红球足够近呢^^,其实夹角的临界是90度,大于90度就不会碰到,因为这样球A就不会向球B的方 向移动了,如果再不懂,去些数学论坛或者找些数学书看一下吧,我的数学只能解释到这里了^^! 接下来两个,多看看图,才好理解~_~ 第三种不碰撞可能检测 : 判断被撞球圆心到撞球速度方向上垂直一点是否大于两球半径和 screen.width-333)this.width=screen.width-333 border=0> 就如上图看到的,有可能球A的速度方向夹角没有大于90度,但是如果球A和球B且离的远了,球A还是不会碰 到球B,所以,这里的第三种判断就是判断,球A再离球B足够近了以后,图中紫红色线条F,即球B圆心到球A速 度方向上垂直一点的距离,是否大于球A和球B的半径之和,如果大于了,那么就是没有碰到(这句是废话了) 第四种不碰撞可能检测 : 判断移动速率是否大于,球此时的位置到球碰撞位置的距离 screen.width-333)this.width=screen.width-333 border=0> 这是最后一个判断,判断球A的速率是否小于判断是dis,如果是,说明下一帧时不会碰到,反之下一帧就要碰到了 OK,4个方法全部讲完,剩余的就是几何数学问题了^0^我们的detectCollision函数即如下 之前写的程序还是有错误和不妥之处 首先我一开始用的是方向和速率来控制移动,这样的话,在计算两球同时移动的时候,使用上面的碰撞检测就会出现很麻烦的事情--两球速度大小和方向--又要用到恐怖的数学三角函数~_~! 而如果只是用(vx,vy)来控制就方便的多,也就是说圆的x和y只要加上各自方向上的速度大小即可,如果要求角度的时候,再即时用Math.atan2来算就可以了,这样,速度的叠加就方便多了,比如
a_vx=1; a_vy=1; b_vx=-2; b_vy=-2;
那么速度叠加 大小
dvx=b_vx-a_vx; dvy=b_vy-a_vy;
方向
dir=Math.atan2(dvy,dvx);
这可比什么平行四边型法则容易太多了^^ ok,了解了以后,我把我的程序修改如下
///////////////////////////////////////////// /// **********08.14.2004 修改速度方向 /// 改去之前使用大小和方向的数学模式 /// 使用x,y方向的各自速度大小即时计算 /// **********08.15.2004 修改相对速度方向和球心之间的夹角 // 增加了求getTheta函数来求atan2时避免出现负角 ///////////////////////////////////////////// stop(); getRadii = function (mc) { return (Math.max(mc._width, mc._height)/2); }; getTheta = function (y, x) { var dir = Math.atan2(y, x); if (x>=0 && y>=0) { return dir; } else if (x>=0 && y<0) { return (dir+6.28); } else { return (dir+3.14); } }; function detectCollision(mc1, mc2) { // 第一种不碰撞可能检测 : 判断移动速率是否大于球表面距离 var sum_radii = mc1.radii+mc2.radii; var dx = mc2._x-mc1._x; var dy = mc2._y-mc1._y; var C = Math.sqrt(dx*dx+dy*dy); var dist = C-sum_radii; dxs = mc2.xsp-mc1.xsp; dys = mc2.ysp-mc1.ysp; var dsp = Math.sqrt(dxs*dxs+dys*dys); if (dsp<dist) { return false; } // 第二种不碰撞可能检测 : 判断速度方向的夹角是否是钝角 var sdir = getTheta(dys, dxs); var cdir = getTheta(dy, dx); var theta = Math.abs(cdir-sdir); var D = C*Math.cos(theta); if (D<=0) { return false; } // 第三种不碰撞可能检测 : 判断被撞球圆心到撞球速度方向上垂直一点是否大于两球半径和 var F = Math.sqrt((C*C)-(D*D)); if (F>=sum_radii) { return false; } // 第四种不碰撞可能检测 : 判断移动速率是否大于,球此时的位置到球碰撞位置的距离 var T = Math.sqrt((sum_radii*sum_radii)-(F*F)); var disc = D-T; if (dsp<disc) { return false; } return true; } function initialize() { toRadian = Math.PI/180; toAngle = 180/Math.PI; Math_rnd = Math.random; redBall.xsp = 10-Math_rnd()*20; redBall.ysp = 10-Math_rnd()*20; redBall.radii = getRadii(redBall); blueBall.xsp = 10-Math_rnd()*20; blueBall.ysp = 10-Math_rnd()*20; blueBall.radii = getRadii(blueBall); } initialize(); onEnterFrame = function () { redBall._x += redBall.xsp; redBall._y += redBall.ysp; blueBall._x += blueBall.xsp; blueBall._y += blueBall.ysp; if (detectCollision(redBall, blueBall)) { touch.text = touch; } else { touch.text = no touch; } };
|
|