引言
現(xiàn)在的手機作為一種娛樂性的電子通信設(shè)備,早已超出原先的通話、短信息等基本通信功能,越來越多的娛樂、休閑性軟件如手機游戲、電子書、音樂編輯、拍照與圖象處理等也都流行于當(dāng)今各種品牌的手機。其中,游戲軟件占有相當(dāng)大的比重。既然我們已經(jīng)掌握了手機軟件的開發(fā)過程,為什么不自己開發(fā)一個個性化的手機游戲呢?本文將介紹一個簡單的圖象化手機游戲--"花皮貓大戰(zhàn)流氓兔"的制作過程。花皮貓自然是筆者養(yǎng)的愛貓了,感興趣的讀者也完全可以讓自己喜歡的阿貓阿狗擔(dān)當(dāng)游戲中的主角,充分展示DIY的魅力!
游戲的設(shè)計 手機游戲的開發(fā)首先需要規(guī)劃好整體流程和具體的游戲規(guī)則(或游戲劇本),然后才能根據(jù)此劇本進行具體的編碼實現(xiàn)。受文章篇幅限制,本游戲劇本設(shè)計不能太復(fù)雜。首先將游戲定位為人機對弈類游戲,程序運行開始首先顯示本游戲的封面畫面,停留幾秒后自動轉(zhuǎn)入角色選擇畫面,在玩家選擇某一角色后開始游戲。游戲開始時隨機決定哪一方先行。人機分別在3乘3大小的棋盤網(wǎng)格中交替落子(雙方棋子圖案是有區(qū)別的),而且只允許在沒有落過子的空網(wǎng)格中下子。只要有一方所落棋子在橫、豎、斜任何一方向上的總數(shù)達到三顆即獲勝。如棋盤被填滿時雙方均未在上述方向達到三顆棋子則該局為平局。無論結(jié)果如何,在每局結(jié)束后均顯示當(dāng)局勝負結(jié)果與總比分。玩家可以選擇退出或是重新開始新的一局。以上便是本游戲的主體框架和基本游戲規(guī)則,隨后進行的編碼工作便以此為依據(jù)。
游戲框架的搭建
 圖1 |
