1. 程式人生 > >向量幾何在遊戲編程中的使用4

向量幾何在遊戲編程中的使用4

all lis func 鏈接 能夠 不變 它的 med com

<4>2-D物體間的碰撞響應

這次我要分析兩個球體之間的碰撞響應,這樣我們就可以結合以前的知識來編寫一款最基本的2-D臺球遊戲了,雖然粗糙了點,但卻是個很好的開始,對嗎?

一、初步分析

中學時候上物理課能夠認真聽講的人(我?哦,不包括我)應該很熟悉的記得:當兩個球體在一個理想環境下相撞之後,它們的總動量保持不變,它們的總機械能也守恒。但這個理想環境是什麽樣的呢?理想環境會不會影響遊戲的真實性?對於前者我們做出在碰撞過程中理想環境的假設:

1)首先我們要排除兩個碰撞球相互作用之外的力,也就是假設沒有外力作用於碰撞系統。
2)假設碰撞系統與外界沒有能量交換。
3)兩個球體相互作用的時間極短,且相互作用的內力很大。

有了這樣的假設,我們就可以使用動量守恒和動能守恒定律來處理它們之間的速度關系了,因為1)確保沒有外力參與,碰撞系統內部動量守恒,我們就可以使用動量守恒定律。2)保證了我們的碰撞系統的總能量不會改變,我們就可以使用動能守恒定律。3)兩球發生完全彈性碰撞,不會粘在一起,沒有動量、能量損失。

而對於剛才的第二個問題,我的回答是不會,經驗告訴我們,理想環境的模擬看起來也是很真實的。除非你是在進行科學研究,否則完全可以這樣理想的去模擬。

技術分享圖片

現在,我們可以通過方程來觀察碰撞前後兩球的速度關系。當兩球球心移動方向共線(1-D處理)時的速度,或不共線(2-D處理)時共線方向的速度分量滿足:

(1)m1 * v1 + m2 * v2 = m1 * v1‘ + m2 * v2‘ (動量守恒定律)


(2)1/2 * m1 * v1^2 + 1/2 * m2 * v2^2 = 1/2 * m1 * v1‘^2 + 1/2 * m2 * v2‘^2 (動能守恒定律)

這裏m1和m2是兩球的質量,是給定的,v1和v2是兩球的初速度也是我們已知的,v1‘和v2‘是兩球的末速度,是我們要求的。好,現在我們要推導出v1‘和v2‘的表達式:

由(1),得到v1‘ = (m1 * v1 + m2 * v2 - m2 * v2‘) / m1,代入(2),得
1/2 * m1 * v1^2 + 1/2 * m2 * v2^2 = 1/2 * m1 * (m1 * v1 + m2 * v2 - m2 * v2‘)^2 + 1/2 * m2 * v2‘^2

=> v2‘ = (2 * m2 * v1 + v2 * (m1 - m2)) / (m1 + m2)


=> v1‘ = (2 * m1 * v2 + v1 * (m1 - m2)) / (m1 + m2)

我們現在得到的公式可以用於處理當兩球球心移動方向共線(1-D處理)時的速度關系,或者不共線(2-D處理)時共線方向的速度分量的關系。不管是前者還是後者,我們都需要把它們的速度分解到同一個軸上才能應用上述公式進行處理。

二、深入分析

首先我要說明一件事情:當兩球碰撞時,它們的速度可以分解為球心連線方向的分速度和碰撞點切線方向的分速度。而由於它們之間相互作用的力只是在切點上,也就是球心連線方向上,因此我們只用處理這個方向上的力。而在切線方向上,它們不存在相互作用的力,而且在理想環境下也沒有外力,因此這個方向上的力在碰撞前後都不變,因此不處理。好,知道了這件事情之後,我們就知道該如何把兩球的速度分解到同一個軸上進行處理。

技術分享圖片

