游戲策劃咨訊
          做一個(gè)游戲并不難,難的是做一個(gè)好游戲;完美在于積累!

          本文通過(guò)一個(gè)簡(jiǎn)單的MIDlet游戲程序示例,簡(jiǎn)要介紹了MIDlet圖形編程.....

          介紹

            本文通過(guò)一個(gè)簡(jiǎn)單的MIDlet游戲程序示例,簡(jiǎn)要介紹了MIDlet圖形編程,以期能對(duì)開(kāi)發(fā)者深入理解MIDP圖形編程和開(kāi)發(fā)復(fù)雜的移動(dòng)游戲有所幫助。

            一. MIDLET圖形

            1、MIDlet 圖形簡(jiǎn)述

            移動(dòng)信息設(shè)備描述(Mobile Information Device Profile ,MIDP)定義了一套應(yīng)用編程接口(API),用于運(yùn)行在MIDP容器中的MIDlet應(yīng)用程序。這套API本身是建立在有限連接設(shè)備配置(Connected Limited Device Configuration ,CLDC)應(yīng)用編程接口的基礎(chǔ)上的。MIDP用戶界面應(yīng)用編程接口類并不是基于Java抽象窗口工具包(Abstract Window Toolkit ,AWT)設(shè)計(jì)。它們是專為手機(jī)和呼機(jī)這樣的小型移動(dòng)信息設(shè)備而設(shè)計(jì)的,這類設(shè)備的特點(diǎn)是只有很小的屏幕和鍵盤(pán)。當(dāng)一個(gè)程序員在編寫(xiě)MIDP圖形應(yīng)用程序的時(shí)候,他可能只能使用MIDP或CLDC應(yīng)用編程接口。

            MIDP的中心抽象是屏幕,這句話的含義是MIDP的用戶界面設(shè)計(jì)是基于屏幕的(screen-based)。也就是說(shuō),Screen類封裝了設(shè)備特定的圖形和用戶交互,所有的用戶界面組件都位于屏幕上,并且一次只顯示一個(gè)屏幕,并且只能瀏覽或使用這個(gè)屏幕上的條目。由屏幕來(lái)處理所有的用戶界面事件。并只把高級(jí)事件傳送給應(yīng)用。之所以采取這種面向屏幕(screen-oriented) 的方式,主要是因?yàn)橐苿?dòng)設(shè)備的顯示屏幕和鍵盤(pán)實(shí)是種類太多了,幾乎每個(gè)廠家都多多少少有所不同。圖1是基于屏幕的MIDP圖形用戶界面的一些例子。


          圖1:基于屏幕的MIDP 圖形用戶界面

            MIDP 應(yīng)用編程接口具有高級(jí)用戶界面類和低級(jí)用戶界面類。高級(jí)用戶界面類(例如Form、List、TextBox、TextField、Alert,及Ticker)可被適配到設(shè)備上:支持圖像、文本、文本輸入域、單選按鈕等。低級(jí)用戶界面類(Canvas類)允許開(kāi)發(fā)者根據(jù)需要繪制任意圖形。MIDlet可以運(yùn)行在各種不同尺寸的彩色、不同灰度等級(jí)或黑白屏幕的手機(jī)上。高級(jí)用戶界面類是通用用戶界面元素的抽象,它的用途在于提高M(jìn)IDlet跨不同設(shè)備的移植性,并且可以使用本地設(shè)備的外觀表現(xiàn)。低級(jí)應(yīng)用編程接口則能夠更直接地控制顯示內(nèi)容,但是MIDlet設(shè)計(jì)者應(yīng)該確保其在不同設(shè)備(顯示尺寸、鍵盤(pán)、色彩等)上的可移植性。上面的例子既用到了高級(jí)應(yīng)用編程接口又用到了低級(jí)應(yīng)用編程接口。

            所有的MIDP圖形用戶界面類都是javax.microedition.lcdui程序包的一部分。

            2、MIDlet屏幕

            MIDP有兩種主要的屏幕類型:

            A 高級(jí)屏幕

             它包括簡(jiǎn)單的高級(jí)屏幕類,例如List和TextBox。用戶不能添加額外的圖形用戶界面組件到這種類型的屏幕中。九宮格MIDlet示例程序使用的屏幕是繼承于名為ChoosePieceScreen的List類,用于游戲者在游戲開(kāi)始時(shí)選擇棋子。

             一般的Form屏幕類和List類很相像,但是它允許使用額外的圖形元素,例如:圖像、只讀文本域、可編輯文本域、可編輯數(shù)據(jù)域、標(biāo)尺和選項(xiàng)組。Form條目可以任意地被添加或刪除。九宮格例程中沒(méi)有使用Form類。

            B 低級(jí)屏幕

             Canvas(畫(huà)布)屏幕(和Graphics、Image類) 可以用來(lái)編寫(xiě)基于低級(jí)應(yīng)用編程接口的用戶界面。這些類給予MIDlet程序員很大程度的繪畫(huà)靈活性。程序員可以繪制各種類型的圖形元素,例如:線、弧、矩形、圓角矩形、圓、文字(不同顏色、字體、大?。⑽粓D剪輯等等。大部分的游戲MIDlet是使用基于畫(huà)布屏幕類的主圖形用戶界面元素編寫(xiě)的。

            一個(gè)MIDlet用戶界面通常包含一個(gè)或多個(gè)屏幕。因?yàn)槊看沃荒茱@示一個(gè)屏幕,因此MIDlet具有良好設(shè)計(jì)的結(jié)構(gòu)是非常重要的進(jìn)行,這樣就能更加容易地處理屏幕之間內(nèi)容的切換。

            下面的代碼段說(shuō)明了在一個(gè)MIDlet中切換屏幕的方法,基于屏幕類和對(duì)應(yīng)的MIDlet回調(diào)。

            代碼段1:

          Class MyMIDlet extends MIDlet
          {
          private FirstScreen firstScreen;
          private SecondScreen secondScreen;
          public MyMIDlet()
          {

          }
          public void startApp()
          {
          Displayable current = Display.getDisplay(this).getCurrent();
          if (current == null)
          {
          firstScreen = new FirstScreen(this, …);
          Display.getDisplay(this).setCurrent(firstScreen);
          //顯示應(yīng)用程序的第一個(gè)用戶界面屏幕
          }
          else
          {
          Display.getDisplay(this).setCurrent(current);
          }
          }
          // FirstScreen 回調(diào)切換到下一個(gè)屏幕
          public void firstScreenDone()
          {

          secondScreen = new SecondScreen(this, …);
          display.getDisplay(this).setCurrent(secondScreen);
          }
          // SecondScreen回調(diào)終止應(yīng)用程序
          public void secondScreenQuit()
          {

          destroyApp(false);
          notifyDestroyed();
          }

          }


            這個(gè)MIDlet使用了兩個(gè)屏幕類(FirstScreen和SecondScreen)作為用戶界面。當(dāng)開(kāi)始執(zhí)行MIDlet的時(shí)候,它設(shè)置當(dāng)前顯示屏幕為FirstScreen。當(dāng)需要從FirstScreen切換到SecondScreen的時(shí)候,F(xiàn)irstScreen 調(diào)用父MIDlet方法firstScreenDone(參見(jiàn)下面的代碼)。firstScreenDone方法創(chuàng)建并設(shè)置SecondScreen為當(dāng)前顯示的屏幕。

            代碼段2: 包含MIDlet回調(diào)的FirstScreen示例

          Class FirstScreen extends Form implements CommandListener {
          private MyMIDlet midlet;
          public FirstScreen(MyMIDlet midlet)
          {
          this.midlet = midlet;

          }
          public void commandAction(Command c)
          {
          if (c == cmdQuit)
          {
          parent.firstScreenDone();
          }

          }

          }


            3、MIDP用戶界面應(yīng)用編程接口

            保證基于高級(jí)應(yīng)用編程接口類的用戶界面對(duì)象的可移植性和適用性是MIDP設(shè)備的職責(zé)。

            另一方面,像Canvas和Graphics這樣的低級(jí)類為程序員提供了更大的自由空間讓其控制其用戶界面的視覺(jué)表現(xiàn),并且監(jiān)聽(tīng)低級(jí)鍵盤(pán)事件。程序員還要負(fù)責(zé)確保應(yīng)用程序在不同特性(例如顯示尺寸、彩色或黑白,以及不同鍵盤(pán)類型)的移動(dòng)設(shè)備上的可移植性。比如說(shuō),有可能需要使用getWidth()和getHeight()方法調(diào)節(jié)用戶界面外觀使其適應(yīng)一個(gè)或更多設(shè)備的可用Canvas尺寸。

            下面的九宮格MIDlet例程將介紹:

             簡(jiǎn)單應(yīng)用高級(jí)應(yīng)用編程接口;

             使用低級(jí)應(yīng)用編程接口來(lái)繪制線、弧、字符串和圖像等圖形;

             不同顯示尺寸的移動(dòng)設(shè)備之間的MIDlet移植問(wèn)題

             鍵盤(pán)代碼與游戲動(dòng)作之間的映射

            本章概述了MIDP圖形用戶界面的設(shè)計(jì),如果想得到更進(jìn)一步的信息,請(qǐng)參閱
          http://java.sun.com/products/midp/ 。

            二. 示例:九宮格(TICTACTOEMIDLET)

            1、設(shè)計(jì)

            概述

            這個(gè)示例應(yīng)用程序是一個(gè)簡(jiǎn)單的MIDlet,允許游戲者與MIDlet程序之間玩一種稱為九宮格的人機(jī)游戲。這個(gè)例程說(shuō)明:

             使用高級(jí)和低級(jí)用戶界面組件

             在多顯示屏幕之間進(jìn)行切換

             處理簡(jiǎn)單的命令

             動(dòng)態(tài)適配顯示尺寸
           
             處理鍵盤(pán)事件

            游戲者首先選擇使用哪種棋子(用圓和叉表示),然后開(kāi)始游戲。游戲者和MIDlet誰(shuí)是先手是隨機(jī)決定的。每走一步棋之后,程序都要檢查游戲狀態(tài),判斷游戲是否已經(jīng)結(jié)束。游戲的幾種可能結(jié)果是:游戲者贏,MIDlet程序贏,或者平局。在應(yīng)用程序運(yùn)行期間,雙方的得分都能顯示出來(lái)。游戲者可以隨時(shí)開(kāi)始新游戲或者退出游戲。 

            圖2:所示的屏幕快照是游戲中的MIDlet用戶界面。


          圖2:游戲屏幕的先后順序

            2、九宮格MIDlet

            下面是九宮格MIDlet的類模式圖:


          圖3:九宮格MIDlet類圖

            當(dāng)MIDlet啟動(dòng)方法startApp()時(shí),將創(chuàng)建閃爍屏幕和第一個(gè)游戲屏幕(ChoosePieceScreen)。閃爍屏幕顯示4秒之后,第一個(gè)游戲屏幕開(kāi)始顯示。ChoosePieceScreen讓游戲者選擇使用哪種棋子(圓還是叉)。當(dāng)游戲者做出選擇之后,他可以使用OK鍵確認(rèn)。這會(huì)使ChoosePieceScreen回調(diào)主MIDlet的choicePieceScreenDone()方法。

            ChoosePieceScreen是使用高級(jí)應(yīng)用編程接口List類實(shí)現(xiàn)的。 

           
          圖4:ChoosePieceScreen是一個(gè)高級(jí)用戶界面List子類

            choosePieceScreenDone()回調(diào)創(chuàng)建并顯示下一個(gè)屏幕,這個(gè)屏幕在此應(yīng)用程序中作為游戲的主屏(GameScreen)。

            每當(dāng)輪到游戲者下棋的時(shí)候,游戲者使用GameScreen的箭頭鍵和Select按鈕來(lái)選擇想要走的空格。每一回合之后,應(yīng)用程序都會(huì)檢查游戲的狀態(tài),檢查其是否符合游戲結(jié)束條件并顯示游戲結(jié)果。游戲者通過(guò)點(diǎn)擊GameScreen的Quit命令結(jié)束游戲,或使用New命令開(kāi)始新一輪游戲。Quit(結(jié)束)命令調(diào)用TicTacToeMIDlet的quit()方法,然后MIDlet就會(huì)調(diào)用destroyApp()方法來(lái)終止整個(gè)MIDlet程序。

            游戲程序邏輯被封裝在一個(gè)單獨(dú)的Game類中。本文只關(guān)注MIDlet的圖形設(shè)計(jì),而對(duì)游戲程序邏輯不作深入探討。如果要與現(xiàn)有的applet Java程序作比較,請(qǐng)參閱http://java.sun.com/applets/jdk/1.0/demo/TicTacToe/TicTacToe.java 和http://java.sun.com/products/jfc/tsc/articles/tictactoe/index.html 中的游戲程序邏輯。
          GameScreen通過(guò)使用低級(jí)Canvas和Graphics類來(lái)實(shí)現(xiàn)。它使用Canvas、Image和Graphics對(duì)象來(lái)繪制圖形。
          GameScreen首先初始化基于畫(huà)布尺寸的顯示面板。這可讓MIDlet能夠運(yùn)行在不同顯示屏幕的移動(dòng)設(shè)備上。在本例中還使用了一個(gè)Image對(duì)象用來(lái)表示游戲面板。然后GameScreen根據(jù)游戲者在ChoosePieceScreen中所做的選擇為游戲者和MIDlet分配棋子。游戲然后進(jìn)行初始化(包括隨機(jī)決定誰(shuí)是先手),然后游戲就開(kāi)始了。
          為了使GameScreen能夠被移植,MIDlet的鍵盤(pán)代碼必須被映射到游戲動(dòng)作上,如:Up、Down、Left、Right和Fire,用于具有不同鍵盤(pán)的移動(dòng)設(shè)備。每當(dāng)一個(gè)鍵被按下的時(shí)候,keyPressed()方法就會(huì)判斷這是一個(gè)方向鍵還是一個(gè)Fire/Select鍵。如果按下的鍵是方向鍵,光標(biāo)就會(huì)相應(yīng)地移動(dòng),幫助游戲者可視化地選擇一個(gè)空格放入棋子。Select鍵用來(lái)選擇一個(gè)空格放入棋子。如果探測(cè)到滿足游戲結(jié)束的條件,就會(huì)顯示一條信息宣布游戲的獲勝者和本輪游戲的得分。(見(jiàn)下圖)

           
          圖5: GameScreen 是一個(gè)低級(jí) Canvas(畫(huà)布)子類

            3、TicTacToeMIDlet.java

            TicTacToeMIDlet非常簡(jiǎn)單:它處理MIDlet的生命周期事件。它根據(jù)需要?jiǎng)?chuàng)建屏幕對(duì)象并且處理來(lái)自屏幕的回調(diào)。ChoosePieceScreenDone回調(diào)被用來(lái)創(chuàng)建GameScreen。quit方法則被GameScreen用來(lái)結(jié)束游戲。

          package example.tictactoe;
          import java.io.IOException;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          import javax.microedition.io.*;
          public class TicTacToeMIDlet extends MIDlet {
          private ChoosePieceScreen choosePieceScreen;
          private GameScreen gameScreen;
          public TicTacToeMIDlet()
          {
          }
          public void startApp() {
          Displayable current = Display.getDisplay(this).getCurrent();
          if (current == null) {
          // first time we've been called
          // Get the logo image
          Image logo = null;
          try
          {
          logo = Image.createImage("/tictactoe.png");
          }
          catch (IOException e) {
          // just use null image
          }
          Alert splashScreen = new Alert(null, "Tic-Tac-Toe\nForum Nokia", logo, AlertType.INFO);


          splashScreen.setTimeout(4000);
          // 4 seconds
          choosePieceScreen = new ChoosePieceScreen(this);
          Display.getDisplay(this).setCurrent(splashScreen, choosePieceScreen);
          }
          else
          {
          Display.getDisplay(this).setCurrent(current);
          }
          }
          public void pauseApp() {
          }
          public void destroyApp(boolean unconditional) {
          }
          public void quit()
          {
          destroyApp(false);
          notifyDestroyed();
          }
          public void choosePieceScreenDone(boolean isPlayerCircle)
          {
          gameScreen = new GameScreen(this, isPlayerCircle);
          Display.getDisplay(this).setCurrent(gameScreen);
          }
          }

            4、ChoosePieceScreen.java

            ChoosePieceScreen是一個(gè)基于高級(jí)應(yīng)用編程接口窗體的屏幕,允許游戲者選擇圓或叉作為棋子。當(dāng)游戲者按下OK鍵時(shí),它使用MIDlet的回調(diào)方法choosePieceScreenDone來(lái)處理游戲者的選擇。

          package example.tictactoe;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          import javax.microedition.io.*;
          public class ChoosePieceScreen extends List implements CommandListener
          {
          private static final String CIRCLE_TEXT = "Circle";
          private static final String CROSS_TEXT = "Cross";
          private final TicTacToeMIDlet midlet;
          private final Command quitCommand;
          public ChoosePieceScreen(TicTacToeMIDlet midlet) {
          super("Choose your piece", List.IMPLICIT);
          this.midlet = midlet;
          append(CIRCLE_TEXT, loadImage("/circle.png"));
          append(CROSS_TEXT, loadImage("/cross.png"));
          quitCommand = new Command("Quit", Command.EXIT, 2);
          addCommand(quitCommand);
          setCommandListener(this);
          }
          public void commandAction(Command c, Displayable d) {
          boolean isPlayerCircle = getString(getSelectedIndex()).equals(CIRCLE_TEXT);
          if (c == List.SELECT_COMMAND) {
          midlet.choosePieceScreenDone(isPlayerCircle);
          }
          else
          // quit Command
          {
          midlet.quit();
          }
          }
          private Image loadImage(String imageFile)
          {
          Image image = null;
          try

          {
          image = Image.createImage(imageFile);
          }
          catch (Exception e)
          {
          // Use a 'null' image in the choice list (i.e. text only choices).
          }
          return image;
          }
          }

            5、GameScreen.java

            GameScreen使用了一個(gè)低級(jí)應(yīng)用編程接口Canvas屏幕,和Image、Graphics類來(lái)繪制游戲面板、棋子,以及游戲的最終結(jié)果狀態(tài)。要獲取更詳細(xì)的信息,請(qǐng)參閱各種繪畫(huà)方法和drawCircle、drawCross、drawPiece、drawPlayerCursor、drawBoard等方法。這個(gè)屏幕使用MIDlet的quit回調(diào)方法來(lái)指示游戲結(jié)束。
          此屏幕可適應(yīng)各種可用顯示性能(高、寬、色彩等)。此外還要注意到可以使用四向?qū)Ш芥I,也可以使用雙向?qū)Ш芥I來(lái)移動(dòng)光標(biāo)。

            它使用了封裝了主游戲程序邏輯的Game類。

          package example.tictactoe;
          import java.util.Random;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          class GameScreen extends Canvas implements CommandListener {
          private static final int BLACK = 0x00000000;
          private static final int WHITE = 0x00FFFFFF;
          private static final int RED = 0x00FF0000;
          private static final int BLUE = 0x000000FF;
          private static final int NO_MOVE = -1;
          private final TicTacToeMIDlet midlet;
          private final Game game;
          private final Command exitCommand;
          private final Command newGameCommand;
          private final Random random = new Random();
          private int screenWidth, screenHeight;
          private int boardCellSize, boardSize, boardTop, boardLeft;
          private boolean playerIsCircle;
          private boolean computerIsCircle;
          private int preCursorPosition, cursorPosition;
          private int computerMove = NO_MOVE;
          private int playerMove = NO_MOVE;
          private int computerGamesWonTally = 0;
          private int playerGamesWonTally = 0;
          private boolean isRestart;
          public GameScreen(TicTacToeMIDlet midlet, boolean playerIsCircle) {
          this.midlet = midlet;
          this.playerIsCircle = playerIsCircle;
          computerIsCircle = !playerIsCircle;
          game = new Game(random);
          initializeBoard();
          // configure Screen commands
          exitCommand = new Command("Exit", Command.EXIT, 1);
          newGameCommand = new Command("New", Command.SCREEN, 2);
          addCommand(exitCommand);
          addCommand(newGameCommand);
          setCommandListener(this);
          // begin the game play initialize();
          }
          // Initialize the Game and Game screen. Also used for game restarts.
          private void initialize() {
          game.initialize();
          preCursorPosition = cursorPosition = 0;
          playerMove = NO_MOVE;
          boolean computerFirst = ((random.nextInt() & 1) == 0);
          if (computerFirst) {
          computerMove = game.makeComputerMove();
          }
          else
          {
          computerMove = NO_MOVE;
          }
          isRestart = true;
          repaint();
          }
          public void paint(Graphics g) {
          if (game.isGameOver()) {
          paintGameOver(g);
          }
          else {
          paintGame(g);
          }
          }
          private void paintGame(Graphics g) {
          if (isRestart) {
          // clean the canvas
          g.setColor(WHITE);
          g.fillRect(0, 0, screenWidth, screenHeight);
          drawBoard(g);
          isRestart = false;
          }
          drawCursor(g);
          if (playerMove != NO_MOVE) {
          drawPiece(g, playerIsCircle, playerMove);
          }
          if (computerMove != NO_MOVE) {
          drawPiece(g, computerIsCircle, computerMove);
          }
          }
          private void paintGameOver(Graphics g)

          {
          String statusMsg = null;
          if(game.isComputerWinner()) {
          statusMsg = "I win !";
          computerGamesWonTally++;
          }
          else if (game.isPlayerWinner()) {
          statusMsg = "You win";
          playerGamesWonTally++;
          }
          else {
          statusMsg = "Stalemate";
          }
          String tallyMsg = "You:" + playerGamesWonTally + " Me:" + computerGamesWonTally;
          Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
          int strHeight = font.getHeight();
          int statusMsgWidth = font.stringWidth(statusMsg);
          int tallyMsgWidth = font.stringWidth(tallyMsg);
          int strWidth = tallyMsgWidth;
          if (statusMsgWidth > tallyMsgWidth)
          {
          strWidth = statusMsgWidth;
          }
          // Get the
          {
          x, y
          }
          position for painting the strings. int x = (screenWidth - strWidth) / 2;
          x = x < 0 ? 0 : x;
          int y = (screenHeight - 2 * strHeight) / 2;
          y = y < 0 ? 0 : y;
          // clean the canvas
          g.setColor(WHITE);
          g.fillRect(0, 0, screenWidth, screenHeight);
          // paint the strings' text
          g.setColor(BLACK);
          g.drawString(statusMsg, x, y, (Graphics.TOP | Graphics.LEFT));
          g.drawString(tallyMsg, x, (y + 1 + strHeight), (Graphics.TOP | Graphics.LEFT));
          }


          public void commandAction(Command c, Displayable d) {
          if (c == exitCommand) {
          midlet.quit();
          }
          else if (c == newGameCommand) {
          initialize();
          }
          }
          private void initializeBoard() {
          screenWidth = getWidth();
          screenHeight = getHeight();
          if (screenWidth > screenHeight) {
          boardCellSize = (screenHeight - 2) / 3;
          boardLeft = (screenWidth - (boardCellSize * 3)) / 2;
          boardTop = 1;
          }
          else {
          boardCellSize = (screenWidth - 2) / 3;
          boardLeft = 1;
          boardTop = (screenHeight - boardCellSize * 3) / 2;
          }
          }
          protected void keyPressed(int keyCode) {
          // can't continue playing until the player restarts
          if (game.isGameOver()) {
          return;
          }
          int gameAction = getGameAction(keyCode);
          switch (gameAction) {
          case FIRE: doPlayerMove();


          break;
          case RIGHT: doMoveCursor(1, 0);
          break;
          case DOWN: doMoveCursor(0, 1);
          break;
          case LEFT: doMoveCursor(-1, 0);
          break;
          case UP: doMoveCursor(0, -1);
          break;
          default: break;
          }
          }
          private void doPlayerMove() {
          if (game.isFree(cursorPosition)) {
          // player move game.
          makePlayerMove(cursorPosition);
          playerMove = cursorPosition;
          // computer move
          if (!game.isGameOver()) {
          computerMove = game.makeComputerMove();
          }
          repaint();
          }
          }
          private void doMoveCursor(int dx, int dy) {
          int newCursorPosition = cursorPosition + dx + 3 * dy;
          if ((newCursorPosition >= 0) && (newCursorPosition < 9))

          {
          preCursorPosition = cursorPosition;
          cursorPosition = newCursorPosition;
          repaint();
          }
          }
          // Draw a CIRCLE or CROSS piece on the board
          private void drawPiece(Graphics g, boolean isCircle, int pos) {
          int x = ((pos % 3) * boardCellSize) + 3;
          int y = ((pos / 3) * boardCellSize) + 3;
          if (isCircle) {
          drawCircle(g, x, y);
          }
          else {
          drawCross(g, x, y);
          }
          }
          // Draw blue CIRCLE onto the board image
          private void drawCircle(Graphics g, int x, int y) {
          g.setColor(BLUE);
          g.fillArc(x + boardLeft, y + boardTop, boardCellSize - 4, boardCellSize - 4, 0, 360);
          g.setColor(WHITE);
          g.fillArc(x + 4 + boardLeft, y + 4 + boardTop, boardCellSize - 4 - 8, boardCellSize - 4 - 8, 0, 360);
          }
          // Draw red CROSS onto the board image
          private void drawCross(Graphics g, int x, int y) {
          g.setColor(RED);
          for (int i = 0;
          i < 4;
          i++) {
          g.drawLine(x + 1 + i + boardLeft, y + boardTop, x + boardCellSize - 4 - 4 + i + boardLeft, y + boardCellSize - 5 + boardTop);


          g.drawLine(x + 1 + i + boardLeft, y + boardCellSize - 5 + boardTop, x + boardCellSize - 4 - 4 + i + boardLeft, y + boardTop);
          }
          }
          // Visually indicates a Player selected square on the board image
          private void drawCursor(Graphics g) {
          // draw cursor at selected Player square.
          g.setColor(WHITE);
          g.drawRect(((preCursorPosition % 3) * boardCellSize) + 2 + boardLeft, ((preCursorPosition/3) * boardCellSize) + 2 + boardTop, boardCellSize - 3, boardCellSize - 3);
          // draw cursor at selected Player square.
          g.setColor(BLACK);
          g.drawRect(((cursorPosition % 3) * boardCellSize) + 2 + boardLeft, ((cursorPosition/3) * boardCellSize) + 2 + boardTop, boardCellSize - 3, boardCellSize - 3);
          }
          private void drawBoard(Graphics g) {
          // clean the board
          g.setColor(WHITE);
          g.fillRect(0, 0, screenWidth, screenHeight);
          // draw the board
          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);
          }
          }
          }

            6、Game.java

            這個(gè)類封裝了九宮格游戲的主要的游戲程序邏輯。前面我們也說(shuō)過(guò),游戲程序邏輯本身并不在本例程重點(diǎn)討論的范圍之內(nèi),本文主要是介紹MIDP圖形編程的基礎(chǔ)知識(shí)。游戲程序邏輯的WINS數(shù)組部分來(lái)自http://java.sun.com/applets/jdk/1.0/demo/TicTacToe/TicTacToe.java 這個(gè)經(jīng)典例程。

            注意游戲程序邏輯是獨(dú)立于游戲用戶界面的(參見(jiàn)類GameScreen),并且可以使用其它實(shí)現(xiàn)方法替代。

          package example.tictactoe;
          import java.util.Random;
          import javax.microedition.midlet.*;
          import javax.microedition.lcdui.*;
          // The game logic for TicTacToe
          class Game {
          private static final int[] WINS = {
          // horizontals
          bit(0) | bit(1) | bit(2),
          bit(3) | bit(4) | bit(5),
          bit(6) | bit(7) | bit(8),
          // verticals
          bit(0) | bit(3) | bit(6),
          bit(1) | bit(4) | bit(7),
          bit(2) | bit(5) | bit(8),
          // diagonals
          bit(0) | bit(4) | bit(8),
          bit(2) | bit(4) | bit(6) }
          ;
          private static final int DRAWN_GAME = bit(0) | bit(1) | bit(2) | bit(3) | bit(4) | bit(5) | bit(6) | bit(7) | bit(8);
          private int playerState;
          private int computerState;
          private Random random;
          Game(Random random) {
          this.random = random;
          initialize();
          }
          void initialize() {
          playerState = 0;
          computerState = 0;
          }
          boolean isFree(int position) {
          int bit = bit(position);
          return (((playerState & bit) == 0) && ((computerState & bit) == 0));
          }
          // The 'Contract' is that caller will always make valid moves.
          // We don't check that it's the player's turn.
          void makePlayerMove(int position) {
          playerState |= bit(position);
          }
          // The 'Contract' is that we will be called only when there is still
          // at least one free square.
          int makeComputerMove() {
          int move = getWinningComputerMove();
          if (move == -1) {
          // can't win
          move = getRequiredBlockingComputerMove();
          if (move == -1) {
          // don't need to block
          move = getRandomComputerMove();
          }
          }
          computerState |= bit(move);
          return move;
          }


          boolean isGameOver() {
          return isPlayerWinner() | isComputerWinner() | isGameDrawn();
          }
          boolean isPlayerWinner() {
          return isWin(playerState);
          }
          boolean isComputerWinner() {
          return isWin(computerState);
          }
          boolean isGameDrawn() {
          return (playerState | computerState) == DRAWN_GAME;
          }
          // Return a winning move if there is at least one, otherwise return -1
          private int getWinningComputerMove() {
          int move = -1;
          for (int i = 0;
          i < 9;
          ++i) {
          if (isFree(i) && isWin(computerState | bit(i))) {
          move = i;
          break;
          }
          }
          return move;
          }
          // Return a required blocking move if there is at least one (more
          // than one and we've inevitably lost), otherwise return -1
          private int getRequiredBlockingComputerMove() {
          int move = -1;
          for (int i = 0;
          i < 9;
          ++i) {


          if (isFree(i) && isWin(playerState | bit(i))) {
          move = i;
          break;
          }
          }
          return move;
          }
          // Return a random move in a free square, // or return -1 if none are available private int getRandomComputerMove() {
          int move = -1;
          // determine how many possible moves there are int numFreeSquares = 0;
          for (int i = 0;
          i < 9;
          ++i) {
          if (isFree(i)) {
          numFreeSquares++;
          }
          }
          // if there is at least one possible move, pick randomly
          if (numFreeSquares > 0) {
          // shift twice to get rid of sign bit, then modulo numFreeSquares
          int pick = ((random.nextInt()<<1)>>>1) % numFreeSquares;
          // now find the chosen free square by counting pick down to zero
          for (int i = 0;
          i < 9;
          ++i) {
          if (isFree(i)) {
          if (pick == 0) {
          move = i;
          break;
          }
          pick--;
          }
          }
          }

          return move;
          }
          private static boolean isWin(int state) {
          boolean isWinner = false;
          for (int i = 0;
          i < WINS.length;
          ++i) {
          if ((state & WINS[i]) == WINS[i]) {
          isWinner = true;
          break;
          }
          }
          return isWinner;
          }
          private static int bit(int i) {
          return 1 << i;
          }
          }

            7、TicTacToe.jad

            下面是九宮格MIDlet的應(yīng)用程序描述文件。

          MIDlet-Name: TicTacToe
          MIDlet-Vendor: Forum Nokia MIDlet-Version: 1.1.1
          MIDlet-Jar-Size: 11409
          MIDlet-Jar-URL: TicTacToe.jar
          MIDlet-1: TicTacToe, /tictactoe.png, example.tictactoe.TicTacToeMIDlet

          posted on 2005-02-16 22:04 藍(lán)色雪焰 閱讀(217) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
           
          主站蜘蛛池模板: 仪陇县| 大庆市| 临武县| 江城| 老河口市| 民县| 巨鹿县| 商河县| 宜川县| 汽车| 嘉祥县| 乌鲁木齐市| 潮安县| 陈巴尔虎旗| 吴忠市| 施甸县| 革吉县| 汕头市| 永州市| 明溪县| 铅山县| 温州市| 竹北市| 陆良县| 巴塘县| 大城县| 福海县| 松潘县| 崇文区| 资兴市| 林西县| 沅江市| 长治市| 福安市| 祥云县| 平凉市| 绍兴市| 辽中县| 青田县| 资溪县| 冕宁县|