国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

Java游戲開發(fā)案例-方塊游戲

2019-11-18 15:03:14
字體:
來源:轉載
供稿:網友
方塊游戲”簡介

“方塊”游戲使用一個3x3的網格,其中每一個單元格要么顯示一種顏色,要么什么都沒有(表示為黑色)。游戲開始時一些單元格隨機填充顏色,其他的都用默認黑色。只要你在30秒內清除所有單元格的顏色(全部變?yōu)楹谏瑳]有其他顏色存在),你就獲勝了。

你要么移動鼠標點擊一個單元格,要么直接按小鍵盤的相應數字鍵,都可以清除那個單元格里的顏色。類似的,假如你所點擊的單元格本身是黑色,那么那個單元格就會被填充一種其他顏色。也就是說會有這樣的循環(huán):黑色變彩色,彩色變黑色。假如僅僅這樣游戲就太輕易了,因此我設計的方塊游戲是,你對單元格的點擊/按鍵會影響他自己和他的四周單元格,如圖1所示。

Java游戲開發(fā)案例-方塊游戲(圖一)
圖1. (A) 游戲板布局;(B) 當單元格1改變而受到影響的單元格;(C) 當單元格2改變而受到影響的單元格;(D) 當單元格5改變而受到影響的單元格

圖1根據數字小鍵盤的布局顯示了相應的游戲板。例如,數字鍵7對應左上角的單元格。圖1中還展示了當一個單元格改變而受到影響的相應單元格(B、C、D中)。假如改變的是角上的,四周三個單元格也會受到影響(B);假如你改變的是邊上的,同一邊的其他兩個單元格也會受到影響(C);假如改變的是中心的,它東南西北的單元格也都會受影響(D)。

java重寫

我最早是用C寫的“方塊”游戲。因為C和Java的語法很相似,所以用Java重寫并不困難。在我展示我的第一個“方塊”applet的代碼之前,你大概想知道界面是怎樣的。圖2顯示了你運行那個applet時的界面。

Java游戲開發(fā)案例-方塊游戲(圖二)
圖2. 包含一個游戲板、兩個按鈕的“方塊”游戲界面

游戲板控件是一個類似于“石頭剪子”游戲的網格的區(qū)域,并且在它下邊有一個白色的消息區(qū)域。這個控件還有一個邊框,這個邊框在空間失去焦點的時候是黑色的,在獲得焦點時變成藍色。“Change Square Color”按鈕初始時無效,只有游戲開始以后才可用(假如游戲沒有進行,也就沒理由改變顏色了)。點擊“Start”按鈕可以開始游戲,如圖3所示。

Java游戲開發(fā)案例-方塊游戲(圖三)
圖3. “方塊”游戲開始以后,在游戲板的消息區(qū)域會顯示當前剩余的秒數

圖3顯示了游戲進行時的界面。消息區(qū)顯示了把所有單元格變?yōu)楹谏€剩余的秒數。假如這個數字到達0,你就輸了。假如你能在此之前把所有單元格變?yōu)楹谏悄憔挖A了。在游戲進行時,你可以點擊“Change Square Color”按鈕以隨機改變各單元的顏色。不過假如你輸了或者贏了,那“Change Square Color”按鈕會變成無效,而“Start”按鈕會恢復有效,這樣你就可以開始另一個游戲了。

