J2ME-MIDP1.0遊戲完整實現-雙人掃雷1.0(一)

來源:互聯網
上載者:User
作者:yinowl
2005年2月

前言
我的上一篇也是第一篇文章《J2ME-MIDP1.0小遊戲入門-五子棋》貼出以後,有好多的朋友發郵件、加QQ、加MSN和我聊有關J2ME的內容,我很開心也很感慨,開心並不是因為自己文章寫得如何如何而有很多人聯絡我,而是有這麼多的朋友在學J2ME,我原來以為現在已經沒有人再會聯絡文章的作者,看來我錯了;感慨是因為,我知道我的第一篇文章其實很差的,從大家的反應,我能感覺出網上的原創資料的缺乏,官方或者書籍雖然權威且,但總感覺學的時候和實際的情況有點距離,我在看這些資料的時候,也經常會想,實際遊戲開發公司裡為怎麼處理、會怎麼寫這個代碼、程式的結構流程會怎麼樣等等。原創文章的最大有點就是它包含了作者的經驗,實際開發中的經驗。介於大家對我的支援並且網上原創文章有限,我非常樂意繼續寫一些自己的經驗和技術,充實我們的中文資源。所以我接著寫了這篇文檔,文檔內容即程式的開發過程都是真實的,不是為寫文章而寫文章,完全是近一段時間裡先完成的程式,現在再把它寫成教學文章。願大家學J順利,多多交流(MSN:yinowl@163.com QQ:47599318 E-mail:yinowl@163.com)

注意
平台:這個遊戲我是在 Nokia 的平台上設計的,也就是說使用了 FullCanvas 類以及針對Nokia-60系列(7650)的螢幕設計的,稍作修改就能運行在其他型號的手機上(我已經製作了 Siemens-C65 ,Nokia-7210 ,以及所有支援標準midp1.0/2.0、螢幕128 x 128的手機的幾個移植版本,有需要可以和我聯絡)
代碼:同我的前一篇文章《J2ME-MIDP1.0小遊戲入門-五子棋》一樣,代碼列出解釋的形式仿照《J2ME Game Programming》一書,按照程式功能思路給出相關代碼,一個檔案的代碼會根據功能在不同的小節給出,文章結束了,代碼也就完整了。這不同於通常書中的代碼以檔案為單位一次全部給出,我認為這樣更有助於讓大家瞭解一個程式從設計到最後完成的思路。

遊戲介紹
掃雷這個遊戲大家一定再熟悉不過了,但這個雙人掃雷遊戲的掃雷新玩法大家可能就沒見過了,其實這就是MSN軟體中的一個網路聯機小遊戲,大家每天在使用MSN,但都很少注意或玩MSN中的遊戲吧,可以說我就是把MSN上的聯機雙人掃雷移植到的手機上,一模一樣。如果你現在不方便上網或者沒有人和你聯機看一下這個遊戲的介面和玩法,沒關係,我現在就來介紹一下,遊戲區(雷區)中一共有 16 x 16 共256個格子,其中有52顆雷,操作除了上下左右鍵(當然電腦上是用滑鼠點的)只需要一個挖雷鍵,你要做的是挖出雷,而不是用另一個鍵去標示雷。兩個玩家,一方開始挖雷,如果挖到(點到)雷,則加一分,沒挖到,就和經典的掃雷一樣,顯示這個位置周圍一圈有幾個雷,如果一個雷都沒有,和經典的掃雷一樣,會把和這一格相連的所有周圍沒有雷的格子和再外面一圈格子開啟,然後另一位玩家挖雷,誰先挖到半數以上(大於26顆)的雷誰就獲勝,其實很簡單吧,遊戲的介面如下:





遊戲邏輯設計
資料結構:這個遊戲屬於二維棋類遊戲,所以我們自然而然想到設計一個 Bomb 類表示每一顆雷位,用一個Bomb類型的二維數組表示整個雷區的所有雷位,每一個雷位(即每一個Bomb執行個體)包含一個表示該雷位是否是雷的boolean類型的變數,一個表示該雷位是否已被挖的boolean類型的變數,一個表示是否是被玩家一挖的boolean類型的變數(否表示是被玩家二挖),一個表示該雷位周圍八個雷位共有幾顆雷的int類型變數。這樣整個雷區的狀態就完全被描述出來了,然後就根據這個二維表來繪製遊戲介面
遊戲流程:整個遊戲只使用唯一一個 FullCanvas 類,用唯一一個表示狀態的變數來控制整個遊戲的狀態,整個遊戲也只使用本身的唯一線程
玩家切換:用一個boolean類型的變數來表示當前執行的玩家,用遊戲的外框顏色及右下角的旗子顏色來表示出來

接下來就開始詳細介紹並列出遊戲程式的各部分代碼,文章的結束,整個遊戲也就完成了

應用程式類:MiningMIDlet.java
首先就是一個MIDlet類。 MiningMIDlet 類繼承自MIDlet類,用於串連裝置的應用程式管理器(Application Manager),通過方法startApp,pauseApp,destroyApp來通知遊戲的開始,暫停和銷毀結束。原始碼如下:

