Java扫雷游戏总结 (小项目)

news2025/1/16 21:13:59

·【尚学堂Java开发扫雷游戏项目】1个半小时做出java扫雷小游戏_java小游戏_Java游戏开发_Java练手项目_java项目实战_java初级项目_哔哩哔哩_bilibili

前言:

  1. 记录的是大致的写代码过程
  2. 为了视觉上更清晰,下面只是放出了完成该功能的核心代码,把每个功能的代码拼装起来,才是完整的项目代码
  3. 项目中导入的图片,是缩小过它们的尺寸的

项目结构: 

一、窗口绘制

5、GameWin

package com.study;

import javax.swing.*;

/**
 * 绘制
 */
public class GameWin extends JFrame {

    void launch(){//绘制窗口
        this.setVisible(true);//窗口是否可见
        this.setSize(500,500);//窗口大小
        this.setLocationRelativeTo(null);//窗口位置居中显示
        this.setTitle("扫雷游戏");//窗口标题
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);//窗口关闭方式
    }

    public static void main(String[] args) {
        GameWin gameWin=new GameWin();
        gameWin.launch();//调用方法绘制窗口
    }
}

 运行结果:

二、雷区绘制

 5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;

/**
 * 绘制
 */
public class GameWin extends JFrame {

    MapBottom mapBottom=new MapBottom();
    public void paint(Graphics g){//绘制组件
        mapBottom.paintSelf(g);
    }
}

6、MapBottom

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {

    //绘制方法
    void paintSelf(Graphics g){
        for(int i=0;i<500;i=i+50){//每隔50像素画一条线
            g.setColor(Color.red);
            //通过画线,组成网格,代表雷区
            //横坐标x1和x2相等,是竖线
            //纵坐标y1和y2相等,是横线
            g.drawLine(0,i,500,i);//横线
            g.drawLine(i,0,i,500);//竖线
        }
    }
}

 运行结果:

三、界面规划

画草图处理变量间的数据关系,整理成公式

4、GameUtil

package com.study;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {

    //地图的宽
    static int MAP_W=11;
    //地图的高
    static int MAP_H=11;
    //雷区偏移量
    static int OFFSET=45;
    //格子边长
    static int SQUARE_LENGTH=50;


}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;

/**
 * 绘制
 */
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;

    void launch(){//绘制窗口
        this.setSize(width,height);//窗口大小
    }
}

6、MapBottom 

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {

    //绘制方法
    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);
        }
    }
}

运行结果:

 

四、底层地图

 4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {
    //底层元素 -1:雷 0:空 1-8:雷数
    static int[][] DATA_BOTTOM=new int[MAP_W+2][MAP_H+2];
    //载入图片
    static Image lei=Toolkit.getDefaultToolkit().getImage("imgs/lei.png");

}

6、MapBottom 

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {

    //绘制方法
    void paintSelf(Graphics g){
        //画雷格
        for(int i=1;i<=GameUtil.MAP_W;i++){
            for(int j=1;j<=GameUtil.MAP_H;j++){
                g.drawImage(GameUtil.lei,
                        GameUtil.OFFSET+(i-1)*GameUtil.SQUARE_LENGTH+1,//+1是为了露出红线
                        GameUtil.OFFSET*3+(j-1)*GameUtil.SQUARE_LENGTH+1,
                        GameUtil.SQUARE_LENGTH-2,
                        GameUtil.SQUARE_LENGTH-2,
                        null);
            }
        }
    }
}

 运行结果:

五、地雷生成

2、BottomRay

package com.study;

/**
 * 初始化地雷
 */
public class BottomRay {

    //存放坐标
    int[] rays=new int[GameUtil.RAY_MAX*2];
    //地雷坐标
    int x,y;
    {
        for(int i=0;i<GameUtil.RAY_MAX*2;i=i+2){//随机生成地雷坐标
            x=(int)(Math.random()*GameUtil.MAP_W+1);
            y=(int)(Math.random()*GameUtil.MAP_H+1);
            rays[i]=x;//生成的地雷坐标存放到一维数组中
            rays[i+1]=y;
        }
        for(int i=0;i<GameUtil.RAY_MAX*2;i=i+2){//把一维数组里的地雷坐标转存到二维数组中,-1表示地雷
            GameUtil.DATA_BOTTOM[rays[i]][rays[i+1]]=-1;
        }
    }
}

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {

    //地雷个数
    static int RAY_MAX=5;
}