下邊是源代碼:
Squares.java
// Squares.java

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class Squares extends JApplet
{
   PRivate void createGUI ()
   {
       // 設定界面
      getContentPane ().setLayout (new FlowLayout ());

      // 創(chuàng)建游戲板控件:每個單元格有40像素寬,默認綠色,并且在獲得焦點時邊框是藍色,
   // 而失去焦點時變?yōu)楹谏0芽丶拥絚ontent pane里。
      final GameBoard gb;
      gb = new GameBoard (40, Color.green, Color.blue, Color.black);
      getContentPane ().add (gb);

      // 界面其他部分包括兩個按鈕,他們會被放到一個panel里以作為整體處理。例如,
      // 假如Applet的寬度變大了,兩個按鈕(而不是一個按鈕)都會向游戲板的右側對齊。
      JPanel p = new JPanel ();

      // 創(chuàng)建“Change Square Color”按鈕并設置為無效。只有游戲進行中可以改變顏色。
    final JButton BTnChangeSquareColor = new JButton ("Change Square Color");
      btnChangeSquareColor.setEnabled (false);

      // 建立“Change Square Color”按鈕的action事件監(jiān)聽器,點擊此按鈕,會隨機改變
   // 單元格的顏色
      ActionListener al;
      al = new ActionListener ()
           {
               public void actionPerformed (ActionEvent e)
               {
                  Random rnd = new Random ();

                  while (true)
                  {
                      int r = rnd.nextInt (256);
                      int g = rnd.nextInt (256);
                      int b = rnd.nextInt (256);

                      // 不使用所有組成原色(紅、綠、藍)都小于192的顏色,因為那不
                      // 輕易和背景的黑色區(qū)分出來。
                      if (r < 192 && g < 192 && b < 192)
                          continue;

                      gb.changeSquareColor (new Color (r, g, b));

                      break;
                  }
               }
           };

      btnChangeSquareColor.addActionListener (al);

      p.add (btnChangeSquareColor);

      // 創(chuàng)建“Start”按鈕
    final JButton btnStart = new JButton ("Start");

      // 建立“Start”按鈕的action事件監(jiān)聽器。點擊這個按鈕時,它本身會變?yōu)闊o效(沒
      // 理由開始一個正在進行的游戲),并使“Change Square Color”按鈕有效(游戲進
      // 行時可以改變單元格顏色)。“done”事件監(jiān)控器則用于在游戲結束時使“Start”按
      // 鈕有效,以及使“Change Square Color”按鈕無效。
      al = new ActionListener ()
           {
               public void actionPerformed (ActionEvent e)
               {
                  btnStart.setEnabled (false);
                  btnChangeSquareColor.setEnabled (true);

                  gb.start (new GameBoard.DoneListener ()
                            {
                                public void done ()
                                {
                                   btnStart.setEnabled (true);
                                   btnChangeSquareColor.setEnabled (false);
                                }
                            });
               }
           };

      btnStart.addActionListener (al);

      // 通過一個panel把兩個按鈕添加到content pane里邊。
      p.add (btnStart);

      getContentPane ().add (p);

      // 在Java 1.4.0里,假如不設置JApplet為焦點循環(huán)根節(jié)點、并且新建一個焦點遍歷
      // 規(guī)則的話,你就沒有辦法把焦點從一個控件切換到另一個。你可以在以下鏈接看到相關信
      // 息:http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4705205
      if (System.getProperty ("java.version").equals ("1.4.0"))
      {
          setFocusCycleRoot (true);
          setFocusTraversalPolicy (new LayoutFocusTraversalPolicy ());
      }
   }

   public void init ()
   {
      // Sun的Java教程說Swing控件應該在事件處理線程里創(chuàng)建、查詢、以及操作。由于大
      // 多數瀏覽器都不去調用Applet的主如init()的那些主要方法,我們在那個線程里調
      // 用SwingUtilities.invokeAndWait()以保證在事件處理線程里GUI被正確創(chuàng)建。
      // 我們用invokeAndWait()而不是invokeLater(),因為后者會導致在GUI創(chuàng)建之前
      // init()方法會返回;這會造成一些很難跟蹤的applet問題。
      try
      {
          SwingUtilities.invokeAndWait (new Runnable ()
                                        {
                                            public void run ()
                                            {
                                               createGUI ();
                                            }
                                        });
      }
      catch (Exception e)
      {
          System.err.println ("Unable to create GUI");
      }
   }
}

class GameBoard extends JPanel
{
   // 游戲狀態(tài)
   private final static int INITIAL = 0;
   private final static int INPLAY = 1;
   private final static int LOSE = 2;
   private final static int WIN = 3;

   // 邊框尺寸
   private final static int BORDER_SIZE = 5;

   // 當前游戲狀態(tài)
   private int state = INITIAL;

   // 在單元格邊框之間的像素寬度
   private int cellSize;

