數學建模-兩球的高級碰撞檢測

數學建模-兩球的高級碰撞檢測
作者:佚名 來自:網絡 人氣: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;
          }
};
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章