Android扫雷游戏源码分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第1章游戏源码
1.1 MinesweeperGame
package com.VertexVerveInc.Games;
import java.util.Random;
import android.app.Activity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import youtParams;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
public class MinesweeperGame extends Activity
{
private TextView txtMineCount;
private TextView txtTimer;
private ImageButton btnSmile;
private TableLayout mineField; // table layout to add mines to
private Block blocks[][]; // blocks for mine field
private int blockDimension = 24; // width of each block
private int blockPadding = 2; // padding between blocks
private int numberOfRowsInMineField = 9;
private int numberOfColumnsInMineField = 9;
private int totalNumberOfMines = 10;
// timer to keep track of time elapsed
private Handler timer = new Handler();
private int secondsPassed = 0;
private boolean isTimerStarted; // check if timer already started or not private boolean areMinesSet; // check if mines are planted in blocks private boolean isGameOver;
private int minesToFind; // number of mines yet to be discovered
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(yout.main);
txtMineCount = (TextView) findViewById(R.id.MineCount);
txtTimer = (TextView) findViewById(R.id.Timer);
// set font style for timer and mine count to LCD style
Typeface lcdFont = Typeface.createFromAsset(getAssets(),
"fonts/lcd2mono.ttf");
txtMineCount.setTypeface(lcdFont);
txtTimer.setTypeface(lcdFont);
btnSmile = (ImageButton) findViewById(R.id.Smiley);
btnSmile.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View view)
{
endExistingGame();
startNewGame();
}
});
mineField = (TableLayout)findViewById(R.id.MineField);
showDialog("Click smiley to start New Game", 2000, true, false);
}
private void startNewGame()
{
// plant mines and do rest of the calculations
createMineField();
// display all blocks in UI
showMineField();
minesToFind = totalNumberOfMines;
isGameOver = false;
secondsPassed = 0;
}
private void showMineField()
{
// remember we will not show 0th and last Row and Columns
// they are used for calculation purposes only
for (int row = 1; row < numberOfRowsInMineField + 1; row++)
{
TableRow tableRow = new TableRow(this);
tableRow.setLayoutParams(new LayoutParams((blockDimension + 2 * blockPadding)
* numberOfColumnsInMineField, blockDimension + 2 * blockPadding));
for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
{
blocks[row][column].setLayoutParams(new LayoutParams(
blockDimension + 2 * blockPadding,
blockDimension + 2 * blockPadding));
blocks[row][column].setPadding(blockPadding, blockPadding, blockPadding, blockPadding);
tableRow.addView(blocks[row][column]);
}
mineField.addView(tableRow,new youtParams(
(blockDimension + 2 * blockPadding) * numberOfColumnsInMineField, blockDimension + 2 * blockPadding));
}
}
private void endExistingGame()
{
stopTimer(); // stop if timer is running
txtTimer.setText("000"); // revert all text
txtMineCount.setText("000"); // revert mines count
btnSmile.setBackgroundResource(R.drawable.smile);
// remove all rows from mineField TableLayout
mineField.removeAllViews();
// set all variables to support end of game
isTimerStarted = false;
areMinesSet = false;
isGameOver = false;
minesToFind = 0;
}
private void createMineField()
{
// we take one row extra row for each side
// overall two extra rows and two extra columns
// first and last row/column are used for calculations purposes only
// x|xxxxxxxxxxxxxx|x
// ------------------
// x| |x
// x| |x
// ------------------
// x|xxxxxxxxxxxxxx|x
// the row and columns marked as x are just used to keep counts of near by mines blocks = new Block[numberOfRowsInMineField + 2][numberOfColumnsInMineField + 2];
for (int row = 0; row < numberOfRowsInMineField + 2; row++)
for (int column = 0; column < numberOfColumnsInMineField + 2; column++) {
blocks[row][column] = new Block(this);
blocks[row][column].setDefaults();
// pass current row and column number as final int's to event listeners // this way we can ensure that each event listener is associated to
// particular instance of block only
final int currentRow = row;
final int currentColumn = column;
// add Click Listener
// this is treated as Left Mouse click
blocks[row][column].setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View view)
{
// start timer on first click
if (!isTimerStarted)
{
startTimer();
isTimerStarted = true;
}
// set mines on first click
if (!areMinesSet)
{
areMinesSet = true;
setMines(currentRow, currentColumn);
}
// this is not first click
// check if current block is flagged
// if flagged the don't do anything
// as that operation is handled by LongClick
// if block is not flagged then uncover nearby blocks
// till we get numbered mines
if (!blocks[currentRow][currentColumn].isFlagged())
{
// open nearby blocks till we get numbered blocks
rippleUncover(currentRow, currentColumn);
// did we clicked a mine
if (blocks[currentRow][currentColumn].hasMine())
{
// Oops, game over
finishGame(currentRow,currentColumn);
// check if we win the game
if (checkGameWin())
{
// mark game as win
winGame();
}
}
}
});
// add Long Click listener
// this is treated as right mouse click listener
blocks[row][column].setOnLongClickListener(new OnLongClickListener()
{
public boolean onLongClick(View view)
{
// simulate a left-right (middle) click
// if it is a long click on an opened mine then
// open all surrounding blocks
if (!blocks[currentRow][currentColumn].isCovered() && (blocks[currentRow][currentColumn].getNumberOfMinesInSorrounding() > 0) && !isGameOver)
{
int nearbyFlaggedBlocks = 0;
for (int previousRow = -1; previousRow < 2; previousRow++)
{
for (int previousColumn = -1; previousColumn < 2; previousColumn++)
{
if (blocks[currentRow + previousRow][currentColumn + previousColumn].isFlagged())
{
nearbyFlaggedBlocks++;
}
}
}
// if flagged block count is equal to nearby mine count
// then open nearby blocks
if (nearbyFlaggedBlocks == blocks[currentRow][currentColumn].getNumberOfMinesInSorrounding()) {
for (int previousRow = -1; previousRow < 2; previousRow++)
{
for (int previousColumn = -1; previousColumn < 2; previousColumn++)
{
// don't open flagged blocks
if (!blocks[currentRow + previousRow][currentColumn + previousColumn].isFlagged())
{
// open blocks till we get numbered block
rippleUncover(currentRow + previousRow, currentColumn + previousColumn);
// did we clicked a mine
if (blocks[currentRow + previousRow][currentColumn + previousColumn].hasMine())
{
// oops game over
finishGame(currentRow + previousRow, currentColumn + previousColumn);
}
// did we win the game
if (checkGameWin())
{
// mark game as win
winGame();
}
}
}
}
}
// as we no longer want to judge this gesture so return
// not returning from here will actually trigger other action
// which can be marking as a flag or question mark or blank
return true;
}
// if clicked block is enabled, clickable or flagged
if (blocks[currentRow][currentColumn].isClickable() &&
(blocks[currentRow][currentColumn].isEnabled() || blocks[currentRow][currentColumn].isFlagged()))
{
// for long clicks set:
// 1. empty blocks to flagged
// 2. flagged to question mark
// 3. question mark to blank
// case 1. set blank block to flagged
if (!blocks[currentRow][currentColumn].isFlagged() && !blocks[currentRow][currentColumn].isQuestionMarked())
{
blocks[currentRow][currentColumn].setBlockAsDisabled(false);
blocks[currentRow][currentColumn].setFlagIcon(true);
blocks[currentRow][currentColumn].setFlagged(true);
minesToFind--; //reduce mine count
updateMineCountDisplay();
}
// case 2. set flagged to question mark
else if (!blocks[currentRow][currentColumn].isQuestionMarked())
{
blocks[currentRow][currentColumn].setBlockAsDisabled(true);
blocks[currentRow][currentColumn].setQuestionMarkIcon(true);
blocks[currentRow][currentColumn].setFlagged(false);
blocks[currentRow][currentColumn].setQuestionMarked(true); minesToFind++; // increase mine count
updateMineCountDisplay();
}
// case 3. change to blank square
else
{
blocks[currentRow][currentColumn].setBlockAsDisabled(true);
blocks[currentRow][currentColumn].clearAllIcons();
blocks[currentRow][currentColumn].setQuestionMarked(false);
// if it is flagged then increment mine count
if (blocks[currentRow][currentColumn].isFlagged())
{
minesToFind++; // increase mine count
updateMineCountDisplay();
}
// remove flagged status
blocks[currentRow][currentColumn].setFlagged(false);
}
updateMineCountDisplay(); // update mine display
}
return true;
}
});
}
}
}
private boolean checkGameWin()
{
for (int row = 1; row < numberOfRowsInMineField + 1; row++)
{
for (int column = 1; column < numberOfColumnsInMineField + 1; column++) {
if (!blocks[row][column].hasMine() && blocks[row][column].isCovered()) {
return false;
}
}
}
return true;
}
private void updateMineCountDisplay()
{
if (minesToFind < 0)
{
txtMineCount.setText(Integer.toString(minesToFind));
}
else if (minesToFind < 10)
{
txtMineCount.setText("00" + Integer.toString(minesToFind));
}
else if (minesToFind < 100)
{
txtMineCount.setText("0" + Integer.toString(minesToFind));
}
else
{
txtMineCount.setText(Integer.toString(minesToFind));
}
}
private void winGame()
{
stopTimer();
isTimerStarted = false;
isGameOver = true;
minesToFind = 0; //set mine count to 0
//set icon to cool dude
btnSmile.setBackgroundResource(R.drawable.cool); updateMineCountDisplay(); // update mine count
// disable all buttons
// set flagged all un-flagged blocks
for (int row = 1; row < numberOfRowsInMineField + 1; row++)
{
for (int column = 1; column < numberOfColumnsInMineField + 1; column++) {
blocks[row][column].setClickable(false);
if (blocks[row][column].hasMine())
{
blocks[row][column].setBlockAsDisabled(false);
blocks[row][column].setFlagIcon(true);
}
}
}
// show message
showDialog("You won in " + Integer.toString(secondsPassed) + " seconds!", 1000, false, true);
}
private void finishGame(int currentRow, int currentColumn)
{
isGameOver = true; // mark game as over
stopTimer(); // stop timer
isTimerStarted = false;
btnSmile.setBackgroundResource(R.drawable.sad);
// show all mines
// disable all blocks
for (int row = 1; row < numberOfRowsInMineField + 1; row++)
{
for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
{
// disable block
blocks[row][column].setBlockAsDisabled(false);
// block has mine and is not flagged
if (blocks[row][column].hasMine() && !blocks[row][column].isFlagged())
{
// set mine icon
blocks[row][column].setMineIcon(false);
}
// block is flagged and doesn't not have mine
if (!blocks[row][column].hasMine() && blocks[row][column].isFlagged())
{
// set flag icon
blocks[row][column].setFlagIcon(false);
}
// block is flagged
if (blocks[row][column].isFlagged())
{
// disable the block
blocks[row][column].setClickable(false);
}
}
}
// trigger mine
blocks[currentRow][currentColumn].triggerMine();
// show message
showDialog("You tried for " + Integer.toString(secondsPassed) + " seconds!", 1000, false, false);
}
private void setMines(int currentRow, int currentColumn)
{
// set mines excluding the location where user clicked
Random rand = new Random();
int mineRow, mineColumn;
for (int row = 0; row < totalNumberOfMines; row++)
{
mineRow = rand.nextInt(numberOfColumnsInMineField);
mineColumn = rand.nextInt(numberOfRowsInMineField);
if ((mineRow + 1 != currentColumn) || (mineColumn + 1 != currentRow))
{
if (blocks[mineColumn + 1][mineRow + 1].hasMine())
{
row--; // mine is already there, don't repeat for same block
}
// plant mine at this location
blocks[mineColumn + 1][mineRow + 1].plantMine();
}
// exclude the user clicked location
else
{
row--;
}
}
int nearByMineCount;
// count number of mines in surrounding blocks
for (int row = 0; row < numberOfRowsInMineField + 2; row++)
{
for (int column = 0; column < numberOfColumnsInMineField + 2; column++)
{
// for each block find nearby mine count
nearByMineCount = 0;
if ((row != 0) && (row != (numberOfRowsInMineField + 1)) && (column != 0) && (column != (numberOfColumnsInMineField + 1)))
{
// check in all nearby blocks
for (int previousRow = -1; previousRow < 2; previousRow++)
{
for (int previousColumn = -1; previousColumn < 2; previousColumn++)
{
if (blocks[row + previousRow][column + previousColumn].hasMine())
{
// a mine was found so increment the counter
nearByMineCount++;
}
}
}
blocks[row][column].setNumberOfMinesInSurrounding(nearByMineCount);
}
// for side rows (0th and last row/column)
// set count as 9 and mark it as opened
else
{
blocks[row][column].setNumberOfMinesInSurrounding(9);
blocks[row][column].OpenBlock();
}
}
}
}
private void rippleUncover(int rowClicked, int columnClicked)
{
// don't open flagged or mined rows
if (blocks[rowClicked][columnClicked].hasMine() || blocks[rowClicked][columnClicked].isFlagged())
{
return;
}
// open clicked block
blocks[rowClicked][columnClicked].OpenBlock();
// if clicked block have nearby mines then don't open further
if (blocks[rowClicked][columnClicked].getNumberOfMinesInSorrounding() != 0 ) {
return;
}
// open next 3 rows and 3 columns recursively
for (int row = 0; row < 3; row++)
{
for (int column = 0; column < 3; column++)
{
// check all the above checked conditions
// if met then open subsequent blocks
if (blocks[rowClicked + row - 1][columnClicked + column - 1].isCovered() && (rowClicked + row - 1 > 0) && (columnClicked + column - 1 > 0)
&& (rowClicked + row - 1 < numberOfRowsInMineField + 1) && (columnClicked +
column - 1 < numberOfColumnsInMineField + 1))
{
rippleUncover(rowClicked + row - 1, columnClicked + column - 1 );
}
}
}
return;
}
public void startTimer()
{
if (secondsPassed == 0)
{
timer.removeCallbacks(updateTimeElasped);
// tell timer to run call back after 1 second
timer.postDelayed(updateTimeElasped, 1000);
}
}
public void stopTimer()
{
// disable call backs
timer.removeCallbacks(updateTimeElasped);
}
// timer call back when timer is ticked
private Runnable updateTimeElasped = new Runnable()
{
public void run()
{
long currentMilliseconds = System.currentTimeMillis();
++secondsPassed;
if (secondsPassed < 10)
{
txtTimer.setText("00" + Integer.toString(secondsPassed));
}
else if (secondsPassed < 100)
{
txtTimer.setText("0" + Integer.toString(secondsPassed));
}
else
{
txtTimer.setText(Integer.toString(secondsPassed));
}
// add notification
timer.postAtTime(this, currentMilliseconds);
// notify to call back after 1 seconds
// basically to remain in the timer loop
timer.postDelayed(updateTimeElasped, 1000);
}
};
private void showDialog(String message, int milliseconds, boolean useSmileImage, boolean useCoolImage)
{
// show message
Toast dialog = Toast.makeText(
getApplicationContext(),
message,
Toast.LENGTH_LONG);
dialog.setGravity(Gravity.CENTER, 0, 0);
LinearLayout dialogView = (LinearLayout) dialog.getView();
ImageView coolImage = new ImageView(getApplicationContext());
if (useSmileImage)
{
coolImage.setImageResource(R.drawable.smile);
}
else if (useCoolImage)
{
coolImage.setImageResource(R.drawable.cool);
}
else
{
coolImage.setImageResource(R.drawable.sad);
}
dialogView.addView(coolImage, 0);
dialog.setDuration(milliseconds);
dialog.show();
}
}
1.2 Block
package com.VertexVerveInc.Games;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.Button;
public class Block extends Button
{
private boolean isCovered; // is block covered yet
private boolean isMined; // does the block has a mine underneath
private boolean isFlagged; // is block flagged as a potential mine
private boolean isQuestionMarked; // is block question marked
private boolean isClickable; // can block accept click events
private int numberOfMinesInSurrounding; // number of mines in nearby blocks public Block(Context context)
{
super(context);
}
public Block(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public Block(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}// set default properties for the block
public void setDefaults()
{
isCovered = true;
isMined = false;
isFlagged = false;
isQuestionMarked = false;
isClickable = true;
numberOfMinesInSurrounding = 0;
this.setBackgroundResource(R.drawable.square_blue); setBoldFont();
}
// mark the block as disabled/opened
// update the number of nearby mines
public void setNumberOfSurroundingMines(int number) {
this.setBackgroundResource(R.drawable.square_grey); updateNumber(number);
}
// set mine icon for block
// set block as disabled/opened if false is passed public void setMineIcon(boolean enabled)
{
this.setText("M");
if (!enabled)
{
this.setBackgroundResource(R.drawable.square_grey); this.setTextColor(Color.RED);
}
else
{
this.setTextColor(Color.BLACK);
}
}
// set mine as flagged
// set block as disabled/opened if false is passed public void setFlagIcon(boolean enabled)
{
this.setText("F");
if (!enabled)
{
this.setBackgroundResource(R.drawable.square_grey); this.setTextColor(Color.RED);
}
else
{
this.setTextColor(Color.BLACK);
}
}
// set mine as question mark
// set block as disabled/opened if false is passed public void setQuestionMarkIcon(boolean enabled) {
this.setText("?");
if (!enabled)
{
this.setBackgroundResource(R.drawable.square_grey); this.setTextColor(Color.RED);
}
else
{
this.setTextColor(Color.BLACK);
}
}
// set block as disabled/opened if false is passed // else enable/close it
public void setBlockAsDisabled(boolean enabled) {
if (!enabled)
{
this.setBackgroundResource(R.drawable.square_grey); }
else
{
this.setBackgroundResource(R.drawable.square_blue); }
}
// clear all icons/text
public void clearAllIcons()
{
this.setText("");
}
// set font as bold
private void setBoldFont()
{
this.setTypeface(null, Typeface.BOLD);
}
// uncover this block
public void OpenBlock()
{
// cannot uncover a mine which is not covered
if (!isCovered)
return;
setBlockAsDisabled(false);
isCovered = false;
// check if it has mine
if (hasMine())
{
setMineIcon(false);
}
// update with the nearby mine count
else
{
setNumberOfSurroundingMines(numberOfMinesInSurrounding); }
}
// set text as nearby mine count
public void updateNumber(int text)
{
if (text != 0)
{
this.setText(Integer.toString(text));
// select different color for each number
// we have already skipped 0 mine count
switch (text)
{
case 1:
this.setTextColor(Color.BLUE);
break;
case 2:
this.setTextColor(Color.rgb(0, 100, 0));
break;
case 3:
this.setTextColor(Color.RED);
break;
case 4:
this.setTextColor(Color.rgb(85, 26, 139));
break;
case 5:
this.setTextColor(Color.rgb(139, 28, 98));
break;
case 6:
this.setTextColor(Color.rgb(238, 173, 14));
break;
case 7:
this.setTextColor(Color.rgb(47, 79, 79));
break;
case 8:
this.setTextColor(Color.rgb(71, 71, 71));
break;
case 9:
this.setTextColor(Color.rgb(205, 205, 0));
break;
}
}
}
// set block as a mine underneath
public void plantMine()
{
isMined = true;
}
// mine was opened
// change the block icon and color
public void triggerMine()
{
setMineIcon(true);
this.setTextColor(Color.RED);
}
// is block still covered
public boolean isCovered()
{
return isCovered;
}
// does the block have any mine underneath
public boolean hasMine()
{
return isMined;
}
// set number of nearby mines
public void setNumberOfMinesInSurrounding(int number) {
numberOfMinesInSurrounding = number;
}
// get number of nearby mines
public int getNumberOfMinesInSorrounding()
{
return numberOfMinesInSurrounding;
}
// is block marked as flagged
public boolean isFlagged()
{
return isFlagged;
}
// mark block as flagged
public void setFlagged(boolean flagged)
{
isFlagged = flagged;
}
// is block marked as a question mark
public boolean isQuestionMarked()
{
return isQuestionMarked;
}
// set question mark for the block
public void setQuestionMarked(boolean questionMarked)
{
isQuestionMarked = questionMarked;
}
// can block receive click event
public boolean isClickable()
{
return isClickable;
}
// disable block for receive click events
public void setClickable(boolean clickable)
{
isClickable = clickable;
}
}
第2章源码详细分析2.1 基本变量设置
private TextView txtMineCount;//剩余地雷数
private TextView txtTimer;//计时
private ImageButton btnSmile;//新游戏按钮
private TableLayout mineField; //表的布局添加地雷
private Block blocks[][]; //所有的块
private int blockDimension = 24; //每块的宽度
private int blockPadding = 2; //块之间填充
private int numberOfRowsInMineField = 9;//雷区为9行
private int numberOfColumnsInMineField = 9;//雷区为9列
private int totalNumberOfMines = 10;//总共有10个雷
//定时器的运行时间保持跟踪
private Handler timer = new Handler();
private int secondsPassed = 0;
private boolean isTimerStarted; //检查是否已经开始或不定时
private boolean areMinesSet; //检查是否已经设置地雷
private boolean isGameOver;//检查是否游戏结束
private int minesToFind; //有待发现的地雷数量
2.2 游戏初始化
游戏的初始化函数中,要设置整个界面的背景图片和游戏开始按钮的图片,还有设置一个提示信息(当点击其它地方时弹出)。
public void onCreate(Bundle savedInstanceState) //游戏初始化
{
super.onCreate(savedInstanceState);
setContentView(yout.main);
txtMineCount = (TextView) findViewById(R.id.MineCount);
txtTimer = (TextView) findViewById(R.id.Timer);
//设置定时器和雷的液晶风格的字体样式
Typeface lcdFont = Typeface.createFromAsset(getAssets(),
"fonts/lcd2mono.ttf");
txtMineCount.setTypeface(lcdFont);
txtTimer.setTypeface(lcdFont);
btnSmile = (ImageButton) findViewById(R.id.Smiley);//设置为笑脸
btnSmile.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View view)
{
endExistingGame();//所有东西重置
startNewGame();//游戏开始
}
});
mineField = (TableLayout)findViewById(R.id.MineField);
showDialog("点击笑脸,开始新游戏", 2000, true, false);
}
2.3 游戏开始
游戏开始函数中,设置的是监听函数和用户界面显示函数,如果游戏要重新开始,要在这之前重置三个变量,即找到的雷数、是否游戏结束、游戏运行秒数。
private void startNewGame()//游戏开始
{
createMineField();//监听点击一次和LongClick
showMineField();//在用户界面显示所有块
minesToFind = totalNumberOfMines;
isGameOver = false;
secondsPassed = 0;
}
2.4 设置每个块的描述
设置每个块的描述函数中,首先设置块为雷,随机产生每个雷的行和列,而且不会使雷的行和列重复。
然后设置块为雷数,检查块的左上、上、右上、左、右、左下、下、右下8个块是否有雷,如果有雷则雷数加1。
//设置雷,并设置块的雷数
private void setMines(int currentRow, int currentColumn)
{
//设置地雷排除用户点击的位置
Random rand = new Random();
int mineRow, mineColumn;//定义地雷所在的行和列
for (int row = 0; row < totalNumberOfMines; row++)
{
//设置地雷所在的行和列
mineRow = rand.nextInt(numberOfColumnsInMineField);
mineColumn = rand.nextInt(numberOfRowsInMineField);
if ((mineRow + 1 != currentColumn) || (mineColumn + 1 != currentRow))
{
if (blocks[mineColumn + 1][mineRow + 1].hasMine())
{
row--; //雷已经在那,不重复同一个块
}
//雷在这个位置
blocks[mineColumn + 1][mineRow + 1].plantMine();
}
//排除用户点击的位置
else
{
row--;
}
}
int nearByMineCount;
//计算地雷周围的块数
for (int row = 0; row < numberOfRowsInMineField + 2; row++)
{
for (int column = 0; column < numberOfColumnsInMineField + 2; column++)
{
//查找每个块附近的雷数
nearByMineCount = 0;
if((row != 0) && (row != (numberOfRowsInMineField+ 1)) && (column != 0) && (column != (numberOfColumnsInMineField + 1)))
{
//检查所有附近的块数
for (int previousRow = -1; previousRow < 2; previousRow++)
{
for (int previousColumn = -1; previousColumn < 2; previousColumn++)
{
if (blocks[row + previousRow][column + previousColumn].hasMine())
{
//发现附近的雷,自加加
nearByMineCount++;
}
}
}
blocks[row][column].setNumberOfMinesInSurrounding(nearByMineCount);
}
// 侧行(第0个和最后一个行/列)
//设置行数和列数为9
else
{
blocks[row][column].setNumberOfMinesInSurrounding(9);
blocks[row][column].OpenBlock();
}
}
}
2.5 赢得游戏
赢得游戏函数中,首先停止定时器,然后重置变量和更新雷数,最后关闭雷区的所有按钮,设置每块的状态,显示“你赢了,花费……秒”信息。
//赢得游戏
private void winGame()
{
stopTimer();
isTimerStarted = false;
isGameOver = true;
minesToFind = 0; //设置雷数为0
//设置图标为酷酷的样子
btnSmile.setBackgroundResource(R.drawable.cool);
updateMineCountDisplay(); //更新雷数
//关闭所有按钮
//设置所有未标记的块
for (int row = 1; row < numberOfRowsInMineField + 1; row++)
{
for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
{
blocks[row][column].setClickable(false);
if (blocks[row][column].hasMine())
{
blocks[row][column].setBlockAsDisabled(false);
blocks[row][column].setFlagIcon(true);
}
}
}
//显示信息
showDialog("你赢了,花费 "+ Integer.toString(secondsPassed) + "秒!", 1000, false, true);
}
2.6 重置
重置函数中,首先停止定时器,然后重置计时器、雷数量、游戏按钮,最后删除所有的雷,并重置变量。
//所有东西重置
private void endExistingGame()
{
stopTimer(); //如果计时器正在运行,停止
txtTimer.setText("000"); //恢复计时器
txtMineCount.setText("000"); //恢复雷数量
btnSmile.setBackgroundResource(R.drawable.smile);
//删除所有的地雷
mineField.removeAllViews();
//设置所有变量,以支持所有比赛结束
isTimerStarted = false;
areMinesSet = false;
isGameOver = false;
minesToFind = 0;
}
第3章游戏运行及结果
3.1 游戏初始化
进入游戏后,游戏的初始化界面如图4.1。
图4.1 界面初始化
3.2 游戏开始
点击笑脸以后游戏开始,界面如图4.2。
随便点击一个砖块,则正式开始游
戏,定时器开始计时,雷数显示为10个雷,如图4.3。
图4.2 游戏开始
图4.3 游戏正式开始
如果是长按这个砖块,可以有三种状态,标记、问号和还原状态。
如果砖块上没有标记,就将其变为标记状态;如果砖块上有标记,就将其变为问号状态;如果砖块上有问号,就将其变为还原状态。
如图4.4、4.5、4.6。
图4.4 标记块为雷。