1. 程式人生 > >2D動作遊戲開發與實現 翻譯

2D動作遊戲開發與實現 翻譯

因為單純的2D遊戲開發過於簡單,所以本文嘗試結合不同的2D平面遊戲並將之歸類,列出他們的優缺點,然後討論下一些實施的細則。長遠目標是在2D平面遊戲開發方面的指導做的十分全面。如果有任何建議,更正,要求或者補充,請留言!

宣告:有些文章的資訊是通過反編譯引擎的,並不是通過程式碼或者程式設計者本身。很有肯能他們不是通過這種方法實施的,只是看起來是這樣而已!並且遊戲tile的大小是為了遊戲邏輯設定的,和實際是有出入的!

四種方案

由簡單到複雜,我先將我知道的四種2D遊戲常見實施方案列舉出來:

方案1: 純粹的瓦片渲染

在這種模式中,遊戲角色的移動範圍其實是完全被地圖Tile限制死的,正常情況下,角色絕對無法站在兩片相鄰Tile的中央的位置。

而我們肉眼所見的漸進式角色移動(動畫),也只是為了讓遊戲看起來比較流暢的權宜之計,它最終到達的也只是某一瓦片的範圍之中,而不可能同時定位於兩塊瓦片之間(比如角色可以在(0,1)和(0,0)這兩個座標間任意移動,但每次實際的位置判定只能取這兩個座標之一,不能出現位於(0,0.5)這種定位情況)。

這種方案是最容易實施的,但暴露了很多嚴重的角色控制方面的限制,尤其是做傳統動作遊戲時,這是肯定不適合的。不過,這種方案在處理畫素風的遊戲或者卡通風格的遊戲中卻出乎意料的受到歡迎。

例子:波斯王子,Toki Tori, Lode Runner, FlashBack

如何實現?

因為地圖是網格狀的,所以每一塊Tile區域(通常以2維陣列形式存在)都儲存都著是否是障礙物,能否通行的資訊。

當然,有時也可以額外包含用什麼圖片表示,角色在此處腳步聲音的資訊,以及其他種種。一般來說,在這種模式中的遊戲角色和其他遊戲用單元通過一套統一的切圖表示(素材由統一的紋理切分而來),但遊戲角色或遊戲單元佔用多少地圖瓦片卻是不固定的。比如在Lode Runner這款遊戲中,遊戲角色佔用的地圖區域是1X1的Tile。

而在遊戲Toki Tori中,遊戲角色則佔用了2X2的Tile。至於在Flashback中,由於背景用了不常見的小Tile,遊戲角色甚至變成了2個Tile寬,站立的時候5個Tile高,匍伏的時候3個Tile高。

通常在這種型別的遊戲中,遊戲角色很少會進行對角線移動。但如果將移動分解成兩個不同方向,也還是可以做到的。類似的,每次角色移動時只移動一畫素位置是很常見的做法,而連續的快速移動,我們則可以通過多次移動畫素位置來達到。具體判定移動的運算過程如下:

1、首先,提前拷貝角色位置資訊,代入到他想移動到的地方(比如,向右移動時,每次拷貝一個位置到右邊一格)。

2、檢查這個拷貝與背景是否有碰撞。

3、如果發現碰撞,運動停止,作出反應。

4、否則,可以通過。把角色移動到此處。(可以加入動畫)

這種方案對於弧線運動的操作十分之差,所以此類遊戲一般不會出現這樣的運動,或者只有上下左右的運動,因為這裡只考慮了直線運動的情況而已。好處是這個方案的簡單和準確性。因為處理的資訊量很少,所以遊戲角色被卡住的狀況極少發生,遊戲的可控性也會十分之強,甚至不需要對不同情況進行微調。

開發基於這種機制的遊戲是十分簡單的,你要做的只是檢查使用者Tile和背景的Tile,然後對齊它們並且適合賦予的動作。

原則上講,這種模式是肯定不允許一個小於Tile的角色存在的,不過,我們也可以通過不同的方法來解決。比如,背景Tile可以設定的比遊戲角色的Tile小些,或者你可以在某個Tile內做些視覺上的縮小效果,但也並不會影響遊戲邏輯。(這個很可能是Lode Runner-The Legend Returns採取的方法)。

方案2:平滑的瓦片渲染