   // 游戲板的寬度(包含邊框)
   private int width;

   // 游戲板及消息區(qū)的總計高度(包含邊框)
   private int height;

   // 每一個單元格的顏色
   private Color squareColor;

   // 在游戲板擁有焦點時的邊框顏色
   private Color focusBorderColor;

   // 在游戲板是去焦點時的邊框顏色
   private Color nonfocusBorderColor;

   // 游戲板當前的邊框顏色
   private Color borderColor;

   // 單元格狀態(tài):true代表特定單元格包含一個有顏色的方塊(非黑色)
   private boolean [] cells = new boolean [9];

   // 對游戲結束監(jiān)聽器的引用
   private GameBoard.DoneListener dl;

   // 對倒計時的計時器的引用;這個計數器判定玩家時候獲勝/失敗,并且通知當游戲結束時通
   // 知DoneListener
   private Timer timer;

   // 計時器的計時數字
   private int counter;

   // 游戲板構造函數
   GameBoard (int cellSize, Color squareColor, Color focusBorderColor,
              Color nonfocusBorderColor)
   {
      this.cellSize = cellSize;

      width = 3*cellSize+2+2*BORDER_SIZE;
      height = width + 50;

      setPreferredSize (new Dimension (width, height));

      this.squareColor = squareColor;


      this.focusBorderColor = focusBorderColor;


      this.nonfocusBorderColor = nonfocusBorderColor;

      this.borderColor = nonfocusBorderColor;

      addFocusListener (new FocusListener ()
                        {
                            public void focusGained (FocusEvent e)
                            {
                               borderColor = GameBoard.this.focusBorderColor;

                               repaint ();
                            }

                            public void focusLost (FocusEvent e)
                            {
                               borderColor = GameBoard.this.nonfocusBorderColor;

                               repaint ();
                            }
                        });

      addKeyListener (new KeyAdapter ()
                      {
                          public void keyTyped (KeyEvent e)
                          {
                             if (state != INPLAY)
                               return;

                             char key = e.getKeyChar ();

                             // 假如玩家通過數字小鍵盤輸入,則將輸入映射到相應的單
                             // 元格,并對此單元格及其四周的單元格做出相應變動。
                             if (Character.isDigit (key))
                                 switch (key)
                                 {
                                    case ’1’: GameBoard.this.toggle (6);
                                              break;

                                    case ’2’: GameBoard.this.toggle (7);
                                              break;

                                    case ’3’: GameBoard.this.toggle (8);
                                              break;


                                    case ’4’: GameBoard.this.toggle (3);
                                              break;

                                    case ’5’: GameBoard.this.toggle (4);
                                              break;

                                    case ’6’: GameBoard.this.toggle (5);
                                              break;

                                    case ’7’: GameBoard.this.toggle (0);
                                              break;

                                    case ’8’: GameBoard.this.toggle (1);
                                              break;

                                    case ’9’: GameBoard.this.toggle (2);
                                 }
                          }
                      });

      addMouseListener (new MouseAdapter ()
                        {
                            public void mouseClicked (MouseEvent e)
                            {
                               if (state != INPLAY)
                                 return;

                               // 當鼠標點擊游戲板時,確保游戲板獲得焦點,以便玩家
                               // 使用鍵盤作為替代輸入方法。
                               GameBoard.this.requestFocusInWindow ();

                               // 哪一個單元格被點擊?
                               int cell = GameBoard.this.
                                          mouseToCell (e.getX (), e.getY ());

                               // 假如一個單元格被點擊(cell != -1),則翻轉那個
                               // 單元格及其鄰居的顏色。
                               if (cell != -1)
                                   GameBoard.this.toggle (cell);
                            }
                        });

      setFocusable (true);
   }

   // 修改當前單元格的顏色。注重:這個方法被事件處理線程調用
   void changeSquareColor (Color squareColor)
   {
      if (!SwingUtilities.isEventDispatchThread ())
          return;

      this.squareColor = squareColor;
      repaint ();
   }