package com.imy.yinowl.miningscroll;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
public class MiningMIDlet extends MIDlet {
MiningCanvas miningCanvas;
//定義遊戲介面的FullCanvas類MiningCanvas的對象miningCanvas
public MiningMIDlet() {
display=Display.getDisplay(this);
miningCanvas=new MiningCanvas(this);
//產生MiningCanvas類的對象miningCanvas
}
protected void startApp(){
MiningMIDlet.display.setCurrent(miningCanvas);
//在螢幕上繪出遊戲見面miningCanvas
}
protected void pauseApp(){
}
protected void destroyApp(boolean arg0){
notifyDestroyed();
}
}


地雷類:Bomb.java
Bomb 類定義了二維雷區內每一個雷位的所有資訊,這樣用一個Bomb類型的二維表就能完全描述整個雷區的狀態。我在Bomb類中,用了三個boolean型變數和一個int型變數來描述一個雷位:boolean型變數isBomb:描述這個位置是否是雷;boolean型變數hasFound:描述這個位置是否已被挖掘;boolean型變數isPlayer1:描述如果這個位置是雷,而且已經被挖掘,那麼是否是玩家一挖掘,如果值是false,表示是被玩家二挖掘,只有在isBomb和hasFound都為true時,這個變數的值才有意義;int型變數bombaround:描述此位置周圍八個位置共有幾顆雷,這個變數除了用來告訴玩家周圍雷數還可以用來開啟成片的非雷地區。通過這四個變數配合不同的圖片就可以把整個雷區的不同狀態的圖形介面呈現在玩家面前。Bomb類的原始碼如下:

package com.imy.yinowl.miningscroll;
public class Bomb {
int bombaround;
boolean isPlayer1;
boolean hasFound;
boolean isBomb;

public Bomb(){
bombaround=0;
isBomb=false;
hasFound=false;
isPlayer1=true;
}
}


介面邏輯類架構:MiningCanvas.java
一般遊戲會出現很多的介面,例如遊戲的LOGO(也稱作閃屏或啟動介面)、開始菜單(主菜單)、設定介面、遊戲介面、協助介面、關於介面、遊戲時協助菜單等等,我的這個遊戲不需要設定所以沒有設定介面,所有的這些介面我們都整合在這一繼承自Nokia-API中的FullCanvas類中,我們要儘可能的較少類的數目以減少資源的開銷,具體是如何整合的,我們會再稍候詳細介紹。FullCanvas不支援Command及CommandListener,所以我們不能再使用原先的命令方法;我們會需要通過線程式控制制啟動介面的停留時間,所以會用到遊戲自己的主線程。
整個遊戲我們通過一個int型變數gamestate配合switch結構來控制遊戲中所處的介面狀態,也就是當前所顯示介面,在switch結構中根據gamestate不同的值,繪製不同的介面,在相應的時間改變gamestate的值,然後repaint,也就改變了接下來玩家所看見的介面,也同樣用這個方法在keyPressed方法中控制玩家按鍵的相應
架構原始碼如下:

package com.imy.yinowl.miningscroll;