一般通過構建專用的Tiemap物件,檢測通過Tilemap的具體角色單元來實現(很多遊戲引擎中都內建有Tilemap類或類似作用類)。與上一方案不同的是,該模式中角色可以在地圖裡面自由移動,角色座標不受網格制約(對於移動畫素小於1畫素的情況,選擇取整)。這在當年的8位(如FC)和16位(如MD)遊戲機器上也是十分常用的技巧,而今天也同樣在使用。它的開發十分簡單,而且對於地圖編輯來說也並非太過負責。同時也能支援斜坡和平滑跳躍的動作。

如果你不確定你要在哪個平臺上面實施遊戲開發,並且你就只是想做個動作遊戲的話,我建議你採取這個方案。它十分靈活,相對容易,堪稱在四種類別中最容易掌握。這也解釋了為什麼絕大多數遊戲開發者都會使用它。

例子: 超級瑪麗, Sonic the Hedgehog, Mega Man, Super Metroid, Contra, Metal Slug

如何做?

就其基礎部分而言,地圖中瓦片資訊的獲取和純Tile渲染方式是一樣的,不同的僅僅是玩家和背景的互動演算法。在Tiemap中,遊戲角色的碰撞單位是一個軸對齊的盒子(AABB演算法,可以把它理解為一個無法旋轉的矩形),它通常會等於單獨Tile的整數倍數大小。一般來說,應該是1Tile寬,1Tile高(小馬里奧)或者2Tile高(大馬里奧)甚至3Tile高。在許多遊戲例子裡面,角色的貼圖是大於邏輯上碰撞單位的,這是為了更友好的視覺效果和遊戲體驗(比起防止讓玩家碰到不該碰到的地方,更應該防止他碰不到應該碰到的地方)。比如,你的角色貼圖約等於2個Tile寬,實際上你要碰撞的目標只有一個Tile寬。

假設沒有斜坡和單方向的平臺,這個演算法是很直接的:

1、分解動作到x和y軸,移動一次。我通常採取y軸,但你可以在兩個方向都移動非常大的一個跨度。

2、得到目標物件(前進面)邊緣的座標。比如,如果是向左移動,則是碰撞單位 的左邊的x軸。如果是右,則是右邊。向上則是上面。同理向下。

3、算出與那個Tile的邊線相重疊-得到最小和最大的正交軸的直(比如我們向左走,那麼正交軸就是y,然後玩家與32,33,34幾個tile發生重疊,那麼就可以算出貼圖在這個方向的範圍: y=32*TS, y=33*TS, y=34*TS, TS是tile的大小)

4.、在自己控制的角色未動前,算出移動方向上的靜態和所有動態障礙物中間座標值,何處離你最近,有多近。

5、玩家在那個方向上的可移動距離,將是最近障礙物離你的距離和你一開始想移動距離的最小值。

6、根據以上計算得出移動後遊戲角色的新位置,更新座標。

斜坡

斜坡計算(綠色箭頭指的),通常是十分棘手的,因為它雖是障礙物,卻又允許玩家部分移動進入它的Tile內。而同樣的演算法,X軸方向移動會引起Y軸變化。一種解決方案是,讓Tile記錄兩次Y值(左右兩邊也就是Math.floor(y))。假設(0,0)為座標左上角,那麼角色左邊的tile的 y為(0,3),站在上面的那個是(4,7),然後是(8,11),然後才是(12, 15)。接下來,是上面的那段Tiles的重複。再接下來是更陡的斜坡(0,7)和(8,15)。

下面的系統是允許任意斜坡的,由於感官上的原因,那兩種斜坡是最常見的,總共是12個種類的tile(6個前面描述的和和他們的映象)。

碰撞演算法(橫向運動):

*  在碰撞檢測階段(第四步),斜坡演算法將只把玩家靠近高邊緣(小y值)算作一次碰撞,那麼玩家就不會看見角色有點在斜坡上的“抽風”(來回震顫)。

*  也許你禁止斜坡去阻止“半穿越”,很多類是Mega Man X一樣的遊戲都採用了這個限制。但如果你不想去限制,那麼玩家從鞋面的低處爬坡的情況就會變的很複雜。一個方法是去預先處理,把這些麻煩的斜坡進行標註。然後在碰撞檢測的階段,如果玩家爬坡的時候在y軸上的最小值(底邊)是大於斜坡的邊緣高在y軸上的值(tile coord*tile size + floor y),就算作一次碰撞。

