Java Swing掃雷遊戲demo分享
好多年前寫過簡略的掃雷遊戲,模擬windows的。
後來由於多次搬遷環境,弄丟了,遺憾不已啊。
於是趁著這兩年還在程式設計的道路上,趁熱再次編寫了一次,同時也擴充套件了功能,更接近windows的掃雷。
此次重寫是用java swing實現的(eclipse開發),考慮到各位看客可能大部分是android崗位,於是我著重注意了功能結構化的處理,使遊戲核心演算法與UI分離,使用回撥互動,便於遷移到android環境。
本人對swing不是很熟練,一直以來用swing做專案的人很少,學習資料也少,所有的這些都是在網上現查現用的。各位看客不會沒關係,都是jdk裡面的api,跟android、winform都很像。
本文思路分以下幾步驟講解:
1、效果圖和基本原理。
2、核心演算法。
3、程式結構和部分重要函式。
4、demo原始碼下載。
一、效果圖:
瞧瞧效果圖,頂部是一個圖片按鈕,使用滑鼠事件處理了按下、釋放、點選時的圖片效果。
中間部分用的是swing的GridLayout佈局,跟android的GridLayout一樣,除了部分用法不一樣。
在某個格子上點選左鍵時,如果是雷,則輸了。同時翻開所有的格子。
在某個格子上點選右鍵時,會標記為旗子;再點右鍵,變成問號,再點又還原。
已經點開了的格子上,點選右鍵無效。
如果點選的某個格子是空白的,則會遞迴攤開他周邊所有不是雷的格子。
遊戲結束時,點選頂部的小黃臉按鈕,重新開始遊戲。
以上部分,是主要功能描述。
二、核心演算法:
其實啊,就是兩個二維陣列的對應關係,介面格子二維陣列 <==> 程式後臺格子狀態二維陣列。
1、先製造二維陣列,然後隨機生成一定數量的隨機數(認為是雷,賦給對應的陣列位置),要求在二維陣列範圍內。後臺資料二維陣列的每個元素,使用了一個物件,需要儲存當前格子的好幾種狀態。
private void initButtons() {
isClickComplete = true;
if (buttonArr == null) {
buttonArr = new JButton[mRowNums][mColumnNums];
}
for (int i = 0; i < mRowNums; i++) {
for (int j = 0; j < mColumnNums; j++) {
if (buttonArr[i][j] == null) {
buttonArr[i][j] = new JButton();
}
setButtonImage(buttonArr[i][j], MineType.MINE_STATUS_BLANK);
}
}
}
JButton的二維陣列,新增到GridLayout佈局中。
// 初始化所有格子
private void resetOrCreateGrids(int rowNums, int columnNums) {
if (beanArr == null) {
beanArr = new MineBean[rowNums][columnNums];
}
for (int i = 0; i < beanArr.length; i++) {
for (int j = 0; j < beanArr[i].length; j++) {
if (beanArr[i][j] == null) {
beanArr[i][j] = new MineBean();
}
beanArr[i][j].reset();
}
}
}
public class MineBean {
public static int MINE_VALUE = 9;// 是雷子的格子值
private boolean isClickOpen = false;// 是否左鍵點開了格子
private int mineCount = 0;// 周圍雷的個數標記值(如果是9,則為雷)
private int imageStatus = MineType.MINE_STATUS_BLANK;// 未點開圖片狀態
...
}
後臺資料服務二維陣列,每個元素使用一個類來儲存資料。
public class MineType {
public static final int MINE_STATUS_OPEN_0 = 0;// 周圍8個格子中沒有雷
public static final int MINE_STATUS_OPEN_1 = 1;// 周圍8個格子中有1個雷
public static final int MINE_STATUS_OPEN_2 = 2;// 周圍8個格子中有2個雷
public static final int MINE_STATUS_OPEN_3 = 3;// 周圍8個格子中有3個雷
public static final int MINE_STATUS_OPEN_4 = 4;// 周圍8個格子中有4個雷
public static final int MINE_STATUS_OPEN_5 = 5;// 周圍8個格子中有5個雷
public static final int MINE_STATUS_OPEN_6 = 6;// 周圍8個格子中有6個雷
public static final int MINE_STATUS_OPEN_7 = 7;// 周圍8個格子中有7個雷
public static final int MINE_STATUS_OPEN_8 = 8;// 周圍8個格子中有8個雷
public static final int MINE_STATUS_OPEN_9 = 9;// 周圍8個格子中有9個雷
public static final int MINE_STATUS_BLANK = 10;// 預設格子圖片
public static final int MINE_STATUS_FLAG = 11;// 格子標記為旗子
public static final int MINE_STATUS_UNKNOW = 12;// 格子標記為問號
public static final int MINE_STATUS_MINE_CLICK = 13;// 點選了雷子時的圖片
public static final int MINE_STATUS_DEAD = 14;// 失敗時,頂部按鈕圖片
public static final int MINE_STATUS_MILE = 15;// 開始時,頂部按鈕圖片
public static final int MINE_STATUS_WAIT = 16;// 等待時,頂部按鈕圖片
public static final int MINE_STATUS_WIN = 17;// 勝利時,頂部按鈕圖片
public static final int MINE_STATUS_LOGO = 18;// logo標記
}
格子需要顯示的圖片狀態。有左鍵點選後需要顯示的狀態,有右鍵點選有需要顯示的狀態。
private ArrayList<Point> getAroundGrids(int i, int j) {
if (beanArr == null) {
return null;
}
// 取當前格子周圍的8個點
Point point1 = new Point((i - 1), (j - 1));
Point point2 = new Point((i - 1), (j));
Point point3 = new Point((i - 1), (j + 1));
Point point4 = new Point((i), (j - 1));
Point point5 = new Point((i), (j + 1));
Point point6 = new Point((i + 1), (j - 1));
Point point7 = new Point((i + 1), (j));
Point point8 = new Point((i + 1), (j + 1));
ArrayList<Point> aroundList = new ArrayList<>();
aroundList.add(point1);
aroundList.add(point2);
aroundList.add(point3);
aroundList.add(point4);
aroundList.add(point5);
aroundList.add(point6);
aroundList.add(point7);
aroundList.add(point8);
for (int k = 0; k < aroundList.size(); k++) {
Point pointTemp = aroundList.get(k);
if (pointTemp.x < 0 || pointTemp.x >= beanArr.length || pointTemp.y < 0
|| pointTemp.y >= beanArr[0].length) {
// 越界
aroundList.remove(k);
k--;
}
}
return aroundList;
}
這個方法是獲取當前位置的周圍8個格子演算法。需要注意的是,靠邊的格子再獲取周圍8個格子時,會包含越界的。在加入到集合中時,需要過濾越界的資料。
然後就不用再考慮[左上角、右上角、左下角、右下角,左邊、上邊、右邊、下邊、內部]等多種情況了。
2、迴圈後臺資料二維陣列,每迴圈一步,找到它周圍的8個格子(過濾掉不在UI範圍內的),統計雷子個數,然後給當前這個格子賦值(周圍雷子數量值)。
3、最核心的演算法:點一個空白格子時,會攤開一大片。這個其實很簡單,每次點選一個格子時,顯示對應的UI的同時,繼續找到它周邊的8個格子,然後迴圈遞迴呼叫當前函式。
public void leftClick(int i, int j) {
if (beanArr == null || this.callBack == null || this.isGameOver) {
return;
}
MineBean mineBean = getMineBean(i, j);
if (mineBean == null) {
return;
}
if (mineBean.isClickOpen()) {
return;
}
mineBean.setClickOpen(true);
if (unOpenMines < 0) {
unOpenMines = 0;
}
unOpenMines--;
if (mineBean.isMineNow()) {
isGameOver = true;
gameOver(i, j);
return;
}
if (gameStartTime <= 0) {
gameStartTime = System.currentTimeMillis();
}
if (this.callBack != null) {
this.callBack.onLeftClick(mineBean, i, j);
}
checkWin();
if (mineBean.getMineCount() == MineType.MINE_STATUS_OPEN_0) {
// 遞迴攤開一片
recursionAround(i, j);
}
}
private void recursionAround(int i, int j) {
ArrayList<Point> list = getAroundGrids(i, j);
for (int k = 0; k < list.size(); k++) {
Point tempPoint = list.get(k);
if (tempPoint == null) {
continue;
}
MineBean mineBean = getMineBean(tempPoint.x, tempPoint.y);
if (mineBean == null) {
continue;
}
if (mineBean.isMineNow()) {
continue;
}
if (mineBean.isClickOpen()) {
continue;
}
leftClick(tempPoint.x, tempPoint.y);
}
}
上面程式碼就是點選某個格子後,顯示UI,同時處理遞迴攤開一片的演算法。
4、UI點選格子時,提供橫豎座標,傳遞給演算法工具類物件處理。演算法函式中找到對應的資料位置,判斷情況再回撥給UI顯示。避免UI與演算法函式耦合度太高,難以移植。
public interface CallBack {
void onInit();
void onWin(long time);// 勝利
void onGameOver();// 失敗
void onLeftClick(MineBean mineBean, int i, int j);
void onRightClick(MineBean mineBean, int i, int j);
}
以上部分是核心演算法描述。
三、程式結構和部分重要函式。
// 初始化隨機雷子
private void makeRandomMines() {
if (beanArr == null) {
return;
}
int nowMines = 0;
while (nowMines < mMineCount) {
int i = random.nextInt(beanArr.length);
int j = random.nextInt(beanArr[0].length);
MineBean mineBean = beanArr[i][j];
if (!mineBean.isMineNow()) {
mineBean.setMineNow();
nowMines++;
}
}
}
// 計算格子周圍雷子狀態
private void initGridAroundStatus() {
if (beanArr == null) {
return;
}
for (int i = 0; i < beanArr.length; i++) {
for (int j = 0; j < beanArr[i].length; j++) {
MineBean mineBean = beanArr[i][j];
if (mineBean.isMineNow()) {
// 當前格子是雷
continue;
}
// 取當前格子周圍有效的格子集合
ArrayList<Point> list = getAroundGrids(i, j);
int mineCount = 0;
Point tempPoint = null;
MineBean tempBean = null;
// 統計這些點是否是雷子
for (int k = 0; k < list.size(); k++) {
tempPoint = list.get(k);
if (tempPoint == null) {
continue;
}
tempBean = getMineBean(tempPoint.x, tempPoint.y);
if (tempBean == null) {
continue;
}
if (tempBean.isMineNow()) {
mineCount++;
}
}
mineBean.setMineCount(mineCount);
}
}
}
怎麼判斷輸贏呢?
private void checkWin() {
if (flagMines != unOpenMines) {
return;
}
isGameOver = true;
if (this.callBack != null) {
this.callBack.onWin(System.currentTimeMillis() - gameStartTime);
}
}
我弄了兩個變數,flagMines被標記的旗子,unOpenMines未被點開的格子個數。如果兩者相等,就是勝利了。如果點到了雷,就輸了
核心演算法和邏輯都已描述,可能說的不直觀,如果沒進入狀態還是很難看明白的,慢慢領悟吧。
還好後面提供原始碼下載。歡迎留言批評。
http://download.csdn.net/detail/fesdgasdgasdg/9867460
Guthub:https://github.com/mengzhinan/MineGame
相關文章
- Java Swing坦克小遊戲Java遊戲
- 掃雷小遊戲-網頁版遊戲網頁
- 經典掃雷遊戲Web版遊戲Web
- 《魔窟掃雷》給掃雷遊戲指明瞭一個進化的方向遊戲
- 無聊的週末用Java寫個掃雷小遊戲Java遊戲
- win10有掃雷嗎?Win10系統掃雷遊戲在哪裡Win10遊戲
- 安全測試之探索 windows 遊戲掃雷Windows遊戲
- python之掃雷小遊戲(附程式碼)Python遊戲
- win10沒有掃雷遊戲怎麼辦_win10自帶遊戲沒有掃雷如何解決Win10遊戲
- win10如何安裝掃雷遊戲_win10系統怎麼玩掃雷Win10遊戲
- 探秘掃雷遊戲的C語言實現遊戲C語言
- 厲害了,一個自動掃雷遊戲專案!遊戲
- 通過編寫掃雷遊戲提高你的 Bash 技巧遊戲
- vb.net使用GDI+實現掃雷小遊戲遊戲
- 遊戲安全入門-掃雷分析&遠端執行緒注入遊戲執行緒
- C語言陣列應用例項2: 掃雷遊戲C語言陣列遊戲
- 被世界遺忘的掃雷遊戲,只有中國人還在沉迷遊戲
- Python:遊戲:寫一個和 XP 上一模一樣的“掃雷”Python遊戲
- 初級掃雷
- 騰訊天美分享:如何獨立製作遊戲demo?遊戲
- JS實現掃雷JS
- 比「掃雷」還古老的遊戲,6000 萬歐美使用者玩瘋了遊戲
- 用ncurses庫寫掃雷
- 微信小程式--遊戲demo微信小程式遊戲
- 經典遊戲《掃雷》的隱祕角落,蘊含著生活的3個“終極真相”遊戲
- GUI 基於Swing製作貪吃蛇小遊戲GUI遊戲
- go語言實現掃雷Go
- 生成一個掃雷矩陣矩陣
- 掃雷--C語言實現C語言
- 雷柏V330遊戲滑鼠怎麼樣?雷柏V330遊戲滑鼠詳細評測遊戲
- WPF 製作雷達掃描圖
- win10掃雷怎麼換主題 如何更換win10的掃雷主題Win10
- java Swing程式設計入門Java程式設計
- python遊戲開發實戰:網路遊戲Demo(客戶端)Python遊戲開發客戶端
- Android自定義View 雷達掃描效果AndroidView
- TechFinger遊戲搬磚系統開發demo遊戲
- java Swing詢問對話方塊Java
- Java-GUI 程式設計之 SwingJavaGUI程式設計
- Java圖形化:Swing表格的使用Java