Unity模擬榴彈運動軌跡

前言

最近迷上了《最高指揮官2》這個遊戲,裏面的遠程火炮的轟擊很讓我着迷,於是本着閒着也是閒着的原則,就寫一篇關於實現這種類似榴彈炮效果的代碼。

斜拋運動

先回憶在高中物理中,是否算過這麼一個問題,給了一個物體的斜拋角度,初速度,重力加速度,然後讓你計算這個物體拋出後,會落在什麼地方。這裏假設斜拋角爲60度,初速度爲100m/s,重力加速度爲10m/s^2;(爲了方便,下面默認單位爲m)
很明顯這是一個簡單的物理題,處理這個問題的主要解決思想爲求出物體豎直方向的運動時間,這個時間就是物體整個斜拋運動的運動時間,乘以水平方向速度,就可以得到位置。先可以把初速度分解爲兩個方向的速度,分別是水平方向和豎直方向(重力加速度的反方向),這裏要特別注意的是被分解的水平速度和豎直速度是兩個互不影響速度,也就是說,可以把整斜拋過程分解爲水平方向的勻速直線運動,豎直方向爲加速度爲-10的勻加速直線運動。這點很重要!
如圖中所示,設Vy = sinθ * V, Vx = cosθ * V,得到了兩個速度,然後開始獨立分解這兩個運動。
首先豎直方向,當物體克服重力做功到達最高點時,物體在豎直方向的速度就爲0,又知道物體在豎直方向做的是勻減速直線運動,所以:
Vc = V0 + at;
Vc爲物體當前的豎直方向的速度,V0爲Vy,加速度a就是重力加速度g;
Vc = 0m/s;
便可以得到:
V0 = -at;(注意這裏的a = g = - 10m/s^2)
就可以求出
t = Vy / -g = 100 * sin(60°) / 10 = 5s;
所以求出了物體向上運動的時間爲5s。
接下來物體要做的就是自由落體運動,可以用同樣的方法計算出下落時間爲5s,當然也可以省略這一步驟直接得出下落時間爲5s,因爲物體做自由落體運動的高度與加速度都不變,所以其花費的時間也不變,等於上拋時間。
這樣計算出了整個斜拋運動的時間花費爲10s,所以就可以直接得出物體落下的位置爲:
距離自身 Vx * t 處的位置,(帶入的計算我就不說了,讀者可以自行帶入計算)。
對於這個題還需要加衆多的限制條件,例如斜拋過程忽略空氣阻力的影響、斜拋前方沒有障礙物等。

逆向的推導

在已知重力加速度,物體初速度和斜拋仰角的情況下計算出了,物體將要擊中的位置。所以當給出重力加速度,物體初速度,以及擊中位置,要求計算斜拋仰角,也是可以的。
設斜拋仰角爲θ,物體初速度與重力加速度和上一節一樣,不同的是這裏多了一個擊中位置,直接取得擊中位置與發射位置的距離爲S。還是對這個速度進行分解,如下圖所示:
在這裏插入圖片描述
Vx = cosθ * V,Vy = sinθ * V,如果將θ當作已知量處理,就會得到和上一節相同的公式:
即:Vy = -gt1;(a)
又因爲,已知道了水平方向的運動距離,所以就有:
S = Vx * t2;
可以得到:
t2 = S / Vx;(b)
聯立a,b兩個式子來看,都有未知量時間t,基於上一節的理論,可以知道,t2 = 2 * t1(因爲式子a只表示物體克服重力做功速度的變化量,不包括後階段的自由落體運動),所以可以將a,b聯立消除未知量t,所以就得到
S / Vx = -2Vy / g;
接下來爲讀者演示推到過程:
Vy = V * sinθ;
Vx = V * cosθ;
Vy = -g * t1;
t1 = -Vy / g;
S / Vx = t2;
T2 = 2 * t1;
S / Vx = -2 * Vy / g;
Vx * Vy = -S * g / 2;
sinθ * cosθ = -S * g / 2 * V^2;
sin(2θ) = - S * g / V^2;
因爲要求的是θ,所以要將帶有θ的項移到一旁,至於cosθ * sinθ = 0.5 * sin(2θ),這是高中數學由涉及到的只是,而且很重要,不理解的讀者可以補補高中的知識,或者你就知道這麼用就可以了,因爲這已經是被數學家所證明過的公式了。就像知道 平均速度V = X / t。
得到了sin(2θ)的值,利用Asin(sin(2θ))就可以計算出發射仰角θ(衆多遊戲引擎中的數學庫已經包含了對 Asin 的實現,所以不必自己手動實現一遍了)。

運用在遊戲中

