綁定帳號登入

Android 台灣中文網

漫畫聯合國

[遊戲] Android掃雷遊戲源代碼及預覽圖

[複製連結] 查看: 6826|回覆: 38|好評: 1
278755328 | 收聽TA | 顯示全部樓層 |閱讀模式
發表於 2011-4-13 11:49

馬上加入Android 台灣中文網,立即免費下載應用遊戲。

您需要 登錄 才可以下載或查看,沒有帳號?註冊

x
   MineSweeper是一個不錯的Android開源掃雷遊戲,對於初學Android 台灣中文網的網友可能有很大的幫助,對於Java遊戲開發也有一定的參考意義。該遊戲主要有以下技術值得學習:

1. 個性化字體,計分器使用的是LED字體,可以幫助我們如何導入外部字體在Android平台中顯示。

2. 帶圖片的Toast,下面的You won in 36 seconds這個Toast使用了自定義的佈局,可以顯示圖片和文字。

3. 自定義Button控件,可以看到標記是否為雷,顯示附近地雷數量的按鈕控件,初學者可以很容易的學習到Android開發中常用的自定義控件技術。

4.  因為遊戲實時性不高,這裡沒有用到SurfaceView,下次Android 台灣中文網給大家提供一個將對高級些的例子。
123.jpg
關鍵代碼如下:
  1.   public class MinesweeperGame extends Activity
  2. {
  3. private TextView txtMineCount;
  4. private TextView txtTimer;
  5. private ImageButton btnSmile;

  6. private TableLayout mineField; // table layout to add mines to

  7. private Block blocks[][]; // blocks for mine field
  8. private int blockDimension = 24; // width of each block
  9. private int blockPadding = 2; // padding between blocks

  10. private int numberOfRowsInMineField = 9;
  11. private int numberOfColumnsInMineField = 9;
  12. private int totalNumberOfMines = 10;

  13. // timer to keep track of time elapsed
  14. private Handler timer = new Handler();
  15. private int secondsPassed = 0;

  16. private boolean isTimerStarted; // check if timer already started or not
  17. private boolean areMinesSet; // check if mines are planted in blocks
  18. private boolean isGameOver;
  19. private int minesToFind; // number of mines yet to be discovered

  20. @Override
  21. public void onCreate(Bundle savedInstanceState)
  22. {
  23.   super.onCreate(savedInstanceState);
  24.   setContentView(R.layout.main);
  25.   
  26.   txtMineCount = (TextView) findViewById(R.id.MineCount);
  27.   txtTimer = (TextView) findViewById(R.id.Timer);
  28.   
  29.   // set font style for timer and mine count to LCD style
  30.   Typeface lcdFont = Typeface.createFromAsset(getAssets(),
  31.     "fonts/lcd2mono.ttf");
  32.   txtMineCount.setTypeface(lcdFont);
  33.   txtTimer.setTypeface(lcdFont);
  34.   
  35.   btnSmile = (ImageButton) findViewById(R.id.Smiley);
  36.   btnSmile.setOnClickListener(new OnClickListener()
  37.   {
  38.    @Override
  39.    public void onClick(View view)
  40.    {
  41.     endExistingGame();
  42.     startNewGame();
  43.    }
  44.   });
  45.   
  46.   mineField = (TableLayout)findViewById(R.id.MineField);
  47.   
  48.   showDialog("Click smiley to start New Game", 2000, true, false);
  49. }

  50. private void startNewGame()
  51. {
  52.   // plant mines and do rest of the calculations
  53.   createMineField();
  54.   // display all blocks in UI
  55.   showMineField();
  56.   
  57.   minesToFind = totalNumberOfMines;
  58.   isGameOver = false;
  59.   secondsPassed = 0;
  60. }

  61. private void showMineField()
  62. {
  63.   // remember we will not show 0th and last Row and Columns
  64.   // they are used for calculation purposes only
  65.   for (int row = 1; row < numberOfRowsInMineField + 1; row++)
  66.   {
  67.    TableRow tableRow = new TableRow(this);  
  68.    tableRow.setLayoutParams(new LayoutParams((blockDimension + 2 * blockPadding) * numberOfColumnsInMineField, blockDimension + 2 * blockPadding));

  69.    for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
  70.    {
  71.     blocks[row][column].setLayoutParams(new LayoutParams(  
  72.       blockDimension + 2 * blockPadding,  
  73.       blockDimension + 2 * blockPadding));
  74.     blocks[row][column].setPadding(blockPadding, blockPadding, blockPadding, blockPadding);
  75.     tableRow.addView(blocks[row][column]);
  76.    }
  77.    mineField.addView(tableRow,new TableLayout.LayoutParams(  
  78.      (blockDimension + 2 * blockPadding) * numberOfColumnsInMineField, blockDimension + 2 * blockPadding));  
  79.   }
  80. }

  81. private void endExistingGame()
  82. {
  83.   stopTimer(); // stop if timer is running
  84.   txtTimer.setText("000"); // revert all text
  85.   txtMineCount.setText("000"); // revert mines count
  86.   btnSmile.setBackgroundResource(R.drawable.smile);
  87.   
  88.   // remove all rows from mineField TableLayout
  89.   mineField.removeAllViews();
  90.   
  91.   // set all variables to support end of game
  92.   isTimerStarted = false;
  93.   areMinesSet = false;
  94.   isGameOver = false;
  95.   minesToFind = 0;
  96. }

  97. private void createMineField()
  98. {
  99.   // we take one row extra row for each side
  100.   // overall two extra rows and two extra columns
  101.   // first and last row/column are used for calculations purposes only
  102.   //  x|xxxxxxxxxxxxxx|x
  103.   //  ------------------
  104.   //  x|              |x
  105.   //  x|              |x
  106.   //  ------------------
  107.   //  x|xxxxxxxxxxxxxx|x
  108.   // the row and columns marked as x are just used to keep counts of near by mines

  109.   blocks = new Block[numberOfRowsInMineField + 2][numberOfColumnsInMineField + 2];

  110.   for (int row = 0; row < numberOfRowsInMineField + 2; row++)
  111.   {
  112.    for (int column = 0; column < numberOfColumnsInMineField + 2; column++)
  113.    {
  114.     blocks[row][column] = new Block(this);
  115.     blocks[row][column].setDefaults();

  116.     // pass current row and column number as final int's to event listeners
  117.     // this way we can ensure that each event listener is associated to
  118.     // particular instance of block only
  119.     final int currentRow = row;
  120.     final int currentColumn = column;

  121.     // add Click Listener
  122.     // this is treated as Left Mouse click
  123.     blocks[row][column].setOnClickListener(new OnClickListener()
  124.     {
  125.      @Override
  126.      public void onClick(View view)
  127.      {
  128.       // start timer on first click
  129.       if (!isTimerStarted)
  130.       {
  131.        startTimer();
  132.        isTimerStarted = true;
  133.       }

  134.       // set mines on first click
  135.       if (!areMinesSet)
  136.       {
  137.        areMinesSet = true;
  138.        setMines(currentRow, currentColumn);
  139.       }

  140.       // this is not first click
  141.       // check if current block is flagged
  142.       // if flagged the don't do anything
  143.       // as that operation is handled by LongClick
  144.       // if block is not flagged then uncover nearby blocks
  145.       // till we get numbered mines
  146.       if (!blocks[currentRow][currentColumn].isFlagged())
  147.       {
  148.        // open nearby blocks till we get numbered blocks
  149.        rippleUncover(currentRow, currentColumn);
  150.       
  151.        // did we clicked a mine
  152.        if (blocks[currentRow][currentColumn].hasMine())
  153.        {
  154.         // Oops, game over
  155.         finishGame(currentRow,currentColumn);
  156.        }

  157.        // check if we win the game
  158.        if (checkGameWin())
  159.        {
  160.         // mark game as win
  161.         winGame();
  162.        }
  163.       }
  164.      }
  165.     });

  166.     // add Long Click listener
  167.     // this is treated as right mouse click listener
  168.     blocks[row][column].setOnLongClickListener(new OnLongClickListener()
  169.     {
  170.      public boolean onLongClick(View view)
  171.      {
  172.       // simulate a left-right (middle) click
  173.       // if it is a long click on an opened mine then
  174.       // open all surrounding blocks
  175.       if (!blocks[currentRow][currentColumn].isCovered() && (blocks[currentRow][currentColumn].getNumberOfMinesInSorrounding() > 0) && !isGameOver)
  176.       {
  177.        int nearbyFlaggedBlocks = 0;
  178.        for (int previousRow = -1; previousRow < 2; previousRow++)
  179.        {
  180.         for (int previousColumn = -1; previousColumn < 2; previousColumn++)
  181.         {
  182.          if (blocks[currentRow + previousRow][currentColumn + previousColumn].isFlagged())
  183.          {
  184.           nearbyFlaggedBlocks++;
  185.          }
  186.         }
  187.        }

  188.        // if flagged block count is equal to nearby mine count
  189.        // then open nearby blocks
  190.        if (nearbyFlaggedBlocks == blocks[currentRow][currentColumn].getNumberOfMinesInSorrounding())
  191.        {
  192.         for (int previousRow = -1; previousRow < 2; previousRow++)
  193.         {
  194.          for (int previousColumn = -1; previousColumn < 2; previousColumn++)
  195.          {
  196.           // don't open flagged blocks
  197.           if (!blocks[currentRow + previousRow][currentColumn + previousColumn].isFlagged())
  198.           {
  199.            // open blocks till we get numbered block
  200.            rippleUncover(currentRow + previousRow, currentColumn + previousColumn);

  201.            // did we clicked a mine
  202.            if (blocks[currentRow + previousRow][currentColumn + previousColumn].hasMine())
  203.            {
  204.             // oops game over
  205.             finishGame(currentRow + previousRow, currentColumn + previousColumn);
  206.            }

  207.            // did we win the game
  208.            if (checkGameWin())
  209.            {
  210.             // mark game as win
  211.             winGame();
  212.            }
  213.           }
  214.          }
  215.         }
  216.        }

  217.        // as we no longer want to judge this gesture so return
  218.        // not returning from here will actually trigger other action
  219.        // which can be marking as a flag or question mark or blank
  220.        return true;
  221.       }

  222.       // if clicked block is enabled, clickable or flagged
  223.       if (blocks[currentRow][currentColumn].isClickable() &&
  224.         (blocks[currentRow][currentColumn].isEnabled() || blocks[currentRow][currentColumn].isFlagged()))
  225.       {

  226.        // for long clicks set:
  227.        // 1. empty blocks to flagged
  228.        // 2. flagged to question mark
  229.        // 3. question mark to blank

  230.        // case 1. set blank block to flagged
  231.        if (!blocks[currentRow][currentColumn].isFlagged() && !blocks[currentRow][currentColumn].isQuestionMarked())
  232.        {
  233.         blocks[currentRow][currentColumn].setBlockAsDisabled(false);
  234.         blocks[currentRow][currentColumn].setFlagIcon(true);
  235.         blocks[currentRow][currentColumn].setFlagged(true);
  236.         minesToFind--; //reduce mine count
  237.         updateMineCountDisplay();
  238.        }
  239.        // case 2. set flagged to question mark
  240.        else if (!blocks[currentRow][currentColumn].isQuestionMarked())
  241.        {
  242.         blocks[currentRow][currentColumn].setBlockAsDisabled(true);
  243.         blocks[currentRow][currentColumn].setQuestionMarkIcon(true);
  244.         blocks[currentRow][currentColumn].setFlagged(false);
  245.         blocks[currentRow][currentColumn].setQuestionMarked(true);
  246.         minesToFind++; // increase mine count
  247.         updateMineCountDisplay();
  248.        }
  249.        // case 3. change to blank square
  250.        else
  251.        {
  252.         blocks[currentRow][currentColumn].setBlockAsDisabled(true);
  253.         blocks[currentRow][currentColumn].clearAllIcons();
  254.         blocks[currentRow][currentColumn].setQuestionMarked(false);
  255.         // if it is flagged then increment mine count
  256.         if (blocks[currentRow][currentColumn].isFlagged())
  257.         {
  258.          minesToFind++; // increase mine count
  259.          updateMineCountDisplay();
  260.         }
  261.         // remove flagged status
  262.         blocks[currentRow][currentColumn].setFlagged(false);
  263.        }
  264.       
  265.        updateMineCountDisplay(); // update mine display
  266.       }

  267.       return true;
  268.      }
  269.     });
  270.    }
  271.   }
  272. }

  273. private boolean checkGameWin()
  274. {
  275.   for (int row = 1; row < numberOfRowsInMineField + 1; row++)
  276.   {
  277.    for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
  278.    {
  279.     if (!blocks[row][column].hasMine() && blocks[row][column].isCovered())
  280.     {
  281.      return false;
  282.     }
  283.    }
  284.   }
  285.   return true;
  286. }

  287. private void updateMineCountDisplay()
  288. {
  289.   if (minesToFind < 0)
  290.   {
  291.    txtMineCount.setText(Integer.toString(minesToFind));
  292.   }
  293.   else if (minesToFind < 10)
  294.   {
  295.    txtMineCount.setText("00" + Integer.toString(minesToFind));
  296.   }
  297.   else if (minesToFind < 100)
  298.   {
  299.    txtMineCount.setText("0" + Integer.toString(minesToFind));
  300.   }
  301.   else
  302.   {
  303.    txtMineCount.setText(Integer.toString(minesToFind));
  304.   }
  305. }

  306. private void winGame()
  307. {
  308.   stopTimer();
  309.   isTimerStarted = false;
  310.   isGameOver = true;
  311.   minesToFind = 0; //set mine count to 0

  312.   //set icon to cool dude
  313.   btnSmile.setBackgroundResource(R.drawable.cool);

  314.   updateMineCountDisplay(); // update mine count

  315.   // disable all buttons
  316.   // set flagged all un-flagged blocks
  317.   for (int row = 1; row < numberOfRowsInMineField + 1; row++)
  318.   {
  319.    for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
  320.    {
  321.     blocks[row][column].setClickable(false);
  322.     if (blocks[row][column].hasMine())
  323.     {
  324.      blocks[row][column].setBlockAsDisabled(false);
  325.      blocks[row][column].setFlagIcon(true);
  326.     }
  327.    }
  328.   }

  329.   // show message
  330.   showDialog("You won in " + Integer.toString(secondsPassed) + " seconds!", 1000, false, true);
  331. }

  332. private void finishGame(int currentRow, int currentColumn)
  333. {
  334.   isGameOver = true; // mark game as over
  335.   stopTimer(); // stop timer
  336.   isTimerStarted = false;
  337.   btnSmile.setBackgroundResource(R.drawable.sad);

  338.   // show all mines
  339.   // disable all blocks
  340.   for (int row = 1; row < numberOfRowsInMineField + 1; row++)
  341.   {
  342.    for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
  343.    {
  344.     // disable block
  345.     blocks[row][column].setBlockAsDisabled(false);
  346.    
  347.     // block has mine and is not flagged
  348.     if (blocks[row][column].hasMine() && !blocks[row][column].isFlagged())
  349.     {
  350.      // set mine icon
  351.      blocks[row][column].setMineIcon(false);
  352.     }

  353.     // block is flagged and doesn't not have mine
  354.     if (!blocks[row][column].hasMine() && blocks[row][column].isFlagged())
  355.     {
  356.      // set flag icon
  357.      blocks[row][column].setFlagIcon(false);
  358.     }

  359.     // block is flagged
  360.     if (blocks[row][column].isFlagged())
  361.     {
  362.      // disable the block
  363.      blocks[row][column].setClickable(false);
  364.     }
  365.    }
  366.   }

  367.   // trigger mine
  368.   blocks[currentRow][currentColumn].triggerMine();

  369.   // show message
  370.   showDialog("You tried for " + Integer.toString(secondsPassed) + " seconds!", 1000, false, false);
  371. }


  372. private void setMines(int currentRow, int currentColumn)
  373. {
  374.   // set mines excluding the location where user clicked
  375.   Random rand = new Random();
  376.   int mineRow, mineColumn;

  377.   for (int row = 0; row < totalNumberOfMines; row++)
  378.   {
  379.    mineRow = rand.nextInt(numberOfColumnsInMineField);
  380.    mineColumn = rand.nextInt(numberOfRowsInMineField);
  381.    if ((mineRow + 1 != currentColumn) || (mineColumn + 1 != currentRow))
  382.    {
  383.     if (blocks[mineColumn + 1][mineRow + 1].hasMine())
  384.     {
  385.      row--; // mine is already there, don't repeat for same block
  386.     }
  387.     // plant mine at this location
  388.     blocks[mineColumn + 1][mineRow + 1].plantMine();
  389.    }
  390.    // exclude the user clicked location
  391.    else
  392.    {
  393.     row--;
  394.    }
  395.   }

  396.   int nearByMineCount;

  397.   // count number of mines in surrounding blocks
  398.   for (int row = 0; row < numberOfRowsInMineField + 2; row++)
  399.   {
  400.    for (int column = 0; column < numberOfColumnsInMineField + 2; column++)
  401.    {
  402.     // for each block find nearby mine count
  403.     nearByMineCount = 0;
  404.     if ((row != 0) && (row != (numberOfRowsInMineField + 1)) && (column != 0) && (column != (numberOfColumnsInMineField + 1)))
  405.     {
  406.      // check in all nearby blocks
  407.      for (int previousRow = -1; previousRow < 2; previousRow++)
  408.      {
  409.       for (int previousColumn = -1; previousColumn < 2; previousColumn++)
  410.       {
  411.        if (blocks[row + previousRow][column + previousColumn].hasMine())
  412.        {
  413.         // a mine was found so increment the counter
  414.         nearByMineCount++;
  415.        }
  416.       }
  417.      }

  418.      blocks[row][column].setNumberOfMinesInSurrounding(nearByMineCount);
  419.     }
  420.     // for side rows (0th and last row/column)
  421.     // set count as 9 and mark it as opened
  422.     else
  423.     {
  424.      blocks[row][column].setNumberOfMinesInSurrounding(9);
  425.      blocks[row][column].OpenBlock();
  426.     }
  427.    }
  428.   }
  429. }

  430. private void rippleUncover(int rowClicked, int columnClicked)
  431. {
  432.   // don't open flagged or mined rows
  433.   if (blocks[rowClicked][columnClicked].hasMine() || blocks[rowClicked][columnClicked].isFlagged())
  434.   {
  435.    return;
  436.   }

  437.   // open clicked block
  438.   blocks[rowClicked][columnClicked].OpenBlock();

  439.   // if clicked block have nearby mines then don't open further
  440.   if (blocks[rowClicked][columnClicked].getNumberOfMinesInSorrounding() != 0 )
  441.   {
  442.    return;
  443.   }

  444.   // open next 3 rows and 3 columns recursively
  445.   for (int row = 0; row < 3; row++)
  446.   {
  447.    for (int column = 0; column < 3; column++)
  448.    {
  449.     // check all the above checked conditions
  450.     // if met then open subsequent blocks
  451.     if (blocks[rowClicked + row - 1][columnClicked + column - 1].isCovered()
  452.       && (rowClicked + row - 1 > 0) && (columnClicked + column - 1 > 0)
  453.       && (rowClicked + row - 1 < numberOfRowsInMineField + 1) && (columnClicked + column - 1 < numberOfColumnsInMineField + 1))
  454.     {
  455.      rippleUncover(rowClicked + row - 1, columnClicked + column - 1 );
  456.     }
  457.    }
  458.   }
  459.   return;
  460. }

  461. public void startTimer()
  462. {
  463.   if (secondsPassed == 0)
  464.   {
  465.    timer.removeCallbacks(updateTimeElasped);
  466.    // tell timer to run call back after 1 second
  467.    timer.postDelayed(updateTimeElasped, 1000);
  468.   }
  469. }

  470. public void stopTimer()
  471. {
  472.   // disable call backs
  473.   timer.removeCallbacks(updateTimeElasped);
  474. }

  475. // timer call back when timer is ticked
  476. private Runnable updateTimeElasped = new Runnable()
  477. {
  478.   public void run()
  479.   {
  480.    long currentMilliseconds = System.currentTimeMillis();
  481.    ++secondsPassed;

  482.    if (secondsPassed < 10)
  483.    {
  484.     txtTimer.setText("00" + Integer.toString(secondsPassed));
  485.    }
  486.    else if (secondsPassed < 100)
  487.    {
  488.     txtTimer.setText("0" + Integer.toString(secondsPassed));
  489.    }
  490.    else
  491.    {
  492.     txtTimer.setText(Integer.toString(secondsPassed));
  493.    }

  494.    // add notification
  495.    timer.postAtTime(this, currentMilliseconds);
  496.    // notify to call back after 1 seconds
  497.    // basically to remain in the timer loop
  498.    timer.postDelayed(updateTimeElasped, 1000);
  499.   }
  500. };

  501. private void showDialog(String message, int milliseconds, boolean useSmileImage, boolean useCoolImage)
  502. {
  503.   // show message
  504.   Toast dialog = Toast.makeText(
  505.     getApplicationContext(),
  506.     message,
  507.     Toast.LENGTH_LONG);

  508.   dialog.setGravity(Gravity.CENTER, 0, 0);
  509.   LinearLayout dialogView = (LinearLayout) dialog.getView();
  510.   ImageView coolImage = new ImageView(getApplicationContext());
  511.   if (useSmileImage)
  512.   {
  513.    coolImage.setImageResource(R.drawable.smile);
  514.   }
  515.   else if (useCoolImage)
  516.   {
  517.    coolImage.setImageResource(R.drawable.cool);
  518.   }
  519.   else
  520.   {
  521.    coolImage.setImageResource(R.drawable.sad);
  522.   }
  523.   dialogView.addView(coolImage, 0);
  524.   dialog.setDuration(milliseconds);
  525.   dialog.show();
  526. }
  527. }
