采用java技术开发出一个简单的扫雷游戏,游戏分为初级、中级和高级三个级别,游戏启动后单击游戏菜单可以选择游戏难度,在游戏过程中单机滚轮可以随时切换难度。选择级别后将出现相应级别的扫雷区域,同时启动计时器。单击方块,若方块下有雷,用户便输了这一局,若所揭方块下没有雷,则显示一个数字,该数字代表此方块周围的8个方块中共有多少颗雷。如果用户认为某个方块下埋着雷,单击右键可以在方块上标识一个用户认为是雷的图标,即给出一个扫雷标记。用户每标记出一个扫雷标记(无论用户的标记是否正确),窗口左上角的雷数计数器将会减少一个。
目前。随着计算机网络的发展,游戏已经成为现在人生活的一部分,人们以不同的方式通过网络来娱乐,休闲。以计算机技术和网络技术为核心的现代网络技术已经在现实生活中得到广泛的使用,休闲类网络游戏集趣味性,娱乐性,互动性和益智性于一体,已经成为了多数人群的休闲方式,也为多数人所喜好。扫雷作为最常见的小游戏之一,其具有简单操作,休闲益智的特点。本程序使用能跨平台使用的Java语言进行编写,其具有跨平台性强,编写简单等特点,保证的游戏的可行性。
进行游戏窗口的绘制以及鼠标监听
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.ProcessBuilder.Redirect;
import javax.swing.JFrame;
public class GameMain extends JFrame{//监听鼠标键盘事件
int wigth = 2 * Tool.OFFSET + Tool.MAP_W * Tool.SQUARE_LENGTH;
int height = 4 * Tool.OFFSET + Tool.MAP_H * Tool.SQUARE_LENGTH;
Image offScreenImage = null;
MapBottom mapBottom = new MapBottom();
MapTop mapTop = new MapTop();
Gamelevel gamelevel = new Gamelevel();
boolean begin=false;
void windows(){//创建窗口
Tool.START_TIME = System.currentTimeMillis();
this.setVisible(true);
if(Tool.state==3){
this.setSize(500,500);
}else {
this.setSize(wigth,height);
}
this.setLocationRelativeTo(null);
this.setTitle("扫雷游戏");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//鼠标事件
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
switch (Tool.state){
case 0 :
if(e.getButton()==1) {
Tool.Mouse_X = e.getX();
Tool.Mouse_Y = e.getY();
Tool.Left = true;
}
if(e.getButton()==3) {
Tool.Mouse_X = e.getX();
Tool.Mouse_Y = e.getY();
Tool.Right = true;
}
case 1:
case 2:
if(e.getButton()==1){
if(e.getX()>Tool.OFFSET + Tool.SQUARE_LENGTH*(Tool.MAP_W/2)
&& e.getX()<Tool.OFFSET + Tool.SQUARE_LENGTH*(Tool.MAP_W/2) + Tool.SQUARE_LENGTH
&& e.getY()>Tool.OFFSET
&& e.getY()<Tool.OFFSET+Tool.SQUARE_LENGTH){
mapBottom.reGame();
mapTop.reGame();
Tool.FLAG_NUM=0;
Tool.START_TIME=System.currentTimeMillis();
Tool.state=0;
}
}
if(e.getButton()==2){
Tool.state=3;
begin=true;
}
break;
case 3:
if(e.getButton()==1){
Tool.Mouse_X = e.getX();
Tool.Mouse_Y = e.getY();
begin = gamelevel.hard();
}
default:
} } });
while (true) {
repaint();
begin();
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
} } }
void begin(){
if(begin){
begin=false;
gamelevel.hard(Tool.level);
dispose();
GameMain gameMain = new GameMain();
Tool.START_TIME = System.currentTimeMillis();
Tool.FLAG_NUM=0;
mapBottom.reGame();
mapTop.reGame();
gameMain.windows();
} }
public void paint(Graphics g) {//循环方式建立网格
if(Tool.state==3){
g.setColor(Color.white);
g.fillRect(0,0,500,500);
gamelevel.paintSelf(g);
}else {
offScreenImage = this.createImage(wigth, height);
Graphics gImage = offScreenImage.getGraphics();
mapBottom.paintself(gImage);
mapTop.paintself(gImage);
g.drawImage(offScreenImage, 0, 0, null);
} }
public static void main(String[] args) {
GameMain gameMain = new GameMain();
gameMain.windows();
}}
绘制游戏相关组件,底层网格设计,获取网格坐标。显示剩余雷数及所用时间。双击中心笑脸可实现游戏重置。点击鼠标滚轮可重新选择游戏难度。游戏失败后,左右方格自动翻开,笑脸变为哭脸。点击后开始新游戏。
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.lang.ProcessBuilder.Redirect;
public class MapBottom {
DLei dLei = new DLei();
BtomNumber btomNumber = new BtomNumber();
{
dLei.newLei();
btomNumber.newNum();
}
//重置游戏
void reGame(){
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
Tool.DATA_SQ[i][j]=0;
}
}
dLei.newLei();
btomNumber.newNum();
}
//绘制方法
void paintself(Graphics g) {
g.setColor(Color.red);
//竖线
for (int i = 0; i <= Tool.MAP_W; i++) {
g.drawLine(Tool.OFFSET + i* Tool.SQUARE_LENGTH,
3* Tool.OFFSET,
Tool.OFFSET + i* Tool.SQUARE_LENGTH,
3* Tool.OFFSET+Tool.MAP_H*Tool.SQUARE_LENGTH);
}
//横线
for (int i = 0; i <= Tool.MAP_H; i++) {
g.drawLine(Tool.OFFSET,
3*Tool.OFFSET + i* Tool.SQUARE_LENGTH,
Tool.OFFSET + Tool.MAP_W* Tool.SQUARE_LENGTH,
3* Tool.OFFSET+i* Tool.SQUARE_LENGTH);
}
for (int i = 1; i <= Tool.MAP_W; i++) {
for (int j = 1; j <= Tool.MAP_H; j++) {
//雷
if(Tool.DATA_SQ[i][j] == -1) {
g.drawImage(Tool.mine,
Tool.OFFSET + (i-1)* Tool.SQUARE_LENGTH+1,
Tool.OFFSET* 3 + (j-1)* Tool.SQUARE_LENGTH+1,
Tool.SQUARE_LENGTH-1,
Tool.SQUARE_LENGTH-1,
null);
}
//数字
if(Tool.DATA_SQ[i][j] >= 0) {
g.drawImage(Tool.images[Tool.DATA_SQ[i][j]],
Tool.OFFSET + (i-1)* Tool.SQUARE_LENGTH+1,
Tool.OFFSET* 3 + (j-1)* Tool.SQUARE_LENGTH+1,
Tool.SQUARE_LENGTH-1,
Tool.SQUARE_LENGTH-1,
null);
}
if(Tool.DATA_SQ[i][j] == 0) {
g.drawImage(Tool.blank,
Tool.OFFSET + (i-1)* Tool.SQUARE_LENGTH+1,
Tool.OFFSET* 3 + (j-1)* Tool.SQUARE_LENGTH+1,
Tool.SQUARE_LENGTH-1,
Tool.SQUARE_LENGTH-1,
null);
}
}
}
//绘制数字 剩余雷数,倒计时
Tool.AddWord(g,""+(Tool.Lei-Tool.FLAG_NUM),
Tool.OFFSET,
2*Tool.OFFSET,30,Color.red);
Tool.AddWord(g,""+(Tool.END_TIME-Tool.START_TIME)/1000,
Tool.OFFSET + Tool.SQUARE_LENGTH*(Tool.MAP_W-1),
2*Tool.OFFSET,30,Color.red);
//
switch (Tool.state){
case 0:
Tool.END_TIME=System.currentTimeMillis();
g.drawImage(Tool.smile,
Tool.OFFSET + Tool.SQUARE_LENGTH * (Tool.MAP_W/2),
Tool.OFFSET,
null);
break;
case 1:
Tool.END_TIME=System.currentTimeMillis();
g.drawImage(Tool.nosmile,
Tool.OFFSET + Tool.SQUARE_LENGTH * (Tool.MAP_W/2),
Tool.OFFSET,
null);
break;
default:
}
}
}
初始化地雷,在游戏开始时随机生成地雷坐标,每个方格判断周围八个格子内的地雷数量,生成数字。进行顶层覆盖,在游戏开始后判断玩家点击位置,进行逻辑判断,实现插旗功能及游戏判定。
public class DLei {
//存放坐标
static int[] leis = new int [Tool.Lei*2];
//地雷坐标
int x,y;
//是否可以放置地雷
boolean isPlace = true;
//生成雷
void newLei(){
for (int i = 0; i < Tool.Lei*2; i=i+2) {
x=(int)(Math.random()*Tool.MAP_W+1);
y=(int)(Math.random()*Tool.MAP_H+1);
for (int j = 0; j < i; j=j+2) {
if(x==leis[j]&&y==leis[j+1]) {
i=i-2;
isPlace = false;
break;
}
}
if(isPlace) {
leis[i]=x;
leis[i+1]=y;
}
isPlace = true;
}
for (int i = 0; i < Tool.Lei*2; i=i+2) {
Tool.DATA_SQ[leis[i]][leis[i+1]]=-1;
} } }
package 扫雷游戏;
public class BtomNumber {
void newNum(){
for (int i = 1; i <= Tool.MAP_W; i++) {
for (int j = 1; j <= Tool.MAP_H; j++) {
if(Tool.DATA_SQ[i][j]== -1) {
for(int k = i-1; k <= i+1; k++) {
for(int l = j-1; l<= j+1 ; l++) {
if(Tool.DATA_SQ[k][l]>=0) {
Tool.DATA_SQ[k][l]++;
}}}}}}}}
package 扫雷游戏;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import java.awt.*;
public class MapTop {
//格子位置
int temp_x;
int temp_y;
//重置游戏
void reGame(){
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
Tool.DATA_Top[i][j]=0;
}
}
}
//判断逻辑
void logic(){
temp_x=0;
temp_y=0;
if(Tool.Mouse_X>Tool.OFFSET && Tool.Mouse_Y>3*Tool.OFFSET){
temp_x = (Tool.Mouse_X - Tool.OFFSET)/Tool.SQUARE_LENGTH+1;
temp_y = (Tool.Mouse_Y - Tool.OFFSET * 3)/Tool.SQUARE_LENGTH+1;
}
if(temp_x>=1 && temp_x<=Tool.MAP_W
&& temp_y>=1 && temp_y<=Tool.MAP_H){
if(Tool.Left){
if(Tool.DATA_Top[temp_x][temp_y]==0){
Tool.DATA_Top[temp_x][temp_y]=-1;
}
spaceOpen(temp_x, temp_y);
Tool.Left=false;
}
if(Tool.Right){
//插旗
if(Tool.DATA_Top[temp_x][temp_y]==0){
Tool.DATA_Top[temp_x][temp_y]=1;
Tool.FLAG_NUM ++;
}
//拔旗
else if(Tool.DATA_Top[temp_x][temp_y]==1){
Tool.DATA_Top[temp_x][temp_y]=0;
Tool.FLAG_NUM --;
}
else if(Tool.DATA_Top[temp_x][temp_y]==-1){
numOpen(temp_x,temp_y);
}
Tool.Right=false;
}
}
Lose();
victory();
}
//数字翻开
void numOpen(int x,int y){
//记录旗数
int count=0;
if(Tool.DATA_SQ[x][y]>0){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
if(Tool.DATA_Top[i][j]==1){
count++;
}
}
}
if(count==Tool.DATA_SQ[x][y]){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
if(Tool.DATA_Top[i][j]!=1){
Tool.DATA_Top[i][j]=-1;
}
//必须在雷区当中
if(i>=1&&j>=1&&i<=Tool.MAP_W&&j<=Tool.MAP_H){
spaceOpen(i,j);
}
}
}
}
}
}
//失败判定 t 表示失败 f 未失败
boolean Lose(){
if(Tool.FLAG_NUM == Tool.Lei){
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
if(Tool.DATA_Top[i][j]==0){
Tool.DATA_Top[i][j] = -1;
}
}
}
}
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
if(Tool.DATA_SQ[i][j]==-1&&Tool.DATA_Top[i][j]==-1){
Tool.state = 1;
seeLei();
return true;
}
}
}
return false;
}
//失败显示
void seeLei(){
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
//底层是雷,顶层不是旗,显示
if(Tool.DATA_SQ[i][j]==-1&&Tool.DATA_Top[i][j]!=1){
Tool.DATA_Top[i][j]=-1;
}
//底层不是雷,顶层是旗,显示差错旗
if(Tool.DATA_SQ[i][j]!=-1&&Tool.DATA_Top[i][j]==1){
Tool.DATA_Top[i][j]=2;
}
}
}
}
//胜利判断 t 表示胜利 f 未胜利
boolean victory(){
//统计未打开格子数
int count=0;
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
if(Tool.DATA_Top[i][j]!=-1){
count++;
}
}
}
if(count==Tool.Lei){
Tool.state = 0;
for (int i = 1; i <=Tool.MAP_W ; i++) {
for (int j = 1; j <=Tool.MAP_H ; j++) {
//未翻开,变成旗
if(Tool.DATA_Top[i][j]==0){
Tool.DATA_Top[i][j]=1;
}
}
}
return true;
}
return false;
}
//打开空格
void spaceOpen(int x,int y){
if(Tool.DATA_SQ[x][y]==0){
for (int i = x-1; i <=x+1 ; i++) {
for (int j = y-1; j <=y+1 ; j++) {
//覆盖,才递归
if(Tool.DATA_Top[i][j]!=-1){
if(Tool.DATA_Top[i][j]==1){Tool.FLAG_NUM --;}
Tool.DATA_Top[i][j]=-1;
//必须在雷区当中
if(i>=1&&j>=1&&i<=Tool.MAP_W&&j<=Tool.MAP_H){
spaceOpen(i,j);
} }} } } }
//绘制方法
void paintself(Graphics g) {
logic();
for (int i = 1; i <= Tool.MAP_W; i++) {
for (int j = 1; j <= Tool.MAP_H; j++) {
//有覆盖
if(Tool.DATA_Top[i][j]==0) {
g.drawImage(Tool.blank1,
Tool.OFFSET + (i-1)*Tool.SQUARE_LENGTH+1,
Tool.OFFSET*3+(j-1)*Tool.SQUARE_LENGTH+1,
Tool.SQUARE_LENGTH,
Tool.SQUARE_LENGTH,
null);
}
//插旗
if(Tool.DATA_Top[i][j]==1) {
g.drawImage(Tool.flag,
Tool.OFFSET + (i-1)*Tool.SQUARE_LENGTH+2,
Tool.OFFSET*3+(j-1)*Tool.SQUARE_LENGTH+2,
Tool.SQUARE_LENGTH,
Tool.SQUARE_LENGTH,
null);
}
//插错旗
if(Tool.DATA_Top[i][j]==2) {
g.drawImage(Tool.error,
Tool.OFFSET + (i-1)*Tool.SQUARE_LENGTH+1,
Tool.OFFSET*3+(j-1)*Tool.SQUARE_LENGTH+1,
Tool.SQUARE_LENGTH-1,
Tool.SQUARE_LENGTH-1,
null);
} } } }}
根据难度选择的不同显示不同游戏界面,修改游戏胜负判读标准
import java.awt.*;
public class Gamelevel {
//判断是否点击到难度
boolean hard(){
if(Tool.Mouse_X>100&&Tool.Mouse_X<400){
if(Tool.Mouse_Y>50&&Tool.Mouse_Y<150){
Tool.level=1;
Tool.state=0;
return true;
}
if(Tool.Mouse_Y>200&&Tool.Mouse_Y<300){
Tool.level=2;
Tool.state=0;
return true;
}
if(Tool.Mouse_Y>350&&Tool.Mouse_Y<450){
Tool.level=3;
Tool.state=0;
return true;
}
}
return false;
}
void paintSelf(Graphics g){
g.setColor(Color.black);
g.drawRect(100,50,300,100);
Tool.AddWord(g,"简单",220,100,30,Color.black);
g.drawRect(100,200,300,100);
Tool.AddWord(g,"普通",220,250,30,Color.black);
g.drawRect(100,350,300,100);
Tool.AddWord(g,"困难",220,400,30,Color.black);
}
void hard(int level){
switch (level){
case 1:
Tool.Lei = 10;
Tool.MAP_W = 9;
Tool.MAP_H = 9;
break;
case 2:
Tool.Lei = 30;
Tool.MAP_W = 12;
Tool.MAP_H = 12;
break;
case 3:
Tool.Lei = 50;
Tool.MAP_W = 20;
Tool.MAP_H = 12;
break;
default:
}
}
}
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.plaf.basic.BasicDesktopIconUI;
public class Tool {
static int Lei = 100;
static int MAP_W = 36;//宽(横着的格子数量)
static int MAP_H = 17;//高
static int OFFSET = 45;//雷区偏移量
static int SQUARE_LENGTH = 50;//格子边长
//插旗数量
static int FLAG_NUM = 0;
//鼠标相关参数
//坐标
static int Mouse_X;
static int Mouse_Y;
//状态
static boolean Left = false;
static boolean Right = false;
//游戏状态 0 表示游戏中 1 胜利 2 失败 3难度选择
static int state = 3;
//游戏难度
static int level;
//倒计时
static long START_TIME;
static long END_TIME;
//底层元素 -1代表雷 0代表空 1-8代表对应数字
static int[][] DATA_SQ = new int[MAP_W+2][MAP_H+2];//+2防止数组越界
//顶层元素 -1无覆盖 0覆盖 1插旗 2插错旗
static int[][] DATA_Top = new int[MAP_W+2][MAP_H+2];//+2防止数组越界
//载入图片
static Image blank = Toolkit.getDefaultToolkit().getImage("pic/0.gif");
static Image mine = Toolkit.getDefaultToolkit().getImage("pic/mine.gif");
static Image blank1 = Toolkit.getDefaultToolkit().getImage("pic/blank1.gif");
static Image flag = Toolkit.getDefaultToolkit().getImage("pic/flag.gif");
static Image error = Toolkit.getDefaultToolkit().getImage("pic/noflag.png");
static Image blood = Toolkit.getDefaultToolkit().getImage("pic/blood.gif");
static Image smile = Toolkit.getDefaultToolkit().getImage("pic/smile.gif");
static Image nosmile = Toolkit.getDefaultToolkit().getImage("pic/Ooo.gif");
static Image[] images = new Image[9];
static {
for (int i = 1; i <= 8; i++) {
images[i] = Toolkit.getDefaultToolkit().getImage("pic/num/"+i+".gif");
}
}
static void AddWord(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);
}
}
测试是否可在状态栏显示剩余雷的个数以及所用时间。
测试结果如图:
测试能否检测到方格下是否埋雷,以及若点击数字周围为空白格则循环遍历展开。
测试能否点击鼠标右键在方格上插旗,无论方格下是否埋雷,剩余雷数都-1。
测试游戏失败后,能否显示全部埋雷位置,插旗错误的位置更改标记。笑脸变为哭脸。
扫雷游戏一款非常经典的小游戏,因为它比较简单有趣,无论老少都比较适合。拼图的设计对每一个Java语言设计者进行语言提高和进阶都是一个很好的锻炼机会。
扫雷游戏的设计比较复杂,它涉及面广、牵涉方面多,如果不好好考虑和设计,将难以成功开发出这个游戏。在这个游戏的设计中,牵涉到图形界面的显示与更新、数据的收集与更新,并且在这个游戏的开发中,还要应用类的继承机制以及一些设计模式。因此,如何设计和开发好这个游戏,对于提高Java开发水平和系统的设计能力有极大的帮助。在设计开发过程中,需要处理好各个类之间的继承关系,还要处理各个类相应的封装,并且还要协调好各个模块之间的逻辑依赖关系和数据通信关系。
经过这段时间的学习我收获不小,总结起来有如下几点:
1、更进一步理解了JAVA编程的思想,体会到JAVA与其他编程语言的不同。对于这种面向对象的编程思想,使刚入门的人很快就能写出自己的程序。
2、锻炼了自己的动手和自学能力,在编程中有许多类和方法在课本中没有学到和提及到。都是通过自己查帮助文档,或通过网络进行学习和了解到的。3、体会到写程序最重要的是程序思想,而不是单纯的写代码。如何解决类之间的关联和继承关系,如何组织类是很关键的。此外对于接口和变量的私有还是公有,以及参数传递等方面也收获不小。
4、在实验的过程中,总有一些错误,必须仔细检查并结合课本知识进行对比、揣摩才能找出错误的语句代码,进而进行改正才能得到需要的结果,最容易忽略的问题就是文件的头文件了,一不小心就会出现一大堆错误,所以一定要做好实验准备,以免越改越乱。