首先建立項目并新建一Midlet TicTacToe加入其中,繼續(xù)添加ChoosePieceScreen、GameScreen和Game三個類到項目。作為一款游戲,如果仍拿文字來作軟件封面顯得也太不專業(yè)了。如果要在J2ME程序中使用圖片,必須預(yù)先將其轉(zhuǎn)換為png格式圖片然后在項目上點擊鼠標(biāo)右鍵,從新建菜單下選擇文件菜單項將彈出如上所示對話框。剛開始下半部分是隱藏的,需要通過點擊高級按鈕將其顯示出來。選中鏈接至文件系統(tǒng)中的文件并通過瀏覽對話框指定要添加的圖片路徑。最后在文件名一欄輸入圖片的文件名并點擊完成,將圖片添加到項目。下面只須在startApp()中通過如下代碼裝載圖片并通過信息框?qū)⑵滹@示出來即可:
Image logo = null; try { logo = Image.createImage("/logo.png"); }catch (IOException e) {} Alert splashScreen = new Alert(null, "郎銳2004年作\n版權(quán)所有(c)\n2004--2005", logo, AlertType.INFO); splashScreen.setTimeout(4000); // 延遲4秒 |
在持續(xù)顯示圖2四秒后進入角色選擇界面(圖3):
choosePieceScreen = new ChoosePieceScreen(this); Display.getDisplay(this).setCurrent(splashScreen, choosePieceScreen); |
此任務(wù)在ChoosePieceScreen類中實現(xiàn),主要的功能有對角色圖標(biāo)的裝載顯示、對選定角色的確認等。在其構(gòu)造函數(shù)中首先指定當(dāng)前界面為列表選擇方式,然后通過append()將裝載的圖象與相應(yīng)的列表文字建立關(guān)聯(lián)。最后,為了響應(yīng)用戶的輸入選擇還必須調(diào)用setCommandListener()來檢測按鍵事件的發(fā)生,并在commandAction()方法中實現(xiàn)對選定角色的確認:
super("請選擇:", List.IMPLICIT); // 設(shè)置列表選擇 this.midlet = midlet; append(CAT_TEXT, loadImage("/cat.png")); // 添加圖象選項到列表 append(RABBIT_TEXT, loadImage("/rabbit.png")); setCommandListener(this); // 偵聽按鍵響應(yīng) …… public void commandAction(Command arg0, Displayable arg1) { if (arg0 == List.SELECT_COMMAND){ // 檢測是否為列表按鍵響應(yīng) // 檢測用戶選中的選項 boolean isPlayerCat = getString(getSelectedIndex()).equals(CAT_TEXT); midlet.choosePieceScreenDone(isPlayerCat); // 進入游戲畫面 } } |
這里是通過檢測用戶選擇的列表項文字來判斷玩家選擇的是花皮貓還是流氓兔并通過變量isPlayerCat來標(biāo)識,在choosePieceScreenDone()方法中新建一個GameScreen對象并將其作為當(dāng)前顯示界面來開始一局新的游戲。GameScreen類負責(zé)游戲界面的繪制,如對棋盤和雙方棋子的繪制以及對光標(biāo)移動的處理等工作。
游戲界面編程 對弈游戲最主要的界面就是棋盤與棋子的繪制。這首先要根據(jù)屏幕大小計算棋盤網(wǎng)格間距和棋子的大小:
screenWidth = getWidth();// 獲取屏幕大小 screenHeight = getHeight(); if (screenWidth > screenHeight) {// 計算網(wǎng)格大小 boardCellSize = (screenHeight - 2) / 3; boardLeft = (screenWidth - (boardCellSize * 3)) / 2; boardTop = 1; }else{ boardCellSize = (screenWidth - 2) / 3; boardLeft = 1; boardTop = (screenHeight - boardCellSize * 3) / 2; } |
繪制棋盤時,首先用背景色清空整個畫布然后再分別按行列繪制出黑色網(wǎng)格即可:
g.setColor(WHITE); g.fillRect(0, 0, screenWidth, screenHeight); g.setColor(BLACK); for (int i = 0; i < 4;i++) { g.fillRect(boardLeft, boardCellSize*i+boardTop,(boardCellSize*3)+2,2); g.fillRect(boardCellSize * i + boardLeft, boardTop, 2, boardCellSize * 3); } |
棋子的繪制可以通過在指定位置顯示裝載的圖象來實現(xiàn)。例如,對于花皮貓棋子的繪制可按如下代碼先裝載預(yù)先準(zhǔn)備好的圖象(大小須與網(wǎng)格相匹配)然后再調(diào)用drawImage方法在指定位置繪制。對于流氓兔棋子的繪制只需更改待裝載的圖象即可:
private void drawCat(Graphics g, int x, int y) { Image image = null; try {// 裝載圖象 image = Image.createImage("/cat.png"); }catch (Exception e) {} g.drawImage(image, x + 1, y + 1, 0); // 在指定位置繪制圖象 } |
至于對移動光標(biāo)的處理,可以先在將要移動到的網(wǎng)格內(nèi)側(cè)繪制一個新的、四邊與棋盤網(wǎng)格緊密相連的黑色矩形框,然后再在原網(wǎng)格位置用原網(wǎng)格背景進行重繪以擦除上次繪制的光標(biāo)痕跡。在擦除舊光標(biāo)痕跡時首先需要判斷該位置是空白還是繪制有棋子圖案,并根據(jù)判斷結(jié)果繪制白色矩形或是重新裝載當(dāng)前顯示的棋子圖象。圖4給出了幾個回合后的游戲截圖,只要游戲沒有結(jié)束,上述繪制模塊將會多次反復(fù)調(diào)用執(zhí)行。如果程序的智能控制部分判斷出游戲已經(jīng)結(jié)束并給出勝負結(jié)果,則不再顯示棋盤界面而是通過下面這段代碼以特定的字體在白色畫布上繪制出當(dāng)前戰(zhàn)績(如圖5所示)。
Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); // 設(shè)置字體 int strHeight = font.getHeight(); int statusMsgWidth = font.stringWidth(statusMsg); int tallyMsgWidth = font.stringWidth(tallyMsg); int strWidth = tallyMsgWidth; if (statusMsgWidth > tallyMsgWidth) strWidth = statusMsgWidth; int x = (screenWidth - strWidth) / 2; // 計算字符繪制位置 x = x < 0 ? 0 : x; int y = (screenHeight - 2 * strHeight) / 2; y = y < 0 ? 0 : y; g.setColor(WHITE); // 白色清空畫布 g.fillRect(0, 0, screenWidth, screenHeight); g.setColor(BLACK); // 黑色顯示信息 g.drawString(statusMsg, x, y, (Graphics.TOP | Graphics.LEFT)); g.drawString(tallyMsg, x, (y + 1 + strHeight), (Graphics.TOP | Graphics.LEFT)); |
在顯示此界面時,如果用戶按下退出或開始鍵,則在commandAction方法中將通過如下代碼分別執(zhí)行程序退出處理或是重新開始下一局新的游戲:
if (arg0 == exitCommand) // 退出 midlet.quit(); else if (arg0 == newGameCommand) // 開始游戲 initialize(); |
人工智能的實現(xiàn)
如果說前面介紹的框架是骨骼,界面是皮肉的話,那么接下來將要介紹的人工智能部分則可以說是整個程序的靈魂了。它將進行對弈雙方落子的合法性檢測、計算機行棋的智能計算、游戲結(jié)束檢測以及對勝負結(jié)果的判定等工作。以上這些都需要有合理的設(shè)計才能實現(xiàn)較高的游戲運行效率。考慮到游戲規(guī)則始終是圍繞雙方棋子的排列形狀來進行的,因此可以把棋盤網(wǎng)格作為主要因素進行設(shè)計。按從左到右,從上到下的次序從0開始依次對棋盤的9個網(wǎng)格進行編號,可以得出如下幾組獲勝條件:0,1,2;3,4,5;6,7,8;0,3,6;1,4,7;2,5,8;0,4,8;2,4,6。只要有一方有三顆棋子的位置符合其中任何一組即可認定該方獲勝(讀者可以在紙上驗證一下)。在程序?qū)崿F(xiàn)過程中以WINS數(shù)組記錄上述幾種獲勝條件,并在每一次行棋完畢后進行比對,以判斷游戲是否有獲勝方產(chǎn)生。限于篇幅,下面主要對計算機行棋思路的人工智能設(shè)計進行介紹。
首先明確計算機的對弈目的:獲勝,如果暫時無法獲勝則阻止選手獲勝,如果雙方都暫時無法獲勝則可以下一些"隨手棋"。在計算出合適的下子位置后將其添加到己方的行棋記錄(打譜)以備后用。由此可以寫出如下代耄?BR>
int move = getWinningComputerMove();//如能立即獲勝則在獲勝位置下子 if (move == -1) { //如選手即將獲勝則在選手將獲勝的位置下子 move = getRequiredBlockingComputerMove(); if (move == -1) // 如雙方均暫時無法獲勝則下隨手棋 move = getRandomComputerMove(); } computerState |= bit(move); // 當(dāng)前計算機占用的所有位置 |
其中,getWinningComputerMove方法通過對所有可能下子位置(即尚未落子的網(wǎng)格)的枚舉,智能判斷計算機下一步走到哪里才能獲勝:
int move = -1; for (int i = 0; i < 9;++i) { if (isFree(i) && isWin(computerState | bit(i))) { move = i; // 找到獲勝位置時中斷 break; } } |
如果循環(huán)完畢仍沒有找到獲勝位置則表示目前己方暫無法獲勝,需要進一步調(diào)用getRequiredBlockingComputerMove方法來計算下一回合對方有無獲勝的可能,其實現(xiàn)代碼與getWinningComputerMove完全類似,只是以選手的行棋記錄playerState替代計算機的行棋記錄computerState而已。以上寥寥數(shù)行代碼即構(gòu)成了計算機對弈算法的人工智能核心部分,顯然人工智能在實現(xiàn)上并沒有想象的那么復(fù)雜與困難。
小結(jié) 通過本系列文章的介紹,陸續(xù)將J2ME手機應(yīng)用程序的一般開發(fā)過程向讀者作了一個較為系統(tǒng)和全面的介紹。尤其是本篇對圖形化手機游戲的介紹相信一定對讀者有不同程度的啟發(fā)作用,而且本文所述程序框架完全是通用的,讀者只需在此基礎(chǔ)之上重新設(shè)計游戲劇本即可實現(xiàn)類似的手機游戲如"華容道"、"俄羅斯方塊"等。本系列文章開發(fā)環(huán)境為:
Windows 2000 Professional + SP4;
Java2SDK 1.5.0;
J2ME Wireless ToolKits 2.1;
SonyErisson J2ME SDK(WTK 1.0.4);
SonyErisson T628;
Eclipse 3.0.1-win32;
EclipseMe 0.5.5;
NLpack-eclipse-SDK-3.0.x-win32
凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉(zhuǎn)載請注明來處和原文作者。非常感謝。