本文通過(guò)使用J2ME開(kāi)發(fā)華容道游戲,介紹了J2ME游戲開(kāi)發(fā)的基本模式....
一、序言
昨天在網(wǎng)上閑逛,發(fā)現(xiàn)一篇講解用delphi實(shí)現(xiàn)華容道游戲的文章,頗受啟發(fā).于是,產(chǎn)生了將華容道游戲移植到手機(jī)中去的沖動(dòng).現(xiàn)在手機(jī)游戲琳瑯滿目,不一而足,華容道的實(shí)現(xiàn)版本也很多.正巧不久前筆者對(duì)J2ME下了一番功夫,正想借這個(gè)機(jī)會(huì)小試牛刀。選用J2ME的原因還有一個(gè)就是目前Java開(kāi)發(fā)大行其到,無(wú)線增殖業(yè)務(wù)迅猛發(fā)展,J2ME的應(yīng)用日漸活躍起來(lái),也希望我的這篇文章能夠?yàn)镴2ME知識(shí)的普及和開(kāi)發(fā)團(tuán)隊(duì)的壯大推波助瀾。由于長(zhǎng)期受ISO規(guī)范的影響,這次小試牛刀我也打算遵照軟件工程的要求,并采取瀑布式的開(kāi)發(fā)模式來(lái)規(guī)劃項(xiàng)目,也希望借此機(jī)會(huì)向各位沒(méi)有機(jī)會(huì)參與正式項(xiàng)目開(kāi)發(fā)的讀者介紹一下軟件開(kāi)發(fā)的流程。
這里我們先定義項(xiàng)目組的人員體制(其實(shí)只有我一個(gè)人):技術(shù)調(diào)研、需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、編碼、測(cè)試均有筆者一人擔(dān)任;美工這里我找了個(gè)捷徑,盜用網(wǎng)上現(xiàn)成的圖片,然后用ACDSee把它由BMP轉(zhuǎn)換成PNG格式(我出于講座的目的,未做商業(yè)應(yīng)用,應(yīng)該不算侵權(quán)吧);至于發(fā)布工作,由于缺少OTA服務(wù)器,此項(xiàng)工作不做(但是我會(huì)介紹這步如何做)。
接下來(lái),我們規(guī)劃一下項(xiàng)目實(shí)現(xiàn)的時(shí)間表,以我個(gè)人經(jīng)驗(yàn),設(shè)想如下:技術(shù)調(diào)研用2天(這部分解決項(xiàng)目的可行性和重大技術(shù)問(wèn)題,時(shí)間會(huì)長(zhǎng)一些),需求分析用半天(畢竟有現(xiàn)成的東東可以參照,只要理清思路就行了,況且還有很多以前用過(guò)的設(shè)計(jì)模式和寫(xiě)好的代碼),概要設(shè)計(jì)再用半天(有了需求,概要只不夠是照方抓藥),詳細(xì)設(shè)計(jì)要用2天(這一步要把所有的問(wèn)題想清楚,還要盡可能的準(zhǔn)確描述出來(lái)),編碼用2天(其實(shí)1天就夠了,技術(shù)已經(jīng)不是問(wèn)題,多計(jì)劃出一天來(lái)應(yīng)付突發(fā)事件),測(cè)試用2天(測(cè)試應(yīng)該至少占全部項(xiàng)目的四分之一,不過(guò)這個(gè)項(xiàng)目只是一個(gè)Demo,也太簡(jiǎn)單了),發(fā)布也要用上半天(盡管我們不去實(shí)際發(fā)布它,但是還要花點(diǎn)時(shí)間搞清楚應(yīng)該如何做),最后就是項(xiàng)目總結(jié)和開(kāi)慶功會(huì)(時(shí)間待定)。
二.利其器
“公欲善其事,必先利其器”,做項(xiàng)目之前第一步是前期調(diào)研.我們要做的華容道這個(gè)東東隨處可見(jiàn),我們要調(diào)研的是兩個(gè)方面:
1、游戲的內(nèi)容:游戲本身很簡(jiǎn)單,就是有幾個(gè)格子,曹操占據(jù)其中一個(gè)較大的格子,然后被幾個(gè)格子包圍,這些格子形狀不一定相同,但是擋住了曹操移動(dòng)的方向.游戲者需要挪動(dòng)這些格子最終把曹操移動(dòng)到一個(gè)指定的位置才算是過(guò)關(guān).更具體的分析我們放在后面需求分析和概要設(shè)計(jì)中討論。
2、技術(shù)儲(chǔ)備:談到技術(shù),這里簡(jiǎn)單介紹一下J2ME.Java有三個(gè)版本,分別是J2ME(微型版).J2SE(標(biāo)準(zhǔn)版).J2EE(企業(yè)版).J2ME是一個(gè)標(biāo)準(zhǔn),采用3層結(jié)構(gòu)設(shè)計(jì).最低層是配置層(Configuration)也就是設(shè)備層,其上是簡(jiǎn)表層(Profile),再上是應(yīng)用層(Application).MIDP就是移動(dòng)信息設(shè)備簡(jiǎn)表,目前主流手機(jī)支持MIDP1.0,最新的是MIDP2.0,它比前一個(gè)版本增加了對(duì)游戲的支持,在javax.microedition.lcdui.game包中提供了一些類(lèi)來(lái)處理游戲中的技術(shù),比如我們后面會(huì)用到的Sprite類(lèi),它是用來(lái)翻轉(zhuǎn)圖片的.權(quán)衡再三,筆者決定使用MIDP2.0來(lái)做開(kāi)發(fā).首先需要安裝一個(gè)J2ME的模擬器,我們就用Sun公司的WTK2.0,我覺(jué)得Sun的東西最權(quán)威.當(dāng)然你也可以使用Nokia.Siemens或是Motolora等其他模擬器,但是他們的JDK不盡相同,寫(xiě)出來(lái)的程序移植是比較麻煩的.Sun公司的WTK2.0可以到<A href="http://here/下">http://here/下</A>載,當(dāng)然要想成功下載的前提是你要先注冊(cè)成為Sun的會(huì)員(其實(shí)這樣對(duì)你是有好處的).當(dāng)下來(lái)之后就是按照提示一步一步的安裝.安裝好了之后,我們用一個(gè)"Hello World"程序開(kāi)始你的J2ME之旅.我們啟動(dòng)WTK2.0工具集中的KToolBar,然后點(diǎn)擊New Project按鈕,在彈出的輸入框中輸入Project Name為HelloWorld,MIDlet Class Name為Hello,然后點(diǎn)擊Create Project,開(kāi)始生成項(xiàng)目,工具會(huì)彈出MIDP配置簡(jiǎn)表,這里接受生成的默認(rèn)值(以后還可以修改)點(diǎn)擊OK,工具提示我們把寫(xiě)好的Java源程序放到[WTK_HOME]\apps\HelloWorld\src目錄之下.我們編輯如下代碼,并保存在上述目錄之下,文件名為Hello.java。
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class Hello extends MIDlet { private Display display; public Hello(){ display =Display.getDisplay(this); } public void startApp(){ TextBox t = new TextBox("Hello","Hello",256,0); display.setCurrent(t); } public void pauseApp(){ } public void destroyApp(boolean unconditional){ } } |
保存好了之后,點(diǎn)擊Build按鈕,工具會(huì)為你編譯程序,如無(wú)意外再點(diǎn)擊Run按鈕,會(huì)彈出一個(gè)手機(jī)界面,剩下的就不用我教了吧(用鼠標(biāo)對(duì)手機(jī)按鍵一頓狂點(diǎn))。呵呵,你的第一個(gè)J2ME程序已經(jīng)OK了.什么?你還一點(diǎn)都沒(méi)懂呢(真是厲害,不懂都能寫(xiě)出J2ME程序來(lái),果然是高手).我這里主要是介紹WTK2.0工具的使用,程序并不是目的,不懂的話后面還會(huì)有詳細(xì)的解說(shuō),這里只是帶你上路.什么?你不懂Java!那也沒(méi)有關(guān)系,后面我再講得細(xì)一點(diǎn)。
跳過(guò)J2ME,我們先來(lái)講點(diǎn)游戲的理論.具體到華容道這個(gè)游戲,主要有三個(gè)方面,貼圖.游戲操作.邏輯判斷.這里講講貼圖,其他兩方面放在概要設(shè)計(jì)和詳細(xì)設(shè)計(jì)里講.所謂的貼圖,其實(shí)就是畫(huà)圖,就是在要顯示圖形的位置上輸出一副圖片,(要是牽扯到動(dòng)畫(huà)就要麻煩一些,可以使用TimerTask.Thread或Rannable之類(lèi)的技術(shù)),這副圖片可以是事先準(zhǔn)備好的也可以是臨時(shí)處理的.在J2ME中有一個(gè)Image類(lèi),專門(mén)用于管理圖片,它有createImage()方法,可以直接讀取圖片文件(J2ME只支持PNG格式的圖片),也可以截取已有的圖片的一部分(這樣我們可以把很多圖片放在一起,然后一張一張的截下來(lái),好處是節(jié)省存儲(chǔ)空間和文件讀取時(shí)間,對(duì)于手機(jī)這兩者都是性能的瓶頸).J2ME還有一個(gè)Graphics類(lèi),專門(mén)用于繪圖,它有drawImage()方法,可以把一副圖片在指定的位置上顯示出來(lái),它還有drawRect()方法和setColor()方法,這兩個(gè)方法在后面我們進(jìn)行游戲操作時(shí)就會(huì)用到,這里先交代一下.有了圖片和繪圖的方法,還需要知道把圖畫(huà)到誰(shuí)身上,J2ME提供了一個(gè)Canvas類(lèi),字面意思就是畫(huà)布,它有一個(gè)paint()方法用于刷新頁(yè)面,還有一個(gè)repaint()方法用于調(diào)用paint()方法.聽(tīng)著有些糊涂是吧,不要緊,我來(lái)結(jié)合具體程序講解一下.為了今后編程的方便,我們創(chuàng)建兩個(gè)類(lèi)Images和Draw,Images用于保存一些常量值和圖片,Draw主要是用于畫(huà)圖,這兩個(gè)類(lèi)的源代碼如下。
Images類(lèi)的源代碼如下:
package huarongroad;
import javax.microedition.lcdui.*; public class Images {//保存常量 public Images() {//構(gòu)造函數(shù) public static boolean init() {//初始化游戲中用到的圖片 return true; |
Draw類(lèi)的源代碼如下:
package huarongroad; import javax.microedition.lcdui.*; public class Draw { public static boolean paint(Graphics g, byte img, int x, int y) { public static boolean paint(Graphics g, byte img, int x, int y, int unit) { |
其中Images類(lèi)存的是繪圖位置常量(也就是在畫(huà)圖時(shí)每個(gè)格子的長(zhǎng)度和相對(duì)坐標(biāo)原點(diǎn)位置要進(jìn)行的調(diào)整)、地圖位置常量(地圖的長(zhǎng)、寬),地圖標(biāo)記常量(人物對(duì)應(yīng)的記號(hào)),地圖組合標(biāo)記常量(后面會(huì)細(xì)說(shuō)),圖片常量(存放人物的圖片);Draw類(lèi)主要負(fù)責(zé)在制定的位置畫(huà)出人物圖片。下面我來(lái)說(shuō)說(shuō)Images類(lèi)中的地圖標(biāo)記常量和地圖組合標(biāo)記常量。為了能夠靈活的安排各個(gè)關(guān)面的布局,我們決定把游戲布局的信息存儲(chǔ)在外部文件中,然后程序啟動(dòng)后把它讀進(jìn)來(lái)。這樣我們制定了一套存儲(chǔ)圖片的代碼,這就是地圖標(biāo)記常量,如上面Images類(lèi)中定義的Caocao(曹操)用a字符來(lái)表示,當(dāng)程序讀到a字符時(shí)就能將它轉(zhuǎn)化成曹操對(duì)應(yīng)的圖片,并在讀到a字符的位置上進(jìn)行顯示。但是從實(shí)際觀察中我們發(fā)現(xiàn)所有的圖片并不是統(tǒng)一大小的,有的占4個(gè)格子,有的占2個(gè)格子,還有的占1個(gè)格子,而且即便同是占兩個(gè)格子的圖片還有橫、豎之分。有鑒于此,我們引入了地圖組合標(biāo)記常量,就是說(shuō)在遇到占有多個(gè)格子的時(shí)候,值1(也就是Images.LEFT)表示它的左邊是一個(gè)真正的地圖標(biāo)記,值2(也就是Images.UP)表示它的上邊是一個(gè)真正的地圖標(biāo)記,值1(也就是Images.LEFTUP)表示它的左上邊是一個(gè)真正的地圖標(biāo)記。地圖組合標(biāo)記常量其實(shí)就是用來(lái)占位置的,與實(shí)際顯示無(wú)關(guān),當(dāng)后面我們將到移動(dòng)時(shí)還會(huì)再來(lái)分析組合標(biāo)記的使用。
Draw類(lèi)主要是用來(lái)在畫(huà)布上畫(huà)出圖形,它有兩個(gè)paint方法,這是很常見(jiàn)的函數(shù)重載。但是程序中實(shí)際上只用到了4個(gè)參數(shù)的paint方法,它直接獲得要畫(huà)圖片的相對(duì)坐標(biāo)位置信息,然后調(diào)用5個(gè)參數(shù)的paint方法。5個(gè)參數(shù)的paint方法將相對(duì)坐標(biāo)位置信息轉(zhuǎn)換成絕對(duì)位置,并實(shí)際調(diào)用Graphics.drawImage()方法,將Images中的圖片畫(huà)了出來(lái)。這種實(shí)現(xiàn)方法的好處是靈活和便于擴(kuò)展,但你需要畫(huà)圖的位置并不能夠?qū)?yīng)到格子中的相對(duì)坐標(biāo)位置時(shí),你就可以直接調(diào)用5個(gè)參數(shù)的paint方法,而不必再去修改這各類(lèi);但你添加新的圖片時(shí),只要在Images中增加對(duì)應(yīng)的常量,然后向Draw中5個(gè)參數(shù)的paint方法添加一條處理就可以了。
寫(xiě)到這里,兩天的時(shí)間剛好用完。
三、需求分析
這部分叫做需求分析,聽(tīng)起來(lái)挺嚇人的,其實(shí)就是搞清楚我們要做什么,做成什么樣,那些不做。下面我引領(lǐng)著大家共同來(lái)完成這一步驟。首先,我們要做一個(gè)華容道的游戲,華容道的故事這里不再贅述了,但其中的人物在這里限定一下,如上面Images類(lèi)里的定義,我們這個(gè)版本只提供曹操(Caocao)、關(guān)羽(Guanyu)、張飛(Zhangfei)、趙云(Zhaoyun)、黃忠(Huangzhong)、馬超(Machao)和卒(Zu)。我們這里也限定一下游戲的操作方法:首先要通過(guò)方向鍵選擇一個(gè)要移動(dòng)的區(qū)域(就是一張圖片),被選擇的區(qū)域用黑色方框框住;選好后按Fire鍵(就是確定鍵)將這塊區(qū)域選中,被選中的區(qū)域用綠色方框框住;然后選擇要移動(dòng)到的區(qū)域,此時(shí)用紅色方框框住被選擇的區(qū)域;選好要移動(dòng)到的區(qū)域之后按Fire鍵將要移動(dòng)的區(qū)域(圖片)移到要移動(dòng)到的區(qū)域,并去掉綠色和紅色的方框。這里需要強(qiáng)調(diào)的概念有選擇的區(qū)域、選中的區(qū)域、要移動(dòng)的區(qū)域和要移動(dòng)到的區(qū)域,這四個(gè)概念請(qǐng)讀者注意區(qū)分,當(dāng)然也應(yīng)當(dāng)把這一部分記入數(shù)據(jù)字典之中。為了使文章的重點(diǎn)突出(介紹如何制作一個(gè)J2ME的收集游戲),我們這里限定一些與本主題無(wú)關(guān)的內(nèi)容暫不去實(shí)現(xiàn):過(guò)關(guān)之后的動(dòng)畫(huà)(實(shí)現(xiàn)時(shí)要用到TimerTask或Thread類(lèi),后續(xù)的系列文章中我會(huì)詳細(xì)介紹動(dòng)畫(huà)方面的知識(shí))、關(guān)面之間的切換(其實(shí)很簡(jiǎn)單,當(dāng)完成任務(wù)之后重新再做一邊)、暫停和保存等操作(這部分的內(nèi)容介紹的資料很多,我也寫(xiě)不出什么新的東東來(lái),難免抄襲,故此免掉)。
需求分析基本完成,離下午還有一段時(shí)間,馬上動(dòng)手用ACDSee把從網(wǎng)上找來(lái)的BMP文件,調(diào)整其大小為271*177(我的這個(gè)圖片是兩個(gè)部分合在一起,所以比手機(jī)實(shí)際屏幕大了),另存為PNG格式。半天時(shí)間剛剛好,不但搞清楚了要做的東東,還把要用的圖片準(zhǔn)備好了。
四、概要設(shè)計(jì)
概要設(shè)計(jì)是從需求分析過(guò)渡到詳細(xì)設(shè)計(jì)的橋梁和紐帶,這一部分中我們確定項(xiàng)目的實(shí)現(xiàn)方法和模塊的劃分。我們決定將整個(gè)項(xiàng)目分成五個(gè)部分,分別是前面介紹的Images、Draw,還有Map和Displayable1和MIDlet1。Images和Draw類(lèi)功能簡(jiǎn)單、結(jié)構(gòu)固定,因此很多項(xiàng)目我們都使用這兩各類(lèi),這里直接拿來(lái)改改就能用了,前面已經(jīng)介紹過(guò)這里不再贅述。Map類(lèi)是用來(lái)從外部文件讀入地圖,然后保存在一個(gè)數(shù)組之中,這部分的內(nèi)容是我們?cè)诒倦A段討論的重點(diǎn)。Displayable1是一個(gè)繼承了Canvas類(lèi)的畫(huà)布,它用來(lái)處理程序的主要控制邏輯和一部分控制邏輯所需的輔助函數(shù),主要函數(shù)應(yīng)該包括用來(lái)繪圖的paint()函數(shù)、用來(lái)控制操作的keyPressed()函數(shù)、用來(lái)控制選擇區(qū)域的setRange()函數(shù)、用來(lái)控制選擇要移動(dòng)到區(qū)域的setMoveRange()函數(shù)、用來(lái)移動(dòng)選中區(qū)域的Move()函數(shù)和判斷是否完成任務(wù)的win()函數(shù),更具體的分析,我們放到詳細(xì)設(shè)計(jì)中去細(xì)化。MIDlet1實(shí)際上就是一個(gè)控制整個(gè)J2ME應(yīng)用的控制程序,其實(shí)也沒(méi)有什么可特別的,它和我們前面介紹的"Hello World"程序大同小異,這里就不展開(kāi)來(lái)說(shuō)了,后面會(huì)貼出它的全部代碼。
Map類(lèi)主要應(yīng)該有一個(gè)Grid[][]的二維數(shù)組,用來(lái)存放華容道的地圖,還應(yīng)該有一個(gè)read_map()函數(shù)用來(lái)從外部文件讀取地圖內(nèi)容填充Grid數(shù)據(jù)結(jié)構(gòu),再就是要有一個(gè)draw_map()函數(shù)用來(lái)把Grid數(shù)據(jù)結(jié)構(gòu)中的地圖內(nèi)容轉(zhuǎn)換成圖片顯示出來(lái)(當(dāng)然要調(diào)用Draw類(lèi)的paint方法)。說(shuō)到讀取外部文件,筆者知道有兩種方法:一種是傳統(tǒng)的定義一個(gè)InputStream對(duì)象,然后用getClass().getResourceAsStream()方法取得輸入流,然后再?gòu)妮斎肓髦腥〉猛獠课募膬?nèi)容,例如
InputStream is = getClass().getResourceAsStream("/filename"); if (is != null) { byte a = (byte) is.read(); } |
這里請(qǐng)注意文件名中的根路徑是相對(duì)于便以后的class文件放置的位置,而不是源文件(java)。第二種方法是使用onnector.openInputStream方法,然后打開(kāi)的協(xié)議是Resource,但是這種方法筆者反復(fù)嘗試都沒(méi)能調(diào)通,報(bào)告的錯(cuò)誤是缺少Resource協(xié)議,估計(jì)第二種方法用到J2ME的某些擴(kuò)展類(lèi)包,此處不再深究。由于以前已經(jīng)做過(guò)一些類(lèi)似華容道這樣的地圖,這里直接給出Map類(lèi)的代碼,后面就不再詳細(xì)解釋Map類(lèi)了,以便于我們可以集中精力處理Displayable1中的邏輯。Map類(lèi)的代碼如下:
package huarongroad; import java.io.InputStream; public class Map { public byte Grid[][];//存放地圖數(shù)據(jù) public Map() {//構(gòu)造函數(shù),負(fù)責(zé)初始化地圖數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu) public int[] read_map(int i) { public boolean draw_map(Graphics g) { |
對(duì)于像華容道這樣的小型地圖可以直接用手工來(lái)繪制地圖的內(nèi)容,比如:
fa1c
2232
bd1e
2gg2
gihg
但是,如果遇到像坦克大戰(zhàn)或超級(jí)瑪莉那樣的地圖,就必須另外開(kāi)發(fā)一個(gè)地圖編輯器了(我會(huì)在后續(xù)的文章中介紹用vb來(lái)開(kāi)發(fā)一個(gè)地圖編輯器)。
五、詳細(xì)設(shè)計(jì)
詳細(xì)設(shè)計(jì)是程序開(kāi)發(fā)過(guò)程中至關(guān)重要的一個(gè)環(huán)節(jié),好在我們?cè)谇懊娴母鱾€(gè)階段中已經(jīng)搭建好了項(xiàng)目所需的一些工具,現(xiàn)在這個(gè)階段中我們只需集中精力設(shè)計(jì)好Displayable1中的邏輯。(兩天的時(shí)間當(dāng)然不只干這點(diǎn)活,還要把其他幾個(gè)類(lèi)的設(shè)計(jì)修改一下)
Displayable1這個(gè)類(lèi)負(fù)責(zé)處理程序的控制邏輯。首先,它需要有表示當(dāng)前關(guān)面的變量level、表示當(dāng)前光標(biāo)位置的變量loc、表示要移動(dòng)區(qū)域的變量SelectArea、表示要移動(dòng)到的區(qū)域的變量MoveArea、表示是否已有區(qū)域被選中而準(zhǔn)備移動(dòng)的變量Selected和Map類(lèi)的實(shí)例MyMap。然后,我們根據(jù)用戶按不同的鍵來(lái)處理不同的消息,我們要實(shí)現(xiàn)keyPressed()函數(shù),在函數(shù)中我們處理按鍵的上下左右和選中(Fire),這里的處理需要我展開(kāi)來(lái)講一講,后面我很快會(huì)把這一部分詳細(xì)展開(kāi)。
接下來(lái),是實(shí)現(xiàn)paint()函數(shù),我們打算在這一部分中反復(fù)的重畫(huà)背景、地圖和選擇區(qū)域,這個(gè)函數(shù)必須處理好區(qū)域被選中之后的畫(huà)筆顏色的切換,具體講就是在沒(méi)有選中任何區(qū)域時(shí)要用黑色畫(huà)筆,當(dāng)選重要移動(dòng)的區(qū)域時(shí)使用綠色畫(huà)筆,當(dāng)選擇要移動(dòng)到的區(qū)域時(shí)改用紅色畫(huà)筆(當(dāng)然附加一張流程圖是必不可少的)。
再下面要實(shí)現(xiàn)的setRange()函數(shù)和setMoveRange()函數(shù),這兩個(gè)函數(shù)用來(lái)設(shè)置要移動(dòng)的區(qū)域和要移動(dòng)到的區(qū)域,我的思路就是利用前面在Images類(lèi)中介紹過(guò)的地圖組合標(biāo)記常量,當(dāng)移動(dòng)到地圖組合標(biāo)記常量時(shí),根據(jù)該點(diǎn)地圖中的值做逆向變換找到相應(yīng)的地圖標(biāo)記常量,然后設(shè)置相應(yīng)的loc、SelectArea和MoveArea,其中setMoveRange()函數(shù)還用到了一個(gè)輔助函數(shù)isInRange(),isInRange()函數(shù)是用來(lái)判斷給定的點(diǎn)是否在已選中的要移動(dòng)的區(qū)域之內(nèi),如果isInRange()的返回值是假并且該點(diǎn)處的值不是空白就表明要移動(dòng)到的區(qū)域侵犯了其他以被占用的區(qū)域。有了setRange()和setMoveRange()函數(shù),Move()函數(shù)就水到渠成了,Move()函數(shù)將要移動(dòng)的區(qū)域移動(dòng)到要移動(dòng)到的區(qū)域,在移動(dòng)過(guò)程中分為三步進(jìn)行:
第一.復(fù)制要移動(dòng)的區(qū)域;
第二.將復(fù)制出的要移動(dòng)區(qū)域復(fù)制到要移動(dòng)到的區(qū)域(這兩步分開(kāi)進(jìn)行的目的是防止在復(fù)制過(guò)程中覆蓋掉要移動(dòng)的區(qū)域);
第三.用isInRange2()判斷給定的點(diǎn)是否在要移動(dòng)到的區(qū)域內(nèi),將不在要移動(dòng)到的區(qū)域內(nèi)的點(diǎn)設(shè)置成空白。
下面我們?cè)敿?xì)的分析一下keyPressed()函數(shù)的實(shí)現(xiàn)方法:首先,keyPressed()函數(shù)要處理按鍵的上下左右和選中(Fire),在處理時(shí)需要用Canvas類(lèi)的getGameAction函數(shù)來(lái)將按鍵的鍵值轉(zhuǎn)換成游戲的方向,這樣可以提高游戲的兼容性(因?yàn)椴煌腏2ME實(shí)現(xiàn),其方向鍵的鍵值不一定是相同的)。
接下來(lái),分別處理四個(gè)方向和選中.當(dāng)按下向上時(shí),先判斷是否已經(jīng)選定了要移動(dòng)的區(qū)域(即this.selected是否為真),如果沒(méi)有選中要移動(dòng)區(qū)域則讓光標(biāo)向上移動(dòng)一格,然后調(diào)用setRange()函數(shù)設(shè)置選擇要移動(dòng)的區(qū)域,再調(diào)用repaint()函數(shù)刷新屏幕,否則如果已經(jīng)選中了要移動(dòng)的區(qū)域,就讓光標(biāo)向上移動(dòng)一格,然后調(diào)用setMoveRange()函數(shù)判斷是否能夠向上移動(dòng)已選中的區(qū)域,如果能移動(dòng)就調(diào)用repaint()函數(shù)刷新屏幕,如果不能移動(dòng)就讓光標(biāo)向下退回到原來(lái)的位置。
當(dāng)按下向下時(shí),先判斷是否已經(jīng)選定了要移動(dòng)的區(qū)域,如果沒(méi)有選中要移動(dòng)的區(qū)域則判斷當(dāng)前所處的區(qū)域是否為兩個(gè)格高,如果是兩個(gè)格高則向下移動(dòng)兩格,如果是一個(gè)格高則向下移動(dòng)一格,接著再調(diào)用setRange()函數(shù)設(shè)置選擇要移動(dòng)的區(qū)域,而后調(diào)用repaint()函數(shù)刷新屏幕,否則如果已經(jīng)選中了要移動(dòng)的區(qū)域,就讓光標(biāo)向下移動(dòng)一格,然后調(diào)用setMoveRange()函數(shù)判斷是否能夠向下移動(dòng)已選中的區(qū)域,如果能移動(dòng)就調(diào)用repaint()函數(shù)刷新屏幕,如果不能移動(dòng)就讓光標(biāo)向上退回到原來(lái)的位置.按下向左時(shí)情況完全類(lèi)似向上的情況,按下向右時(shí)情況完全類(lèi)似向下的情況,因此這里不再贅述,詳細(xì)情況請(qǐng)參見(jiàn)程序的源代碼。
當(dāng)按下選中鍵時(shí),先判斷是否已經(jīng)選中了要移動(dòng)的區(qū)域,如果已經(jīng)選中了要移動(dòng)的區(qū)域就調(diào)用Move()函數(shù)完成由要移動(dòng)的區(qū)域到要移動(dòng)到的區(qū)域的移動(dòng)過(guò)程,接著調(diào)用repaint()函數(shù)刷新屏幕,然后將已選擇標(biāo)記置成false,繼續(xù)調(diào)用win()函數(shù)判斷是否完成了任務(wù),否則如果還沒(méi)有選定要移動(dòng)的區(qū)域則再判斷當(dāng)前選中區(qū)域是否為空白,如果不是空白就將選中標(biāo)記置成true,然后刷新屏幕.這里介紹一個(gè)技巧,在開(kāi)發(fā)程序遇到復(fù)雜的邏輯的時(shí)候,可以構(gòu)造一格打印函數(shù)來(lái)將所關(guān)心的數(shù)據(jù)結(jié)構(gòu)打印出來(lái)以利調(diào)試,這里我們就構(gòu)造一個(gè)PrintGrid()函數(shù),這個(gè)函數(shù)純粹是為了調(diào)試之用,效果這得不錯(cuò).至此我們完成了編碼前的全部工作。
六.編碼
整個(gè)項(xiàng)目共有五個(gè)類(lèi),有四個(gè)類(lèi)的代碼前面已經(jīng)介紹過(guò)了,而且是在其他項(xiàng)目中使用過(guò)的相對(duì)成熟的代碼.現(xiàn)在只需全力去實(shí)現(xiàn)Displayable1類(lèi).Displayable1類(lèi)的代碼如下:
package huarongroad; import javax.microedition.lcdui.*; public class Displayable1 extends Canvas implements CommandListener { private int[] loc = new int[2]; <A href="file://光">file://光</A>標(biāo)的當(dāng)前位置,0是水平位置,1是豎直位置 public void commandAction(Command command, Displayable displayable) { protected void paint(Graphics g) { private void setRange() { private boolean setMoveRange() { private boolean isInRange(int x, int y) { private boolean isInRange2(int x, int y) { protected void keyPressed(int keyCode) { private boolean win(){ private void PrintGrid(String a) { private void Move() { |
代碼的相關(guān)分析,在詳細(xì)設(shè)計(jì)階段已經(jīng)講過(guò),代碼中有比較相近的注釋,請(qǐng)讀者自行研讀分析.將全部的代碼寫(xiě)好,用wtk2.0自帶的Ktoolbar工具建立一個(gè)工程,接下來(lái)把去不源文件放到正確位置下,然后點(diǎn)擊build,再點(diǎn)run,就完成了程序的編寫(xiě).當(dāng)然如果有錯(cuò)誤還要修改和調(diào)試.
七、測(cè)試
作為一個(gè)真正的產(chǎn)品要經(jīng)過(guò)單體測(cè)試、結(jié)合測(cè)試和系統(tǒng)測(cè)試。由于項(xiàng)目本身簡(jiǎn)單,而且大部分代碼已經(jīng)是相對(duì)成熟的,我們跳過(guò)單體測(cè)試;又由于筆者的實(shí)際環(huán)境所限,無(wú)法搞到Java手機(jī),無(wú)法架設(shè)OTA服務(wù)器,因此我們也只能放棄系統(tǒng)測(cè)試。那么就讓我們開(kāi)始結(jié)合測(cè)試吧。測(cè)試之前要先出一個(gè)測(cè)試式樣書(shū),也就是測(cè)試的計(jì)劃。我們將它簡(jiǎn)化一下,只測(cè)試如下幾種情況:第一、對(duì)各種形狀的區(qū)域的選擇和移動(dòng);第二、臨近邊界區(qū)域的選擇和移動(dòng);第三、同一區(qū)域的反復(fù)選擇和反復(fù)移動(dòng);第四、非法選擇和非法移動(dòng)。有了測(cè)試的目標(biāo),接下來(lái)的工作就是用wtk2.0自帶的Run MIDP Application工具進(jìn)行測(cè)試。打開(kāi)這個(gè)工具,加載huarongRoad的jad文件,程序就會(huì)自動(dòng)運(yùn)行,選擇launch上MIDlet1這個(gè)程序,華容道游戲就會(huì)躍然屏幕之上,接下來(lái)的工作就是左三點(diǎn).右三點(diǎn),拇指扭扭,來(lái)做測(cè)試。測(cè)試過(guò)程中發(fā)現(xiàn)任何的問(wèn)題,立刻發(fā)一個(gè)bug票給自己,然后就又是痛苦的調(diào)試和修正bug,如此如此。
八.發(fā)布
談到發(fā)布,其實(shí)是個(gè)關(guān)鍵,再好的產(chǎn)品不能很好的發(fā)布出去也只是個(gè)產(chǎn)品而已,變不成商品也就得不到回報(bào).由于筆者的條件所限,這里只能是紙上談兵,不過(guò)還是希望能夠使讀者對(duì)這一過(guò)程有所了解(網(wǎng)上的資料也很多)。
J2ME的程序發(fā)布一般都是通過(guò)OTA(Over The Air),你只需要一臺(tái)有公網(wǎng)IP的主機(jī)和一個(gè)普通的web Server就可以了(盡管要求很低,但筆者還是沒(méi)有),這里我們以apache為例介紹一下OTA服務(wù)的配置,首先是安裝好了apache服務(wù)器,然后在conf目錄下找到mime.types文件,在該文件中加入如下兩行
application/java-archive jar
text/vnd.sun.j2me.app-descriptor jad
然后重起apache服務(wù)器就可以了。接下來(lái)的工作就是修改jad文件中MIDlet-Jar-URL:后面的參數(shù),將它改為URL的絕對(duì)路徑,即<A href="http://***/">http://***/</A>huarongroad.jar(其中***是你的域名或IP地址)。在下面就是用java手機(jī)下載jad文件,它會(huì)自動(dòng)部署相應(yīng)的jar文件并加載它。剩下的工作就和在模擬器上操作是一樣的了。
九、項(xiàng)目總結(jié)
至此,我們已經(jīng)完成了一個(gè)J2ME游戲的全部開(kāi)發(fā)過(guò)程,程序中涉及到了調(diào)研、分析、設(shè)計(jì)、編碼、測(cè)試和發(fā)布等方面的問(wèn)題,其實(shí)在實(shí)際的工作中還有很多更為具體的問(wèn)題,畢竟技術(shù)只在軟件開(kāi)發(fā)過(guò)程中占據(jù)很有限的一部分,這里限于篇幅的限制無(wú)法一一具體展開(kāi)。今后,筆者計(jì)劃再寫(xiě)一篇使用J2ME開(kāi)發(fā)手機(jī)屏保的文章,借此機(jī)會(huì)向讀者展示J2ME動(dòng)畫(huà)技術(shù);然后再寫(xiě)一篇J2ME網(wǎng)絡(luò)應(yīng)用的文章,做一個(gè)類(lèi)似開(kāi)心辭典那樣的知識(shí)問(wèn)答游戲,以便向讀者展示J2ME的網(wǎng)絡(luò)技術(shù);待這兩方面的技術(shù)交待清楚之后,我將引領(lǐng)讀者制作一個(gè)稍大一些的游戲。
只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。 | ||
![]() |
||
網(wǎng)站導(dǎo)航:
博客園
IT新聞
Chat2DB
C++博客
博問(wèn)
管理
|
||
相關(guān)文章:
|
||