• 全文手敲代码,教你用Java实现扫雷小游戏


    摘要:本程序共封装了五个类,分别是主类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扫雷,用户在图形化用户界面内利用鼠标监听事件标记雷区,左上角表示剩余雷的数量,右上角动态显示使用的时间。用户可选择中间组件按钮重新游戏。为了解决程序窗口闪动的问题,本程序采用了双缓冲技术。

    程序的总体界面布局:

    项目结构:

    程序测试:

    请大家指正!

     

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

  • 相关阅读:
    Golang:命令行框架cobra简介
    kafka集群管理工具kafkamanager
    GAN 简介
    部分梯度下降算法简述
    EfficientNet 简介
    数据库查表空间Oracel、Vertica
    Vue 组件自动注册
    使用jmeter+操作BeanShell调用java生成jar包实例
    ACCESS数据库中SQL语句,数字不必加'123'
    反射取得静态类中的属性,方法,字段
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/16428107.html
Copyright © 2020-2023  润新知