6、MapBottom 

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {
    BottomRay bottomRay=new BottomRay();
    //绘制方法
    void paintSelf(Graphics g){
        //画地雷
        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,//+1是为了露出红线
                            GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 1,
                            GameUtil.SQUARE_LENGTH - 2,
                            GameUtil.SQUARE_LENGTH - 2,
                            null);
                }
            }
        }
    }
}

运行结果:

六、地雷重合

2、BottomRay

package com.study;

/**
 * 初始化地雷
 */
public class BottomRay {
    //是否可以放置 T:可以;F:不可以
    boolean isPlace=true;
    {
        for(int i=0;i<GameUtil.RAY_MAX*2;i=i+2){//随机生成地雷坐标
            x=(int)(Math.random()*GameUtil.MAP_W+1);
            y=(int)(Math.random()*GameUtil.MAP_H+1);

            //生成地雷坐标后,判断是否与前面生成的重复
            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;//无论是否可以放置,最终都要释放状态
        }        
    }
}

 4、GameUtil

package com.study;

import java.awt.*;

public class GameUtil {
    //地雷个数
    static int RAY_MAX=121;
}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    void launch(){//绘制窗口
        while(true){//让窗口实时刷新
            repaint();
            try {
                Thread.sleep(40);//每隔40毫秒刷新一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:

 

七、数字生成

1、BottomNum

package com.study;

/**
 * 底层数据类
 */
public class BottomNum {
    {
        for(int i=1;i<=GameUtil.MAP_W;i++){
            for(int j=1;j<=GameUtil.MAP_H;j++){
                //如果遍历整个雷区时,遍历到了雷,就进一步遍历该雷周围3*3区域的雷数
                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]++;
                            }
                        }
                    }
                }
            }
        }
    }
}

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {

    //地雷个数
    static int RAY_MAX=5;    
    //载入数字图片
    static Image[] images=new Image[9];
    static{
        for(int i=1;i<=8;i++){
            images[i]=Toolkit.getDefaultToolkit().getImage("imgs/nums/"+i+".png");
        }
    }
}

6、MapBottom

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {

    BottomNum bottomNum=new BottomNum();

    //绘制方法
    void paintSelf(Graphics g){
        //画地雷
        for(int i=1;i<=GameUtil.MAP_W;i++){    
                //雷数
                if(GameUtil.DATA_BOTTOM[i][j]>=0) {
                    g.drawImage(GameUtil.images[GameUtil.DATA_BOTTOM[i][j]],
                            GameUtil.OFFSET + (i - 1) * GameUtil.SQUARE_LENGTH + 15,//+15是为了让图片更居中
                            GameUtil.OFFSET * 3 + (j - 1) * GameUtil.SQUARE_LENGTH + 5,
                            null);
                }
            }
        }
    }
}

运行结果:

八、顶层绘制

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {
    //项层元素 -1:无覆盖 0:覆盖 1:插旗 2:差错旗
    static int[][] DATA_TOP=new int[MAP_W+2][MAP_H+2];
    //载入图片
    static Image top=Toolkit.getDefaultToolkit().getImage("imgs/top.png");
    static Image flag=Toolkit.getDefaultToolkit().getImage("imgs/flag.png");
    static Image noflag=Toolkit.getDefaultToolkit().getImage("imgs/noflag.png");
}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    MapTop mapTop=new MapTop();
    public void paint(Graphics g){//绘制组件
        mapTop.paintSelf(g);
    }
}

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {

    //绘制方法
    void paintSelf(Graphics g){
        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);
                }
            }
        }
    }
}

运行结果:

 

九、双缓存技术

  • 用于解决窗口不断闪动问题
  • 闪动原因:在不停地绘制窗口,底层组件和顶层组件是分开被绘制进窗口,所以会交叉出现,造成闪动
  • 解决办法:新建一个画布,把底层组件和顶层组件都绘制好在画布上,再把画布绘制进窗口

 5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    //新建画布,用于组合底层组件和顶层组件
    Image offScreenImage=null;
    public void paint(Graphics g){//绘制组件
        offScreenImage=this.createImage(width,height);//初始化画布
        Graphics gImage=offScreenImage.getGraphics();//新建画笔
        gImage.setColor(Color.orange);//背景颜色
        gImage.fillRect(0,0,width,height);//填充
        mapBottom.paintSelf(gImage);//调用画笔
        mapTop.paintSelf(gImage);//调用画笔
        g.drawImage(offScreenImage,0,0,null);//将画布绘制进窗口
    }
}