   // 繪制組件:先畫邊框,對后畫消息
   public void paintComponent (Graphics g)
   {
      // 推薦首先調用父類的paintComponent()
      super.paintComponent (g);

      // 用當前邊框顏色繪制四邊
      g.setColor (borderColor);
      for (int i = 0; i < BORDER_SIZE; i++)
           g.drawRect (i, i, width-2*i-1, height-2*i-1);

      // 將組件的游戲板畫為黑色(除了邊框及消息區(qū))
      g.setColor (Color.black);
      g.fillRect (BORDER_SIZE, BORDER_SIZE, width-2*BORDER_SIZE,
                  width-2*BORDER_SIZE);

      // 畫游戲板的水平線
      g.setColor (Color.white);
      g.drawLine (BORDER_SIZE, BORDER_SIZE+cellSize,
                  BORDER_SIZE+width-2*BORDER_SIZE-1, BORDER_SIZE+cellSize);

      g.drawLine (BORDER_SIZE, BORDER_SIZE+2*cellSize+1,
                  BORDER_SIZE+width-2*BORDER_SIZE-1, BORDER_SIZE+2*cellSize+1);

      // 畫游戲板的垂直線
      g.drawLine (BORDER_SIZE+cellSize, BORDER_SIZE, BORDER_SIZE+cellSize,
                  BORDER_SIZE+width-2*BORDER_SIZE-1);

      g.drawLine (BORDER_SIZE+2*cellSize+1, BORDER_SIZE,
                  BORDER_SIZE+2*cellSize+1, BORDER_SIZE+width-2*BORDER_SIZE-1);

      // 畫方格
      g.setColor (squareColor);
      for (int i = 0; i < cells.length; i++)
      {
           if (cells [i])
           {                          
               int x = BORDER_SIZE+(i%3)*(cellSize+1)+3;
               int y = BORDER_SIZE+(i/3)*(cellSize+1)+3;

               int w = cellSize-6;
               int h = w;

               g.fillRect (x, y, w, h);
           }
      }

      // 將消息區(qū)畫為白色(在游戲板下方,邊框之內)
      g.setColor (Color.white);
      g.fillRect (BORDER_SIZE, width-BORDER_SIZE, width-2*BORDER_SIZE,
                  height-width);

      // 假如游戲板不是初始化狀態(tài),則打印出相應消息
      if (state != INITIAL)
      {
          g.setColor (Color.black);

          String text;

          switch (state)
          {
             case LOSE:
                  text = "YOU LOSE!";
                  break;

             case WIN:
                  text = "YOU WIN!";

                  break;

             default:
                  text = "" + counter;
          }

          g.drawString (text, (width-g.getFontMetrics ().stringWidth (text))/2,
                        width-BORDER_SIZE+30);
      }
   }

   // 假如游戲不再進行中,則開始一個新游戲。注冊游戲結束監(jiān)聽器,并且初始化一個方塊顏色
   // 的圖案,同時啟動一個間隔為1秒的計時器。注重:這個方法將被事件處理線程調用。
   void start (GameBoard.DoneListener dl)
   {
      if (!SwingUtilities.isEventDispatchThread ())
          return;

      if (state == INPLAY)
          return;

      this.dl = dl;

      Random rnd = new Random ();

      while (true)
      {
         for (int i = 0; i < cells.length; i++)
              cells [i] = rnd.nextBoolean ();

         int counter = 0;
         for (int i = 0; i < cells.length; i++)
              if (cells [i])
                  counter++;

         if (counter != 0 && counter != cells.length)
             break;
      }

      ActionListener al;
      al = new ActionListener ()
           {
               public void actionPerformed (ActionEvent e)
               {
                  // 假如玩家贏了,則通知游戲結束監(jiān)聽器
                  if (state == WIN)
                  {
                      timer.stop ();
                      GameBoard.this.dl.done ();
                      return;
                  }

                  // 假如計時器到達0,則玩家輸了;通知游戲結束監(jiān)聽器
                  if (--counter == 0)
                  {
                      state = LOSE;
                      timer.stop ();
                      GameBoard.this.dl.done ();
                  }

                  repaint ();
               }
           };

      timer = new Timer (1000, al);

      state = INPLAY;
      counter = 30;
                
      timer.start ();
   }

