關於制作賽車游戲的一些入門知識(一)


題外話

這是我的第一篇博客。話說對於一個在碼界已經混跡了7年的人來說,現在才開博的確是一件令人費解的事情。這也怪我本人性格比較奇葩,生性懶惰,好浮游潛水。然而三個月前我辭職了,現如今突然有了一刻閑暇,恍然意識到在過去7年中自己敲過的代碼遠多於敲過的人話。為了能夠順利地回歸自然界,我試着從今天開始養成隔三差五寫博的習慣,練練人話水平,好讓自己今后能看起來更像個生物。

 

前言/背景

在過去的幾個月里,我拉着幾個死黨一起搞了一個iOS賽車游戲。由於當時還在上班,所以我一邊白天上班,一邊晚上+周末倒騰這個游戲。盡管這只是一個很簡單的2D游戲,但我卻在里面用了一些比較‘有趣’的方法來使這個游戲能看上去比較生動,這其中包括了一些真實的車輛驅動原理以及一些基礎的物理受力分析。接下來我准備用幾篇文章來簡單介紹下這些方法,同時我也希望這些知識能夠對初學的你提供一些幫助。

我總共計划會寫3篇關於此的文章:

第一篇主要是對車輛的驅動原理以及車輛在直線行駛下的受力分析做一個簡單的介紹;

第二篇結合第一篇的內容,再加上對車輛轉向情況下的物理分析,最終得出一個更完善的解決方案;

第三篇主要是淺談關於引擎聲音模擬方面的一些方法和常用工具。

當然,這幾篇文章僅僅只是一個比較初級的基礎教程,比較籠統,意在能夠帶領初學者迅速的入門。如果你的需求是進階提高,那么這些教程並不適合你。事實上對於初學者來說,這幾篇教程會讓你理解到一個大致的物理原理和實現流程,這對於你日后去研究那些更成熟的賽車引擎來說,會有一些幫助。而且,如果你玩過我的游戲,你也會發現這些基礎的知識在我的游戲里是完全夠用的。

此外,本教程更注重的是說明一些知識和原理,而非展示實際的代碼。事實上本人歷來本着現實世界的原理高於編程語言的觀點,始終認為五花八門的編程語言僅僅只是工具而已,相比之下,搞清楚真實世界的道理我認為更加重要。因為最終我們還是要落實到運用工具解決現實的問題這一點上。因此,只要道理搞清楚了,你可以用oc,可以用c/c++,也可以用js,等等,那僅僅只是根據平台,選個工具而已。(注:我的游戲用的是oc,游戲引擎是cocos2d-swift, 物理部分基於內置的物理引擎chipmunk)

現如今我的這個游戲已經上線了,叫3 Lanes。如果你們有興趣的話,可以點擊這里下載,它是免費的。 

 

接下來我們進入正題

首先我們來談談車輛在直線行駛下的受力分析。

如上圖所示,當車輛沿直線行駛時受到的合力F可表示為:

[公式1]  F = fraction + drag + rollingResistance

其中,fraction是車輛受到的牽引力,我們稍后來講牽引力的計算;

drag是空氣阻力,詳細的計算方法是:

[公式2.1]  drag = -0.5 *p * A * Cd * v *|v|

這個公式看上去貌似很嚇人,但僅僅只是看上去。因為在這其中,p是空氣密度,常量;A是車輛的迎風面積,對於同款車來說也是一定的;Cd學名叫“流體動力阻尼系數”,管他是什么鬼,總之還是一個常數。因此,我們不妨暴力的把[公式2.1]的前面那一堆捏到一起,dragFactor=0.5 *p * A * Cd,最后得:

[公式2.2]  drag = -dragFactor * v *|v|

這樣就簡潔明了多了。其中dragFactor是一個參數,由於此參數里包括了迎風面積A,不同的車輛迎風面積不同,因此你可以給不同的車輛設置不同的dragFactor參數; 而v是車輛的速度,-v *|v|保證了drag的方向與速度方向始終相反。

