使用libgdx開發Android遊戲(2)——動畫

zerob13發表於2014-06-30

這是基於LibGdx遊戲開發系列的第二篇文章。閱讀本文前,請確保你已經讀過前面的第一部分.

由於本文包含了大量的內容,所以我會盡可能的切分段落來讓文章容易理解消化。我們之前已經做了一個最基本的遊戲世界,並且在這個世界裡可以通過方向鍵和觸控動作來控制 Bob 前進後退。那麼現在,就讓我們為這個角色的移動增加一些更真實的動畫和動作吧。

角色動畫

為了讓 Bob 動起來,我們決定用一個叫做 精靈動畫 (sprite animation) 的簡單技術來實現。所謂動畫,不過就是一組連續的圖片在一定時間內連續播放從而造成一種動畫的錯覺。下圖所示的就是角色跑動時候的圖片序列:

running-bob

我曾經大量的玩 Star Guard 這個遊戲,通過去分析遊戲中角色跑動的過程然後使用 Gimp 來建立我自己的跑動角色。
構建一個動畫是非常簡單的。我們只需要把每張圖片展現一定的時間,然後再展現下一張。當這個動畫圖片序列展現完畢後,我們重複這個過程即可,這樣的流程我們稱之為迴圈 (looping) 。在這裡,我們需要去確定動畫的幀長(每張圖片顯示的時間)。如果說一個動畫的 FPS 是 60 FPS,那麼就意味著我們的每過 1/60=0.016 秒就會渲染一幀新的動畫。在這裡,我們的動畫總共只有5幀,考慮到一個每分鐘180步的典型運動員的動作,我們可以算出最接近現實效果的幀長。

奔跑動畫的計算

從上面的條件我們可以知道,典型的運動員每秒的步數是180/60=3步,所以我們的動畫需要每秒播放3輪。又由於我們的動畫每秒需要展示 3 * 5 = 15 幀從而模擬出一個較為真實的專業運動員跑步的姿態(非衝刺狀態)。至此,我們可以算出這裡的幀長為 1/15=0.066秒,也就是66毫秒。

影像優化

在把圖片做成動畫之前,我們需要對圖片進行一些優化操作。在 star-assault-android 專案中的圖片檔案目前是儲存在 assets/images 的目錄下的,我們可以看到目前我們用到的圖片有 block.png 以及 bob_01.png。 我們還需要其他的幾張圖片,連續命名為 bob_02 – 06.png,這些圖片是用來做動畫的幀序列的。

由於 LibGdx 底層是基於 OpenGL 的,所以用大量的圖片作為紋理來渲染顯然不是一個好的選擇。而我們所使用的技術,是稱為 紋理貼圖集(Texture Atlas) 。 一個紋理貼圖集就是指一張包含了所有動畫所需圖片的大圖並且還帶有一個說明了這些圖片在紋理集中的偏移量和尺寸的描述資訊。貼圖集中的每一個獨立的圖片被稱為紋理區域。如果有許多的小圖,那麼紋理貼圖集也可以有許多頁,每一頁都會以單獨一張圖片的形式載入記憶體並且可以把圖片從各個紋理區域中切出來。這一塊的具體原理並不需要充分的去了解,只要懂得這樣的優化操作可以讓你的動畫圖片載入的更快更流暢就好了。

LibGdx 使用了一個稱為 TexturePacker2 的工具去建立處理這些貼圖集。這個工具可以在命令列下使用,也可以在程式中使用。如果你要用 Java 去呼叫這個程式,你可以用如下程式碼:

在這裡,請確保你的專案的 libs 目錄下已經包含了 gdx-tools.jar 這個庫。你也可以通過修改 process 方法的引數來指定貼圖集載入的目錄。
處理圖片的過程中有兩個檔案都會被處理,一個是 textures.png 另一個是 textures.pack。貼圖集看起來應該是類似與下圖的狀態:

textures

