Java练手项目实战——五子棋游戏实现思路及源码

目录

      • 一、实现功能
      • 二、实现思路
          • 1、窗体设计
          • 2、棋盘设计
          • 3、棋子坐标存储设计
          • 4、游戏状态设计
          • 5、开始游戏与重新开始游戏的实现
          • 6、悔棋的实现
          • 7、认输的实现
          • 8、游戏结束的判断,即是否有同色五连棋子。
          • 9、双缓冲技术防止屏幕闪烁
      • 三、实现代码
          • 1、主函数
          • 2、窗体及实现类
      • 四、项目素材
          • 1、棋盘
          • 2、菜单背景图
          • 3、黑白棋子
          • 4、游戏小图标
      • 五、完整代码

一、实现功能

  • 1、有良好的UI界面,用户体验良好
  • 2、鼠标点击进行出棋子,玩家两人轮流下棋
  • 3、能够判断是否五子相连及输赢
  • 4、玩家能够重新开始游戏
  • 5、可以悔棋,要经过对方同意
  • 6、轮到玩家的回合可以认输,然后直接游戏结束
  • 7、如果棋盘看腻了,还可以切换棋盘
  • 8、在已经落有棋子的地方不能再落棋子
  • 9、有退出游戏的功能,并有用户手误操作的退出提示

二、实现思路

1、窗体设计
窗体实现 使用函数 参数描述
设置标题 setTitle() “五子棋”
设置图标 setIconImage() (new ImageIcon(“images/logo.png”).getImage()) 图片
设置大小 SetSize() (宽1200像素,高920像素)
设置显示位置 setLocation() (屏幕宽度-窗体宽度/2,屏幕高度-窗体高度/2)
设置大小是否可变 setResizable() (false) 不能修改窗体大小
设置关闭方式 setDefaultCloseOperation() (JFrame.EXIT_ON_CLOSE) 关闭就退出程序
设置是否显示 setVisible() (true)将窗体显示出来
添加鼠标监听 addMouseListener() (this) 表示给当前窗体添加监听
2、棋盘设计

采用两种棋盘,一种需要直接绘制图片即可,另一种需要绘制图片、绘制棋盘格子和标注点位,两种棋盘都是18*18的格子。

   /**
     *  棋盘
     *  1 [1号棋盘]
     *  2 [2号棋盘]
     */
    int checkerBoard = 2;

这里使用int型checkerBoard来记录当前是哪一个棋盘,若为1表示一号棋盘,若为2表示二号棋盘,这里用于切换棋盘的实现。

一号棋盘实现 使用函数 参数描述
绘制图像 drawImage() (Image,x,y,observer)Image图片绘制在observer对象的x,y像素处
Java练手项目实战——五子棋游戏实现思路及源码_第1张图片
二号棋盘实现 使用函数 参数描述
绘制图像 drawImage() (Image,x,y,observer)Image图片绘制在observer对象的x,y像素处
设置颜色 setColor() (Color.black)设置颜色为黑色
使用画线绘制格子 drawLine() (x1,y1,x2,y2)线的起始点坐标
使用画点绘制标志点位 fillOval() (x,y,height,weight)点的坐标以及点的宽、高
3、棋子坐标存储设计
    /**
     * 保存棋子
     *  [0 :无]
     *  [1 :黑子]
     * [2 : 白子]
     */
    int[][] allPiece = new int[19][19];

    // 记录上一个棋子位置
    int[] lastPiece = new int[2];
    // 下一步要下的是否是黑子
    boolean isBlack = true;

这里通过19*19的二维数组allPiece存放棋子的位置,默认为0,表示无棋子,1表示此处为黑子,2表示此处为白子,然后通过一维数组lastPiece来存放上一步棋子的位置,方便悔棋的实现,然后通过布尔型的isBlack来记录下一步是黑方下还是白方下,若为true表示下一步改黑方下,反之则是白方下。

4、游戏状态设计
  /**
    *  游戏状态 默认0
    *   [0:未开始]
    *   [1: 已开始]
    *   [2: 游戏结束]
    */
   int game_status = 0;
   // 提示信息
   String message = "黑方先行";

这里使用 int型game_status来记录游戏的状态,默认为0即是未开始游戏,1表示已开始游戏,2表示游戏结束。
右侧菜单和右侧上方的游戏信息都会根据当前游戏状态进行不同的绘制。

Java练手项目实战——五子棋游戏实现思路及源码_第2张图片

提示信息会通过String类型的message记录,默认是黑方先行,若还没有开始游戏,这个也不会绘制上去,当开始游戏后,显示黑方先行,然后根据isBlack判断下次要执行的是黑方还是白方进行修改message的信息,即“轮到白方”和“轮到黑方”。

Java练手项目实战——五子棋游戏实现思路及源码_第3张图片
Java练手项目实战——五子棋游戏实现思路及源码_第4张图片