這樣空氣阻力drag我們就扯完了。

接下來我們回到[公式1],再來看看rollingResistance又是什么鬼。這個叫滾動摩擦力,這是由於橡膠輪胎與地面接觸的部分被擠壓變形而產生的一種阻礙輪胎運動的摩擦力(注意這可不是‘滑動摩擦力’。回憶回憶當年高中老師給你講的自行車驅動時的情形,如果在輪子與地面之間不打滑的情況下,是不存在滑動摩擦力的。輪子靠着靜摩擦力的‘推動’而前進)。如果你覺得滾動摩擦力不大好理解的話,那么可以想象一下,騎自行車,在輪胎快沒氣的時候和剛打完氣的時候,哪種情況下騎着更累?顯然是快沒氣的時候,因為這種情況下輪胎形變更大,因此你要克服更大的滾動摩擦力。rollingResistance的計算方法如下:

 [公式3]  rollingResistance = -load * g *cRR

如果單個車輪分開算,那其中load就是單個車輪的載重(kg),當然你也可以直接用車輛的總質量代替load,這樣你求的rollingResistance即是4個輪胎滾動摩擦力之和;g是重力加速度;cRR叫做滾動摩擦系數,跟路面的材質有關。通常來說干燥的瀝青路面cRR取值一般是0.01左右。

注意,關於滾動摩擦力公式,我也在另一些資料上還看到過另一種說法:

[公式3*]  rollingResistance = -cRR * vLong

在這種公式下,vLong是輪胎沿其滾動方向的速度分量,注意它不一定和車速v相同。我個人感覺[公式3*]是一種近似的做法,並且認為rollingResistance與輪胎的速度有關(間接與車速有關)。而且此時的cRR也和原來[公式3] 的cRR也不是一個概念,它一般取值會是空氣阻力系數dragFactor的30倍。(不大理解,貌似是一種湊出來的數字。不管,之后的教程以[公式3] 為准)

到此,[公式1]的后兩位就講完了。下面我們來說說牽引力。

我們知道,車輛行駛的牽引力,是靠車輪的滾動而產生的,而車輪的滾動,又是靠發動機的轉動通過傳動裝置而帶動的。我們把這個過程稍微描述得具體一點:

首先發動機燃燒燃油做功,帶動了某個連桿運動,因此產生了一個力矩(扭矩)。之后該力矩通過了一個傳動裝置,期間經歷了一些變化(放大或縮小),然后被傳導到車輪上。最后車輪在力矩的作用下發生了滾動,最終構成了車輛前進的牽引力。因此我們要做的,就是用一些公式來描述這個過程。

讓我們先從車輪看起。根據力矩公式,我們知道,車輪上產生的牽引力可以表示為:

[公式4]  fraction = torqueWheel / r

其中,r是車輪的半徑, torqueWheel是作用在車輪上的力矩。剛才說了,作用在車輪上的力矩torqueWheel是由發動機輸出的力矩torqueEngine經傳動裝置傳導過來的,並且torqueEngine在經過傳動裝置的時候發生了一些變化,這些變化具體包括如下幾個部分。

1. 變速箱。本質上是一系列齒輪組。由於兩個相互咬合的齒輪彼此半徑大小不同,因此轉動時兩者角速度不同,從而導致了力矩通過該齒輪組后其大小發生了變化。一般來說,變化后的力矩 = 原始力矩 * 這兩個齒輪的半徑比值gearRatio。而我們知道變速箱存在多個檔位,其中每一個檔位對應着一個不同的齒輪比值。因此,對於你要實現的換擋邏輯來說,其本質就是切換不同的變速箱gearRatio而已。

