摘要:本程序共封装了五个类,分别是主类GameWin类,绘制底层地图和绘制顶层地图的类MapBottom类和MapTop类,绘制底层数字的类BottomNum类,以及初始化地雷的BottomRay类和工具GameUtil类,用于存静态参数和方法。

本文分享自华为云社区《Java实现扫雷小游戏【完整版】》,作者:橙子!。

效果展示

主类: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(); void launch(){
GameUtil.START_TIME=System.currentTimeMillis();
this.setVisible(true);
this.setSize(width,height);
this.setLocationRelativeTo(null);
this.setTitle("Java扫雷小游戏");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//鼠标事件
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;
}
//去掉break,任何时候都监听鼠标事件
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;
}
}
break;
default:
}
}
}); while (true){
repaint();
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} @Override
public void paint(Graphics g) {
offScreenImage = this.createImage(width,height);
Graphics gImage = offScreenImage.getGraphics();
//设置背景颜色
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.BLACK);
//画竖线
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);
}
//数字
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);
}
}
} //绘制数字,剩余雷数,倒计时
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();
}
//数字翻开
void numOpen(int x,int y){
//记录旗数
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;
}
//必须在雷区当中
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(){
//统计未打开格子数
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;
//必须在雷区当中
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);
}
}
}
}
}

底层数字BottomNum类

//底层数字类
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 {
//存放坐标
int[] rays = new int[GameUtil.RAY_MAX*2];
//地雷坐标
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
//判断坐标是否存在
for (int j = 0; j < i ; j=j+2) {
if(x==rays[j] && y==rays[j+1]){
i=i-2;
isPlace = false;
break;
}
}
//将坐标放入数组
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类

//工具类:存放静态参数,工具方法
package com.sxt;
import java.awt.*; public class GameUtil {
//地雷个数
static int RAY_MAX = 5;
//地图的宽
static int MAP_W = 11;
//地图的高
static int MAP_H = 11;
//雷区偏移量
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 失败
static int state = 0; //倒计时
static long START_TIME;
static long END_TIME; //底层元素 -1 雷 0 空 1-8 表示对应数字
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);
}
}

总结

在使用Java编写扫雷小游戏时遇到了很多问题,在解决问题时,确实对java的面向对象编程有了更加深入的理解。虽然GUI现在并没有很大的市场,甚至好多初学者已经放弃了学习GUI,但是利用GUI编程的过程对于培养编程兴趣,深入理解Java编程有很大的作用。

本程序共封装了五个类,分别是主类GameWin类,绘制底层地图和绘制顶层地图的类MapBottom类和MapTop类,绘制底层数字的类BottomNum类,以及初始化地雷的BottomRay类和工具GameUtil类,用于存静态参数和方法。

游戏的设计类似windows扫雷,用户在图形化用户界面内利用鼠标监听事件标记雷区,左上角表示剩余雷的数量,右上角动态显示使用的时间。用户可选择中间组件按钮重新游戏。为了解决程序窗口闪动的问题,本程序采用了双缓冲技术。

程序的总体界面布局:

项目结构:

程序测试:

请大家指正!

点击关注,第一时间了解华为云新鲜技术~