运行结果:

十、鼠标事件

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {
    //鼠标相关
    //鼠标点击处的坐标
    static int MOUSE_X;
    static int MOUSE_Y;
    //鼠标左右键的状态
    static boolean LEFT=false;
    static boolean RIGHT=false;
}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    void launch(){//绘制窗口     
        //鼠标事件
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                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;
                }
            }
        });
    }
}

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {

    //判断逻辑
    void logic(){
        if(GameUtil.LEFT){
            System.out.println(GameUtil.MOUSE_X);
            System.out.println(GameUtil.MOUSE_Y);
            GameUtil.LEFT=false;//释放左键
        }
        if(GameUtil.RIGHT){
            System.out.println(GameUtil.MOUSE_X);
            System.out.println(GameUtil.MOUSE_Y);
            GameUtil.RIGHT=false;//释放右键
        }
    }

    //绘制方法
    void paintSelf(Graphics g){
        logic();//调用上面的逻辑方法
    }
}

运行结果:

十一、左键翻开

格子区域的数据分析:

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    void launch(){//绘制窗口
        //鼠标事件
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                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;
                }
            }
        });
    }
}

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {

    //格子位置
    int temp_x;
    int temp_y;
    //判断逻辑
    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;
                }
                GameUtil.LEFT = false;//释放左键
            }
            if (GameUtil.RIGHT) {
                System.out.println(GameUtil.MOUSE_X);
                System.out.println(GameUtil.MOUSE_Y);
                GameUtil.RIGHT = false;//释放右键
            }
        }
    }
}

 运行结果:

十二、递归翻开

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {

    //判断逻辑
    void logic(){
        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);
            }
        }
    }
    //打开空格
    //此处用递归,递归打开点击处的空格周围相邻的所有空格
    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){//判断是否有覆盖,没有打开过的才可继续
                        GameUtil.DATA_TOP[i][j]=-1;//打开格子
                        if(i>=1 && j>=1 && i<=GameUtil.MAP_W && j<=GameUtil.MAP_H){//判断是否在雷区内
                            spaceOpen(i,j);//调用方法本身,是递归明显的特征
                        }
                    }
                }
            }
        }
    }
}

运行结果:

十三、右键插旗

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {
    //判断逻辑
    void logic(){
        if(temp_x>=1 && temp_x<=GameUtil.MAP_W
                && temp_y>=1 && temp_y<=GameUtil.MAP_H) {
            if (GameUtil.RIGHT) {
                //覆盖则插旗
                if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
                    GameUtil.DATA_TOP[temp_x][temp_y]=1;
                }
                //插旗则取消
                else if(GameUtil.DATA_TOP[temp_x][temp_y]==1){
                    GameUtil.DATA_TOP[temp_x][temp_y]=0;
                }
                GameUtil.RIGHT = false;//释放右键
            }
        }
    }
}

运行结果:

 

十四、右键翻开

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {
    //判断逻辑
    void logic(){
        if(temp_x>=1 && temp_x<=GameUtil.MAP_W
                && temp_y>=1 && temp_y<=GameUtil.MAP_H) {
            if (GameUtil.RIGHT) {
                //数字翻开
                else if(GameUtil.DATA_TOP[temp_x][temp_y]==-1){
                    numOpen(temp_x,temp_y);
                }
            }
        }
    }

    //数字翻开
    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);//调用方法本身,是递归明显的特征
                        }
                    }
                }
            }
        }
    }
}

运行结果:

 

十五、失败判定

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {
    //判断逻辑
    void logic(){
       boom();
    }
    //失败判定 true:失败;false:未失败
    boolean boom(){
        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){
                    System.out.println("fail");
                    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;
                }
            }
        }
    }
}

运行结果:

十六、胜利判定

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {
    //判断逻辑
    void logic(){
        victory();
    }

    //胜利判断 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){
            System.out.println("success");
            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;
    }
}

运行结果:

十七、游戏状态

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {
    //游戏状态 0:游戏中;1:胜利;2:失败
    static int state=0;
    //载入图片
    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");
}