   // 將鼠標位置映射到單元格編號[0,8],假如鼠標坐標在任何單元格之外,則返回-1。
   private int mouseToCell (int x, int y)
   {
       // 檢查第一列
       if (x >= BORDER_SIZE && x < BORDER_SIZE+cellSize)
       {
           if (y >= BORDER_SIZE && y < BORDER_SIZE+cellSize)
               return 0;

           if (y >= BORDER_SIZE+cellSize+1 && y < BORDER_SIZE+2*cellSize+1)
               return 3;

           if (y >= BORDER_SIZE+2*cellSize+2 && y < BORDER_SIZE+3*cellSize+2)
               return 6;
       }

       // Examine second column.
       // 檢查第二列
       if (x >= BORDER_SIZE+cellSize+1 && x < BORDER_SIZE+2*cellSize+1)
       {
           if (y >= BORDER_SIZE && y < BORDER_SIZE+cellSize)
               return 1;

           if (y >= BORDER_SIZE+cellSize+1 && y < BORDER_SIZE+2*cellSize+1)
               return 4;

           if (y >= BORDER_SIZE+2*cellSize+2 && y < BORDER_SIZE+3*cellSize+2)
               return 7;
       }

       // 檢查第三列
       if (x >= BORDER_SIZE+2*cellSize+2 && x < BORDER_SIZE+3*cellSize+2)
       {
           if (y >= BORDER_SIZE && y < BORDER_SIZE+cellSize)
               return 2;

           if (y >= BORDER_SIZE+cellSize+1 && y < BORDER_SIZE+2*cellSize+1)
               return 5;

           if (y >= BORDER_SIZE+2*cellSize+2 && y < BORDER_SIZE+3*cellSize+2)
               return 8;
       }

       return -1;
   }

   // 翻轉一個單元格及其四周的顏色。文中圖1A展示了如下遵循數字鍵盤布局的單元格映射表:
   // 7 8 9
   // 4 5 6
   // 1 2 3
   //
   // 由于單元格數組從0開始,更輕易使用的映射方式如下圖所示:
   // 0 1 2
   // 3 4 5
   // 6 7 8
   //
   // 當調用toggle(),調用的代碼必須把數字鍵(1-9)轉換為如上所示的索引(0-8)。
   private void toggle (int cell)
   {
      // 切換單元格顏色
      switch (cell)
      {
         case 0: cells [0] = !cells [0];
                 cells [1] = !cells [1];
                 cells [3] = !cells [3];
                 cells [4] = !cells [4];
                 break;

         case 1: cells [0] = !cells [0];
                 cells [1] = !cells [1];
                 cells [2] = !cells [2];
                 break;

         case 2: cells [1] = !cells [1];
                 cells [2] = !cells [2];
                 cells [4] = !cells [4];
                 cells [5] = !cells [5];
                 break;

         case 3: cells [0] = !cells [0];
                 cells [3] = !cells [3];
                 cells [6] = !cells [6];
                 break;

         case 4: cells [0] = !cells [0];
                 cells [2] = !cells [2];
                 cells [4] = !cells [4];
                 cells [6] = !cells [6];
                 cells [8] = !cells [8];
                 break;

         case 5: cells [2] = !cells [2];
                 cells [5] = !cells [5];
                 cells [8] = !cells [8];
                 break;

         case 6: cells [3] = !cells [3];
                 cells [4] = !cells [4];
                 cells [6] = !cells [6];
                 cells [7] = !cells [7];
                 break;

         case 7: cells [6] = !cells [6];
                 cells [7] = !cells [7];
                 cells [8] = !cells [8];
                 break;

         case 8: cells [4] = !cells [4];
                 cells [5] = !cells [5];
                 cells [7] = !cells [7];
                 cells [8] = !cells [8];
      }

      // 檢測玩家是否獲勝。這段代碼放在這兒不和遞減計時器及判定玩家是否失敗的代碼一塊兒放到
//start()方法的事件監(jiān)聽器,否則假如玩家碰巧把所有方塊都交換成黑色,而又馬上換成了其它顏色,
//結果本來該獲勝的玩家卻被判輸了。這種辦法不可取。
      int i;
      for (i = 0; i < cells.length; i++)
           if (cells [i])
               break;

      if (i == cells.length)
          state = WIN;

      // 繪制游戲板,以及單元的顏色
      repaint ();
   }