2. 最終差速(differential,不知道中文這么說對不對,反正我不會開車...)。這是除變速箱外另一個齒輪組,其本質作用是放大發動機輸出的力矩。一般用final drive ratio這個詞代表它的齒輪比(本文用的是differentialRatio,中文貌似叫最終傳動比)。比如,BMW m3 coupe的differentialRatio你能查到是3.38。

3. 傳動效率。顧名思義,通常機械結構都會有一定的損耗。我們用efficiency來代表有效的傳動效率。據說一些好車的傳動效率能達到90%以上。

綜合上述幾點,最終我們可以把車輪上的力矩與發動機輸出的力矩的關系表示為:

[公式5]  torqueWheel = torqueEngine * gearRatio * differentialRatio * efficiency

那么,問題來了,[公式5]還有一個未知量,發動機的輸出扭矩torqueEngine怎么算?答案也許會讓你意外:查表!

沒錯,每一款發動機,廠家都給出一份形式如下的表,這一般是通過實際的實驗得到的,它表示了發動機輸出的扭矩和發動機轉速之間的對應關系。

我們假設上圖是‘騾子’牌發動機的扭矩/轉速示意圖。圖的橫軸方向RPM代表發動機的轉速(每分鍾多少轉),豎方向代表發動機輸出的扭矩。那么從上圖我們可以知道:當發動機轉速在1000轉左右時,輸出的扭矩為300牛米左右;並且在轉速達到4300轉左右時,輸出最大扭矩約405牛米;之后隨着轉速繼續增大,輸出扭矩開始減小。

如果你是一個數學瘋子,那么上面這個曲線對你來說是小菜一碟。但對於我這種凡夫俗子來說,為了計算方便,通常會將此圖表簡化為:

甚至更近一步,會繼續簡化為:

也就是說當轉速不足1000轉的時候,我仍然認為發動機輸出最小的300牛米的扭矩。這樣做更有助於維護RPM和扭矩的對應關系。

總而言之,上述曲線也屬於你車輛發動機的參數,你為不同的發動機設置不同的曲線即可,然后,我們可以把上述函數計作:

[公式6]  torqueEngine =f(RPM)

此外還要注意一點,一般來說為了保護發動機,廠家會把最大轉速限制在某個范圍內,這個最大值一般叫redline RPM,顧名思義,就是紅線,別踩紅線,對發動機不好,所以對於你來說,具體實現的時候你也同樣需要設置一個redlineRPM並且始終保持RPM不超過這個值。

最后,我們把[公式4],[公式5] 和[公式6]結合,得到最終輪胎牽引力的公式:

[公式7]  fraction = f(RPM) * gearRatio * differentialRatio * efficiency / r

到此,也許你已經注意到,把[公式7],[公式3]和[公式2.2]再帶回到[公式1],我們就能出最終結果了! 但此時先別捉急,我們還有2個東西沒考慮到,油門throttle和剎車brake。

(摘抄[公式1]到此:  F = fraction + drag + rollingResistance)

 我們不妨在[公式1]的基礎上如此考慮油門和剎車:

 [公式8]  F = fraction * throttle + drag + rollingResistance + brake * brakeForce

比較一下[公式1]和[公式8]可以看出,我們將油門throttle視為牽引力fraction的乘數,並且讓throttle的取值范圍是0-1,這意味着,當throttle為0時,我們認為是駕駛員完全松開了油門,此時 fraction * 0 = 0,即不產生牽引力;反之throttle為1時,意味着此時駕駛員油門踩滿,牽引力fraction完全參與合力的計算。

brakeForce即剎車力,不難想象它其實就是滑動摩擦力,且是定值。而brake的取值范圍同樣是0-1,為0時表示剎車踏板完全松開,為1時剎車踏板完全被踩下。

通常來說,你要在剎車的時候同時設置油門松開,但有沒有可能throttle和brake同時為1呢?? 貌似現實中是可以出現這個情況的,只是一般人不這么做吧?不懂。。。