6、MapBottom

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {
    //绘制方法
    void paintSelf(Graphics g){
        switch (GameUtil.state){
            case 0:
                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:
        }
    }
}

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {
    //失败判定,true:失败;false:未失败
    boolean boom(){
        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;                }
            }
        }
        return false;
    }
   //胜利判断 t:胜利;f:未胜利
    boolean victory(){
        if(count==GameUtil.RAY_MAX){
            GameUtil.state=1;
        }
    }
}

运行结果:

失败:

胜利:

游戏中:

十八、游戏重置

1、BottomNum

package com.study;

/**
 * 底层数据类
 */
public class BottomNum {
    void newNum(){
        for(int i=1;i<=GameUtil.MAP_W;i++){
            //...
        }
    }
}

2、BottomRay

package com.study;

/**
 * 初始化地雷
 */
public class BottomRay {
    //生成雷
    void newRay(){
        for(int i=0;i<GameUtil.RAY_MAX*2;i=i+2){//随机生成地雷坐标
           //...
        }
    }
}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    void launch(){//绘制窗口
        //鼠标事件
        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.state=0;
                            }
                        }
                        break;
                    default:
                }

            }
        });
    }
}

6、MapBottom

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {
    {
        //游戏初始化界面
        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();
    }
}

7、MapTop

package com.study;

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;
            }
        }
    }
}

运行结果:

十九、数字添加

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {
    //倒计时
    static long START_TIME;
    static long END_TIME;
    //绘制数字
    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);
    }
}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    void launch(){//绘制窗口
        GameUtil.START_TIME=System.currentTimeMillis();
        //鼠标事件
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                switch (GameUtil.state){
                    case 0:
                    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){
                                GameUtil.FLAG_NUM=0;//释放旗数
                                GameUtil.START_TIME=System.currentTimeMillis();//重置游戏开始时间
                            }
                        }
                        break;
                    default:
                }

            }
        });
}

6、MapBottom

package com.study;

import java.awt.*;

/**
 * 底层地图
 * 绘制游戏相关组件
 */
public class MapBottom {
    //绘制方法
    void paintSelf(Graphics g){
        //绘制数字,剩余雷数
        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();//实时刷新
        }
    }
}

7、MapTop

package com.study;

import java.awt.*;

/**
 * 顶层地图类
 * 绘制顶层组件
 * 判断逻辑
 */
public class MapTop {
    //判断逻辑
    void logic(){
        if(temp_x>=1 && temp_x<=GameUtil.MAP_W
                && temp_y>=1 && temp_y<=GameUtil.MAP_H) {
            if (GameUtil.RIGHT) {
                //覆盖则插旗
                if(GameUtil.DATA_TOP[temp_x][temp_y]==0){
                    GameUtil.FLAG_NUM++;
                }
                //插旗则取消
                else if(GameUtil.DATA_TOP[temp_x][temp_y]==1){
                    GameUtil.FLAG_NUM--;
                }
            }
        }
    }

    //失败判定,true:失败;false:未失败
    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;
                    }
                }
            }
        }
    }
    //打开空格
    //此处用递归,递归打开点击处的空格周围相邻的所有空格
    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--;//释放旗子
                        }
                    }
                }
            }
        }
    }
}

运行结果:

二十、难度选择

2、BottomRay

package com.study;

/**
 * 初始化地雷
 */
public class BottomRay {
    //存放坐标,改成static,初始化为最大值,后续可防止数组越界
    static int[] rays=new int[GameUtil.RAY_MAX*2];
}

3、GameSelect

package com.study;

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.drawRect(100,50,300,100);
        GameUtil.drawWord(g,"简单",220,100,30,Color.black);
        g.drawRect(100,200,300,100);
        GameUtil.drawWord(g,"普通",220,250,30,Color.black);
        g.drawRect(100,350,300,100);
        GameUtil.drawWord(g,"困难",220,400,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=40;
                GameUtil.MAP_W=16;
                GameUtil.MAP_H=16;
                break;
            case 3:
                GameUtil.RAY_MAX=99;
                GameUtil.MAP_W=30;
                GameUtil.MAP_H=16;
                break;
            default:
        }
    }
}

4、GameUtil

package com.study;

import java.awt.*;

/**
 * 工具类
 * 存放静态参数
 * 工具方法
 */