import java.io.IOException;
import java.util.Random;
import java.util.Vector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;import javax.microedition.lcdui.Image;import com.nokia.mid.ui.FullCanvas;
public class MiningCanvas extends FullCanvas implements Runnable{
MiningMIDlet miningMIDlet;
int gamestate;
static final int GAMESTATE_SPLASH=0;
static final int GAMESTATE_MENU=1;
static final int GAMESTATE_GAMEING=2;
static final int GAMESTATE_HELP=3;
static final int GAMESTATE_SETTING=4;
static final int GAMESTATE_ABOUT=5;
static final int GAMESTATE_GAMEMENU=6;
static final int GAMESTATE_COUNT=7;
public MiningCanvas(MiningMIDlet miningMIDlet){
super();
this.miningMIDlet=miningMIDlet;
gamestate=0;//遊戲載入時預設介面為啟動介面
}
protected void paint(Graphics g) {
g.setColor(0x00FFFFFF);
g.fillRect(0,0,canvasW,canvasH);
switch(gamestate){
case GAMESTATE_SPLASH:
paintSplashScreen(g);//繪製遊戲啟動介面
break;
case GAMESTATE_MENU:
paintMenuScreen(g);//繪製遊戲主菜單
break;
case GAMESTATE_HELP:
paintHelpScreen(g);//繪製協助介面
break;
case GAMESTATE_GAMEING:
paintGameScreen(g);//繪製遊戲介面
break;
case GAMESTATE_GAMEMENU:
paintGameMenuScreen(g);//繪製遊戲時菜單介面
break;
default:
paintMenuScreen(g);//繪製遊戲主菜單
break;
}
public void run() {
}
protected synchronized void keyPressed(int keyCode) {
int action = getGameAction(keyCode);
switch(gamestate){
}
}
}


接下來就開始設計繪製遊戲的每一個介面了

啟動畫面
一般啟動畫面是一個靜態或動態圖片(靜態居多),顯示了遊戲的LOGO和遊戲的發行或製作商的名稱,停留時間為三秒,而且不能通過按鍵跳過,如果遊戲需要進行一些費時的初始化,可以在這三秒中同時進行。這停留的三秒通過控制本線程來實現。圖片:原始碼如下:

在MiningCanvas.java中添加如下代碼

Thread thread;
Image splashImage;
int splashDelayTime;
public MiningCanvas(MiningMIDlet miningMIDlet){
...
splashDelayTime=3000;
try{
splashImage=Image.createImage("/occo.png");
}catch(IOException e){}
thread=new Thread(this);
thread.start();
}
private void paintSplashScreen(Graphics g){
g.setColor(0x00000000);
g.drawImage(splashImage,getWidth()/2,getHeight()/2-5,Graphics.HCENTER|Graphics.VCENTER);
}
public void run() {
try{
Thread.sleep(splashDelayTime);
}catch(InterruptedException e){}
gamestate=GAMESTATE_MENU;//在啟動動畫停留3秒後,改變遊戲狀態變數值,跳轉到主菜單狀態
repaint();
}


遊戲主菜單介面
主菜單通過不同大小的兩種字型來顯示選中和未選中的功能表項目,用一個int型變數表示當前選中的功能表項目,用一個String型的一維數組儲存功能表項目的內容,在keyPresseed方法中根據菜單選項變數menuIdx來相應的改變遊戲的狀態,從而使遊戲跳轉到相應的狀態。需添加的原始碼如下:

在MiningCanvas.java中添加如下代碼
static final Font lowFont = Font.getFont (Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);
static final Font highFont = Font.getFont (Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
//兩種字型,分別是未選中和選中狀態的字型
static final int lowColor = 0x000000FF;//未選中狀態的顏色static final int highColor = 0x00FF0000;//選中狀態的顏色static final int highBGColor = 0x00CCCCCC;//選中狀態的背景顏色
static final int MAIN_NEW_GAME = 0;static final int MAIN_SETTINGS = 1;static final int MAIN_HELP = 2;static final int MAIN_ABOUT = 3;static final int MAIN_EXIT = 4;static final int MAIN_MENU_ITEM_COUNT = 5;static String[] mainMenu = new String[MAIN_MENU_ITEM_COUNT];
static int canvasW;//螢幕寬static int canvasH;//螢幕高static int startHeight;//菜單列表的起始高度static int spacing;//功能表項目間寬度static int menuIdx;//當前選中的功能表項目
public MiningCanvas(MiningMIDlet miningMIDlet){
...
menuIdx = 0;
canvasW=getWidth();canvasH=getHeight();
spacing = highFont.getHeight()/2;
startHeight = (canvasH-(lowFont.getHeight()*mainMenu.length)-(mainMenu.length-1)*spacing)/2;
mainMenu[0] = "New Game";
mainMenu[1] = "Settings";
mainMenu[2] = "Help";
mainMenu[3] = "About";
mainMenu[4] = "Exit";
}
private void paintMenuScreen(Graphics g){
for(int i=0;i < mainMenu.length;i++){
if(i==menuIdx){
g.setColor(highBGColor);
g.fillRect(0,startHeight+i*(lowFont.getHeight()+spacing)-(highFont.getHeight()
-lowFont.getHeight())/2,canvasW,highFont.getHeight());
g.setFont(highFont);
g.setColor(highColor);
g.drawString(mainMenu[i],(canvasW-highFont.stringWidth(mainMenu[i]))/2,
startHeight+i*(lowFont.getHeight()+spacing)-(highFont.getHeight()-lowFont.getHeight())/2,
Graphics.TOP|Graphics.LEFT);
} else {
g.setFont(lowFont);
g.setColor(lowColor);
g.drawString(mainMenu[i],(canvasW - lowFont.stringWidth(mainMenu[i])) / 2,
startHeight + i*(lowFont.getHeight() + spacing),Graphics.TOP|Graphics.LEFT);
}
}
}
在keyPressed方法中的switch結構中添加
case GAMESTATE_MENU:
{
if (getGameAction(keyCode) == FullCanvas.UP && menuIdx - 1 >= 0) {
menuIdx--;
}
else if (getGameAction(keyCode) == FullCanvas.DOWN && menuIdx + 1 < mainMenu.length) {
menuIdx++;
}
else if (getGameAction(keyCode) == FullCanvas.FIRE) {
switch(menuIdx) {//選中後,按照選項值改變遊戲狀態值,跳轉到相應的狀態
case MAIN_NEW_GAME:
gamestate=GAMESTATE_GAMEING;
break;
case MAIN_SETTINGS:
gamestate=GAMESTATE_SETTING;
break;
case MAIN_HELP:
gamestate=GAMESTATE_HELP;
break;
case MAIN_ABOUT:
gamestate=GAMESTATE_ABOUT;
break;
case MAIN_EXIT:
miningMIDlet.destroyApp(false);
break;
}
}
break;
}
}






相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。