5、开始游戏与重新开始游戏的实现
  • ①棋盘数据清空,即将allPiece[i][j]置空
  • ②修改游戏状态,即game_status = 1,设置为开始游戏状态
  • ③游戏提示信息修改为初始状态,即message=“黑方先行”。
  • ④下一步棋改为黑方,即isBlack=true;

Java练手项目实战——五子棋游戏实现思路及源码_第5张图片

6、悔棋的实现
  • ①上次落棋位置置空
  • ②调整下次落子颜色与上次一致
  • ③调整提示信息
  • ④游戏状态设置为已开始
    Java练手项目实战——五子棋游戏实现思路及源码_第6张图片

如图所示,白方点击悔棋,白色棋子消失,提示信息改为"轮到白方"。

Java练手项目实战——五子棋游戏实现思路及源码_第7张图片

再次落子也还是白子

Java练手项目实战——五子棋游戏实现思路及源码_第8张图片

7、认输的实现
  • ①根据isBlack判断当前是谁的回合,判断是哪方认输
    * ②设置游戏状态为结束状态
    Java练手项目实战——五子棋游戏实现思路及源码_第9张图片

游戏结束,将无法再进行棋盘上的操作

Java练手项目实战——五子棋游戏实现思路及源码_第10张图片

8、游戏结束的判断,即是否有同色五连棋子。

根据当前位置向四周进行判断,根据(横向、纵向、右上、左下)四个方向进行判断,使用count从1开始进行计数,同一个方向上每存在相连的同色棋子count就加1,每一个方向判断完了,就把count置为1,,直到出现count>=5则游戏胜利。

9、双缓冲技术防止屏幕闪烁
 BufferedImage bi = new BufferedImage(frame_width,frame_height,BufferedImage.TYPE_INT_ARGB);
 Graphics g2 = bi.createGraphics();

三、实现代码

1、主函数
package com.lyb.gobang;

import com.lyb.gobang.frame.GobangFrame;

/**
 * 五子棋游戏 实现功能如下:
 *  1、有良好的UI界面,用户体验良好
 *  2、鼠标点击进行出棋子,玩家两人轮流下棋
 *  3、能够判断是否五子相连及输赢
 *  4、玩家能够重新开始游戏
 *  5、可以悔棋,要经过对方同意
 *  6、轮到玩家的回合可以认输,然后直接游戏结束
 *  7、如果棋盘看腻了,还可以切换棋盘
 *  8、在已经落有棋子的地方不能再落棋子
 *  9、有退出游戏的功能,并有用户手误操作的退出提示
 */

// 主函数入口
public class Main {
    public static void main(String[] args) {
        // 初始化一个五子棋界面对象
       GobangFrame gf = new GobangFrame();
       gf.initUI();
    }
}