public class GameUtil {
    //游戏状态 0:游戏中;1:胜利;2:失败 3:难度选择
    static int state=3;
    //游戏难度 1:简单2:普通 3:困难
    static int level;
}

5、GameWin

package com.study;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

/**
 * 绘制
 */
public class GameWin extends JFrame {
    GameSelect gameSelect=new GameSelect();

    //是否开始 f:未开始 t:开始
    boolean begin=false;
    void launch(){//绘制窗口
        if(GameUtil.state==3){
            this.setSize(500,500);//游戏难度选择窗口大小
        }else{
            this.setSize(width,height);//游戏界面窗口大小
        }
        //鼠标事件
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                switch (GameUtil.state){
                    case 0:
                    case 1:
                    case 2:
                        //在游戏重置时点击鼠标中间的滚轮可以再次选择难度
                        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){
            begin();//时时刻刻在调用
        }
    }
    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();
        }
    }
    public void paint(Graphics g) {//绘制组件
        if (GameUtil.state == 3) {
            //难度选择窗口的背景颜色
            g.setColor(Color.white);
            g.fillRect(0,0,500,500);
            gameSelect.paintSelf(g);
        } else {
            //...
        }
    }
}

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1104434.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分享Java NET Python三大技术下AutojsPro7云控代码

引言 有图有真相&#xff0c;那短视频就更是真相了。下面是三大语言的短视频。 Java源码版云控示例&#xff1a; Java源码版云控示例在线视频 Net源码版云控示例&#xff1a; Net源码版云控示例在线视频亚丁号-知识付费平台 支付后可见 扫码付费可见 Python源码版云控示例&…

【容器】Docker(学习笔记)

一、初识Docker 1、Docker概述 Docker 是一个开源的应用容器擎。 诞生于 2013 年初&#xff0c;基于 Go 语言实现&#xff0c;dotcloud 公司出品&#xff08;后改名为Docker Inc&#xff09;。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&a…

【小黑嵌入式系统第三课】嵌入式系统硬件平台(一)——概述、总线、存储设备(RAMROMFLASH)

上一课&#xff1a; 【小黑嵌入式系统第二课】嵌入式系统的概述&#xff08;二&#xff09;——外围设备、处理器、ARM、操作系统 文章目录 一、概述二、总线1. 总线的概念1.1 总线结构1.2 总线类型1.2.1 数据总线1.2.2 程序总线1.2.3 数据地址总线1.2.4 程序地址总线 2. 总线协…

new Object()到底占用几个字节

Java内存模型 对象内存中可以分为三块区域&#xff1a;对象头(Header)&#xff0c;实例数据(Instance Data)和对齐填充(Padding)&#xff0c;以64位操作系统为例(未开启指针压缩的情况)Java对象布局 如下图所示&#xff1a; 其中对象头中的Mark Word中的详细信息在文章synchr…

地下水与饮用水提标处理树脂

随着饮用水和地下水污染物检测技术水平的不断提高&#xff0c;世界各国管理机构跟踪监测的水体污染数目也不断增加。近年来&#xff0c;针对砷、高氯酸盐和铀等水体污染物&#xff0c;新的强化控制措施不断的付诸实施。此外&#xff0c;用氯化物及其衍生物进行水体消毒会带来诸…

leetcode 1143. 最长公共子序列、1035. 不相交的线、53. 最大子数组和

1143. 最长公共子序列 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些…

【ajax】withCredentials

默认值&#xff1a;false。在获取同域资源时设置 withCredentials 没有影响。 true&#xff1a;在跨域请求时&#xff0c;会携带用户凭证 false&#xff1a;在跨域请求时&#xff0c;不会携带用户凭证&#xff1b;返回的 response 里也会忽略 cookie ajax中的作用 跨域请求时…

药物滥用第一篇介绍

AMP&#xff1a; Ampicillin&#xff0c;中文名氨苄青霉素&#xff0c;同义名氨苄西林&#xff0c;一种β-内酰胺类抗生素&#xff0c;属于青霉素家族的一员&#xff0c;化学式为C16H19N3O4S&#xff0c;可治疗多种细菌感染。 氨苄西林为半合成的广谱青霉素&#xff08;结构如上…

基于单片机设计的家用自来水水质监测装置

一、前言 本文介绍基于单片机设计的家用自来水水质监测装置。利用STM32F103ZET6作为主控芯片&#xff0c;结合水质传感器和ADC模块&#xff0c;实现对自来水水质的检测和监测功能。通过0.96寸OLED显示屏&#xff0c;将采集到的水质数据以直观的方式展示给用户。 随着人们对健…

