雖然多數用戶都把 Eclipse 當成構建 Java ™ 應用程序的集成開發環境,實際上它是更基本的東西。Eclipse 是構建插件的框架,允許擴展其功能去解決幾乎任何問題 —— 只要利用一套 API 和現成可用的庫。在這份四個部分的 “創建 Eclipse 游戲插件” 教程系列中,將解決多數程序員每天都會遇到的一個迫切問題:如何不用切換應用程序就抽出時間玩一個迅速的視頻游戲,還能讓它不太明顯。教程中將開發一個簡單的程序,讀取進入視圖的蟲子,並把它們打爆。游戲將在 Eclipse 中作為插件運行,這個游戲演示了如何編寫 Eclipse API,如何使用 Standard Widget Toolkit、Open Graphics Library 和 Lightweight Java Games Library。第 3 部分將增加蟲子和 BB 彈之間的碰撞檢測並破壞蟲子。
開始之前
這個系列面向的是對使用 OpenGL 構建 Eclipse 游戲插件有興趣的開發人員。這份教程在 第 2 部分 的基礎上繼續開發,用 OpenGL 為 BB 彈添加移動和碰撞檢測。
關於這個系列
在這個四部分的系列中,介紹用來創建特性完整的 Eclipse 插件的基本技術、工具和庫,將使用 Standard Widget Toolkit(SWT)和 OpenGL 庫提供圖形。第 1 部分 介紹如何構建簡單視頻游戲的框架,通過創建插件,可以在 Eclipse 內部啟動並玩這個游戲。第 2 部分 在第 1 部分創建的基本框架基礎上,開始用 Open GL 添加實際的可視元素。第 3 部分添加實際的游戲元素,讓用戶能夠與第 2 部分創建的圖形進行交互。第 4 部分把前三部分創建的所有內容合在一起,確保它們能一起工作。
關於這份教程
這份教程從 第 2 部分 結束的地方開始,使用我們已經創建的形狀和功能讓游戲走向實用。在這篇文章中,將做以下工作:
為 BB 槍添加移動。
為 BB 彈和蟲子添加碰撞效果,使被擊中的蟲子粉碎並消失,顯示文本 “POW”。
前提條件
這份教程假設讀者擁有 Java 語法和編碼以及 Eclipse 插件編程的基本知識,正如這個系列的 第 1 部分 所示。圖形編程的知識會有益,但並不是必需的。不要求關於 OpenGL 的知識。
系統需求
需要以下軟件:
Eclipse Eclipse 是創建第 1 部分的插件的框架。現在要繼續利用 Eclipse 構建游戲插件。請從 Eclipse.org 下載 Eclipse V3.2 M3 或以上版本 。Eclipse SWT SWT 是 Eclipse 的部件包,用於制作窗口,由 IBM 捐贈給 Eclipse。 請下載 SWT 3.2 M3 或以上版本。Eclipse OpenGL 需要用 OpenGL 來創建形狀等等。請下載實驗性的 org.eclipse.opengl binding, version 0.5 for SWT 3.2。使用 OpenGL 的 Eclipse 示例插件 請下載 使用 OpenGL 的帶有一個視圖的示例插件。將用這個插件中的源代碼作為構建自己的定制 OpenGL 布景的框架。Java 技術 Eclipse 和它的所有插件都需要 Java 技術。
概述和設置
在 第 2 部分 中,我們著重用 OpenGL 構建了游戲對象。現在將編寫游戲功能的代碼。
當前進展
目前,我們已經用 OpenGL 開發了幾個形狀,包括貼了圖的 BB 槍、球形的蟲子和圓柱形的子彈。我們還創建了在蟲子被擊中時顯示的文本(“POW”)。下面,我們將了解要編寫的功能。
游戲
游戲的各部分已經就位,可以編寫代碼了。先從放在一條線上的 BB 槍和蟲子開始,槍是射擊一方。應當能夠上下左右移動 BB 槍,並射出 BB 彈,就像我們在第 2 部分中用空格鍵所做的那樣。但是這次,子彈不僅僅是穿過蟲子,我們要編寫代碼,讓子彈觸發 “打爆” 蟲子功能,並顯示文本 “POW”。在半秒之後,蟲子和 “POW” 文本消失。
移動 BB 槍
每個游戲都允許玩家移動他控制的部件 —— 在這個示例中就是 BB 槍。在添加這個功能之前,需要為我們創建和初始化的每個對象創建 3 維坐標。這一節介紹這個功能,這個功能允許玩家四處移動槍。
添加正確的坐標:蟲子
在這份教程後面,當添加碰撞檢測時,需要知道游戲中每個部件的正確坐標。請把坐標添加到 Bug 類中,如清單 1 所示。
清單 1. 掛上蟲子的坐標
...
private float ox, oy, oz;
public Bug(float[] color, float ox, float oy, float oz) {
this.ox = ox;
this.oy = oy;
this.oz = oz;
...
現在有了當前蟲子的 x、y 和 z 坐標。需要重載 Bug 類的 draw 方法,如清單 2 所示。
清單 2. draw 方法
public void draw(){
GL.glTranslatef(ox, oy, oz);
super.draw();
GL.glTranslatef(-ox, -oy, -oz);
}
現在我們要把原點轉到繪制蟲子的位置,繪制蟲子,然後換回原點。
添加正確的坐標:子彈
除了向 Bullet 類添加 3 維坐標之外,還必須添加其他一些東西。例如,為了避免每秒發射數百次,我們將限制槍的發射速率。請參閱清單 3。
清單 3. 添加新變量
...
private float ox, oy, oz;
private float translationGunX;
private float translationGunY;
private GameScene game;
private static long lastShotMilliseconds;
public Bullet(GameScene game, float ox, float oy, float oz) {
lastShotMilliseconds = System.currentTimeMillis();
this.game = game;
this.ox = ox;
this.oy = oy;
this.oz = oz;
...
在創建 Bullet 類時,把靜態變量 lastShotMilliseconds 初始化為當前時間,並設置 x、y 和 z 坐標,就像對 Bug 類所做的那樣。請按照清單 4 修改發射 BB 彈(稱為 “bullets”)的方法。
清單 4. 發射子彈
public void shoot(){
for(int i = 0; i < bullets.length; i++)
if(!bullets[i].fired){
bullets[i].shoot(gun.translationx, gun.translationy);
break;
}
}
現在發射 BB 彈時將提供槍的當前坐標,這樣在射擊時 BB 彈就會在槍口指向的位置出來,而不是在槍的原始位置出來。請照此修改 Bullet 類的 shoot 方法,如清單 5 所示。
清單 5. 修改 shoot 方法
public void shoot(float tGunX, float tGunY){
long now = System.currentTimeMillis();
if(now - lastShotMilliseconds < 250)
return;
lastShotMilliseconds = now;
translationGunX = tGunX;
translationGunY = tGunY;
...
這裡就是射擊的地方,但是只有不超過允許的射擊速率才可以。然後設置槍的坐標,分別保存在 translationGunX 和 translationGunY 中。請修改 draw 方法,利用槍和子彈的坐標,如清單 6 所示。
清單 6. 在正確的位置繪制槍
public void draw(){
if(translation >= -100 && fired){
GL.glTranslatef(translationGunX + ox,
translationGunY + oy,
translation + oz);
super.draw();
GL.glTranslatef(-translationGunX - ox,
-translationGunY - oy,
-translation - oz);
}
}
就像在 Bug 類中一樣,在繪制 BB 槍之前,要把原點轉換到要繪制 BB 槍的位置,然後再轉換回原點。
添加正確的坐標:槍
現在設置 Gun 類。Gun 類需要沿著 x 和 y 平面移動,移動從子彈的起始位置開始。請修改 Gun 類的構造函數添加這個功能,如清單 7 所示。
清單 7. 修改 Gun 類
...
這段代碼把槍的位置初始化為它的原始位置,至於槍,則由清單 8 中的代碼繪制。
private float ox, oy, oz;
private float translationx;
private float translationy;
public Gun(float ox, float oy, float oz) {
translationy = 0;
translationx = 0;
this.ox = ox;
this.oy = oy;
this.oz = oz;
...
清單 8. 繪制槍
public void draw(){
GL.glTranslatef(translationx + ox,
translationy + oy,
oz);
super.draw();
GL.glTranslatef(-translationx - ox,
-translationy - oy,
-oz);
}
很好!三個類都在正確的位置顯示。剩下的惟一一件事就是如何初始化它們,下面馬上介紹。
初始化各個部件
在這裡,我們把游戲中每個部件的初始坐標發送到它們各自的構造函數。它們的初始位置與第 2 部分中的位置一樣,但是現在的處理不同,以支持碰撞偵測。請參閱清單 9。
清單 9. 初始化游戲部件的位置
...
現在,在創建部件中的對象時,也傳遞它們的初始位置。
this.gun = new Gun(0, 0, 12);
bugCount = ShootoutView.viewer.getTable().getItemCount();
this.bugs = new Bug[bugCount];
float tx = -Bug.RADIUS*bugCount;
for (int i = 0; i < this.bugs.length; i++){
this.bugs[i] = new Bug(COLOR[i % COLOR.length], tx, 0, -8);
tx += 2*Bug.RADIUS;
}
pow = new Pow();
bullets = new Bullet[25];
for(int i = 0; i < bullets.length; i++)
bullets[i] = new Bullet(this, .2f, -.2f, 9.5f);
...
游戲性和用戶交互
沒有用戶交互的游戲就不是游戲了。這個游戲通過鍵盤輸入來玩。也可以修改游戲,讓它接受鼠標或其他輸入設備的輸入。現在來控制槍。
用鍵盤輸入控制槍
在得到鍵盤輸入時,需要設置 Gun 類中的布爾值,讓它表示槍的移動方向。請修改方向鍵的事件代碼,如清單 10 所示。
清單 10. 處理 keyPressed 事件
public void keyPressed(KeyEvent e) {
switch (e.keyCode) {
case SWT.ARROW_UP:
game.moveGun(false, true, false, false);
break;
case SWT.ARROW_DOWN:
game.moveGun(false, false, false, true);
break;
case SWT.ARROW_LEFT:
game.moveGun(false, false, true, false);
break;
case SWT.ARROW_RIGHT:
game.moveGun(true, false, false, false);
break;
case SWT.PAGE_UP:
...
在這裡,我們調用 moveGun 方法。這個方法調用 Gun 類的 move 方法,設置一個布爾值,表示槍的移動方向。然後,處理 keyReleased 事件,如清單 11 所示。
清單 11. 處理 keyReleased 事件
public void keyReleased(KeyEvent e) {
switch (e.keyCode) {
case SWT.ARROW_UP:
case SWT.ARROW_DOWN:
case SWT.ARROW_LEFT:
case SWT.ARROW_RIGHT:
game.moveGun(false, false, false, false);
break;
}
}
現在,在釋放鍵的時候,槍的方向會被清除。
下面,把需要的方法添加到 GameScene 類和 Gun 類。
添加鉤子到 Gun 類
在槍移動時,事件通知被發送到 keyEvent 偵聽器。在第 2 部分的實現中,SceneGrip 類是處理這些事件的偵聽器。所以,在按下方向鍵時,SceneGrip 調用 GameScene 類的 moveGun 方法。請定義這個 moveGun 方法,如清單 12 所示。
清單 12. 移動槍
...
public void moveGun(boolean movingPosX, boolean movingPosY,
boolean movingNegX, boolean movingNegY){
gun.move(movingPosX, movingPosY, movingNegX, movingNegY);
}
我們只是把布爾變量傳遞給 Gun 類的 move 方法。現在把 move 方法添加到槍,如清單 13 中的代碼所示。
清單 13. 把移動方向添加到 Gun 類
private boolean movingPosX;
private boolean movingPosY;
private boolean movingNegX;
private boolean movingNegY;
public void move(boolean movingPosX, boolean movingPosY,
boolean movingNegX, boolean movingNegY){
this.movingPosX = movingPosX;
this.movingPosY = movingPosY;
this.movingNegX = movingNegX;
this.movingNegY = movingNegY;
}
用布爾值表示槍的當前移動方向,並把它們保存在 Gun 類本地留作後用。
有了槍的方向之後,槍的移動就可以實現動畫效果了。
動畫實現槍的移動
現在我們知道了槍要移動的方向,可以用 TimerTask 實現移動,做出動畫效果,如清單 14 所示。
清單 14. 移動槍
public Gun(float ox, float oy, float oz) {
this.ox = ox;
this.oy = oy;
this.oz = oz;
translationy = 0;
translationx = 0;
movingPosX = false;
movingPosY = false;
movingNegX = false;
movingNegY = false;
t = new Timer();
tt = new TimerTask(){
float max = 10;
float delta = 0.01f;
public void run(){
if(movingPosY)
translationy += delta;
if(movingPosX)
translationx += delta;
if(movingNegY)
translationy -= delta;
if(movingNegX)
translationx -= delta;
if(translationx < -max)
translationx = -max;
if(translationx > max)
translationx = max;
if(translationy < -max)
translationy = -max;
if(translationy > max)
translationy = max;
}
};
t.scheduleAtFixedRate(tt, 0, 2);
...
首先,添加新的變量並把它們初始化為 false,表示槍在開始時並未移動。然後設置 TimerTask 的 run 方法,根據槍的當前方向 ,加上或減去 translationx 或 translationy。另外,通過限制移動不能超過 x 軸和 y 軸 +/- 極限,限制槍的移動。這樣,槍就不會離開原點太遠,因為蟲子怎麼也不會在那裡。
碰撞檢測
多數計算機游戲都使用某種碰撞檢測。在許多情況下,碰撞檢測用在一個范圍上,好讓玩家操縱某個東西在屏幕上移動。在這個游戲中,我們在 BB 彈和蟲子之間實現了碰撞檢測。
游戲和碰撞檢測
如果沒有某種形式的碰撞檢測,計算機游戲就沒有意義,因為沒有它游戲就無法對玩家做的 “好事” 作出響應(在這個游戲中,“好事” 指的就是擊中了一只蟲子)。第一人稱射擊游戲用許多方式實現碰撞檢測,例如可以攻擊的牆或身體的一部分,采用某些比其他方式更致命的射擊。爆炸也涉及碰撞檢測,與大型 BB 彈的功能類似,而且離得越遠,受的傷害越小。
碰撞檢測實現
實現碰撞檢測有多種方法,有些比較復雜。游戲編程對計算機處理能力的消耗很大。在游戲中可做的事情很多,如果同時做這些事,那麼游戲會慢下來,甚至不堪忍受。最復雜的碰撞檢測方法可以是強力算法,搜索對象表面的每個像素,查看 BB 彈是否擊中了它。另一個方法可能是某種非線性函數,檢測曲線對象上的碰撞;但是這個方法可能會更復雜。在這個教程中,我們將使用相對簡單的碰撞檢測。
簡單的碰撞檢測
為了最大限度地利用這個游戲,在每件事上我們都用最簡單的方法,包括碰撞檢測。簡單方法的好處就是游戲的玩家並不介意碰撞檢測是否完美。一個簡單的方法是 3 維距離算法,可以用 Bug.RADIUS 進行計算,這樣如果一顆 BB 彈進入了蟲子的范圍,就觸發蟲子上的 “爆炸”。不過這可能會涉及一個平方函數,這個函數是比較運算密集型的。更好的方法可能是一種簡單的 3 維六邊形窗口化函數。這個函數需要 BB 彈所在的點,以及蟲子位於其中的最小盒子,這樣,可以想像有一個看不到的立方體包著蟲子。如果 BB 彈進入這個立方體,蟲子就被擊中了。這一節剩下的部分將實現這個算法。
擴展 Bug 類
為了幫助 Bug 類進行碰撞檢測,我們需要添加兩個布爾類型的變量,區分活著的、爆炸的和死掉的蟲子。這些變量有助於在 Bug 類中保存蟲子的狀態 —— 這意味著如果蟲子是活的,就應當顯示並正確地活動。如果蟲子爆炸了,就顯示為爆炸。如果死掉,就不應當影響游戲或顯示。請添加這些狀態變量和計時器功能,如清單 15 所示。
清單 15. 擴展 Bug 類的功能
private boolean blowUp;
private boolean dead;
Timer t;
TimerTask tt;
public Bug(float[] color, float ox, float oy, float oz) {
dead = false;
blowUp = false;
t = new Timer();
...
添加了兩個布爾變量保存蟲子是否爆炸,添加了一個計時器,用來顯示 “POW” 文本,並讓蟲子消失。現在我們學習在哪裡放置碰撞檢測的代碼。
在哪裡檢測
既然已經知道了在碰撞檢測中要做什麼,現在就要把檢測碰撞的代碼添加到某個地方。可以把代碼添加到蟲子或 BB 彈(子彈)上。但是,子彈通常比蟲子多,所以我們在 Bullet 類的計時器任務中做這件事,如清單 16 所示。
清單 16. 子彈負責檢測是否擊中了蟲子
public void shoot(float tGunX, float tGunY){
...
tt = new TimerTask(){
public void run(){
translation-=0.1;
for(int j = 0; j < game.bugs.length; j++){
if(!game.bugs[j].blowUp && !game.bugs[j].dead)
game.bugs[j].checkShot(Bullet.this);
}
if(translation < -100){
...
我們循環檢查每個蟲子,如果蟲子沒爆炸,也沒死,就調用 checkShot,檢查是否擊中了它。下面來實現這個方法。
實現最簡單的碰撞檢測
現在進入碰撞檢測算法的實質部分。checkShot 算法檢測 BB 彈是否進入了蟲子所在的立方體(如前所述)。請把這個方法添加到 Bug 類,如清單 17 所示。
清單 17. 蟲子和 BB 彈之間的核心碰撞檢測算法
public void checkShot(Bullet b){
// position of end closest to the gun
float bx = b.ox + b.translationGunX;
float by = b.oy + b.translationGunY;
float bz = b.oz + b.translation;
// center point of the circle
float x = ox;
float y = oy;
float z = oz;
// 3-dimensional window of the bug
float x1 = x - RADIUS;
float x2 = x + RADIUS;
float y1 = y - RADIUS;
float y2 = y + RADIUS;
float z1 = z - RADIUS;
float z2 = z + RADIUS;
// if the bullet is inside the bug, he's going to blow up
if(bx > x1 && bx < x2)
if(by > y1 && by < y2)
if(bz > z1 && bz < z2)
blowUp = true;
}
首先,得到 BB 彈的確切(x, y, z)位置,然後得到蟲子的中心點。接下來,通過加/減蟲子的半徑(在 Bug.RADIUS 中定義)得到構成立方體的六個點,每個軸兩個點。現在有了必要的值,可以判斷點(x, y, z)是否在蟲子所在的立方體之中。如果正在檢測沿著 x 軸的碰撞,那麼在 x1 和 x2 之間(可以用 bx > x1 && bx < x2 計算)的蟲子就會觸發碰撞。對於其他兩個軸,y 和 z 軸也如法炮制,這樣就會知道 BB 彈是否落入蟲子的范圍內,並把布爾變量 blowup 設為 true。下一節介紹如何處理這個工作和啟用破壞蟲子的動畫。
破壞蟲子
如果我們擊中了蟲子,就需要觸發讓它爆炸的動畫,顯示 “POW” 文本,然後消失。
響應用戶輸入和事件
如果開槍擊中,而被擊中的對象什麼也沒發生,那麼游戲不會讓人興奮和上瘾。所以,在游戲中對事件響應的方式要使游戲更興奮、更好玩。在這個游戲中,我們要讓蟲子爆炸,顯示 “POW”,然後消失。還可以添加其他事件,例如在所有蟲子爆炸和死掉時,可以顯示 “We win” 或者出現一個更大更強壯的蟲子,只有擊中它的兩只眼睛才能打掉它。我們也可以給用戶設一個時間限制,如果在指定時間內沒有把蟲子破壞掉,就觸發一個事件,告訴用戶游戲結束。這節剩下的部分添加的代碼,在蟲子標記為爆炸的時候(布爾變量 blowup 為 true)觸發一個事件,開始顯示 “POW”。
顯示 “POW”
蟲子爆炸的第一個事件顯示 “POW”。所以,只需要在蟲子爆炸卻還沒死的時候繪制 POW。我們在 drawScene 方法中添加這個功能,如清單 18 所示。
清單 18. 蟲子爆炸時顯示 POW
protected void drawScene() {
super.drawScene();
this.grip.adjust();
GL.glBindTexture(GL.GL_TEXTURE_2D, Gun.TEXTURES[1]);
boolean drawPow = false;
for(int i = 0; i < bugs.length; i++)
if(!drawPow && bugs[i].blowUp && !bugs[i].dead)
drawPow = true;
if(drawPow){
pow.draw();
}
for(int i = 0; i < bullets.length; i++)
...
在這裡,檢查每個蟲子,如果蟲子爆炸了卻還沒死(沒有消失),就顯示 “POW”。接下來,添加顯示爆炸蟲子的代碼。
把蟲子炸掉
既然顯示了 “POW”,現在添加把蟲子炸掉的代碼。請修改 Bug 類的 draw 方法,如清單 19 所示。
清單 19. 炸掉蟲子
public void draw(){
GL.glTranslatef(ox, oy, oz);
if(blowUp)
this.explode();
super.draw();
GL.glTranslatef(-ox, -oy, -oz);
}
如果蟲子的布爾變量 blowUp 設置為 true,那麼這個代碼會炸掉蟲子,並顯示蟲子爆炸的碎片。接下來,配置一個計時器,讓蟲子消失。
讓蟲子的碎片消失
蟲子爆炸之後,就要讓它消失。與其他游戲類似,被消滅的敵人要等一會兒才消失。下面的代碼讓爆炸的蟲子在半秒之後消失。請修改 Bug 類的 draw 方法,如清單 20 所示。
清單 20. 讓死蟲子和蟲子碎片消失
public void draw(){
if(dead)
return;
GL.glTranslatef(ox, oy, oz);
if(blowUp){
this.explode();
tt = new TimerTask(){
public void run(){
cancel();
}
public boolean cancel(){
boolean cancelled = super.cancel();
dead = true;
return cancelled;
}
};
t.scheduleAtFixedRate(tt, 500, 500);
}
super.draw();
GL.glTranslatef(-ox, -oy, -oz);
}
如果蟲子死掉,就從方法返回,什麼也不繪制。另外,除了在布爾變量 blowUp 為 true 的時候把蟲子炸掉,我們還初始化了一個計時器任務,在半秒之後把布爾變量 dead 設為 true。
玩和重新開始游戲
玩游戲與第 2 部分中一樣。但是,在所有蟲子都被擊中的時候,我們想重新開始游戲,射掉更多蟲子。這是這一節的內容。
初始化游戲部件
游戲開始時通常有一個初始設置,用戶據此移動和操縱游戲。所以,我們需要把游戲部件初始化的部分隔離出來,好讓這項工作能夠進行。請修改 initGL 方法,並添加一個 initPieces 方法,如清單 21 所示。
清單 21. 初始化游戲部件
...
GLU.gluQuadricNormals(Bug.QUADRIC, GLU.GLU_SMOOTH);
GL.glEnable(GL.GL_TEXTURE_2D);
initPieces();
}
public void initPieces(){
this.gun = new Gun(0, 0, 12);
bugCount = \
ShootoutView.viewer.getTable().getItemCount();
this.bugs = new Bug[bugCount];
float tx = -Bug.RADIUS*bugCount;
for (int i = 0; i < this.bugs.length; i++){
this.bugs[i] = new Bug(COLOR[i % \
COLOR.length], tx, 0, -8);
tx += 2*Bug.RADIUS;
}
pow = new Pow();
bullets = new Bullet[25];
for(int i = 0; i < bullets.length; i++)
bullets[i] = new Bullet(this, .2f, -.2f, 9.5f);
}
我們采用了來自 initGL 方法的原始代碼,創建了一個新方法 initPieces,然後把代碼放在這個方法內。現在可以重新初始化游戲設置,供玩游戲使用。
釋放部件
就像對部件初始化所做的那樣,對釋放部件也要做同樣的事。請修改 dispose 方法,添加一個 disposePieces 方法,如清單 22 所示。
清單 22. 釋放部件
public void disposePieces() {
this.gun.dispose();
for(int i = 0; i < bugs.length; i++)
bugs[i].dispose();
for(int i = 0; i < bullets.length; i++)
bullets[i].dispose();
pow.dispose();
}
public void dispose() {
GLU.gluDeleteQuadric(Gun.QUADRIC);
GLU.gluDeleteQuadric(Bullet.QUADRIC);
GLU.gluDeleteQuadric(Bug.QUADRIC);
disposePieces();
super.dispose();
}
我們只是刪除了現有的釋放代碼,並把它們移動到 disposePieces 方法中。
重新開始游戲
現在添加了處理舊部件和初始化新部件的功能,可以在 ShootoutView 類中添加合適的功能了。請修改 ShootoutView 類中的以下代碼段,如清單 23 所示。
清單 23. 重新開始游戲
...
startGameButton.setText("(Re)start Game");
startGameButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
...
if(!ShootoutView.this.sceneRunning){
...
ShootoutView.this.sceneRunning = true;
}
else{
ShootoutView.this.scene.disposePieces();
ShootoutView.this.scene.initPieces();
}
}
...
如果布景已經啟動運行,就需要重新初始化它,如 else 語句中的代碼所示。這就行了!現在,只要殺死了所有蟲子,就可以點擊 “(Re)start Game” 按鈕讓它們全復活。下面簡要總結一下如何才能讓這個游戲與眾不同。
要考慮的事情
OpenGL 是個強大的圖形編程 API,在編程時還有多個選項可用。例如,可以利用它的 2 維特性,創建平面 2 維游戲,就像老式的 Atari 游戲做的那樣。
正如提到過的,還可以做其他改進。一個改進是添加蟲子的隨機或定向移動,當它們到達某一點時,可以在屏幕的另一邊出現,就像 Atari's Asteroids 一樣;或者從牆上彈回,繼續向另一個方向移動。這可以讓游戲更難,更有挑戰性,更有意思,運動技能和手眼協調會得到測試,因為必須在 3 維空間中移動和瞄准並擊中目標。
添加聲音也是可能的。通過這種方式,可以讓每次命中和爆炸時都有聲音,不僅用視頻而且用音頻刺激游戲玩家。
最後,可以看看其他游戲,找找可以應用到自己游戲中的想法。
結束語
游戲終於完成了!我們用 OpenGL 在標准的 Eclipse 窗口中完成了一個 Eclipse 游戲。現在可以在工作的間隙,打開 Shootout 視圖,在 Eclipse 上玩游戲,稍事休息,射掉代表任務列表中每個項目的蟲子。
在第 4 部分中,將總結插件的完整操作和部署。
本文配套源碼