通常情況下,遊戲開發的基本架構中,一般包括以下模組:
視窗管理(Window management):該模組負責在Android平台上建立、運行、暫停、恢複遊戲介面等功能。
輸入模組(Input):該模組和視窗管理模組是密切相關的,用來監測追蹤使用者的輸入(比如觸摸事件、按鍵事件、加速計事件等)。
檔案輸入輸出(File I/O):此模組用來讀取assets檔案片、音頻等資源。
映像模組(Graphics):在實際遊戲開發中,這個模組或許是最複雜的部分。它負責載入圖片並把它們繪製到螢幕上。
音頻模組(Audio):這個模組負責在不同的遊戲介面載入音各類頻。
網路(networking):如果遊戲提供多人遊戲連網功能,此模組就是必須的。
遊戲架構(Game framework):該模組把以上各種模組整合起來,提供一個易用的架構,來輕鬆地實現我們的遊戲。
下面對每一個模組進行詳細的描述。
1. 視窗管理
我們可以把遊戲的視窗想象成一個可以在它上面繪製內容的畫布。視窗管理模組負責定製視窗、添加各種UI組建、接受各類使用者的輸入事件。這些UI組件或許可以通過GPU等硬體加速(比如使用了OpenGL ES)。
該模設計時不是提供介面,而是和遊戲架構整合在一起,之後會有相關的代碼貼出。我們需要記住的是應用程式狀態和視窗事件是該模組必須處理的事情:
Create: 當視窗被建立時被調用的方法。
Pause: 當應用程式由於默寫原因暫停時調用的方法。
Resume: 當應用程式恢複到前台時調用的方法。
2.輸入模組
大部分作業系統中,輸入事件( 比如觸屏事件、按鍵事件)是通過當前的視窗調度(dispatched)的,視窗再進一步把這些事件派發給當前選中的組件。因此我們只需要關注組件的事件即可。作業系統提供的UI APIs提供了事件分發機制,我們可以很容易地註冊和監聽事件,這也是輸入模組的主要職責。有兩種處理事件的做法:
輪詢(Polling):在這種機制下,我們僅檢查輸入裝置的目前狀態,之前和之後的狀態並無儲存。這種輸入事件處理適合處理諸如觸屏按鈕事件,而不適合跟蹤文本的輸入,因為按鍵事件的順序並未儲存。
基於事件的處理(Event-based handling):這種機制提供了記憶功能的事件處理,比較適合處理文本輸入或者其他需要按鍵次序的操作。
在Android平台中,主要有三種輸入事件:觸屏事件、按鍵事件和加速計事件,前兩種時間使用輪詢機制和基於事件處理的機制都適合,加速計事件通常是輪詢機制。
觸屏事件有三種:
Touch down: 手機觸屏時發生。
Touch drag: 手指拖動時發生,此前有Touch down事件產生。
Touch up: 手指抬起時發生。
每種觸摸事件有相關的輔助資訊:觸屏的位置、指標索引(多點觸摸時用來追蹤識別不同的觸點)
鍵盤事件包括兩種:
Key down: 按下鍵盤時觸發。
Key up: 釋放鍵盤時觸發。
每種按鍵事件也有相關的輔助資訊:Key-down事件儲存按鍵碼,Key-up事件儲存按鍵碼和實際的Unicode字元。
加速計事件,系統不停的輪詢加速劑的狀態,並以三位座標標識。
基於以上介紹,下面定義輸入模組的一些介面,用來輪詢觸屏事件、按鍵事件和加速計事件。代碼如下:
Input.java
package com.badlogic.androidgames.framework;
import java.util.List;
public interface Input {
public static class KeyEvent {
public static final int KEY_DOWN = 0;
public static final int KEY_UP = 1;
public int type;
public int keyCode;
public char keyChar;
}
public static class TouchEvent {
public static final int TOUCH_DOWN = 0;
public static final int TOUCH_UP = 1;
public static final int TOUCH_DRAGGED = 2;
public int type;
public int x, y;
public int pointer;
}
public boolean isKeyPressed(int keyCode);
public boolean isTouchDown(int pointer);
public int getTouchX(int pointer);
public int getTouchY(int pointer);
public float getAccelX();
public float getAccelY();
public float getAccelZ();
public List<KeyEvent> getKeyEvents();
public List<TouchEvent> getTouchEvents();
}
上述定義包括兩個靜態類:KeyEvent 和 TouchEvent。KeyEvent類和TouchEvent類都定義了相關事件的常量。KeyEvent同時定義了幾個儲存事件資訊的變數:類型(type)、按鍵碼(keyCode)和Unicode字元(keyChar)。TouchEvent也一樣,定義了位置資訊(x,y)、某個出觸摸點的ID。比如第一個手指按下,ID是0,第二個手指按下 ID是1;如果兩個手指按下,手指0釋放,手指1保持,又一個手指按下時,ID為已經釋放的0.
下面是輸入介面中的輪詢方法:Input.isKeyPressed()輸入參數是 keyCode,返回相應的按鍵是否按下的布爾值;Input.isTouchDown(),Input.getTouchX()和Input.getTouchY()返回給定指標索引是否按下、對應的橫豎座標值,注意當對應的指標索引不存在時座標值是未定義的。Input.getAccelX(), Input.getAccelY(), and Input.getAccelZ() 返回各自的加速計的座標值;最後兩個方法用作基於事件處理機制的,他們返回KeyEvent和TouchEvent執行個體,用於記錄上次事件觸發的資訊,最新的事件在列表的最後。
通過這些簡單的介面和類的,我們構建了我們的輸入介面。下節繼續分析檔案處理的內容(File I/O)。
3. 檔案讀寫(File I/O)
讀寫檔案在遊戲開發中是一項十分重要的功能。在Java開發中,我們主要關注InputStream和OutputStream及其執行個體,它們是Java中讀寫檔案的標準方法。遊戲開發中,比較多的是讀取資源檔,比如設定檔、圖片、音頻檔案等;寫入檔案的操作一般在儲存使用者進度和配置資訊時使用。
下面是檔案讀寫的介面:
FileIO.java
package com.badlogic.androidgames.framework;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public interface FileIO {
public InputStream readAsset(String fileName) throws IOException;
public InputStream readFile(String fileName) throws IOException;
public OutputStream writeFile(String fileName) throws IOException;
}
上述代碼中,我們傳遞一個檔案名稱作為參數,返回一個流,具體的執行會在介面的實現中體現。同時我們會拋出一個IOException異常以防止讀寫檔案出錯。當我們完成讀寫後,需要關閉輸入輸出資料流。Asset 檔案從應用程式的APK檔案中讀取,其他檔案一般從內建儲存空間或者SDCard上讀取,分別對應上述代碼中的三個方法。