2、窗体及实现类
package com.lyb.gobang.frame;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class GobangFrame extends JFrame implements MouseListener {
    //获取屏幕的宽度和高度
    int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;
    int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;

    //窗体大小
    int frame_width = 1200;
    int frame_height = 920;

    Image bgImage = null;
    Image bgImage2 = null;
    Image menu1 = null;
    Image menu2 = null;
    Image black_piece = null;
    Image white_piece = null;

    // 保存棋子的坐标
    int x = 0;
    int y = 0;

    /**
     * 保存棋子
     *  [0 :无]
     *  [1 :黑子]
     * [2 : 白子]
     */
    int[][] allPiece = new int[19][19];

    // 记录上一个棋子位置
    int[] lastPiece = new int[2];
    // 下一步要下的是否是黑子
    boolean isBlack = true;
    /**
     *  游戏状态 默认0
     *   [0:未开始]
     *   [1: 已开始]
     *   [2: 游戏结束]
     */
    int game_status = 0;
    // 提示信息
    String message = "黑方先行";

    /**
     *  棋盘
     *  1 [1号棋盘]
     *  2 [2号棋盘]
     */
    int checkerBoard = 2;


    public void initUI() {
        readImages();//读取图片资源
        setTitle("五子棋"); //设置标题
        setIconImage(new ImageIcon("images/logo.png").getImage());//设置
        setSize(frame_width,frame_height);
        setLocation((screen_width - frame_width) / 2, (screen_height - frame_height) / 2); //设置窗体出现位置
//        jf.setLocationRelativeTo(null);//居中显示
        setResizable(false);  //设置窗体大小不可改变
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗体关闭方式为关闭直接退出程序
        addMouseListener(this); //为窗体添加监听器

        //将窗体显示出来
        this.setVisible(true);
    }

    //重写重绘方法,这里重写的是第一个大的JPanel的方法
    public void paint(Graphics g) {

        // 双缓冲技术防止屏幕闪烁
        BufferedImage bi = new BufferedImage(frame_width,frame_height,BufferedImage.TYPE_INT_ARGB);
        Graphics g2 = bi.createGraphics();
        if (checkerBoard == 2) {
            g2.drawImage(bgImage2, 3 , 26 ,this);
        } else {
            g2.drawImage(bgImage, 3 , 26 ,this);
            // 绘制 18*18 的网格
            g2.setColor(Color.black);
            for (int i = 0; i < 19 ; i++) {

                g2.drawLine(50+i*45,69,50+i*45,879);
                g2.drawLine(50,69+45*i,860,69+45*i);
            }
            // 标注点位
            g2.fillOval(179,198,12,12);//左上
            g2.fillOval(179,737,12,12);//左下
            g2.fillOval(449,467,12,12);//中点
            g2.fillOval(719,198,12,12);//右上
            g2.fillOval(719,737,12,12);//右下
        }

        if (game_status == 0) {
            g2.drawImage(menu1,897,26,this);
        } else {
            g2.drawImage(menu2,897,26,this);
        }


        // 绘制全部棋子
        for (int i = 0; i < 19; i++) {
            for (int j = 0; j < 19; j++) {
                int piece_x = i * 45 + 49;
                int piece_y = j * 45 + 67;
                if (allPiece[i][j] == 1) {
                    // 棋子40像素
                    g2.drawImage(black_piece,piece_x - 20,piece_y - 20,this);
                }
                if (allPiece[i][j] == 2) {
                    // 棋子40像素
                    g2.drawImage(white_piece,piece_x - 20,piece_y - 20,this);
                }
            }
        }
        // 游戏提示信息
        if (game_status != 0) {
            g2.setFont(new Font("黑体",Font.BOLD,40));//设置字体
            g2.setColor(Color.red);//设置红色
            g2.drawString(message,960,190);
        }

        g.drawImage(bi,0,0,this);
    }

    public void mouseClicked(MouseEvent e) {

    }



    public void mousePressed(MouseEvent e) {
        x = e.getX();
        y = e.getY();
        // 在棋盘范围中生效 {实际棋盘范围 49<=x<=859,67<=y<=877 ,判断范围边缘多出半格大小(23像素),这样更精确}
        if (game_status == 1) {
            if (x >= 26 && x <= 882 && y >= 44 && y <= 900) {
                // 每格45像素
                int i = (x - 26) / 45;
                int j = (y - 44) / 45;
                // 记录棋子位置
                lastPiece[0] = i;
                lastPiece[1] = j;
                if ( i < 19  && j < 19 && allPiece[i][j] == 0) {
                    // 判断要下的是什么棋子
                    if (isBlack) {
                        allPiece[i][j] = 1;
                        isBlack = false;
                        message = "轮到白方";
                    } else {
                        allPiece[i][j] = 2;
                        isBlack = true;
                        message = "轮到黑方";
                    }
                    // 重新执行paint()方法
                    this.repaint();

                    // 判断游戏是否结束(是否有同色棋子连成五个)
                    boolean winFlag = checkWin(i,j);
                    if (winFlag) {
                        if (allPiece[i][j] == 1) {
                            message = "黑方获胜";
                        } else {
                            message = "白方获胜";
                        }
                        JOptionPane.showMessageDialog(this,"游戏结束,"+(allPiece[i][j] == 1 ? "黑方":"白方") + "获胜!");
                        game_status = 2;
                    }
                } else {
//                    JOptionPane.showMessageDialog(this, "当前位置已有棋子,请重新落子!");
                }
                System.out.println("棋盘内的坐标为[x:"+x+",y:"+y+"],对应位置为[x:"+i+",y:"+j+"]");
            } else {
                System.out.println("棋盘外的坐标为[x:"+x+",y:"+y+"]");
            }
        } else {
            System.out.println("棋盘外的坐标为[x:"+x+",y:"+y+"]");
        }

        // 开始游戏和重新开始游戏
        if (x >= 934 && x <= 1168 && y >= 374 && y <= 416) {
            if (game_status == 1) {
                int result = JOptionPane.showConfirmDialog(this,"是否重新开始游戏?");
                /**
                 *  重新开始游戏
                 *  1、棋盘数据清空
                 *  2、修改游戏状态
                 *  3、游戏提示信息修改为初始状态
                 *  4、下一步棋改为黑方
                 */
                if (result == 0) {
                    for (int i = 0; i < 19; i++) {
                        for (int j = 0; j < 19; j++) {
                            allPiece[i][j] = 0;
                        }
                    }
                }
                message = "黑方先行";
                isBlack = true;
            }
            game_status = 1;
            this.repaint();
        }

        // 切换棋盘
        if (x >= 934 && x <= 1168 && y >= 454 && y <= 506) {
            if (checkerBoard == 2) {
                checkerBoard = 1;
            } else {
                checkerBoard = 2;
            }
            this.repaint();
        }

        // 悔棋
        if (x >= 934 && x <= 1168 && y >= 540 && y <= 596) {
            int result = JOptionPane.showConfirmDialog(this,"是否同意对方悔棋?");
            /**
             *  悔棋
             *  1、上次落棋位置置空
             *  2、调整下次落子颜色与上次一致
             *  3、调整提示信息
             *  4、游戏状态设置为已开始
             */
            if (result == 0) {
                int x_last = lastPiece[0];
                int y_last = lastPiece[1];
                int color = allPiece[x_last][y_last];
                allPiece[lastPiece[0]][lastPiece[1]] = 0;
                if (color == 1) {//若为黑子
                    message = "轮到黑方";
                    isBlack = true;
                } else {
                    message = "轮到白方";
                    isBlack = false;
                }
                game_status = 1;
            }
            this.repaint();
        }

        // 认输
        if (x >= 934 && x <= 1168 && y >= 625 && y <= 684) {
            int result = JOptionPane.showConfirmDialog(this,"确定要认输么?");
            /**
             *  1、根据是谁的回合,判断是谁认输
             *  2、设置游戏状态为结束状态
             */
            if (result == 0) {
                if (isBlack) {
                    message = "白方获胜";
                } else {
                    message = "黑方获胜";
                }
            }
            game_status = 2;
            this.repaint();
        }

        // 退出游戏
        if (x >= 934 && x <= 1168 && y >= 856 && y <= 898) {
            int result = JOptionPane.showConfirmDialog(this,"是否退出游戏");
            if (result == 0) {
                System.exit(0);
            }
        }
    }

    public void mouseReleased(MouseEvent e) {

    }

    public void mouseEntered(MouseEvent e) {

    }

    public void mouseExited(MouseEvent e) {


    }

    public boolean checkWin(int i, int j) {
        boolean flag = false;
        int color = allPiece[i][j];
        // ①判断横向
        int t = 1;
        int count = 1;
        while (i + t <19 && allPiece[i + t][j] == color) {//防止数组越界
            t++;
            count++;
        }
        t = 1;
        while ( i -t >= 0 && allPiece[i - t][j] == color) {
            t++;
            count++;
        }
        if (count >= 5) {
            flag = true;
        }

        // ②判断纵向
        int t2 = 1;
        int count2 = 1;
        while (j + t2 < 19 && allPiece[i][j + t2] == color) {//防止数组越界
            t2++;
            count2++;
        }
        t2 = 1;
        while (j -t2 >= 0 && allPiece[i][j - t2] == color) {
            t2++;
            count2++;
        }
        if (count2 >= 5) {
            flag = true;
        }

        // ③判断斜向(右上+左下)
        int t3 = 1;
        int count3 = 1;
        while (i + t3 < 19 && j -t3 >= 0 && allPiece[i + t3][j - t3] == color) {//防止数组越界
            t3++;
            count3++;
        }
        t3 = 1;
        while (i -t3 >= 0 && j + t3 < 19 && allPiece[i - t3][j + t3] == color) {
            t3++;
            count3++;
        }
        if (count3 >= 5) {
            flag = true;
        }

        // ③判断斜向(左上+右下)
        int t4 = 1;
        int count4 = 1;
        while (i - t4 >= 0 && j -t4 >= 0 && allPiece[i - t4][j - t4] == color) {//防止数组越界
            t4++;
            count4++;
        }
        t4 = 1;
        while (i + t4 < 19 && j + t4 < 19 && allPiece[i + t4][j + t4] == color) {
            t4++;
            count4++;
        }
        if (count4 >= 5) {
            flag = true;
        }
        return flag;
    }
    public void readImages() {
        try {
            bgImage = ImageIO.read(new File("images/background.png"));
            bgImage2 = ImageIO.read(new File("images/background2.png"));
            menu1 = ImageIO.read(new File("images/menu1.png"));
            menu2 = ImageIO.read(new File("images/menu2.png"));
            black_piece = ImageIO.read(new File("images/black.png"));
            white_piece = ImageIO.read(new File("images/white.png"));
        } catch (
                IOException e) {
            e.printStackTrace();
        }
    }
}

四、项目素材

1、棋盘

Java练手项目实战——五子棋游戏实现思路及源码_第11张图片

2、菜单背景图

Java练手项目实战——五子棋游戏实现思路及源码_第12张图片
Java练手项目实战——五子棋游戏实现思路及源码_第13张图片

3、黑白棋子

在这里插入图片描述

在这里插入图片描述

4、游戏小图标

在这里插入图片描述

五、完整代码

  • 访问Gitee开源代码库自取
    https://gitee.com/liu-yipin/gobang

Java练手项目实战——五子棋游戏实现思路及源码_第14张图片

你可能感兴趣的:(Java练手项目实战,Java,java,游戏开发,swing)