引言:
当年我还是学生时候,同学基本都是人手一台台式机,但我们那时都还不太会电脑,又恰逢病毒盛行的年代,不太会打理电脑,所以经常电脑中毒什么的、毛病一堆,出了问题也不会搞,我有个室友高中的时候学了重装系统(他自己说他跟师傅学过修电脑,逮谁都这么说),黑色小皮包里装了个光盘(他人又长得跟伙夫样的胖胖憨憨的,像极了夹着皮包的大款),每当他把舍友或者同学的电脑重装好以后,周围男同学的眼神都充满了慈祥!更何况女同学,那是一口一个“好厉害哦,小***,好棒哦,*** ”,我都要吐了,于是经常有女同学们请他去女生宿舍修电脑(女宿舍一般是不让进去的)、修完电脑请他吃饭,回来就跟我们吹牛逼,我不听我不听,我呸。。。,这样就拥有了优先择偶权?慢慢的我们发现他并不像表面看起来那么憨憨的(大家懂的)。后面我们还发现他其实就只会重装系统,也就会那么两句命令,然而。。。,可恶,给他装到了!!
我们那时候上机经常玩扫雷,试想如果我当年可以用 java 写个扫雷出来,那场面不用我多说了吧,大家让开,我要开始装逼了(你们帮我想,留言给我,我看看你们都能想到啥),只能说我太纯洁了,我肯定做满雷,让师姐师妹点开,不说了我咽口水先。至于现在的话我就不确定咯,毕竟我已经老了。
之前用JavaScript写过了一个扫雷,这次我用java再写了一遍,权当是复习咯。用两种语言写,虽然有相似之处,但区别也蛮大,基本等于重写。
效果图
实现思路
1.界面上可以点开的各种实际都是按钮,创建9行9列的二维数组,然后根据这个数组来创建JButton。
2.对应创建二维数组data,用来存取数据,0表示周围无雷,-1表示当前是雷,其他数字表示周围雷的数量。
3.对应创建二维数组state,用来存取按钮状态,0未打开,1 打开 2旗子 3 未知(控制显示对应的图标)
4.设置雷:随机行数 i 和列数 j,根据随机到 i、j 从二维数组data中取出对应的元素值,若值不为-1(不是雷),则将此元素data[i][j]设置为-1,若值是-1(已经是雷了),则跳过,不管是否跳过都进行递归,直到雷的数量达到设定的最大数量,跳出递归。
5.设置周围雷的数量:计算每个元素周围的雷数量(周围指的是 左上、上、右上、右、右下、下、左下、左 这8个位置),循环二维数组data,判断当前值不是-1,则需要计算周围雷的数量,等会细说。
6.有任一格子被揭开,则游戏开始并且计时,当格子被揭开的时候分3种情况
(1)格子是雷,执行爆炸动画,游戏结束。
(2)当前格子周围有雷,则仅仅打开此格子,对应显示周围雷数量的数字图片。
(3)当前格子不是雷且周围没有雷(data取到的元素值为0),则依次打开周围,并且被打开的周围元素也没有雷的情况下,继续打开(递归)。
7.右键可以进行插小旗、打问号等操作(对数组state进行的操作)。
代码实现
设置头部
-
//设置头部
-
private void setHeader() {
-
Container container =
new Container();
-
container.setLayout(
new GridLayout(
1,
3));
-
-
timeJLabel =
new JLabel(
"时间:"+time,JLabel.CENTER);
-
timeJLabel.setForeground(Color.DARK_GRAY);
-
timeJLabel.setFont(
new Font(
"微软雅黑",Font.BOLD,
16));
-
-
leiJLabel =
new JLabel(
"雷:"+curLeiCount,JLabel.CENTER);
-
leiJLabel.setForeground(Color.DARK_GRAY);
-
leiJLabel.setFont(
new Font(
"微软雅黑",Font.BOLD,
16));
-
-
reStart =
new JButton((ImageIcon)imageMap.
get(
21));
-
Dimension preferredSize =
new Dimension(
100,
40);
-
reStart.setPreferredSize(preferredSize);
-
reStart.addActionListener(
this);
-
-
//注意添加顺序
-
container.
add(timeJLabel);
-
container.
add(reStart);
-
container.
add(leiJLabel);
-
-
mainFrame.
add(container,BorderLayout.NORTH);
-
}
设置游戏区域按钮
1.创建容器,并采用GridLayout 布局。
2.根据设定的ROWS、COLS创建二维数组,数组存储JButton,给每个按钮设置图标。
3.给每个按钮添加鼠标点击事件,右键事件。
-
private void setButtons() {
-
Container container =
new Container();
-
container.setLayout(
new GridLayout(ROWS, COLS));
-
ImageIcon icon=
null;
-
for (
int i =
0; i <ROWS; i++) {
-
for (
int j =
0; j < COLS; j++) {
-
JButton btn =
new JButton();
-
btn.setBounds(
0,
0,
39,
39);
-
icon = (ImageIcon)imageMap.
get(
10);
-
setBtnImage(btn,icon);
-
container.
add(btn);
-
btns[i][j]=btn;
-
-
btn.addActionListener(
this);
-
-
btn.addMouseListener(
this);
-
}
-
}
-
mainFrame.
add(container,BorderLayout.CENTER);
-
}
设置雷
1.随机行数 i 和列数 j,根据随机到 i、j 从二维数组data中取出对应的元素值。
2.判断值,若值不为-1(不是雷),则将此元素data[i][j]设置为-1,若值是-1(已经是雷了),则跳过。
3.不管上一步是否跳过都进行递归,直到雷数量达到设定的最大数量,跳出递归。
-
private void setLei() {
-
if(computedLeiCount==LEICOUNT){
//如果达到雷的最大数量则跳出
-
return;
-
}
-
Random random =
new Random();
-
int r = random.nextInt(ROWS);
-
int c = random.nextInt(COLS);
-
-
//0 无; -1表示雷 ; 其他表示周围的雷数量
-
if(data[r][c]!=
-1){
//如果不是雷则设置为雷
-
data[r][c]=
-1;
-
computedLeiCount++;
-
}
-
setLei();
//递归调用
-
}
计算周围雷的数量并显示
1.循环之前的二维数组data,元素值是-1(雷)跳过,不是-1则继续。
2.如果当前元素的下标是(i,j),则左上为(i-1,j-1),上为(i-1,j ),右上为(i-1,j+1),以此类推,如下图所示:
3.分别取出这8个元素,并判断他们是不是雷,如果是则计数累加,最后把这个计数赋值给元素data[i][j]。
-
//设置周围雷的数量
-
private void setAroundLei() {
-
for (int i =
0; i <
ROWS; i++) {
-
for (int j =
0; j <
COLS; j++) {
-
if(
data[i][j]!=-1){如果当前不是雷,则判断他周围有几个雷,并设置值
-
data[i][j] = computedLei(i,j);
-
}
-
}
-
}
-
}
-
//计算周围雷的数量
-
private int computedLei(int i,int j) {
-
int count=
0;
-
//左上
-
int ci = i
-1;
-
int cj = j
-1;
-
if(ci>=
0 && cj>=
0){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//上
-
ci = i
-1;
-
cj = j;
-
if(ci>=
0){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//右上
-
ci = i
-1;
-
cj = j+
1;
-
if(ci>=
0 && cj<
COLS){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//右
-
ci = i;
-
cj = j+
1;
-
if(cj<
COLS){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//右下
-
ci = i+
1;
-
cj = j+
1;
-
if(ci<
ROWS && cj<
COLS){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//下
-
ci = i+
1;
-
cj = j;
-
if(ci<
ROWS){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//左下
-
ci = i+
1;
-
cj = j
-1;
-
if(ci<
ROWS && cj >=
0){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
//左
-
ci = i;
-
cj = j
-1;
-
if(cj >=
0){
-
if(
data[ci][cj]==-1){
-
count++;
-
}
-
}
-
return count;
-
}
添加点击事件
1.让代码实现 ActionListener
2.重写方法actionPerformed,获取点击的按钮进行揭开操作(分3种情况):
(1)格子是雷,执行爆炸动画,游戏结束。
(2)当前格子周围有雷,则仅仅打开此格子,显示周围雷数量的数字图片。
(3)当前格子不是雷且周围没有雷(data取到的元素值为0),则依次打开周围,并且被打开的周围元素也没有雷的情况下,继续打开(递归)。
打开指定按钮
-
//打开指定的button
-
private void open(int i,int j) {
-
JButton button = btns[i][j];
-
if(state[i][j]==
1){
//已经打开直接返回
-
return ;
-
}
-
state[i][j]=
1;
//设置打开状态
-
int num = data[i][j];
-
if(num==
-1){
//直接使用雷的图片
-
setBtnImage(button,(ImageIcon)imageMap.
get(
18));
-
//游戏结束,并爆炸
-
boom(button);
-
}
else{
//如果当前不是雷,显示对应数字类图片
-
if(num==
0){
-
num=
9;
-
//显示周围的图标,并且递归
-
openAround(i,j);
-
}
-
setBtnImage(button,(ImageIcon)imageMap.
get(num));
-
setBtnEnabled(button,
false);
//按钮被打开设置不能再点击了
-
-
//判定是否成功
-
successOrNot(
1);
-
}
-
}
触雷爆炸
爆炸采用线程来执行,就是切换图片,图片切换到最后一张后线程结束,回调定义好的方法进行结束提示、打开所有格子等操作。
-
//爆炸效果
-
private void boom(JButton button) {
-
flag=
true;
-
-
GameRunnable gameRunnable =
new GameRunnable();
-
gameRunnable.setParam(button, boomImageMap,
this);
-
thread =
new Thread(gameRunnable);
-
thread.start();
-
}
-
//爆炸回调方法
-
public void reback(JButton button) {
-
setBtnImage(button,(ImageIcon)imageMap.
get(
18));
-
flag=
false;
-
-
//设置全部按钮打开
-
openAll();
-
//弹出结束提示
-
UIManager.put(
"OptionPane.buttonFont",
new FontUIResource(
new Font(
"宋体", Font.ITALIC,
13)));
-
UIManager.put(
"OptionPane.messageFont",
new FontUIResource(
new Font(
"宋体", Font.ITALIC,
13)));
-
JOptionPane.showMessageDialog(mainFrame,
"你失败了!点击上方按钮重新开始");
-
}
递归打开周围
-
//打开周围
-
private void openAround(int i,int j) {
-
//左上
-
int ci = i-
1;
-
int cj = j-
1;
-
if(ci>=
0 && cj>=
0){
-
open(ci,cj);
-
}
-
//上
-
ci = i-
1;
-
cj = j;
-
if(ci>=
0){
-
open(ci,cj);
-
}
-
//右上
-
ci = i-
1;
-
cj = j+
1;
-
if(ci>=
0 && cj<COLS){
-
open(ci,cj);
-
}
-
//右
-
ci = i;
-
cj = j+
1;
-
if(cj<COLS){
-
open(ci,cj);
-
}
-
//右下
-
ci = i+
1;
-
cj = j+
1;
-
if(ci<ROWS && cj<COLS){
-
open(ci,cj);
-
}
-
//下
-
ci = i+
1;
-
cj = j;
-
if(ci<ROWS){
-
open(ci,cj);
-
}
-
//左下
-
ci = i+
1;
-
cj = j-
1;
-
if(ci<ROWS && cj >=
0){
-
open(ci,cj);
-
}
-
//左
-
ci = i;
-
cj = j-
1;
-
if(cj >=
0){
-
open(ci,cj);
-
}
-
}
鼠标右键事件
1.实现MouseListener,重写mouseClicked方法。
2.如果按钮是未打开状态则设置为旗子(设置state数组对应的元素值:2)。
3.如果按钮是旗子状态则设置为未知(设置state数组对应的元素值:3)。
4.如果按钮是未知状态则设置为原来的状态未打开(设置state数组对应的元素值:0)。
-
//鼠标右键的处理
-
@Override
-
public void mouseClicked(MouseEvent e) {
-
if(e.getButton()==MouseEvent.BUTTON3){
-
JButton button = (JButton)e.getSource();
-
for (
int i =
0; i <ROWS; i++) {
-
for (
int j =
0; j < COLS; j++) {
-
if(button.
equals(btns[i][j])){
//找到对应的按钮
-
if(state[i][j]==
0){
//如果是未打开状态则设置为旗子
-
state[i][j]=
2;
-
setBtnImage(button,(ImageIcon)imageMap.
get(
12));
-
-
curLeiCount--;
-
leiJLabel.setText(
"雷:"+curLeiCount);
-
-
//需求判断是否成功
-
successOrNot(
2);
-
-
}
else
if(state[i][j]==
2){
//如果是旗子状态则设置为未知
-
state[i][j]=
3;
-
setBtnImage(button,(ImageIcon)imageMap.
get(
13));
-
-
curLeiCount++;
-
leiJLabel.setText(
"雷:"+curLeiCount);
-
}
else
if(state[i][j]==
3){
//如果是未知状态则设置为原来的未打开
-
state[i][j]=
0;
-
setBtnImage(button,(ImageIcon)imageMap.
get(
10));
-
}
-
}
-
}
-
}
-
}
-
-
}
胜利判定
1.判断旗子的位置是不是正确的雷,并统计数量,如果统计到的数量刚好和设定的雷总数一样,证明雷全部被查出了,判定为胜利。
2.如果未打开的数量与设定雷的总数一样,也判定为胜利。
-
//判断是否成功 参数type=2表示判断右键插旗子,参数 type=其他 表示判断鼠标点击
-
private void successOrNot(int type) {
-
int count=
0;
-
if(type==
2){
-
/*
-
* 1.判断旗子的位置是否是正确的雷,并统计数量
-
* 2.如果统计到的数量刚好和雷的总数一样,证明全部被查出了,判定为胜利
-
*/
-
for (
int i =
0; i <ROWS; i++) {
-
for (
int j =
0; j < COLS; j++) {
-
if(state[i][j]==
2 && data[i][j]==
-1){
//是旗子,也是雷,则计数
-
count++;
-
}
-
}
-
}
-
}
else{
-
//最终的未打开的数量与雷的数量一样,则表示成功
-
for (
int i =
0; i <ROWS; i++) {
-
for (
int j =
0; j < COLS; j++) {
-
if(state[i][j]!=
1){
//未打开就计数
-
count++;
-
}
-
}
-
}
-
}
-
-
if(count==LEICOUNT){
//成功
-
//关闭计时线程
-
gameTimeRunnable.setFlag(
false);
-
-
//设置全部按钮打开
-
openAll();
-
//弹出结束提示
-
UIManager.put(
"OptionPane.buttonFont",
new FontUIResource(
new Font(
"宋体", Font.ITALIC,
13)));
-
UIManager.put(
"OptionPane.messageFont",
new FontUIResource(
new Font(
"宋体", Font.ITALIC,
13)));
-
JOptionPane.showMessageDialog(mainFrame,
"恭喜你获得了胜利!你太棒了");
-
}
-
}
最后加入重新开始事件就完成了。
重新开始就是重新设置相关参数。
-
//重新开始游戏
-
private void restart() {
-
//关闭计时线程(可能正在进行游戏,所以要把计时线程关闭)
-
gameTimeRunnable.setFlag(
false);
-
-
startFlag=
false;
-
computedLeiCount=
0;
-
-
curLeiCount=
10;
-
leiJLabel.setText(
"雷:"+curLeiCount);
-
time=
0;
-
timeJLabel.setText(
"时间:"+time);
-
-
data=
new
int[ROWS][COLS];
//存取数据
-
state=
new
int[ROWS][COLS];
//存取打开状态,0未打开,1 打开
-
setLei();
-
setAroundLei();
-
-
ImageIcon icon =
null;
-
for (
int i =
0; i <ROWS; i++) {
-
for (
int j =
0; j < COLS; j++) {
-
icon = (ImageIcon)imageMap.
get(
10);
-
setBtnImage(btns[i][j],icon);
-
setBtnEnabled(btns[i][j],
true);
//按钮重新设置可以点击
-
}
-
}
-
-
}
看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏 ,能【关注】一波就更好了。
需要源码的留言给我或者私信我,我发给你
★ 更多源码
♥ java学生宿舍管理系统♥
♥ 学生成绩管理系统♥
♥ 老父亲给女儿做的下雪特效,满足女儿看雪的愿望(附源码)♥
转载:https://blog.csdn.net/dkm123456/article/details/116637184