*  碰撞處理好後,玩家可以開始移動(第6步),但縱向位置需要調整。當上坡的時候,這相對比較容易:以玩家碰撞盒子的下邊緣中點在座標軸裡面的值,計算它的高度:首先,找到處於哪快地面tile。如果是斜邊,在當前的座標系統中計算它的floor y。比如這樣:t=(centX-tileX)/tileSize; floorY=t*leftFloorY+(1-t)*rightFloorY。移動玩家來找到最低的位置(假設玩家的固定在貼圖的下邊緣中間,那就是這點)。

*  下坡有點棘手。一種方法是計算玩家移動前地面上多少個 畫素,然後移動後又有多少個(運用上面的公式),然後調整他的高度使這兩個一樣。這種方法對於上下坡都可以運用。另外的方法是引入重力,但十分麻煩。

*  非斜坡的障礙物tile,如果它和斜坡相鄰,是不應該在碰撞檢測中考慮進去的。如果玩家處於(0, *)位置,左上至右下方向出現斜坡,那就忽略他左邊的tile。如果是(*,0)左下至右上方向斜坡,那就忽略右邊的。如果玩家大於兩個tile,那麼要忽略更多的tile。這麼做是為了防止玩家爬坡時候卡住(黃色區域)。

*  當向下的時候,不用考慮斜邊tile的上邊緣作為碰撞邊界,相反,計算它在當前縱向的floor座標(使用同樣的演算法)。

One-way platforms


此處對比超級馬里奧,展示了馬里奧下墜和站立在同一個單方向平臺的情景。

所謂One-way platforms(單方向平臺)就是你可以站立,也可以跳躍穿過的那種平臺。而另一種說法是,如果你站在上面了,就算是障礙物,否則將是可以被穿越的。這是理解這類平臺的關鍵。演算法也要做如下改變

*  在x方向上,永遠沒有障礙物

*  在y方向上,如果在玩家運動之前已經處於它的上方了,則是障礙物。(玩家的碰撞盒子的下底邊至少是處在單向平臺的上方的1畫素)。在移動之前,你必須儲存玩家位置的資訊,才可以檢查到這個狀態。

有些人似乎認為,當玩家y軸的速度是正向的時候(向下)把單向平臺算作障礙物是不錯的想法,但這個想法是錯誤的。因為玩家有可能在跳躍的時候和平臺重疊,但下落的時候腳步並沒有接觸平臺。這那種情況下,玩家應該繼續下落才對。

有些人則想要允許玩家從此類平臺跳下,有幾種相對比較簡單的演算法來解決。比如,在某一幀的時候禁止單向平臺,然後保持玩家至少y方向速度為1(那麼玩家在下一幀的碰撞檢測中就不必考慮具體的遊戲碰撞單元)。或者檢查玩家是否正好在單向平臺的上方,如果是的話,手動把玩家往下移動一個畫素來達到穿越平臺的效果。

Ladders

Ladders(樓梯)

遊戲中的樓梯,看起來需要十分複雜的運算才能實現。但實際上,它不過是最簡單的一條IF語句:當你在樓梯上面的時候,你可以忽略大部分的碰撞檢測,用新的規則來定義就好了。大多數人的做法,是將樓梯定義為只有一個tile寬。

進入樓梯的的兩種方法:

*  讓你的玩家碰撞盒子與樓梯重疊(比如LGame中,就直接算角色的getRectBox是否碰撞了Tile),可以是空中的也可以是地面上的,然後向上。

*  讓你的玩家站在樓梯正上方,先用上述的單向平臺方法處理,然後向下。

有時,這些方法會突然讓玩家的x方向的值與樓梯tile對齊。如果從樓梯向下爬的話,移動y軸就可以讓玩家實際上處於樓梯的內部了。而在此時,有些遊戲會用不同的碰撞盒子來判斷玩家時候處於樓梯上面。比如Mega Man,似乎就用了單tile來做這個事情。

離開樓梯的方法也有幾種:

*  到達樓梯的頂部,這會產生一個把玩家向上推動幾個畫素的動畫。

*  到達懸梯的底部,這會產生一個簡單的下墜,儘管有些遊戲會不讓玩家離開樓梯。

*  向左或者向右。如果沒有障礙物,則允許通過。

