本篇雖然起源於國外一網站的教程,但絕非單純翻譯,包括文中圖片也是自己所截,所以爲本人和閃吧所有,如有轉載,請通知本人,謝謝 數學建模 - 兩球的高級碰撞檢測 碰撞對於遊戲來說是少不了的環節,碰撞的可能性也很多,圓形碰撞是其中一種,也是最經典之一.很多遊 戲,比如桌球就是單純的判斷球和球之間的碰撞(假設無邊界).下面我就講一些球於球之間的碰撞(思路來 自一個遊戲開發網站) 假設兩個球中一個是不動的,並且兩個球的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; } };
|
|