android 遊戲導引(2. 遊戲的基礎設定)
上一節已經學習了一個基本的 OpenGL 架構了,今天這一節就進一步設定一下 2D 遊戲相關的東西了。對 2D 遊戲的喜愛甚於 3D。 相信大多數人也是吧。
Table of Contents
- 1 遊戲全螢幕顯示
- 2 設定 OpenGL 的 2D 環境
- 2.1 onSurfaceCreated
- 2.2 onSurfaceChanged
- 2.3 onDrawFrame
- 3 三角形-測試
1 遊戲全螢幕顯示
2行代碼搞定,一個讓應用去掉自己的標題列,一個設定全屏,放在應用的建立函數 OnCreate 中:
public class GlGame extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN , WindowManager.LayoutParams. FLAG_FULLSCREEN); // ... 其他略 }}
2 設定 OpenGL 的 2D 環境
我們先要設計好要建立的世界定位方式,就是座標系,一般的 opengl 程式是標準的笛卡爾座標系,即左下角為原點,x橫向右延伸,y縱向上延伸。像著名的 cocos2d 引擎就是此種方式。我比較偏好另一套就是左上方為原點的描述方式,所以採用了此套座標系:
GLSurfaceView 的 Renderer,給出了三個介面:建立時,大小改變時,繪製。我們在三個函數中採取的操作如下:
- 建立時, onSurfaceCreated, 進行 OpenGL 的基本設定,如陰影平滑,深度測試啊神馬的。
- 大小改變時,onSurfaceChanged, 進行投影,視口等設定。
- 繪製,onDrawFrame, 我們後續的遊戲繪製主要在這個函數了。
2.1 onSurfaceCreated
不多說,都是基本設定。
public void onSurfaceCreated(GL10 gl, EGLConfig config) { // 告訴系統對透視進行修正 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); // 背景黑色 gl.glClearColor(0, 0, 0, 1); // 啟用陰影平滑 gl.glShadeModel(GL10.GL_SMOOTH); // 設定深度緩衝 gl.glClearDepthf(1.0f); // 啟用深度測試 gl.glEnable(GL10.GL_DEPTH_TEST); // 所做深度測試的類型 gl.glDepthFunc(GL10.GL_LEQUAL);}
2.2 onSurfaceChanged
public void onSurfaceChanged(GL10 gl, int width, int height) { // 設定 OpenGL 情境的大小 gl.glViewport(0, 0, width, height); // 設定投影矩陣 gl.glMatrixMode(GL10.GL_PROJECTION); // 重設投影矩陣 gl.glLoadIdentity(); // 設定視口的大小 // gl.glOrthof(0, width, height, 0, -1000, 1000); GLU.gluOrtho2D(gl, 0, width, height, 0); // 重設模型矩陣 gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity();}
代碼中先用 glViewPort 設定了情境的大小,我們所有的opengl世界都被投射到這個情境中,在這裡,我們將情境的大小設定為視窗 Surface 的大小。
繼而設定了投影矩陣,投影矩陣定義了 opengl 世界怎樣反應在你的情境中,在 OpenGL 中又兩種投影方式: 透視和正交。透視比較接近我們現實的方式了,你的眼睛發出的光形成一個夾角,離你的眼睛越近,東西越大,範圍越小;反之離眼睛越遠,東西越小,視野越開闊。因此多用於 3D 中。 而正交卻是世界中的物體按照平行的光線投射到一張紙上(你的畫布),彷彿被壓縮在上面,無論這個物體在世界中多遠,投射結果還是原來的大小, 2d 遊戲多用此種投影。
設定透視投影有 glFrustum 和 glu 庫的 gluPerspective, 都可以設定上面透視投影中的角度,遠近等參數。還可以 gluLookAt 來設定眼睛(相機)的位置。
設定正交投影有 glOrtho 和 glu 庫的 gluOrth2D :
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far); |
gluOrth2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top); |
glu版本的y軸參數不用我們設定了,因為不會用到。由於我們的座標系以左上方為原點,所以 left 和 top 參數為 0。
glMatrixMode() 函數指定了其下面的代碼操作的是何種矩陣。 我們這裡用到兩類矩陣變換:投影變換和模型視圖變換。投影變換中我們更改了世界空間的裁切範圍,在模型視圖變換中,我們可以移動和旋轉世界中的物體,所以我們在繪製的時候就是在模型視圖矩陣變換中。 glLoadIdentity() 用於重設矩陣,清除上次的殘留資訊。
代碼最後設定為模型視圖變換,便於我們在繪製函數中移動和變換物體模型了。
2.3 onDrawFrame
先說明一下 GLSurfaceView 的 Renderer 繪製是在獨立的線程中完成的,這個函數被不斷的迴圈調用。
前面已經設定為模型視圖矩陣了,每次 onDrawFrame 的時候,我們都要 glLoadIdentity 重設一次,清除上次繪製造成的殘留資訊。
public void onDrawFrame(GL10 gl) { // 清除螢幕和深度緩衝 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 重設模型矩陣 gl.glLoadIdentity(); //... 要添加的具體繪製代碼}
3 三角形-測試
好了,上面已經設定好了,我們先來個簡單的測試吧,在你的視窗中繪製一個紅色三角形。先定義好三角形的三個角度,左上方為 (60,200), 右上方為 (180, 200), 下角為 (120,300)。
private FloatBuffer triggerBuffer = FloatBuffer.wrap(new float[]{ 60,200, // 左上方 180, 200, // 右上方 120,300, // 下頂角}); public void onDrawFrame(GL10 gl) { // 清除螢幕和深度緩衝 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 重設模型矩陣 gl.glLoadIdentity(); gl.glPushMatrix(); gl.glColor4f(1, 0, 0, 0); // 允許設定頂點 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(2, GL10.GL_FLOAT, 0, triggerBuffer); gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); // 取消設定頂點 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glPopMatrix();}
主要程式碼封裝圍在了 glPushMatrix() 和 glPopMatrix() 中,OpenGL內部維護了各種矩陣棧,棧的操作就是 push 和 pop 了。這裡我們的操作是在模型視圖矩陣變換中進行的,所以操作的是模型視圖棧。之所以壓棧,我們不想繪製三角形的矩陣資訊汙染到外層的矩陣中,比如顏色設定等。所以大家在繪製一個單位體的時候盡量使用 push 和 pop 操作。
先用 glColor4f 設定繪製顏色為紅色。opengles 中少了 glBegin, glEnd 類標準函數,採用了數組頂點來設座標方式,主要還是效能考慮吧。預設是關掉這個選項的,所以先開啟,不用了再關閉: glEnableClientState(GL10.GLVERTEXARRAY), glDisableClientState(GL10.GLVERTEXARRAY).
指定數組頂點用 glVertexPointer, 指定完了就可以用 glDrawArray() 來將指定的數組中的頂點繪製出來了。這裡有必要羅嗦一下這兩個函數了,因為這兩個函數用到的太多了。先看第一個:
void glVertexPointer (int size, int type, int stride, Buffer pointer) |
參數:
-
size
-
每個頂點有幾個數值描述。必須取值2,3,4之一。初始值是 4.
-
type
-
數組中每個頂點座標的類型。取值:GLBYTE, GLSHORT, GLFIXED, GLFLOAT。初始值為 GLFLOAT。
-
stride
-
數組中每個頂點間的間隔,步長(位元組位移)。取值若為 0 表示數組是連續的。初始值為 0。
-
pointer
-
就是你的數組了,儲存著每個頂點的座標值。初始值為 0。
注意了, type 中告訴 opengl 你的數群組類型。 GLBYTE, GLSHORT, GLFLOAT 對應 byte[], short[], float[]. GLFIXED 對應 int[]. 有一個特別的地方, GLFIXED 描述的時候,大家的點座標單位是 0x10000, 比如一個點是 (60, 120), 用 GLFIXED 的時候,需要設定為 (60 * 0x10000, 120 * 0x10000), 所以經常看到 int one = 0x10000 的編程語句, 這個數值就是這麼來的。
再來看 glDrawArray:
void glDrawArrays (int mode, int first, int count); |
參數:
-
mode
-
指定你要繪製何種圖元, opengl 中的圖元就這幾個: GLPOINTS, GLLINESTRIP, GLLINELOOP, GLLINES, GLTRIANGLESTRIP, GLTRIANGLEFAN, GLTRIANGLES.
-
first
-
在已制定的數組中的開始位置(索引位置)
-
count
-
點的繪製次數, 比如我們繪製一個三角形,就是繪製三個頂點,即此參數為 3。
這兩個函數就介紹這麼多了,足以應付這個程式了。有問題可以查官方函數文檔: http://www.khronos.org/opengles/sdk/1.1/docs/man/
好了,代碼就介紹完了。一個三角形大家應該會繪製了吧,大家可以試試其他的圖元,點,線,四邊形,多邊形等等。
學習愉快。