現在看上面的分析圖,s和t是我們根據兩個相碰球m1和m2的位置建立的輔助軸,我們一會就將把速度投影到它們上面。v1和v2分別是m1和m2的初速度,v1‘和v2‘是它們碰撞後的末速度,也就是我們要求的。s‘是兩球球心的位置向量,t‘是它的逆時針正交向量。s1是s‘的單位向量,t1是t‘的單位向量。

我們的思路是這樣的:首先我們假設兩球已經相碰(在程序中可以通過計算兩球球心之間的距離來判斷)。接下來我們計算一下s‘和t‘,註意s‘和t‘的方向正反無所謂(一會將解釋),現在設m1球心為(m1x, m1y),m2球心為(m2x, m2y),則s‘為(m1x-m2x, m1y-m2y),t‘為(m2y-m1y, m1x-m2x)(第一篇的知識)。

則設
sM = sqrt((m1x-m2x)^2+(m1y-m2y)^2),
tM = sqrt((m2y-m1y)^2+(m1x-m2x)^2),有

s1 = ((m1x-m2x)/sM, (m1y-m2y)/sM) = (s1x, s1y)
t1 = ((m2y-m1y)/tM, (m1x-m2x)/tM) = (t1x, t1y)

現在s和t軸的單位向量已經求出了,我們根據向量點乘的幾何意義,計算v1和v2在s1和t1方向上的投影值,然後將s軸上投影值代
入公式來計算s方向碰撞後的速度。註意,根據剛才的說明,t方向的速度不計算,因為沒有相互作用的力,因此,t方向的分速度不變。
所以我們要做的就是:把v1投影到s和t方向上,再把v2投影到s和t方向上,用公式分別計算v1和v2在s方向上的投影的末速度,然後把得到的末速度在和原來v1和v2在t方向上的投影速度再合成,從而算出v1‘和v2‘。好,我們接著這個思路做下去:

先算v1(v1x, v1y)在s和t軸的投影值,分別設為v1s和v1t:

v1s = v1.s1
=> v1s = v1x * s1x + v1y * s1y

v1t = v1.t1
=> v1t = v1x * t1x + v1y * t1y

再算v2(v2x, v2y)在s和t軸的投影值,分別設為v2s和v2t:

v2s = v2.s1
=> v2s = v2x * s1x + v2y * s1y

v2t = v2.t1
=> v2t = v2x * t1x + v2y * t1y

接下來用公式

v1‘ = (2 * m1 * v2 + v1 * (m1 - m2)) / (m1 + m2)

v2‘ = (2 * m2 * v1 + v2 * (m1 - m2)) / (m1 + m2)

計算v1s和v2s的末值v1s‘和v2s‘,重申v1t和v2t不改變:

假設m1 = m2 = 1

v1s‘ = (2 * 1 * v2s + v1s * (1 - 1)) / (1 + 1)
v2s‘ = (2 * 1 * v1s + v2s * (1 - 1)) / (1 + 1)

=> v1s‘ = v2s
=> v2s‘ = v1s

好,下一步,將v1s‘和v1t再合成得到v1‘,將v2s‘和v2t再合成得到v2‘,我們用向量和來做:

首先求出v1t和v2t在t軸的向量v1t‘和v2t‘(將數值變為向量)

v1t‘ = v1t * t1 = (v1t * t1x, v1t * t1y)
v2t‘ = v2t * t1 = (v2t * t1x, v2t * t1y)

再求出v1s‘和v2s‘在s軸的向量v1s‘和v2s‘(將數值變為向量)

v1s‘= v1s‘ * s1 = (v1s‘ * s1x, v1s‘ * s1y)
v2s‘= v2s‘ * s1 = (v2s‘ * s2x, v2s‘ * s2y)

最後,合成,得

v1‘ = v1t‘ + v1s‘ = (v1t * t1x + v1s‘ * s1x, v1t * t1y + v1s‘ * s1y)
v2‘ = v2t‘ + v2s‘ = (v2t * t1x + v2s‘ * s2x, v2t * t1y + v2s‘ * s2y)

