來,我們用Unity做一個(gè)大炮
(本文作者 @沈琰 )
前言
如標(biāo)題所述,這次的目標(biāo)是在unity里實(shí)現(xiàn)一門較為貼近現(xiàn)實(shí)情況的大炮,操作方式參考坦克世界的火炮視角。主要涉及的知識(shí)點(diǎn)是坐標(biāo)系,向量,四元數(shù)的應(yīng)用,難度適中。
話不多說,開搞。
偏轉(zhuǎn)軸與俯仰軸
現(xiàn)實(shí)中的火炮進(jìn)行瞄準(zhǔn)的時(shí)候,一般在機(jī)械結(jié)構(gòu)上驅(qū)動(dòng)的部分為兩個(gè)軸:偏轉(zhuǎn)軸(yaw)與俯仰軸(pitch),并且是偏轉(zhuǎn)軸帶動(dòng)俯仰軸旋轉(zhuǎn)。

換句話說就是在unity物體結(jié)構(gòu)中,俯仰軸是偏轉(zhuǎn)軸的子物體:

用unity里默認(rèn)的物體拼一個(gè)簡單的火炮模型,外形長什么樣無所謂,有那意思就行。關(guān)鍵點(diǎn)是確定旋轉(zhuǎn)軸,使其在兩個(gè)軸上的旋轉(zhuǎn)相互獨(dú)立:

旋轉(zhuǎn)
按照功能需求,應(yīng)該是鼠標(biāo)指向場景中的一個(gè)點(diǎn),然后火炮依據(jù)目標(biāo)點(diǎn)的位置按一個(gè)設(shè)定的速度旋轉(zhuǎn)到瞄準(zhǔn)指向的目標(biāo)。
偏轉(zhuǎn)角的計(jì)算很好寫,獲取落點(diǎn)的xz平面坐標(biāo)到位置的向量,與火炮的朝向計(jì)算夾角,然后讓偏轉(zhuǎn)軸按設(shè)定速度轉(zhuǎn)到目標(biāo)方向。
俯仰角的計(jì)算要用到一些數(shù)學(xué)和物理知識(shí),稍微棘手一點(diǎn)。
如果把大炮當(dāng)時(shí)朝向的yz平面看作一個(gè)坐標(biāo)系,那么炮彈飛出去形成的軌跡是這個(gè)坐標(biāo)系上的一條拋物線。

這部分內(nèi)容,包括后面要用到的火炮射程計(jì)算,都是關(guān)于彈道的數(shù)學(xué)問題,這部分內(nèi)容不難但比較繁瑣。為不影響文章節(jié)奏,這里會(huì)略過涉及到彈道計(jì)算的數(shù)學(xué)推導(dǎo)部分,在代碼里這些計(jì)算部分會(huì)抽象到一個(gè)類中,感興趣的同學(xué)可以在工程中自行查閱,如有必要以后會(huì)把數(shù)學(xué)推導(dǎo)部分單獨(dú)寫出來。
根據(jù)計(jì)算得到的目標(biāo)角去旋轉(zhuǎn)俯仰軸部分,與偏轉(zhuǎn)軸部分基本一樣:
這里會(huì)遇到一個(gè)問題,如果我們把拋物線的二維坐標(biāo)系的橫軸視為火炮出射角的0度角,仰角為正俯角為負(fù),那么旋轉(zhuǎn)軸就是垂直于坐標(biāo)系且朝外,而加入這根轉(zhuǎn)軸的三維坐標(biāo)系是一個(gè)左手坐標(biāo)系,unity使用的則是右手坐標(biāo)系,這就使計(jì)算出來的值與實(shí)際的旋轉(zhuǎn)表現(xiàn)是一個(gè)相反的關(guān)系。

編輯
解決方法有很多,這里為了后續(xù)設(shè)置與計(jì)算的方便,選擇顛倒俯仰軸節(jié)點(diǎn)的本地坐標(biāo)系。隨之而來的改變是:當(dāng)后續(xù)要使用pitch.forward的值的時(shí)候都要取負(fù),這點(diǎn)需要特別注意。

顯示彈道
現(xiàn)在要驗(yàn)證一下反向計(jì)算出的彈道落點(diǎn)是否與鼠標(biāo)射線在場景中的坐標(biāo)相吻合,需要把彈道顯示出來。先暫時(shí)用Gizmos畫出彈道線。

限制俯仰角與最大射界
按我們現(xiàn)在的火炮外形設(shè)計(jì),在偏轉(zhuǎn)軸上沒有角度限制,可以360度無死角旋轉(zhuǎn),但是在現(xiàn)實(shí)中絕大部分火炮肯定都有俯仰角的限制,然后加上火炮出膛最大速度與射擊高度,進(jìn)一步還能計(jì)算出火炮的最大射界。
這里有個(gè)有趣的數(shù)學(xué)問題:中學(xué)的時(shí)候我們都學(xué)過,沿45度拋出的石頭能丟的最遠(yuǎn),但這個(gè)結(jié)論有一個(gè)限制條件:出射點(diǎn)和落點(diǎn)的高度必須相同。
但現(xiàn)在明顯我們的火炮出射點(diǎn)要略高于落點(diǎn),這會(huì)使得最大射程的傾角發(fā)生改變。具體的推導(dǎo)和計(jì)算也屬于彈道數(shù)學(xué)的部分,暫且略過,直接說結(jié)論:若出射高度大于落點(diǎn)高度,會(huì)使最大射程的傾角小于45度,反之亦然。
所以只要火炮的出射高度不低于落點(diǎn),且最大仰角不小于45度的情況下,火炮的最大射程其實(shí)就與角度限制無關(guān)了,只與出射高度和炮彈的出膛速度有關(guān)。
根據(jù)這個(gè)結(jié)論我們可以分別計(jì)算出火炮的最遠(yuǎn)、最近和零度角的射界范圍,依然用Gizmos顯示在場景中。
??

