数学建模-两球的高级碰撞检测

数学建模-两球的高级碰撞检测
作者:佚名 来自:网络 人气:1002 加入时间:2006-2-6
本篇虽然起源于国外一网站的教程,但绝非单纯翻译,包括文中图片也是自己所截,所以为本人和闪吧所有,如有转载,请通知本人,谢谢
数学建模 - 两球的高级碰撞检测
碰撞对于游戏来说是少不了的环节,碰撞的可能性也很多,圆形碰撞是其中一种,也是最经典之一.很多游 戏,比如桌球就是单纯的判断球和球之间的碰撞(假设无边界).下面我就讲一些球于球之间的碰撞(思路来 自一个游戏开发网站) 假设两个球中一个是不动的,并且两个球的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;
          }
};
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章