*  跳躍。有些人允許玩家在樓梯中做此類動作。

在樓梯上,玩家的行動也會改變。通常是隻能上下移動,或者做些攻擊之類。

Stairs

Stairs(階梯)


階梯是樓梯的變體,在Castlevania系列中十分著名。實施方法其實是很簡單的,基本與樓梯相同,但有幾處例外要提前指出:


*  玩家通常採取一個一個,或者半個半個tile移動


*  每次移動玩家在x和y方向移動相同的距離


*  初始的重疊檢測會檢查前面一個tile而不是當前的tile。


某些遊戲也有象斜坡一樣的階梯。實際上,它們大多隻是在視覺上做了些效果。

Moving Platforms

Moving platforms(可移動平臺)

可移動的平臺看起來也很棘手,但實際上卻也是很簡單的。不過與通常的平臺不一樣,它是運動的,不能用固定的tile來表示(事實上,多數時候可移動平臺算作Sprite(精靈))。所以一般會用碰撞盒子(AABB)來表示。在碰撞監測中算作障礙物。

實施移動平臺的方法有幾種。一種演算法是:


1、在螢幕中人和物體移動前,判斷玩家是否處於移動平臺上面。這個可以通過檢查玩家的下底邊中點是否正好1畫素高於平臺表面。如果是這樣的,在平臺上儲存一個處理方法然後玩家現在的位置。

2、移動所有的移動平臺,讓它在玩家移動前發生。

3、對於每個站在平臺上的角色,算出所在平臺的平移量,然後把每個角色都移動相同的平移量。

Other Features

有些遊戲有更復雜的特性,不如Sonic the hedgehog系列在這個方面十分著名,這些超過了作者的知識範圍,所以不再深究(這種實現,可以使用LGame的Polygon建立形狀,然後結合瓦片地圖進行碰撞計算)。

方案3:畫素位掩碼計算

效果上類似於方案2,但在處理碰撞關係時卻不是比較Tile與遊戲角色的大小,而是直接使用一整張圖片做背景,然後通過對背景中每個畫素和遊戲角色的圖片畫素進行碰撞檢查,再得出移動關係。

這種看似更細緻的處理方式,卻也提高了遊戲的複雜度,並且記憶體佔用也會上升很多。同理,對地圖的編輯處理也有很高的要求(美工的噩夢)。因為Tile不能用來建立檢視了,也就導致了每層的單元,都要手工繪製得出(苦逼的美工~)。也因為這些問題,這個方法不是特別常用,但會比Tile產生更高質量的遊戲結果。對於動態場景也是挺適用的,比如某些可破壞的場景(對美工有仇者,就可以天天讓他們做這種地圖玩~)。

例子: Worms, Talbot’s Odyssey

如何做?

其實,筆者在製作此類地圖的原始想法,與第二種方案很類似:即把每個畫素想象成一個Tile,然後實施一模一樣的演算法。不過,這種想法隱含著一個重大的差別,那就是斜坡的處理。因為,現在的斜坡是被周圍的Tiles隱性定義的,前面的方案也就不再適合了。所以,我們也需要採用更復雜的演算法(另外,樓梯處理也會相應的變得十分棘手)。

Slopes

Slopes(斜坡)

這種方案之所以難以實施,很大程度上是因為斜坡的存在。不幸的是,斜坡在很多遊戲中是必須存在的場景。

下面粗略的描述了Talbot’s Odyssey對於斜坡的演算法:

*  通過加速度和縱向速度計算每個軸方向的位移。

*  軸方向移動時優先適用位移較大的那個方向。

*  對於縱向,把碰撞單位(AABB)向上移動3個畫素,這樣達到爬坡的目的。

*  提前掃描座標,通過檢查所有實體存在的障礙物和點陣圖本身來決定玩家在碰到障礙物之前能移動多少距離。然後把玩家移動到新位置。

*  如果是縱向移動,把玩家在斜坡能往上移動多少就移動多少,最多3個畫素。

*  如果移動結束後,發現玩家與某個障礙物重疊。取消此次移動。

*  無論結果如何,在另外一個方向上繼續以上述方法移動。

因為此種方案對於玩家正在下坡移動或者下墜根本沒有區分(都是算畫素),你就只需要建立一個系統去計算距離上次玩家在地面上已經過去多少幀了。這樣,一但滿足判定標準,我們就能根據判斷結果,處理角色是否可以跳躍或者開始某種動畫了。在Talbot中,通常是十幀一處理。

另一個竅門,是在碰撞發生之前就有效的計算出來可以移動的畫素。不過有些因素會使計算比較複雜,比如單向通過的平臺,向下滑的陡坡。總之,這個技術需要很多調優,並且相對第二種方案來說十分不穩定。所以除非你十分需要的(比如調戲美工妹妹-_-),一般不推薦使用。

方案四:向量地圖

這種技術採用了向量資料(線或者多邊形,比較典型的是使用SVG,可伸縮向量圖形)來判斷碰撞的邊緣。雖然實施起來很難,但由於物理引擎的大行其道,這個方案還是很受歡迎的。這種方案即提供了bitmask方案的優點,又對記憶體計算消耗不大,還很便於圖層編輯。

例子: Braid, Limbo

如何做?

一般有兩種解決方案:

*  自己計算碰撞,類似於bitmask方案(方案3),使用多邊形碰撞來計算反射或者滑動。

*  直接上游戲引擎……

基本上,大多數人都會選擇第二種方法,這不僅僅 是方便簡單的原因。更重要的是,你可以把心思和精力花在其他更重要的地方。但有一點需要注意:如果僅僅通過預設設定開發,那麼你的遊戲將和其它同款引擎開發出來的遊戲極度近似,變得毫無特色可言。

Compound objects

Compound objects(複雜物體)

在此方案中,存在著非常獨有的缺陷,它可能會突然無法判斷玩家是否正在地面上(大多出於取整原因),或者撞到牆了,或者通過斜面向下滑動之類。用遊戲引擎的話(Box2D之類),你還需要考慮摩擦力,移動的時候比較大,滑行的時候比較小之類。

而處理這些情況的比較通用方案,是把遊戲角色劃分成幾個多邊形。每個多邊形代表不同的東西:比如,你會有主體軀幹,兩條細長的腿,一個方方的頭部;甚至通過不同的多邊形來組合之類。有時候,我們可以通過這些小小的修改,來避免角色被障礙物卡住。遊戲角色的不同組成部分,它們大多也會具有不同的物理屬性,或者碰撞反應。要獲得更多資訊,還可以使用感測器監聽。通常會用它來判斷條約前是否離地面太近,或者玩家是否在推牆(反覆撞牆)。

全域性考量

不論你在進行何種2D遊戲開發,你都必須考慮到一些全域性因素。

Acceleration(加速)

角色的加速度對於遊戲玩家來說,將是有很大影響的部分。

加速,通常是指遊戲角色速度的突然改變(上升),它大多包含實際的移動畫素增加,以及短暫的加速動畫兩部分。當遊戲角色速度很慢的時候,角色一般需要花很長的時間去達到最大速度,但這時的加速動畫,則可能半途被停止下來(如果玩家鬆開按鈕)。這樣,又很可能會讓玩家覺得角色很遜,很難控制。而當角色速度已經很快的時候,則很容易加速到最大速度,但是因為動畫變更的關係,它又很可能猛然卡住一下。這樣看起來,動畫和加速的組合關係十分敏感,我們編碼時也要十分小心。

即使一款遊戲橫向沒有加速度,它至少會在跳躍的時候加入這類因素。不然,跳躍的弧線就變成三角形了。

如何做?

實現加速通常很簡單,但要注意幾個地方:

*  注意x方向上面的速度,如果控制按鈕沒有按下去的話,加速度就應該是0(具體設定的話,可以向左鍵一直按著會達到最大負速度,右邊的話是最大正速度)。

*  注意y方向速度。在平地上是0,下落的時候達到最大。

*  對於每個具體方向,可以把當前速度加速後的最大速度,用每個方向的權重來平均下或者直接加入速度值中。

兩種加速方法如下:

*  權重平均:加速度是一個介於0(無變化)和1(瞬時加速)的數字。用那個直去對目標速度和現在速度進行線性插值。把結果作為現在速度。
vector2f curSpeed = a * targetSpeed + (1-a) * targetSpeed;if (fabs(curSpeed.x) < threshold) curSpeed.x = 0;if (fabs(curSpeed.y) < threshold) curSpeed.y = 0;

*  加速度:先判斷在哪個方向上新增加速度(用sin函式),然後檢查是不是加速過多。

vector2f direction = vector2f(sign(targetSpeed.x - curSpeed.x),sign(targetSpeed.y - curSpeed.y));curSpeed += acceleration * direction;if (sign(targetSpeed.x - curSpeed.x) != direction.x)    curSpeed.x = targetSpeed.x;if (sign(targetSpeed.y - curSpeed.y) != direction.y)    curSpeed.y = targetSpeed.y;

在移動角色前把加速度整合到角色速度上面是很重要的,否則在角色畫面顯示上,你會發現總有一幀的落後。當碰到障礙物後,應把對應方向的加速度設定為0。

Jump control

在2D平面遊戲中,跳躍只需要檢測玩家是否處於地面就好了(或者在前幾幀的時候,是否在地面上)。如果在的話,設定給角色一個初始的負y速度(或物理上說的“推動力”)然後再讓重力做剩下的事情。

通常玩家有四種控制跳躍的方法:

*  推動力:在超級馬里奧遊戲中經常看見。角色在跳躍之前,蓄好動能。在有些遊戲中,這是唯一可以影響跳躍弧線的方法,與現實生活一樣。你其實不需要實現什麼,除非你強行干預去停止它。

*  空中加速:在空中的時候保持對於橫向的移動控制。雖然在現實生活中是不可能實現的,但在遊戲中這個特性很受歡迎,這會讓玩家更容易控制角色。幾乎所有的平面遊戲都會有這個特性,但波斯王子是個特列。通常空中的加速度減少的十分快,所以推動力十分重要,但有些遊戲(Mega Man)會給力完全的空中控制。這個僅僅是在空中的時候稍微修改下加速度引數就好了。

*  上升控制:另一個物力上無法實現的行為,也是很受歡迎的。你按住跳躍按鈕越久,你就可以跳得更高。這種特性,通過不斷得補充推動力給角色(推動力的大小是遞減的趨勢)來達到,或者也可以通過克服重力影響也可以達到。不過這種補充是有時間限制的,不然你會看到你的角色“一飛沖天”(遊戲王zexal主角臺詞)。

*  多重跳躍:一旦到了空中,有些遊戲允許玩家再次跳躍,甚至會無限制的跳躍,直到玩家到達地面(兩次跳躍一般是被大家統一採用的)。這種方法可以在遊戲內部增加一個計數器來達到。這個計數器在每次跳躍時自動加1,然後到達地面後,自動變成0。只有在計數器沒有超過限定值的時候,才可以繼續跳躍。通常第二次的跳躍會比第一次的短很多。另外一個限制是,空中跳躍只有在角色完成了旋轉跳躍且開始回落的時候才可以被觸發。

Animations and leading

在許多遊戲中,你的角色在實際執行你的命令前,都會先執行一段預先設定好的動畫。如果在對操作有很高要求的遊戲中,這可能會讓你的玩家抓狂的。所以,此類遊戲裡千萬不要這麼做。但是,你仍然可以加入嚮導動畫(在跳躍和跑動之前)。如果你在意遊戲的響應速度,最好只是裝飾下而已,然後立刻讓角色對命令進行反應。

Smoother movement with pseudo-float coordinates(更加平滑的移動)

相對使用浮點計算而言,用整形來代表玩家位置是很明智的選擇,因為這會讓計算變得更快更穩定。但是,如果你把什麼都整形了,你會發現移動十分卡(因為所有位置都取整了)。對於這種情形,有一些處理方案可供參考:

*  用浮點型資料來計算和儲存位置資訊,在計算和表現碰撞的時候轉成整型,很快也很簡單。但如果你從原點開始移動,會發現偏差會很大。不過,如果你的場景十分小,這點差異是不那麼明顯的。但是記住,如果真碰到了,你可以用雙精度來進行計算修正。

*  用固定長度數字來計算位置,同樣在碰撞的時候轉成整型。雖然場景大小限制更大,但精度是全域性統一的,在某些硬體上更快(比如手機上)。

*  把位置資訊儲存成整型,把小數點後面的數字轉化成浮點型。當計算未知位置的時候,把位移按照浮點來算,然後加上小數點後面的數字,再後把整數部分加到未知整數上面,把小數部分加到未知的小數上面。在下一幀的時候,同樣這麼計算。這樣做的好處是,你只有在移動的時候才會用浮點型,而其他地方都是整型,可以提高整體效能。