而其中的目錄結構則看起來如下圖:

Screen-Shot-2013-02-11-at-13.33.22

至此,我們確定了動畫應該怎麼是什麼樣子的,動畫的幀長並且還優化了素材圖片,然後我們就該把動畫加入到程式中去了。首先,我們把 Bob.java 當作一個最小的畫素來修改。

可以看到,我們增加了一個叫做 stateTime 的新屬性。這個屬性會跟蹤記錄 Bob 在遊戲中特定狀態下的時間,我們也會使用這個來提供 Bob在遊戲中所消耗的時間。這個時間刻度對於動畫確定當前顯示那張圖片是非常重要的。如果你不太理解這個概念,請不要擔心,你可以把動畫的每一幀想象成一個特定狀態。如此, Bob 的狀態就會從 state_frame_1 到 state_frame_2 等等,每一個狀態都持續 0.066秒,一但一個狀態超過0.066秒,Bob 就進入到下一個狀態中去。這裡的動畫類會知道當前狀態需要提供哪一張圖片,這也被稱為關鍵幀

修改最多的一個類是 WorldRenderer.java ,下面的程式碼包含了這些修改:

  • 行5——申明一個名為 RUNNING_FRAME_DURATION 的常量,用來定義跑動動畫播放的每一幀長度。
  • 行8-行11—— 一系列的TextureRegion 物件,用來表述 Bob 的不同狀態。其中 bobFrame 用來指向當前狀態的紋理貼圖。
  • 行14-行15—— 兩個 Animation 物件用來做 Bob 跑動的動畫。

然後是一個 TextureAtlas 用來載入貼圖資源。

  • 行19——修改後的 loadTextures() 函式。
  • 行20——從資原始檔中夾在貼圖資源。這裡的資原始檔是一個TexturePacker2生成的 .pack 的包。
  • 行21——把名為 “bob-01”的貼圖賦值給 bobIdleLeft(注意,這裡的名字就是真實的png圖片的名字去掉字尾,詳細可以參考 TexturePaker2的說明)。
  • 行22-行23 —— 構造一個新的TextureRegion 物件(注意使用拷貝構造,因為這裡需要的是一個全新的物件而不是一個引用),然後將這個物件的X軸翻轉,這樣我們就得到了一個一模一樣但是左右映象的 Bob 閒置狀態貼圖。翻轉操作非常有用,可以讓我們無需重複載入圖片,僅僅通過拷貝已有物件就可以建立新的物件。
  • 行24—— 把“block”貼圖賦值給相應的變數。
  • 行25-行28 —— 構建一個TextureRegion 物件陣列用來製作後面的動畫,我們知道這個動畫有五幀分別稱為:bob-02, bob-03, bob-04, bob-05 以及 bob-06 。我們用一個 for 迴圈來處理這些資源。
  • 行29——這是向左走的動畫構造的地方,第一個引數傳入的是幀長也就是0.06秒,第二個引數傳入的則是剛剛構造的貼圖陣列以便於順序播放產生動畫。
  • 行31-行38——製作向右走動畫,這部分的操作其實就是向左走動畫的翻轉,唯一需要注意的地方就是貼圖物件記得使用拷貝構造全新的物件,而不是去修改原有的引用。
  • 行40——修改後的 drawBob() 函式。
  • 行42——根據當前 Bob 面向的方向來選擇播放的動畫。
  • 行44——如果當前 Bob 已經是在行走狀態,我們就根據他的面向的方向來判斷需要處理的動畫序列,然後對其進行處理,並將當前需要繪製的幀賦值給 bobFrame。

現在你可以執行下StarAssaultDesktop 這個 App 來看看 Bob 運動時的動畫。具體的效果應該和下面的視訊類似:

你可以從 https://github.com/obviam/star-assault 獲得原始碼。同時你需要切換你的分支到 part2

你可用如下命令直接 checkout 到原始碼

你也可以點選這裡下載原始碼。

相關文章