從而就求出了v1‘和v2‘。下面解釋為什麽說s‘和t‘的方向正反無所謂:不論我們在計算s‘時使用m1的球心坐標減去m2的球心坐標還是相反的相減順序,由於兩球的初速度的向量必有一個和s1是夾角大於90度小於270度的,而另外一個與s1的夾角在0度和90度之間或者說在270度到360度之間,則根據向量點積的定義|a|*|b|*cosA,計算的到的兩個投影值一個為負另一個為正,也就是說,速度方向相反,這樣就可以用上面的公式區求得末速度了。同時,求出的末速度也是方向相反的,從而在轉換為v1s‘和v2s‘時也是正確的方向。同樣的,求t‘既可以是用s‘逆時針90度得到也可以是順時針90度得到。

三、編寫代碼

按照慣例,該編寫代碼了,其實編寫的代碼和上面的推導過程極為相似。但為了完整,我還是打算寫出來。

// 用於球體碰撞響應的函數,其中v1a和v2a為兩球的初速度向量,
// v1f和v2f是兩球的末速度向量。
// m1和m2是兩球的位置向量
// s‘的分量為(sx, sy),t‘的分量為(tx, ty)
// s1是s的單位向量,分量為(s1x, s1y)
// t1是t的單位向量,分量為(t1x, t1y)

void Ball_Collision(v1a, v2a, &v1f, &v2f, m1, m2){

// 求出s‘
double sx = m1.x - m2.x ;
double sy = m1.y - m2.y ;

// 求出s1
double s1x = sx / sqrt(sx*sx + sy*sy) ;
double s1y = sy / sqrt(sx*sx + sy*sy) ;

// 求出t‘
double tx = -sy ;
double ty = sx ;

// 求出t1
double t1x = tx / sqrt(tx*tx + ty*ty) ;
double t1y = ty / sqrt(tx*tx + ty*ty) ;

// 求v1a在s1上的投影v1s
double v1s = v1a.x * s1x + v1a.y * s1y ;

// 求v1a在t1上的投影v1t
double v1t = v1a.x * t1x + v1a.y * t1y ;

// 求v2a在s1上的投影v2s
double v2s = v2a.x * s1x + v2a.y * s1y ;

// 求v2a在t1上的投影v2t
double v2t = v2a.x * t1x + v2a.y * t1y ;

// 用公式求出v1sf和v2sf
double v1sf = v2s ;
double v2sf = v1s ;

// 最後一步,註意這裏我們簡化一下,直接將v1sf,v1t和v2sf,v2t投影到x,y軸上,也就是v1‘和v2‘在x,y軸上的分量
// 先將v1sf和v1t轉化為向量

double nsx = v1sf * s1x ;
double nsy = v1sf * s1y ;
double ntx = v1t * t1x ;
double nty = v1t * t1y ;

// 投影到x軸和y軸
// x軸單位向量為(1,0),y軸為(0,1)
//
v1f.x = 1.0 * (nsx * 1.0 + nsy * 0.0) ;
// v1f.y = 1.0 * (nsx * 0.0 + nsy * 1.0) ;
// v1f.x+= 1.0 * (ntx * 1.0 + nty * 0.0) ;
// v1f.y+= 1.0 * (ntx * 0.0 + nty * 1.0) ;

v1f.x = nsx + ntx ;
v1f.y = nsy + nty ;

// 然後將v2sf和v2t轉化為向量

nsx = v2sf * s1x ;
nsy = v2sf * s1y ;
ntx = v2t * t1x ;
nty = v2t * t1y ;

// 投影到x軸和y軸
// x軸單位向量為(1,0),y軸為(0,1)
//
v2f.x = 1.0 * (nsx * 1.0 + nsy * 0.0) ;
// v2f.y = 1.0 * (nsx * 0.0 + nsy * 1.0) ;
// v2f.x+= 1.0 * (ntx * 1.0 + nty * 0.0) ;
// v2f.y+= 1.0 * (ntx * 0.0 + nty * 1.0) ;


v2f.x = nsx + ntx ;
v2f.y = nsy + nty ;

}// end of function

原文鏈接

向量幾何在遊戲編程中的使用4