最終,千回萬轉,我們終於又繞了回來。把[公式7],[公式3]和[公式2.2]帶入[公式8]:

[公式9]  F = f(RPM) * gearRatio * differentialRatio * efficiency / r * throttle - dragFactor * v *|v| -cRR  * load * g + brake * brakeForce

[公式9] 便是車輛在直線行駛時所受合力的最終公式。(注意load是車子總質量)。

之后根據合力算加速度,a=F/m,再用加速度更新速度,v += a * delta, 整個直線行駛的物理系統你就建好了。(delta是刷新間隔,秒,一般是每幀的時間間隔)。

 

后記

[公式9]里面還有最后一個未知量,發動機轉速RPM,這鬼怎么求?簡單提示下,最后咱不是更新了車輛的速度v嗎,可以靠這個速度v算更新后的車輪轉速,然后利用得到的車輪轉速,結合變速箱齒輪比以及最終傳動比再反推發動機轉速(RPM)。當然這里有個前提,那就是為什么我上面假設RPM小於1000的時候仍然取最小扭矩300而不是0。如果不這么做,貌似會出什么問題?你猜。

剩下的事情,就是你采用‘上網查’或者‘花錢買’或者‘自己編’的方式,收集到你需要的特定車輛的如下性能數據:

發動機的轉速與輸出扭矩的曲線圖: f(…), 

變速箱各個檔位的齒輪比: gearRatios, 

差速比: differentialRatio,

傳動效率: efficiency, 

輪胎半徑: r, 

迎風面積以計算風阻系數: dragFactor,

輪胎滾動摩擦系數: cRR(這個在賣輪胎的網站上一般能查到),

車重: load(千克),

最后是輪胎摩擦系數,並結合車重和重力加速度計算剎車力: brakeForce。

當然,這里我們還有一些忽略掉的問題,我大概提一下其中幾點:

1. 車輛加減速時的“重心轉移問題”,這個一般是用來模擬車輛懸掛的運動效果。主要是在3D游戲里面用的比較多,有興趣的話不妨搜搜看。

2. 車輛暴力起步時輪胎打滑的問題。這個也很好理解。之前我提到過車輛的前行靠的是輪胎與地面的靜摩擦力“推動”的,這意味着輪胎受到的牽引力要始終小於等於輪胎與地面的最大靜摩擦力,但如果在某一時刻(通常是起步時)牽引力大過最大靜摩擦力的話,輪胎就會產生相對於地面的滑動,此時就打滑了。要模擬這個的話,你可以考慮在得到牽引力之后,判斷牽引力是否大於輪胎與地面的最大靜摩擦力,然后… …

最大靜摩擦力約等於滑動摩擦力=mgu,橡膠輪胎和瀝青路面的話u貌似取0.9左右,記不大清了

最后,[公式9]僅僅是車輛直線行駛的情況。在下一次的節目里,我會結合車輛轉向的物理分析,最終得出一個更完善一些的解決方案。

 

補充:關於換擋

之前忘了說這個,簡單補充一下。一般來說,手動換擋就不用考慮了,讓玩家自己換擋即可;自動擋的話,我不清楚一般汽車制造商們會怎么做,也許會基於多種因素的考慮,但我猜有一個簡單的策略是依據車速來判斷是否該升降檔。上面我們曾提到,通過車速是可以反推出發動機轉速RPM的,加之RPM又存在着最大的限制,那么,如果我們反過來考慮的話,就可以計算出在各個檔位下(不同的齒輪比),當RPM最大時,車輛所對應的最大速度,然后:

當加速時,你可以直接判斷當RPM達到最大時,就升檔;

當減速時,如果當前車速小於了上一檔所對應的最大車速的話,就降檔;

另外還需要注意的是,在換擋結束的一瞬間,你需要重設RPM,具體的做法是:

換擋后的RPM = 換擋前的RPM * 換擋后的齒輪比 / 換擋前的齒輪比

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM