Java實現(xiàn)掃雷游戲詳細(xì)代碼講解
大家好!上一期我們使用GUI技術(shù)寫了一個簡單的掃雷小游戲,今天對這個Java應(yīng)用程序更新迭代,增加了難度選擇等功能,修復(fù)了已知的幾個問題。成為初學(xué)者學(xué)習(xí)的好項目!Java實現(xiàn)掃雷小游戲【完整版】
效果展示
難度選擇展示
游戲界面展示
代碼展示
主類:GameWin類
//主類 package com.sxt; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; public class GameWin extends JFrame { int width = 2 * GameUtil.OFFSET + GameUtil.MAP_W * GameUtil.SQUARE_LENGTH; int height = 4 * GameUtil.OFFSET + GameUtil.MAP_H * GameUtil.SQUARE_LENGTH; Image offScreenImage = null; MapBottom mapBottom = new MapBottom(); MapTop mapTop = new MapTop(); GameSelect gameSelect = new GameSelect(); //是否開始,false未開始,true開始 boolean begin=false; void launch(){ GameUtil.START_TIME=System.currentTimeMillis(); this.setVisible(true); if(GameUtil.state==3){ this.setSize(500,500); }else { this.setSize(width,height); } this.setLocationRelativeTo(null); this.setTitle("Java掃雷小游戲"); this.setDefaultCloseOperation(EXIT_ON_CLOSE); //鼠標(biāo)事件 this.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { super.mouseClicked(e); switch (GameUtil.state){ case 0 : if(e.getButton()==1){ GameUtil.MOUSE_X = e.getX(); GameUtil.MOUSE_Y = e.getY(); GameUtil.LEFT = true; } if(e.getButton()==3) { GameUtil.MOUSE_X = e.getX(); GameUtil.MOUSE_Y = e.getY(); GameUtil.RIGHT = true; } case 1 : case 2 : if(e.getButton()==1){ if(e.getX()>GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W/2) && e.getX()<GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W/2) + GameUtil.SQUARE_LENGTH && e.getY()>GameUtil.OFFSET && e.getY()<GameUtil.OFFSET+GameUtil.SQUARE_LENGTH){ mapBottom.reGame(); mapTop.reGame(); GameUtil.FLAG_NUM=0; GameUtil.START_TIME=System.currentTimeMillis(); GameUtil.state=0; } } if(e.getButton()==2){ GameUtil.state=3; begin=true; } break; case 3: if(e.getButton()==1){ GameUtil.MOUSE_X = e.getX(); GameUtil.MOUSE_Y = e.getY(); begin = gameSelect.hard(); } break; default: } } }); while (true){ repaint(); begin(); try { Thread.sleep(40); } catch (InterruptedException e) { e.printStackTrace(); } } } void begin(){ if(begin){ begin=false; gameSelect.hard(GameUtil.level); dispose(); GameWin gameWin = new GameWin(); GameUtil.START_TIME = System.currentTimeMillis(); GameUtil.FLAG_NUM=0; mapBottom.reGame(); mapTop.reGame(); gameWin.launch(); } } @Override public void paint(Graphics g) { if(GameUtil.state==3){ g.setColor(Color.lightGray); g.fillRect(0,0,500,500); gameSelect.paintSelf(g); }else { offScreenImage = this.createImage(width, height); Graphics gImage = offScreenImage.getGraphics(); //設(shè)置背景顏色 gImage.setColor(Color.lightGray); gImage.fillRect(0, 0, width, height); mapBottom.paintSelf(gImage); mapTop.paintSelf(gImage); g.drawImage(offScreenImage, 0, 0, null); } } public static void main(String[] args) { GameWin gameWin = new GameWin(); gameWin.launch(); } }
底層地圖MapBottom類
//底層地圖類 package com.sxt; import java.awt.*; public class MapBottom { BottomRay bottomRay = new BottomRay(); BottomNum bottomNum = new BottomNum(); { bottomRay.newRay(); bottomNum.newNum(); } //重置游戲 void reGame(){ for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { GameUtil.DATA_BOTTOM[i][j]=0; } } bottomRay.newRay(); bottomNum.newNum(); } //繪制方法 void paintSelf(Graphics g){ g.setColor(Color.red); //畫豎線 for (int i = 0; i <= GameUtil.MAP_W; i++) { g.drawLine(GameUtil.OFFSET + i * GameUtil.SQUARE_LENGTH, 3*GameUtil.OFFSET, GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH, 3*GameUtil.OFFSET+GameUtil.MAP_H*GameUtil.SQUARE_LENGTH); } //畫橫線 for (int i = 0; i <=GameUtil.MAP_H; i++){ g.drawLine(GameUtil.OFFSET, 3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH, GameUtil.OFFSET+GameUtil.MAP_W*GameUtil.SQUARE_LENGTH, 3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH); } for (int i = 1; i <= GameUtil.MAP_W ; i++) { for (int j = 1; j <= GameUtil.MAP_H; j++) { //雷 if (GameUtil.DATA_BOTTOM[i][j] == -1) { g.drawImage(GameUtil.lei, GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.SQUARE_LENGTH - 2, GameUtil.SQUARE_LENGTH - 2, null); } //數(shù)字 if (GameUtil.DATA_BOTTOM[i][j] >=0) { g.drawImage(GameUtil.images[GameUtil.DATA_BOTTOM[i][j]], GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 15, GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 5, null); } } } //繪制數(shù)字 剩余雷數(shù),倒計時 GameUtil.drawWord(g,""+(GameUtil.RAY_MAX-GameUtil.FLAG_NUM), GameUtil.OFFSET, 2*GameUtil.OFFSET,30,Color.red); GameUtil.drawWord(g,""+(GameUtil.END_TIME-GameUtil.START_TIME)/1000, GameUtil.OFFSET + GameUtil.SQUARE_LENGTH*(GameUtil.MAP_W-1), 2*GameUtil.OFFSET,30,Color.red); switch (GameUtil.state){ case 0: GameUtil.END_TIME=System.currentTimeMillis(); g.drawImage(GameUtil.face, GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2), GameUtil.OFFSET, null); break; case 1: g.drawImage(GameUtil.win, GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2), GameUtil.OFFSET, null); break; case 2: g.drawImage(GameUtil.over, GameUtil.OFFSET + GameUtil.SQUARE_LENGTH * (GameUtil.MAP_W/2), GameUtil.OFFSET, null); break; default: } } }
頂層地圖MapTop類
//頂層地圖類 package com.sxt; import java.awt.*; public class MapTop { //格子位置 int temp_x; int temp_y; //重置游戲 void reGame(){ for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { GameUtil.DATA_TOP[i][j]=0; } } } //判斷邏輯 void logic(){ temp_x=0; temp_y=0; if(GameUtil.MOUSE_X>GameUtil.OFFSET && GameUtil.MOUSE_Y>3*GameUtil.OFFSET){ temp_x = (GameUtil.MOUSE_X - GameUtil.OFFSET)/GameUtil.SQUARE_LENGTH+1; temp_y = (GameUtil.MOUSE_Y - GameUtil.OFFSET * 3)/GameUtil.SQUARE_LENGTH+1; } if(temp_x>=1 && temp_x<=GameUtil.MAP_W && temp_y>=1 && temp_y<=GameUtil.MAP_H){ if(GameUtil.LEFT){ //覆蓋,則翻開 if(GameUtil.DATA_TOP[temp_x][temp_y]==0){ GameUtil.DATA_TOP[temp_x][temp_y]=-1; } spaceOpen(temp_x,temp_y); GameUtil.LEFT=false; } if(GameUtil.RIGHT){ //覆蓋則插旗 if(GameUtil.DATA_TOP[temp_x][temp_y]==0){ GameUtil.DATA_TOP[temp_x][temp_y]=1; GameUtil.FLAG_NUM++; } //插旗則取消 else if(GameUtil.DATA_TOP[temp_x][temp_y]==1){ GameUtil.DATA_TOP[temp_x][temp_y]=0; GameUtil.FLAG_NUM--; } else if(GameUtil.DATA_TOP[temp_x][temp_y]==-1){ numOpen(temp_x,temp_y); } GameUtil.RIGHT=false; } } boom(); victory(); } //數(shù)字翻開 void numOpen(int x,int y){ //記錄旗數(shù) int count=0; if(GameUtil.DATA_BOTTOM[x][y]>0){ for (int i = x-1; i <=x+1 ; i++) { for (int j = y-1; j <=y+1 ; j++) { if(GameUtil.DATA_TOP[i][j]==1){ count++; } } } if(count==GameUtil.DATA_BOTTOM[x][y]){ for (int i = x-1; i <=x+1 ; i++) { for (int j = y-1; j <=y+1 ; j++) { if(GameUtil.DATA_TOP[i][j]!=1){ GameUtil.DATA_TOP[i][j]=-1; } //必須在雷區(qū)當(dāng)中 if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){ spaceOpen(i,j); } } } } } } //失敗判定 t 表示失敗 f 未失敗 boolean boom(){ if(GameUtil.FLAG_NUM==GameUtil.RAY_MAX){ for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { if(GameUtil.DATA_TOP[i][j]==0){ GameUtil.DATA_TOP[i][j]=-1; } } } } for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]==-1){ GameUtil.state = 2; seeBoom(); return true; } } } return false; } //失敗顯示 void seeBoom(){ for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { //底層是雷,頂層不是旗,顯示 if(GameUtil.DATA_BOTTOM[i][j]==-1&&GameUtil.DATA_TOP[i][j]!=1){ GameUtil.DATA_TOP[i][j]=-1; } //底層不是雷,頂層是旗,顯示差錯旗 if(GameUtil.DATA_BOTTOM[i][j]!=-1&&GameUtil.DATA_TOP[i][j]==1){ GameUtil.DATA_TOP[i][j]=2; } } } } //勝利判斷 t 表示勝利 f 未勝利 boolean victory(){ //統(tǒng)計未打開格子數(shù) int count=0; for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { if(GameUtil.DATA_TOP[i][j]!=-1){ count++; } } } if(count==GameUtil.RAY_MAX){ GameUtil.state=1; for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { //未翻開,變成旗 if(GameUtil.DATA_TOP[i][j]==0){ GameUtil.DATA_TOP[i][j]=1; } } } return true; } return false; } //打開空格 void spaceOpen(int x,int y){ if(GameUtil.DATA_BOTTOM[x][y]==0){ for (int i = x-1; i <=x+1 ; i++) { for (int j = y-1; j <=y+1 ; j++) { //覆蓋,才遞歸 if(GameUtil.DATA_TOP[i][j]!=-1){ if(GameUtil.DATA_TOP[i][j]==1){GameUtil.FLAG_NUM--;} GameUtil.DATA_TOP[i][j]=-1; //必須在雷區(qū)當(dāng)中 if(i>=1&&j>=1&&i<=GameUtil.MAP_W&&j<=GameUtil.MAP_H){ spaceOpen(i,j); } } } } } } //繪制方法 void paintSelf(Graphics g){ logic(); for (int i = 1; i <= GameUtil.MAP_W ; i++) { for (int j = 1; j <= GameUtil.MAP_H; j++) { //覆蓋 if (GameUtil.DATA_TOP[i][j] == 0) { g.drawImage(GameUtil.top, GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.SQUARE_LENGTH - 2, GameUtil.SQUARE_LENGTH - 2, null); } //插旗 if (GameUtil.DATA_TOP[i][j] == 1) { g.drawImage(GameUtil.flag, GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.SQUARE_LENGTH - 2, GameUtil.SQUARE_LENGTH - 2, null); } //差錯旗 if (GameUtil.DATA_TOP[i][j] == 2) { g.drawImage(GameUtil.noflag, GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1, GameUtil.SQUARE_LENGTH - 2, GameUtil.SQUARE_LENGTH - 2, null); } } } } }
底層數(shù)字BottomNum類
//底層數(shù)字類 package com.sxt; public class BottomNum { void newNum() { for (int i = 1; i <=GameUtil.MAP_W ; i++) { for (int j = 1; j <=GameUtil.MAP_H ; j++) { if(GameUtil.DATA_BOTTOM[i][j]==-1){ for (int k = i-1; k <=i+1 ; k++) { for (int l = j-1; l <=j+1 ; l++) { if(GameUtil.DATA_BOTTOM[k][l]>=0){ GameUtil.DATA_BOTTOM[k][l]++; } } } } } } } }
初始化地雷BottomRay類
//初始化地雷類 package com.sxt; public class BottomRay { //存放坐標(biāo) static int[] rays = new int[GameUtil.RAY_MAX*2]; //地雷坐標(biāo) int x,y; //是否放置 T 表示可以放置 F 不可放置 boolean isPlace = true; //生成雷 void newRay() { for (int i = 0; i < GameUtil.RAY_MAX*2 ; i=i+2) { x= (int) (Math.random()*GameUtil.MAP_W +1);//1-12 y= (int) (Math.random()*GameUtil.MAP_H +1);//1-12 //判斷坐標(biāo)是否存在 for (int j = 0; j < i ; j=j+2) { if(x==rays[j] && y==rays[j+1]){ i=i-2; isPlace = false; break; } } //將坐標(biāo)放入數(shù)組 if(isPlace){ rays[i]=x; rays[i+1]=y; } isPlace = true; } for (int i = 0; i < GameUtil.RAY_MAX*2; i=i+2) { GameUtil.DATA_BOTTOM[rays[i]][rays[i+1]]=-1; } } }
工具GameUtil類
//工具類,存放靜態(tài)參數(shù),工具方法 package com.sxt; import java.awt.*; public class GameUtil { //地雷個數(shù) static int RAY_MAX = 100; //地圖的寬 static int MAP_W = 36; //地圖的高 static int MAP_H = 17; //雷區(qū)偏移量 static int OFFSET = 45; //格子邊長 static int SQUARE_LENGTH = 50; //插旗數(shù)量 static int FLAG_NUM = 0; //鼠標(biāo)相關(guān) //坐標(biāo) static int MOUSE_X; static int MOUSE_Y; //狀態(tài) static boolean LEFT = false; static boolean RIGHT = false; //游戲狀態(tài) 0 表示游戲中 1 勝利 2 失敗 3 難度選擇 static int state = 3; //游戲難度 static int level; //倒計時 static long START_TIME; static long END_TIME; //底層元素 -1 雷 0 空 1-8 表示對應(yīng)數(shù)字 static int[][] DATA_BOTTOM = new int[MAP_W+2][MAP_H+2]; //頂層元素 -1 無覆蓋 0 覆蓋 1 插旗 2 差錯旗 static int[][] DATA_TOP = new int[MAP_W+2][MAP_H+2]; //載入圖片 static Image lei = Toolkit.getDefaultToolkit().getImage("imgs/lei.png"); static Image top = Toolkit.getDefaultToolkit().getImage("imgs/top.gif"); static Image flag = Toolkit.getDefaultToolkit().getImage("imgs/flag.gif"); static Image noflag = Toolkit.getDefaultToolkit().getImage("imgs/noflag.png"); static Image face = Toolkit.getDefaultToolkit().getImage("imgs/face.png"); static Image over = Toolkit.getDefaultToolkit().getImage("imgs/over.png"); static Image win = Toolkit.getDefaultToolkit().getImage("imgs/win.png"); static Image[] images = new Image[9]; static { for (int i = 1; i <=8 ; i++) { images[i] = Toolkit.getDefaultToolkit().getImage("imgs/num/"+i+".png"); } } static void drawWord(Graphics g,String str,int x,int y,int size,Color color){ g.setColor(color); g.setFont(new Font("仿宋",Font.BOLD,size)); g.drawString(str,x,y); } }
難度選擇GameSelect類
//難度選擇類 package com.sxt; import javax.swing.*; import java.awt.*; public class GameSelect { //判斷是否點擊到難度 boolean hard(){ if(GameUtil.MOUSE_X>100&&GameUtil.MOUSE_X<400){ if(GameUtil.MOUSE_Y>50&&GameUtil.MOUSE_Y<150){ GameUtil.level=1; GameUtil.state=0; return true; } if(GameUtil.MOUSE_Y>200&&GameUtil.MOUSE_Y<300){ GameUtil.level=2; GameUtil.state=0; return true; } if(GameUtil.MOUSE_Y>350&&GameUtil.MOUSE_Y<450){ GameUtil.level=3; GameUtil.state=0; return true; } } return false; } void paintSelf(Graphics g){ g.setColor(Color.BLACK); g.drawRoundRect(100,50,300,100,40,40); g.setColor(Color.GRAY); g.fillRoundRect(100,50,300,100,40,40); GameUtil.drawWord(g,"簡單模式",185,110,30,Color.black); g.drawRoundRect(100,200,300,100,40,40); g.setColor(Color.CYAN); g.fillRoundRect(100,200,300,100,40,40); GameUtil.drawWord(g,"中等模式",185,260,30,Color.black); g.drawRoundRect(100,350,300,100,40,40); g.setColor(Color.PINK); g.fillRoundRect(100,350,300,100,40,40); GameUtil.drawWord(g,"困難模式",185,410,30,Color.black); } void hard(int level){ switch (level){ case 1: GameUtil.RAY_MAX = 10; GameUtil.MAP_W = 9; GameUtil.MAP_H = 9; break; case 2: GameUtil.RAY_MAX = 25; GameUtil.MAP_W = 14; GameUtil.MAP_H = 14; break; case 3: GameUtil.RAY_MAX = 45; GameUtil.MAP_W = 20; GameUtil.MAP_H = 14; break; default: } } }
項目結(jié)構(gòu)
本程序共封裝了六個類,分別是主類GameWin類,繪制底層地圖和繪制頂層地圖的類MapBottom類和MapTop類,繪制底層數(shù)字的類BottomNum類,以及初始化地雷的BottomRay類和工具GameUtil類,用于存靜態(tài)參數(shù)和方法,最后用于難度選擇的方法封裝在GameSelect類中。
程序界面布局
不同的難度雷區(qū)格子數(shù)不同!
總結(jié)
游戲的設(shè)計類似windows掃雷,用戶在圖形化用戶界面內(nèi)利用鼠標(biāo)監(jiān)聽事件標(biāo)記雷區(qū),左上角表示剩余雷的數(shù)量,右上角動態(tài)顯示使用的時間。用戶可選擇中間組件按鈕重新游戲。
為了解決程序窗口閃動的問題,本程序采用了雙緩沖技術(shù)。 在使用Java編寫掃雷小游戲時遇到了很多問題,在解決問題時,確實對java的面向?qū)ο缶幊逃辛烁由钊氲睦斫?。雖然GUI現(xiàn)在并沒有很大的市場,甚至好多初學(xué)者已經(jīng)放棄了學(xué)習(xí)GUI,但是利用GUI編程的過程對于培養(yǎng)編程興趣,深入理解Java編程有很大的作用。
本程序是初學(xué)者練習(xí)的好項目,歡迎大家指正!
到此這篇關(guān)于Java實現(xiàn)掃雷游戲詳細(xì)代碼講解的文章就介紹到這了,更多相關(guān)Java掃雷內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
AJAX省市區(qū)三級聯(lián)動下拉菜單(java版)
這篇文章主要介紹了AJAX省市區(qū)三級聯(lián)動下拉菜單(java版)的相關(guān)資料,需要的朋友可以參考下2016-01-01解決idea 拉取代碼出現(xiàn)的 “ Сannot Run Git Cannot identify version of
這篇文章主要介紹了解決idea 拉取代碼出現(xiàn)的 “ Сannot Run Git Cannot identify version of git executable: no response“的問題,需要的朋友可以參考下2020-08-08SpringBoot中的WebSocketSession原理詳解
這篇文章主要介紹了SpringBoot中的WebSocketSession原理詳解,傳統(tǒng)的?HTTP?協(xié)議是無法支持實時通信的,因為它是一種無狀態(tài)協(xié)議,每次請求都是獨立的,無法保持連接。為了解決這個問題,WebSocket?協(xié)議被引入,需要的朋友可以參考下2023-07-07SpringMvc+Angularjs 實現(xiàn)多文件批量上傳
本文通過實例代碼給大家講解了SpringMvc+Angularjs 實現(xiàn)多文件批量上傳功能,非常不錯,具有參考借鑒價值,需要的朋友一起學(xué)習(xí)吧2017-03-03用SpringMVC編寫一個HelloWorld的詳細(xì)過程
SpringMVC是Spring的一個后續(xù)產(chǎn)品,是Spring的一個子項目<BR>SpringMVC?是?Spring?為表述層開發(fā)提供的一整套完備的解決方案,本文我們將用SpringMVC編寫一個HelloWorld,文中有詳細(xì)的編寫過程,需要的朋友可以參考下2023-08-08