炮彈運動
當然,的目的是把代碼運用在遊戲中,所以在真正編寫代碼的過程中沒辦法去推導這些公式。
我以Unity爲例來實現這個過程,但是在計算仰角之前必須先搞定炮彈的運動算法。
1.炮彈的初速度方向與炮口方向保持一致;
2.炮彈運動爲豎直方向的勻加/減速的直線運動,水平方向爲勻速直線運動。
因爲遊戲中的時間是以幀來表示的,且這些遊戲中的時間都是離散的,而不像生活中所處世界的時間是連續的。所以要用微積分的思想解決炮彈的運動問題。
豎直方向的運動
對於速度的表達式
V = V0 + dv;
這裏的V是當前速度,V0是上一幀的速度。
又因爲速度關於時間的導數是加速度,所以可以得到:
dv = a * dt;
a爲重力加速度g,dt爲一個很短的時間段,在unity中爲0.02s,或者Unity中的

Time.deltaTime

所以就可以得到在豎直方向運動的速度表達式:
V += g * dt;
同理得到位移的表達式:
H = H0 + dH;
相同的可以得到:
dH = V * dt;
所以最終所需要的表達式爲:
H += V * dt;
水平方向的運動
解決了上面的問題這個久相對簡單了,直接通過S = S0 + V * dt,直接得到其表達式:
S += V * dt ;
所以最終的拋體運動公式爲:
Sc = S + H;
Sc爲炮彈的位置。
下面爲在Unity中的C#實現代碼:

float h;//豎直方向的位移量
float s;//水平方向的位移量
float g;//重力加速度大小
float speed;//炮彈的初速度大小

Transform bullet;//炮彈
Transform cannon;//加農炮
void Bullet()
{
	h += g * Time.deltaTime;  //豎直方向位移量
	s += speed * Time.deltaTime;//水平方向位移量
	bullet.position += Cannon.forward * s + Vector3.up * h * Time.deltaTime;
}

這裏要注意的是,對於速度,加速度都是矢量,有大小有方向,所以在最後一行要給他們添加上相應的方向。
當然你還要再初始化的時候將炮彈的位置更新到炮口位置,這樣運行後你就會看到一個炮彈的完美斜拋運動軌跡。
計算髮射仰角
在最開始已經將計算髮射仰角的方法給出,但是在真正寫代碼的時候,不可能去在CPU中一步步的化簡式子,所以應該求出所需要結果的代數式。
在計算方法中最終化簡了這麼一個式子:
sinθ * cosθ = XXX;
然後一進步化簡得到:
sin(2θ) * 0.5 = XXX;
所以:
sin(2θ) = 2 * XXX;
這樣就很明瞭了,只需要求出XXX的結果就好,當然不要計算,知識他的代數式,在開頭已經得到其代數式,所以:
θ = Asin(- S * g / V^2);
代碼形式爲:

float distance;//大炮與目標點的水平距離
float g;//重力加速度大小
float speed;//炮彈初速度大小
float theta;//發射仰角

theta = -Mathf.Asin(distance * g / Mathf.Pow(speed, 2)) * 0.5f * Mathf.Rad2Deg;

注意Mathf.Asin(float f),計算出來的結果爲弧度制,所以根據需要將它轉化爲角度或者什麼都不做。
有了發射仰角就可以求出炮管的朝向或者改變炮管的歐拉角,當然前者可能需要使用到勾股定理,而後者則需要注意自身空間與世界空間的問題。這裏不在給出實現,讀者可以自己嘗試。當然炮彈是無法擊中範圍外的目標的,所以這個也要控制好。

總結

這是一個模擬迫擊炮的標準目標的代碼,但是這是一個最理想的狀態下的模擬,他要求大炮與目標同處於同一水平面,而且計算過程中只考慮了重力的影響。我也希望在以後的學習中可以突破自己。其實這麼久以來我以爲大家都有一個誤區,就是有現成的插件用就好了,不需要知道它的實現是怎麼樣的,也不考慮這個插件內在到底是怎麼實現的。我認爲這是非常不好的,如果你只是在工作項目的需要,那麼你大可去學習怎麼使用,例如剛體,當然使用官方自帶的組件要方便,而因爲工期需要,也不可能自己去實現一個剛體,這種情況我覺得可以理解。但如果是爲了自己學習那就很不好,對於一個剛體的實現其所需要具備的知識不僅包括了物理,還有高數,線性代數等知識,也就是說,如果是正在學習的你,有這種拿來主義的思想,那麼就等於你直接放棄了一個使你自身提高的機會,當你你真的花時間學習這些知識,你所製作出來的遊戲的品質也會不斷的提升,這纔是遊戲開發的樂趣所在!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章