Unity DOTS World Entity ArchType Component EntityManager System概述

最近DOTS终于发布了正式的版本, 我们来分享以下DOTS里面地几个关键概念&#xff0c;方便大家上手学习掌握Unity DOTS开发。 Unity DOTS 中所有的Entities 都是被放到World世界中。每个Entity在它所在的World里面有唯一不同的ID号来区分。DOTS项目中可以同时有多个World。每个W…

04 接口隔离原则

官方定义 <<代码整洁之道>>作者罗伯特 C马丁 为 “接口隔离原则” 的定义是&#xff1a;客户端不 应该被迫依赖于它不使用的方法&#xff08;Clients should not be forced to depend on methods they do not use&#xff09;。 该原则还有另外一个定义&#xff1…

【Proteus仿真】【STM32单片机】太阳能追光系统设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD1602液晶、光敏传感器、PCF8591 ADC模块、按键模块、28BYJ48步进电机驱动模块、直流电机模块等。 主要功能&#xff1a; 系统运行后&#x…

网络工程师知识点6

91、3、IP ABC类私有地址和个数 A类私有地址1个&#xff1a;10.0.0.0/8 B类私有地址16个&#xff1a;172.16.0.0~172.31.0.0/16 C类私有地址256个&#xff1a;192.168.0.0~192.168.255.0/24 92、拥塞管理机制的实现过程分为哪两步&#xff1f; 第一步&#xff1a;将准备从一个…

MATLAB中sos2tf函数用法

目录 语法 说明 示例 二阶节系统的传递函数表示 sos2tf函数的功能是将数字滤波器的二阶节&#xff08;section&#xff09;数据转换为传递函数形式。 语法 [b,a] sos2tf(sos) [b,a] sos2tf(sos,g) 说明 [b, a] sos2tf(sos) 返回由 sos 描述的离散时间系统的传递函数系…

璟丰机电丨Parker派克江苏代理商 供应高品质驱动器和电机产品

苏州璟丰机电有限公司是一家专注于工业自动化领域的系统集成商&#xff0c;为客户提供非标自动化系统的设计研发、量身定做、批量生产等非标自动化解决方案&#xff0c;并代理这世界一流品牌的美国Parker派克。 派克Parker是全球领先的运动和控制技术与系统多元化制造商&#…

Minio 文件上传(后端处理同文件判断,同一文件秒传)

记录minio 文件上传 MinIO提供多个语言版本SDK的支持&#xff0c;下边找到java版本的文档&#xff1a; 地址&#xff1a;https://docs.min.io/docs/java-client-quickstart-guide.html maven依赖如下&#xff1a; XML <dependency><groupId>io.minio</groupId…

openHarmony新建项目及本地模拟机配置

新建项目 新建项目 选择空模板 选择一个非中文路径 在新建项目过程中可能会存在杀毒软件报病毒信息&#xff0c;建议退出退出杀毒软件 直到右侧窗口出现 Previewer预览选项&#xff0c;证明项目搭建完成 相关常用文件及文件夹解析 实时预览 调整预览设备类型 …

CSS3 渐变

CSS3 渐变可以让你在两个或多个指定的颜色之间显示平稳的过渡。 CSS3渐变有两种类型&#xff1a;线性渐变&#xff08;Linear Gradients&#xff09;和径向渐变&#xff08;Radial Gradients&#xff09;。 线性渐变&#xff08;Linear Gradients&#xff09;&#xff1a; 线性…

数据抓取代码示例

以下是一个使用lua-http和Lua编写的爬虫程序&#xff0c;用于爬取内容。此程序使用了https://www.duoip.cn/get_proxy的代码。 -- 引入lua-http库 local http require "http" ​ -- 定义get\_proxy函数 local function get_proxy()-- 使用https://www.duoip.cn/get…

idea jrebel热部署插件免费激活

介绍 jrebel是一款热部署的插件 idea上原生是不支持热部署的&#xff0c;一般更新了 Java 文件后要手动重启 Tomcat 服务器&#xff0c;才能生效&#xff0c;我们可以使用jrebel来热部署。 安装jRebel 在plugin中&#xff0c;选中marketplace&#xff0c;搜索jrebel&#x…