public class Images {//保存常量 //繪圖位置常量 public static final int UNIT = 32;//方塊的單位長度 public static final int LEFT = 10;//畫圖的左邊界頂點 public static final int TOP = 9;//畫圖的上邊界頂點 //地圖位置常量 public static final int WIDTH = 4;//地圖的寬度 public static final int HEIGHT = 5;//地圖的高度 //地圖標記常量 public static final byte CAOCAO = (byte) ´a´; <A href="file://曹">file://曹</A>操的地圖標記 public static final byte MACHAO = (byte) ´b´;//馬超的地圖標記 public static final byte HUANGZHONG = (byte) ´c´;//黃忠的地圖標記 public static final byte GUANYU = (byte) ´d´;//關羽的地圖標記 public static final byte ZHANGFEI = (byte) ´e´;//張飛的地圖標記 public static final byte ZHAOYUN = (byte) ´f´;//趙云的地圖標記 public static final byte ZU = (byte) ´g´;//卒的地圖標記 public static final byte BLANK = (byte) ´h´;//空白的地圖標記 public static final byte CURSOR = (byte) ´i´;//光標的地圖標記 //地圖組合標記常量 public static final byte DLEFT = (byte) ´1´; <A href="file://組">file://組</A>合圖形左邊標記 public static final byte DUP = (byte) ´2´; <A href="file://組">file://組</A>合圖形上邊標記 public static final byte DLEFTUP = (byte) ´3´; <A href="file://組">file://組</A>合圖形左上標記 //圖片常量 public static Image image_base;//基本圖片 public static Image image_Zhaoyun;//趙云的圖片 public static Image image_Caocao;//曹操的圖片 public static Image image_Huangzhong;//黃忠的圖片 public static Image image_Machao;//馬超的圖片 public static Image image_Guanyu;//關羽的圖片 public static Image image_Zhangfei;//張飛的圖片 public static Image image_Zu;//卒的圖片 public static Image image_Blank;//空白的圖片 public static Image image_Frame;//游戲框架的圖片
public Images() {//構造函數 }
public static boolean init() {//初始化游戲中用到的圖片 try { image_base = Image.createImage("/huarongroad/BITBACK.png"); image_Frame = Image.createImage(image_base, 126, 0, 145, 177, Sprite.TRANS_NONE); //Sprite類是用來翻轉圖片的,是MIDP2.0新新增加的支持游戲的特性 image_Zhaoyun = Image.createImage(image_base, 0, 0, UNIT, 2 * UNIT, Sprite.TRANS_NONE); image_Caocao = Image.createImage(image_base, UNIT, 0, 2 * UNIT, 2 * UNIT, Sprite.TRANS_NONE); image_Huangzhong = Image.createImage(image_base, 3 * UNIT, 0, UNIT, 2 * UNIT, Sprite.TRANS_NONE); image_Machao = Image.createImage(image_base, 0, 2 * UNIT, UNIT, 2 * UNIT, Sprite.TRANS_NONE); image_Guanyu = Image.createImage(image_base, UNIT, 2 * UNIT, 2 * UNIT, UNIT, Sprite.TRANS_NONE); image_Zhangfei = Image.createImage(image_base, 3 * UNIT, 2 * UNIT, UNIT, 2 * UNIT, Sprite.TRANS_NONE); image_Zu = Image.createImage(image_base, 0, 4 * UNIT, UNIT, UNIT, Sprite.TRANS_NONE); image_Blank = Image.createImage(image_base, 1 * UNIT, 4 * UNIT,UNIT, UNIT, Sprite.TRANS_NONE);
public class Draw { //繪制游戲中的圖片 public Draw(Canvas canvas) {//構造函數 }
public static boolean paint(Graphics g, byte img, int x, int y) { //在地圖的x,y點繪制img指定的圖片 try { paint(g, img, x, y, Images.UNIT);//把地圖x,y點轉化成畫布的絕對坐標,繪圖 return true; } catch (Exception ex) { return false; } }
public static boolean paint(Graphics g, byte img, int x, int y, int unit) { try { switch (img) { case Images.CAOCAO://畫曹操 //變成絕對坐標,并做調整 g.drawImage(Images.image_Caocao, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.GUANYU://畫關羽 g.drawImage(Images.image_Guanyu, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.HUANGZHONG://畫黃忠 g.drawImage(Images.image_Huangzhong, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.MACHAO://畫馬超 g.drawImage(Images.image_Machao, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.ZHANGFEI://畫張飛 g.drawImage(Images.image_Zhangfei, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.ZHAOYUN://畫趙云 g.drawImage(Images.image_Zhaoyun, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.ZU://畫卒 g.drawImage(Images.image_Zu, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.BLANK://畫空白 g.drawImage(Images.image_Blank, Images.LEFT + x * unit, Images.TOP + y * unit, Graphics.TOP Graphics.LEFT); break; case Images.CURSOR://畫光標 g.drawRect(Images.LEFT + x * unit, Images.TOP + y * unit,Images.UNIT,Images.UNIT); break; } return true; }catch (Exception ex) { return false; } } } 其中Images類存的是繪圖位置常量(也就是在畫圖時每個格子的長度和相對坐標原點位置要進行的調整)、地圖位置常量(地圖的長、寬),地圖標記常量(人物對應的記號),地圖組合標記常量(后面會細說),圖片常量(存放人物的圖片);Draw類主要負責在制定的位置畫出人物圖片。下面我來說說Images類中的地圖標記常量和地圖組合標記常量。為了能夠靈活的安排各個關面的布局,我們決定把游戲布局的信息存儲在外部文件中,然后程序啟動后把它讀進來。這樣我們制定了一套存儲圖片的代碼,這就是地圖標記常量,如上面Images類中定義的Caocao(曹操)用a字符來表示,當程序讀到a字符時就能將它轉化成曹操對應的圖片,并在讀到a字符的位置上進行顯示。但是從實際觀察中我們發現所有的圖片并不是統一大小的,有的占4個格子,有的占2個格子,還有的占1個格子,而且即便同是占兩個格子的圖片還有橫、豎之分。有鑒于此,我們引入了地圖組合標記常量,就是說在遇到占有多個格子的時候,值1(也就是Images.LEFT)表示它的左邊是一個真正的地圖標記,值2(也就是Images.UP)表示它的上邊是一個真正的地圖標記,值1(也就是Images.LEFTUP)表示它的左上邊是一個真正的地圖標記。地圖組合標記常量其實就是用來占位置的,與實際顯示無關,當后面我們將到移動時還會再來分析組合標記的使用。 Draw類主要是用來在畫布上畫出圖形,它有兩個paint方法,這是很常見的函數重載。但是程序中實際上只用到了4個參數的paint方法,它直接獲得要畫圖片的相對坐標位置信息,然后調用5個參數的paint方法。5個參數的paint方法將相對坐標位置信息轉換成絕對位置,并實際調用Graphics.drawImage()方法,將Images中的圖片畫了出來。這種實現方法的好處是靈活和便于擴展,但你需要畫圖的位置并不能夠對應到格子中的相對坐標位置時,你就可以直接調用5個參數的paint方法,而不必再去修改這各類;但你添加新的圖片時,只要在Images中增加對應的常量,然后向Draw中5個參數的paint方法添加一條處理就可以了。 寫到這里,兩天的時間剛好用完。 三、需求分析 這部分叫做需求分析,聽起來挺嚇人的,其實就是搞清楚我們要做什么,做成什么樣,那些不做。下面我引領著大家共同來完成這一步驟。首先,我們要做一個華容道的游戲,華容道的故事這里不再贅述了,但其中的人物在這里限定一下,如上面Images類里的定義,我們這個版本只提供曹操(Caocao)、關羽(Guanyu)、張飛(Zhangfei)、趙云(Zhaoyun)、黃忠(Huangzhong)、馬超(Machao)和卒(Zu)。我們這里也限定一下游戲的操作方法:首先要通過方向鍵選擇一個要移動的區域(就是一張圖片),被選擇的區域用黑色方框框?。贿x好后按Fire鍵(就是確定鍵)將這塊區域選中,被選中的區域用綠色方框框住;然后選擇要移動到的區域,此時用紅色方框框住被選擇的區域;選好要移動到的區域之后按Fire鍵將要移動的區域(圖片)移到要移動到的區域,并去掉綠色和紅色的方框。這里需要強調的概念有選擇的區域、選中的區域、要移動的區域和要移動到的區域,這四個概念請讀者注意區分,當然也應當把這一部分記入數據字典之中。為了使文章的重點突出(介紹如何制作一個J2ME的收集游戲),我們這里限定一些與本主題無關的內容暫不去實現:過關之后的動畫(實現時要用到TimerTask或Thread類,后續的系列文章中我會詳細介紹動畫方面的知識)、關面之間的切換(其實很簡單,當完成任務之后重新再做一邊)、暫停和保存等操作(這部分的內容介紹的資料很多,我也寫不出什么新的東東來,難免抄襲,故此免掉)。 需求分析基本完成,離下午還有一段時間,馬上動手用ACDSee把從網上找來的BMP文件,調整其大小為271*177(我的這個圖片是兩個部分合在一起,所以比手機實際屏幕大了),另存為PNG格式。半天時間剛剛好,不但搞清楚了要做的東東,還把要用的圖片準備好了。 四、概要設計 概要設計是從需求分析過渡到詳細設計的橋梁和紐帶,這一部分中我們確定項目的實現方法和模塊的劃分。我們決定將整個項目分成五個部分,分別是前面介紹的Images、Draw,還有Map和Displayable1和MIDlet1。Images和Draw類功能簡單、結構固定,因此很多項目我們都使用這兩各類,這里直接拿來改改就能用了,前面已經介紹過這里不再贅述。Map類是用來從外部文件讀入地圖,然后保存在一個數組之中,這部分的內容是我們在本階段討論的重點。Displayable1是一個繼承了Canvas類的畫布,它用來處理程序的主要控制邏輯和一部分控制邏輯所需的輔助函數,主要函數應該包括用來繪圖的paint()函數、用來控制操作的keyPressed()函數、用來控制選擇區域的setRange()函數、用來控制選擇要移動到區域的setMoveRange()函數、用來移動選中區域的Move()函數和判斷是否完成任務的win()函數,更具體的分析,我們放到詳細設計中去細化。MIDlet1實際上就是一個控制整個J2ME應用的控制程序,其實也沒有什么可特別的,它和我們前面介紹的"Hello World"程序大同小異,這里就不展開來說了,后面會貼出它的全部代碼。 Map類主要應該有一個Grid[][]的二維數組,用來存放華容道的地圖,還應該有一個read_map()函數用來從外部文件讀取地圖內容填充Grid數據結構,再就是要有一個draw_map()函數用來把Grid數據結構中的地圖內容轉換成圖片顯示出來(當然要調用Draw類的paint方法)。說到讀取外部文件,筆者知道有兩種方法:一種是傳統的定義一個InputStream對象,然后用getClass().getResourceAsStream()方法取得輸入流,然后再從輸入流中取得外部文件的內容,例如 InputStream is = getClass().getResourceAsStream("/filename"); if (is != null) { byte a = (byte) is.read(); } 這里請注意文件名中的根路徑是相對于便以后的class文件放置的位置,而不是源文件(java)。第二種方法是使用onnector.openInputStream方法,然后打開的協議是Resource,但是這種方法筆者反復嘗試都沒能調通,報告的錯誤是缺少Resource協議,估計第二種方法用到J2ME的某些擴展類包,此處不再深究。由于以前已經做過一些類似華容道這樣的地圖,這里直接給出Map類的代碼,后面就不再詳細解釋Map類了,以便于我們可以集中精力處理Displayable1中的邏輯。Map類的代碼如下: package huarongroad;