   // 游戲結束監(jiān)聽器的接口定義。Start()方法接受一個實現(xiàn)此接口的對象作為參數。
   interface DoneListener
   {
      void done ();
   }
}


由于已經包含了豐富的注釋,我們不再重述。這里我要強調兩點。
-我并沒有用運行JApplet的public void init()方法的線程創(chuàng)建GUI,而是把創(chuàng)建過程延遲到Swing的事件處理線程;這正是Sun的Java教程里推薦的辦法。我通過把所有applet的活動限制在事件處理線程里以避免同步問題。
-在J2SE 1.4(此專欄所使用的版本)之前的版本里,聚焦系統(tǒng)(控制你用TAB鍵在組件之間切換)有很多缺陷,并且具有平臺差異。J2SE 1.4通過提供java.awt.KeyboardFocusManager類、焦點循環(huán)根節(jié)點、以及焦點遍歷策略來修正了聚焦系統(tǒng)。由于J2SE 1.4的JApplet類依靠于Abstract Window Toolkit(AWT)的焦點遍歷策略(AppletViewer及Java Plug-in都使用java.awt.Frame類作為JApplet的頂級父類,因此說他們依靠于AWT的焦點遍歷策略),因此假如沒有外在幫助,你無法在一個JApplet里同TAB鍵從一個組件切換到另一個。這樣的外在幫助包括將J2SE 1.4的JApplet設為焦點循環(huán)根節(jié)點,以及設定一個焦點遍歷策略。此外,我在GameBoard的構造函數里調用setFocusable(true),以保證游戲板組件可以獲得焦點。(盡管我們做了這么多,在我們開始游戲的時候,游戲板及兩個按鈕都沒有得到焦點。)這個卻現(xiàn)在J2SE 1.4及以后的版本中已經得到糾正。

音效

到目前為止,“方塊”游戲并沒有想象中的那么有趣。不過我們可以通過增加音效來讓游戲更有趣。我們至少可以有三種音效:當玩家切換單元格(及其四周)顏色時的音效,當玩家獲勝時的音效,以及當玩家失敗時的音效。

我為這些情形預備了一套適當的音效,分別是toggle.au,win.au,lose.au。(我決定使用Sun的聲音文件,而不是Microsoft的wave文件,以增強可移植性。)在下邊從第二版的Squares.java里摘錄的代碼片斷里,音效文件被加載到聲音剪輯里,并且在applet初始化時通過構造函數傳遞給GameBoard。

// 加載玩家切換單元格顏色、獲勝、以及失敗時播放的聲音剪輯。
AudioClip acToggle;
acToggle = getAudioClip (getClass ().getResource ("toggle.au"));
AudioClip acWin = getAudioClip (getClass ().getResource ("win.au"));
AudioClip acLose = getAudioClip (getClass ().getResource ("lose.au"));

// 創(chuàng)建游戲板組件:每個單元格有40像素寬,方塊顏色是綠色,并且游戲板在得到焦點時邊框是藍色,失
// 去焦點時邊框是黑色。游戲板組件被添加到content pane里。
final GameBoard gb;
gb = new GameBoard (40, Color.green, Color.blue, Color.black, acToggle,acWin, acLose);



發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 林口县| 广安市| 博乐市| 陆河县| 定陶县| 榆树市| 南陵县| 大厂| 余姚市| 聂拉木县| 滨海县| 宜都市| 即墨市| 西畴县| 孟村| 阳信县| 东辽县| 梨树县| 灵石县| 哈密市| 鹤壁市| 建始县| 依安县| 哈尔滨市| 晋州市| 唐河县| 北流市| 左贡县| 陈巴尔虎旗| 邹城市| 松阳县| 澳门| 祥云县| 青浦区| 杭锦旗| 响水县| 吴川市| 特克斯县| 金堂县| 南安市| 讷河市|