編輯切換為居中
再把限制角的范圍也用Gizmos顯示在場景中,調(diào)整不同的參數(shù)看下邏輯是否正確:

精度變化
下一步工作是模擬火炮在瞄準(zhǔn)時(shí),由于旋轉(zhuǎn)炮管產(chǎn)生的抖動(dòng)造成的角度偏差,使得實(shí)際出射角變化產(chǎn)生的落點(diǎn)散布問題,也就是原版游戲中的“縮圈”功能。
按照原版表現(xiàn),需要以落點(diǎn)為圓心顯示一個(gè)“圈”,表示火炮落點(diǎn)可能的散布范圍。當(dāng)火炮旋轉(zhuǎn)瞄準(zhǔn)時(shí)散布圈會(huì)變大,當(dāng)靜止時(shí),散布圈會(huì)變小。
那么第一步是記錄火炮的旋轉(zhuǎn)狀態(tài),這里簡單點(diǎn)寫,把偏轉(zhuǎn)軸和俯仰軸合在一起計(jì)算狀態(tài),也就是不管哪個(gè)軸在旋轉(zhuǎn),都會(huì)實(shí)時(shí)影響精度。
接著根據(jù)旋轉(zhuǎn)狀態(tài)來更新精度因子。先以落點(diǎn)為圓心,精度乘以一個(gè)常數(shù)作為半徑畫一個(gè)圈來觀察下邏輯是否正確。

落點(diǎn)散布
但是我們應(yīng)該能猜到,實(shí)際的落點(diǎn)散布形狀肯定不會(huì)是個(gè)圓,因?yàn)槠D(zhuǎn)角和俯仰角分別對(duì)落點(diǎn)位置的影響系數(shù)不同,那么投影到地面上的形狀肯定不會(huì)是一個(gè)規(guī)則圖形。但是直接這么想有點(diǎn)太過抽象,所以干脆試著讓不同的炮口擾動(dòng)形狀投影到地面的形狀顯示在場景中。
以四個(gè)最大偏轉(zhuǎn)角度為端點(diǎn)連接起來會(huì)形成一個(gè)矩形,我們假設(shè)偏轉(zhuǎn)和俯仰的擾動(dòng)角相等并且實(shí)時(shí)精度系數(shù)也相等,那么就相當(dāng)于用炮管畫出來一個(gè)正方形。

然后遍歷這個(gè)“框”計(jì)算彈道落點(diǎn),把落點(diǎn)形成的形狀顯示在場景中。

這個(gè)形狀有點(diǎn)像汽車的前車窗,上下兩個(gè)邊是弧形,并且隨著落點(diǎn)遠(yuǎn)近,長寬都會(huì)發(fā)生變化。
但是在實(shí)際情況中,炮口的隨機(jī)抖動(dòng)在兩個(gè)軸的角度上都取到極值的幾率很低,兩個(gè)擾動(dòng)角都取更接近實(shí)際的正態(tài)分布的隨機(jī)值的話,炮口出射角的分布形狀應(yīng)該更接近一個(gè)圓。所以用同樣的方法再來計(jì)算一下圓形炮口擾動(dòng)的落點(diǎn)散布形狀。

此時(shí)散布形狀就變成了一個(gè)上面較寬下面較窄的近似橢圓,與原型功能的散布形狀相似,這也從側(cè)面也解釋了游戲里散布形狀的由來。

收尾與完善
到這一步基本上要實(shí)現(xiàn)的功能都完成了,剩下的就是補(bǔ)齊之前用Gizmos替代的功能,如彈道線、散布圈等,還有炮彈的發(fā)射、爆炸特效,攝像機(jī)移動(dòng)等一些瑣碎功能。這些東西比較簡單,就不展開細(xì)說了,對(duì)實(shí)現(xiàn)方式感興趣的同學(xué)可以直接查看工程。
最后的效果如下圖:

工程鏈接: https://pan.baidu.com/s/1u3Z94yD8UzSmvBaz9wkWQg?pwd=6g2a

皮皮關(guān)與網(wǎng)易聯(lián)合開發(fā)了完備的游戲開發(fā)線上課程。想要進(jìn)軍游戲開發(fā)領(lǐng)域的童鞋,可戳下面鏈接了解課程詳情:
https://ke.study.163.com/course/detail/100103487
如果有任何問題,還可以通過以下入口與網(wǎng)易小伙伴取得聯(lián)系,有什么問題直接正面懟他(戰(zhàn)術(shù)后退

同時(shí),歡迎加入游戲開發(fā)群歡樂攪基:1082025059