全文手敲代码,教你用Java实现扫雷小游戏的更多相关文章

  1. Java打飞机小游戏(附完整源码)

    写在前面 技术源于分享,所以今天抽空把自己之前用java做过的小游戏整理贴出来给大家参考学习.java确实不适合写桌面应用,这里只是通过这个游戏让大家理解oop面向对象编程的过程,纯属娱乐.代码写的很 ...

  2. 无聊的周末用Java写个扫雷小游戏

    周末无聊,用Java写了一个扫雷程序,说起来,这个应该是在学校的时候,写会比较好玩,毕竟自己实现一个小游戏,还是比较好玩的.说实话,扫雷程序里面核心的东西,只有点击的时候,去触发更新数据这一步. Sw ...

  3. java猜数字小游戏

    /* * * 猜数字小游戏 * * 先由系统生成一个2-100之间的随机数字, * * 然后捕获用户从控制台中输入的数字是否与系统生成的随机数字相同, * * 如果相同则统计用户所猜的次数,并给出相应 ...

  4. 软件设计之基于Java的连连看小游戏(一)——开题及游戏首页的制作

    原本计划紧张忙碌的考试月在图书馆和实验室度过,结果突如其来为期两周的软件设计把课余时间几乎捆绑在了机房.软设没有太多知识上的要求,只要成品简洁美观.实用准确即可.考虑了很久决定要用Java swing ...

  5. 教你用Python自制拼图小游戏,一起来制作吧

    摘要: 本文主要为大家详细介绍了python实现拼图小游戏,文中还有示例代码介绍,感兴趣的小伙伴们可以参考一下. 开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Pyt ...

  6. 手敲代码太繁琐?“拖拉拽”式Python编程惊艳到我了

    Python到底有多火,从后端开发到前端开发:从金融量化分析到大数据:从物联网到人工智能,都有Python的踪迹. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后, ...

  7. Java实现2048小游戏【代码】

    元旦刚过,祝大家新年快乐呀! 感觉2017实在是过得太快了... 正如之前所说,这个游戏最开始的版本其实在去年5月份就写好了,其实当时就已经实现了主要功能,后来经历了几次更新,加入了Undo功能,加入 ...

  8. 软件设计之基于Java的连连看小游戏(二)——游戏基础界面的制作及事件的添加

    上次完成到游戏首页的制作,今天完成了游戏基础界面的制作以及事件的简单添加.由于功能尚未完全实现,因此游戏界面的菜单列表只是简单地添加了一下,其余菜单列表以及倒计时等在后续的制作中逐一完善. 1.首先在 ...

  9. 软件设计之基于Java的连连看小游戏(三)——所有功能的实现

    新年快乐!期末接二连三的考试实在太忙了忘记连连看没有更新完,今天想要学习生信时才发现.所以这次直接把连连看所有功能全部放上. 在传统的连连看的基础上,我增加了上传头像的功能,即可以自行上传图片作为游戏 ...

  10. Java自制人机小游戏——————————剪刀、石头、布

    package com.hello.test; import java.util.Scanner; public class TestGame { public static void main(St ...

随机推荐

  1. 每天5分钟复习OpenStack(八)存储虚拟化

    KVM存储虚拟化是通过存储池(Storage Pool)和卷(Volume)来管理的.Storage Pool 是宿主机上可以看到的一片存储空间,可以是多种类型,Volume 是在 Storage P ...

  2. [Python急救站课程]整数数列求和

    整数数列求和程序: n = input("请输入整数N: ") sum = 0 for i in range(int(n)): # range用于计数整数.for表示循环 sum ...

  3. easyEZbaby_app

    for循环,这里给它化简255-i+2-98-未知数x需要等于'0'对应的ASCII值48,那么求x的值,x=111-i,而i的值就是从0到14,这样便可以计算出15位的密码 所以写出来的脚本

  4. 大白话说Python+Flask入门(三)

    写在前面 今天状态很不好,我发现学这部分知识的时候,会出现溜号或者注意力无法集中的情况. 我能想到的是,大概率是这部分知识,应该是超出了我现在的水平了,也就是说我存在知识断层了,整体感觉真的是一知半解 ...

  5. python循环之for循环

    当我们想让列表中的元素一个一个打印出时,用多个print()就显得代码很繁琐了,这是我们就可以用到循环 list_1 = ['one', 'two', 'three', 'four', 'five'] ...

  6. Bash—source命令&export命令&bashrc文件

    当不使用 source 命令执行脚本时,会创建一个子 shell,在该子 shell 中执行完脚本后退出子 shell.不是用 export 定义的变量只对该 shell 有效,对子 shell 是无 ...

  7. 大数据 - MapReduce:从原理到实战的全面指南

    本文深入探讨了MapReduce的各个方面,从基础概念和工作原理到编程模型和实际应用场景,最后专注于性能优化的最佳实践. 关注[TechLeadCloud],分享互联网架构.云服务技术的全维度知识.作 ...

  8. HTML中的三个列表,具体的使用及列表样式

    HTML有三种列表:## 一.有序列表 有序列表(Ordered lists):用数字或字母来标记列表项,每个列表项前会有一个数字或字母. ```html<ol> <li>列表 ...

  9. 芯片SDC约束 -复制保存

    https://www.cnblogs.com/pcc-uvm/p/16996456.html?share_token=9651df97-e94c-4653-bf71-0a0fd6ca415e& ...

  10. VBA常用的函数

    space(8)加空格 vbcrlf换行 trim()去掉两侧空格 lrim()去掉左侧空格 rtrim()去掉右侧空格 left()取字符的左侧 right()取字符串的右侧开始 mid()取字符串 ...