複製代碼
「用Android 就來APK.TW」,快來加入粉絲吧!
Android 台灣中文網(APK.TW)

android_mine_src.zip

482.72 KB, 下載次數: 114, 下載積分: 碎鑽 -1 個

評分

參與人數 3碎鑽 +1 幫助 +3 收起 理由
joe.lai + 1 + 1 非常讚
ichiro2200 + 1
oiojrd + 1 好內容。朕有賞,還不謝主隆恩.

查看全部評分

用Android 就來Android 台灣中文網(https://apk.tw)
FM-901 | 收聽TA | 顯示全部樓層
發表於 2011-5-16 17:57
感謝大大分享!
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

caspar0331 | 收聽TA | 顯示全部樓層
發表於 2011-5-18 01:21
GOOD 好耶...有範例可以看..學習又加分了...謝謝大大分享
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

okawakenta | 收聽TA | 顯示全部樓層
發表於 2011-5-22 20:50
真是不錯  有得玩又可以看程式學習真不賴
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

asiaman | 收聽TA | 顯示全部樓層
發表於 2011-5-23 13:32
3Q 謝謝分享  讓我的手機內容更豐富
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

c600700 | 收聽TA | 顯示全部樓層
發表於 2011-6-1 00:47

好軟體喔~
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

tomcatkan | 收聽TA | 顯示全部樓層
發表於 2011-6-8 22:36
最近在研究類似的範例 感謝囉
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

asd49712 | 收聽TA | 顯示全部樓層
發表於 2011-6-13 10:38
找了好久.....
值得學習...
謝謝分享...
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

superperdue | 收聽TA | 顯示全部樓層
發表於 2011-6-21 14:45
真的是值的讓我們去學習喔
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

szuzy | 收聽TA | 顯示全部樓層
發表於 2011-6-23 18:35
還在學習中,謝謝!
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 註冊

本版積分規則