Java学习历程14——制作一款五子棋游戏(4)

上次我们基本实现了五子棋游戏的功能,这次我们进行一些优化和添加一些便于用户使用的功能。

新增功能及优化

一、复盘功能

复盘功能就是指在下完一局棋后,我们可以通过复盘按钮使本局棋的所有棋子重头开始自动下一遍。分析得知,我们首先要保存以及下过的棋子数据,然后清空棋盘上的数据并刷新棋盘,然后利用保存的数据开始自动有间隔的绘制。

①保存数据

我们重绘所需要的数据都存在一个一维数组shapeArr中,我们需要取出并保存。

由于下一步需要清空这个数组的数据,我们需要定义一个新的数据依次保存shapeArr中的数据,我们命名为tempArr(临时数组)。需要注意的是,我们不能直接tempArr=shapeArr保存,否则这两个对象会公用同一个内存空间,到时候清空时两个会一起清空而无法保存数据。我们应该采用遍历的方法保存数组数据。最后,调用一下下棋面板的重绘方法刷新棋盘即可。⭐⭐⭐

java 复制代码
//先保存所有下过的棋子数据
        for (int i = 0; i < shapeArr.length; i++) {
            tempArr[i] = shapeArr[i];
        }
        //再清除所有数据,刷新棋盘
        for (int i = 0; i < tempArr.length; i++) {
            shapeArr[i] = null;//tempArr数据还在!
        }
centerPanel.paint(g);

保存好数据之后,我们就可以利用保存的数据逐个绘制棋子。

②绘制棋子

我们希望的是点击复盘按钮后,先等待一会再开始下棋,并且下每颗棋子中间需要有间隔,所以需要使用到延时操作。代码为:

try {

Thread.sleep(延时时间,单位为毫秒);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

然后我们遍历一下新定义的数组tempArr,取出数据调用定义的drawShape方法(绘制棋子)就可以重新绘制棋子,再加一个延时操作就可以实现复盘功能。

java 复制代码
try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //再遍历tempArr数组,重新绘制棋子
        for (int i = 0; i < tempArr.length; i++) {
            Shape s = tempArr[i];
            if (s == null) break;
            s.drawShape(g);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

二、标注所下的最后一颗棋子

我们希望使用者能时刻知道所下最后一颗棋子的位置,所以我们可以添加在最后一颗棋子上绘制一个红点的功能。

分析可知道,我们可以在MPanel类里面书写,因为里面有paint方法,不用担心重绘的问题。我们可以单独书写一个方法point,用于书写绘制的代码,并在paint中调用。同时,我们需要随时更新chessX和chessY的数据以绘制在最后一颗棋子上,而所需的数据在drawListener里,所以在drawListener里调用centerPanel对象中的x,y,用本类中的chessX和chessY赋值即可。注意:要写在点击方法中,以实现每点击下一颗棋子就更新一次数据。

java 复制代码
public void point(Graphics g){
        Graphics2D g1=(Graphics2D)g;
        ((Graphics2D) g).setStroke(new BasicStroke(5));
        g.setColor(Color.RED);
        g.drawLine(x * SIZE + X0 , y * SIZE + Y0 , x * SIZE + X0, y * SIZE + Y0);
    }
java 复制代码
//实时更新棋子的坐标情况(写在点击方法中)
            centerPanel.x = chessX;
            centerPanel.y = chessY;

完成上述步骤后,发现点击悔棋、复盘按钮时红色标注无法移动到上一颗棋子中,点击重新开始按钮时无法将红色标点去掉的问题,下面来逐个解决优化。

①悔棋移动

点击悔棋时,我们需要的效果是将红色标注移动到最近的上一颗棋子,其实就是我们需要的chessX和chessY发生了变化,而由于每步的点坐标数据都存储到一维数组shapeArr中,我们只需要定义一个同类对象保存数组下标为(index-1)对应的数据,并通过这个数据给MPanel里面的x,y重新赋值就可以了。

java 复制代码
case "悔棋": {
                index--;
//保存上一颗最近棋子的数据
                Shape s1 = shapeArr[index - 1];
                centerPanel.x = s1.x;
                centerPanel.y = s1.y;
}

②重新开始、复盘时清空红色标注

点击重新开始和复盘时,我们需要棋盘上什么都没有,所以我们需要将传过去的chessX和chessY设置成不能用于绘制红色标点的数值,这样就不会残存红色标点了。

java 复制代码
case "重新开始": {
//将最后一个红点去除掉
                centerPanel.x=-1;
                centerPanel.y=-1;
}
case "复盘": {
                //将最后一个红点去除掉
                centerPanel.x=-1;
                centerPanel.y=-1;
}

三、显示当前下棋方以及步数

我们希望在南边容器中显示当前下棋方以及步数,为了效果更好,建议使用JLabel标签组件,而不是绘制字符串的drawString。(注意:JLabel组件只能在GameUI(窗体)中创建并添加!)

添加好之后,我们需要用标记位flag来判断是哪方即将下棋,用index来显示当前的步数,而这些数据都在drawListener类中,但是千万注意:不能把数据传到GameUI中!因为这个类里面的init方法只能调用一次,即使传递数据也无法实现数据的更新,所以我们应该考虑将JLabel传递到drawLisener中去实现更改。由于在GameUI中已经创建过drawLisener对象,所以直接通过构造方法传递JLabel即可。

传递完毕之后,添加一个方法,按照下棋的情况用setText关键字输入对应的文字即可。设置字体:setFont;设置前颜色:setForeBackground

然后,在控制下棋的switch分支中调用UI方法。

java 复制代码
JLabel jl=new JLabel("黑方执子:"+0);
jl.setFont(new Font("宋体",Font.BOLD,20));
java 复制代码
//显示当前下棋方以及步数
    public void UI() {
        if (flag == 0) {
            jl.setFont(new Font("宋体", Font.BOLD, 20));
            jl.setText("黑方执子:" + (index + 1));
        } else if (flag == 1) {
            jl.setFont(new Font("宋体", Font.BOLD, 20));
            jl.setText("白方执子:" + (index + 1));
        }
    }

同时,我们可以优化显示胜利的标语,将其改成JLabel,并显示在北边的容器中。

java 复制代码
 JLabel jl1=new JLabel("谁会获得最终胜利?敬请期待!");
        jl1.setForeground(Color.BLACK);
jl1.setFont(new Font("宋体",Font.BOLD,20));
northPanel.add(jl1);
java 复制代码
//判断输赢
        if (isWin() >= 5 || isWin1() >= 5 || isWin2() >= 5 || isWin3() >= 5) {
            flag = 3;
            System.out.println("本局已结束,请点击重新开始按钮!");
            if (chess[chessY][chessX] == 1) {
                jl1.setForeground(Color.RED);
                jl1.setText("黑方获胜!");
            } else if (chess[chessY][chessX] == 2) {
                jl1.setForeground(Color.RED);
                jl1.setText("白方获胜!");
            }
        }
    }

经历以上的步骤,五子棋游戏的第一版本功能添加以及优化就基本完成了!

完整代码以及效果

完整代码

java 复制代码
import javax.swing.*;
import java.awt.*;
public class GameUI implements Conbig {
    public void init() {
        JFrame jf = new JFrame();
        jf.setSize(700, 700);
        jf.setTitle("五子棋游戏1.0");
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(3);
        BorderLayout bl=new BorderLayout();
        jf.setLayout(bl);
        JPanel southPanel=new JPanel();
        JPanel northPanel=new JPanel();
        Dimension dm=new Dimension(0,35);
        southPanel.setBackground(Color.pink);
        southPanel.setPreferredSize(dm);
        northPanel.setPreferredSize(new Dimension(0,30));
        northPanel.setBackground(Color.lightGray);
        jf.add(southPanel,BorderLayout.SOUTH);
        jf.add(northPanel,BorderLayout.NORTH);
        JLabel jl=new JLabel("黑方执子:"+0);
        JLabel jl1=new JLabel("谁会获得最终胜利?敬请期待!");
        jl1.setForeground(Color.BLACK);
        jl.setFont(new Font("宋体",Font.BOLD,20));
        jl1.setFont(new Font("宋体",Font.BOLD,20));
        MPanel centerPanel=new MPanel();
        centerPanel.setBackground(Color.YELLOW);
        jf.add(centerPanel);
        drawListener listener = new drawListener(centerPanel,jl,jl1);
        //按钮添加
        JButton jb1=new JButton("开始");
        southPanel.add(jb1);
        JButton jb2=new JButton("悔棋");
        southPanel.add(jb2);
        JButton jb3=new JButton("重新开始");
        southPanel.add(jb3);
        JButton jb4=new JButton("复盘");
        southPanel.add(jb4);
        //添加显示组件
        southPanel.add(jl);
        northPanel.add(jl1);
        jf.setVisible(true);

        centerPanel.shapeArr=listener.shapeArr;
        centerPanel.addMouseListener(listener);
        jb1.addActionListener(listener);
        jb2.addActionListener(listener);
        jb3.addActionListener(listener);
        jb4.addActionListener(listener);
        Graphics g = centerPanel.getGraphics();
        listener.g = g;
    }
    public static void main(String[] args) {
        GameUI gu = new GameUI();
        gu.init();
    }
}
java 复制代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class drawListener implements MouseListener, ActionListener, Conbig {
    public Graphics g;
    public int x, y;
    public int chessX, chessY;
    public int flag;
    public String name = "null";
    public Color color;
    public static gl.game0820.Shape[] shapeArr = new gl.game0820.Shape[200];
    public int[][] chess = new int[LINE][LINE];
    public int index = 0;
    public boolean start = false;
    public MPanel centerPanel;
    public JLabel jl, jl1;

    //引用传递
    public drawListener(MPanel centerPanel, JLabel jl, JLabel jl1) {
        this.centerPanel = centerPanel;
        this.jl = jl;
        this.jl1 = jl1;
    }


    public void actionPerformed(ActionEvent e) {
        name = e.getActionCommand();
        switch (name) {
            case "开始": {
                start = true;
                break;
            }
            case "悔棋": {
                index--;
                jl1.setForeground(Color.BLACK);
                jl1.setText("谁会获得最终胜利?敬请期待!");
                gl.game0820.Shape s = shapeArr[index];
                //保存上一颗最近棋子的数据
                Shape s1 = shapeArr[index - 1];
                centerPanel.x = s1.x;
                centerPanel.y = s1.y;
                if (index > 0) {
                    shapeArr[index] = null;
                } else {
                    break;
                }
                //手动调用centerPanel里的paint方法,刷新棋盘
                centerPanel.repaint();
                if (flag == 0) {
                    flag = 1;
                    jl.setText("白方执子:" + (index));
                } else if (flag == 1) {
                    flag = 0;
                    jl.setText("黑方执子:" + (index));
                } else if (flag == 3 && chess[s.y][s.x] == 1) {
                    flag = 0;
                } else if (flag == 3 && chess[s.y][s.x] == 2) {
                    flag = 1;
                }
                if (s == null) break;
                chess[s.y][s.x] = 0;
                break;
            }
            case "重新开始": {
                //将最后一个红点去除掉
                centerPanel.x=-1;
                centerPanel.y=-1;
                //初始化标注
                jl.setText("黑方执子:" + 0);
                jl1.setForeground(Color.BLACK);
                jl1.setText("谁会获得最终胜利?敬请期待!");
                jl.setFont(new Font("宋体", Font.BOLD, 20));
                for (int i = 0; i < index; i++) {
                    shapeArr[i] = null;
                }

                for (int i = 0; i < chess.length; i++) {  //行
                    for (int j = 0; j < chess[0].length; j++) { //列
                        chess[i][j] = 0;
                    }
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
                centerPanel.repaint();
                flag = 0;
                index = 0;
//                printArr();
                for (int i = index; i > 0; i--) {
                }
                break;
            }
            case "复盘": {
                //将最后一个红点去除掉
                centerPanel.x=-1;
                centerPanel.y=-1;
                drawAgain();
                break;
            }
        }
    }

    public void mouseClicked(MouseEvent e) {

        x = e.getX();
        y = e.getY();
        if ((x - X0) % SIZE > SIZE / 2 && (y - Y0) % SIZE < SIZE / 2) {//靠右,靠上
            chessX = (x - X0) / SIZE + 1;
            chessY = (y - Y0) / SIZE;
        } else if ((x - X0) % SIZE > SIZE / 2 && (y - Y0) % SIZE > SIZE / 2) {//靠右,靠下
            chessX = (x - X0) / SIZE + 1;
            chessY = (y - Y0) / SIZE + 1;
        } else if ((x - X0) % SIZE < SIZE / 2 && (y - Y0) % SIZE < SIZE / 2) {//靠左,靠上
            chessX = (x - X0) / SIZE;
            chessY = (y - Y0) / SIZE;
        } else if ((x - X0) % SIZE < SIZE / 2 && (y - Y0) % SIZE > SIZE / 2) {//靠左,靠下
            chessX = (x - X0) / SIZE;
            chessY = (y - Y0) / SIZE + 1;
        }
        if (!start) {
            System.out.println("请点击开始按钮!");
            return;
        }
        if (chess[chessY][chessX] != 0) {
            System.out.println("此处已有棋子,不可下棋!");
            return;
        }
        //实时更新棋子的坐标情况
            centerPanel.x = chessX;
            centerPanel.y = chessY;
//         printArr();
        switch (flag) {//标记位法
            case 0: {

                chess[chessY][chessX] = 1;//用1代表黑棋

                color = Color.BLACK;
                flag++;
                gl.game0820.Shape shape = new gl.game0820.Shape(chessX, chessY, SIZE, color);
                shape.drawShape(g);
                //刷新棋盘,清楚之前的红点标注
                centerPanel.repaint();
                UI();
                shapeArr[index] = shape;
                index++;
                break;
            }
            case 1: {
                chess[chessY][chessX] = 2;//用2代表白棋
                color = Color.WHITE;
                flag = 0;
                Shape shape = new Shape(chessX, chessY, SIZE, color);
                shape.drawShape(g);
                //刷新棋盘,清楚之前的红点标注
                centerPanel.repaint();
                UI();
                shapeArr[index] = shape;
                index++;
                break;
            }
        }
        //判断输赢
        if (isWin() >= 5 || isWin1() >= 5 || isWin2() >= 5 || isWin3() >= 5) {
            flag = 3;
            System.out.println("本局已结束,请点击重新开始按钮!");
            if (chess[chessY][chessX] == 1) {
                jl1.setForeground(Color.RED);
                jl1.setText("黑方获胜!");
            } else if (chess[chessY][chessX] == 2) {
                jl1.setForeground(Color.RED);
                jl1.setText("白方获胜!");
            }
        }
    }
    //复盘的功能方法
    public void drawAgain() {
        gl.game0820.Shape[] tempArr = new gl.game0820.Shape[200];

        //先保存所有下过的棋子数据
        for (int i = 0; i < shapeArr.length; i++) {
            tempArr[i] = shapeArr[i];
        }
        //再清除所有数据,刷新棋盘
        for (int i = 0; i < tempArr.length; i++) {
            shapeArr[i] = null;//tempArr数据还在!
        }
        centerPanel.paint(g);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //再遍历tempArr数组,重新绘制棋子
        for (int i = 0; i < tempArr.length; i++) {
            Shape s = tempArr[i];
            if (s == null) break;
            s.drawShape(g);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //显示当前下棋方以及步数
    public void UI() {
        if (flag == 0) {
            jl.setFont(new Font("宋体", Font.BOLD, 20));
            jl.setText("黑方执子:" + (index + 1));
        } else if (flag == 1) {
            jl.setFont(new Font("宋体", Font.BOLD, 20));
            jl.setText("白方执子:" + (index + 1));
        }
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {

    }

    public void mouseExited(MouseEvent e) {

    }

    //统计棋子横向
    public int isWin() {
        int number = 0;
        //向左检查
        for (int i = chessX; i >= 0; i--) {
            if (chess[chessY][chessX] == chess[chessY][i]) {
                number++;
            } else {
                break;
            }
        }
        //向右检查
        for (int i = chessX + 1; i < chess.length; i++) {
            if (chess[chessY][chessX] == chess[chessY][i]) {
                number++;
            } else {
                break;
            }
        }
        return number;
    }

    //统计棋子纵向
    public int isWin1() {
        int number = 0;
        //向上检查
        for (int i = chessY; i >= 0; i--) {
            if (chess[chessY][chessX] == chess[i][chessX]) {
                number++;
            } else {
                break;
            }
        }
        //向下检查
        for (int i = chessY + 1; i < chess.length; i++) {
            if (chess[chessY][chessX] == chess[i][chessX]) {
                number++;
            } else {
                break;
            }
        }
        return number;
    }

    //统计棋子斜向(撇)
    public int isWin2() {
        int number = 0;
        //向右上方检查
        for (int i = chessY, j = chessX; i < chess.length && j > 0; i++, j--) {
            if (chess[chessY][chessX] == chess[i][j]) {
                number++;
            } else {
                break;
            }
        }
        //向左下方检查
        for (int i = chessY - 1, j = chessX + 1; i > 0 && j < chess.length; i--, j++) {
            if (chess[chessY][chessX] == chess[i][j]) {
                number++;
            } else {
                break;
            }
        }
        return number;
    }

    //统计棋子斜向(捺)
    public int isWin3() {
        int number = 0;
        //向右下方检查
        for (int i = chessY + 1, j = chessX + 1; i < chess.length && j < chess.length; i++, j++) {
            if (chess[chessY][chessX] == chess[i][j]) {
                number++;
            } else {
                break;
            }
        }
        //向左上方检查
        for (int i = chessY, j = chessX; i > 0 && j > 0; i--, j--) {
            if (chess[chessY][chessX] == chess[i][j]) {
                number++;
            } else {
                break;
            }
        }
        return number;
    }

    public void printArr() {
        for (int i = 0; i < chess.length; i++) {  //行
            for (int j = 0; j < chess[0].length; j++) { //列
                System.out.print(chess[i][j] + " ");
            }
            System.out.println();
        }
    }
}
java 复制代码
import javax.swing.*;
import java.awt.*;
public class MPanel extends JPanel implements Conbig {
    public gl.game0820.Shape[] shapeArr;
    public int x=-1,y;
    public ImageIcon image=new ImageIcon("D:\\图片\\12.jpg");
    public void paint(Graphics g) {
        super.paint(g);
        g.drawImage(image.getImage(),0,0,900,900,null);
        //绘制棋盘
        for (int i = 0; i < LINE; i++) {
            g.drawLine(X0, Y0 + i * SIZE, (LINE - 1) * SIZE + X0, Y0 + i * SIZE);
            g.drawLine(X0 + i * SIZE, Y0, X0 + i * SIZE, (LINE - 1) * SIZE + Y0);
        }
        for (int i = 0; i < drawListener.shapeArr.length; i++) {

            Shape shape = drawListener.shapeArr[i];
            if (shape != null) {
                shape.drawShape(g);
            } else {
                break;
            }
        }
        if(x>=0){
            point(g);
        }
    }
    //标注最后一颗棋子的位子
    public void point(Graphics g){
        Graphics2D g1=(Graphics2D)g;
        ((Graphics2D) g).setStroke(new BasicStroke(5));
        g.setColor(Color.RED);
        g.drawLine(x * SIZE + X0 , y * SIZE + Y0 , x * SIZE + X0, y * SIZE + Y0);
    }
}
java 复制代码
import java.awt.*;

public class Shape implements Conbig {
    public Color color;
    public int x, y, r;
    public MPanel centerPanel;

    public Shape(int x, int y, int r, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
        this.r = r;
    }
    //标注最后一颗棋子的位置


    public void drawShape(Graphics g) {

        if (color.equals(Color.BLACK)) {
            for (int i = 0; i < SIZE; i++) {
                g.setColor(new Color(i * 3, i * 3, i * 3));
                g.fillOval(x * SIZE + X0 - SIZE / 2 + i / 2, y * SIZE + Y0 - SIZE / 2 + i / 2, r - i, r - i);
            }
        } else if (color.equals(Color.WHITE)) {
            Graphics2D g3 = (Graphics2D) g;
            ((Graphics2D) g).setStroke(new BasicStroke(1));
            for (int i = 0; i < SIZE; i++) {
                g.setColor(new Color(170 + i * 2, 170 + i * 2, 170 + i * 2));
                g.fillOval(x * SIZE + X0 - SIZE / 2 + i / 2, y * SIZE + Y0 - SIZE / 2 + i / 2, r - i, r - i);
            }
        }
    }
}
java 复制代码
public interface Conbig {
    public int X0 = 50;
    public int Y0 = 36;
    public int LINE = 15;
    public int SIZE = 40;
}

效果

相关推荐
fs哆哆20 分钟前
在VB.net中一维数组,与VBA有什么区别
java·开发语言·数据结构·算法·.net
johnZhangqi25 分钟前
深圳大学-计算机信息管理课程实验 C++ 自考模拟题
java·开发语言·c++
David爱编程38 分钟前
并发编程三大特性全解析:原子性、可见性、有序性,一文讲透!
java·后端
Sally璐璐39 分钟前
Go语言变量声明与初始化详解
java·开发语言·golang
C4程序员2 小时前
北京JAVA基础面试30天打卡14
java·开发语言·面试
qq_589568104 小时前
javaweb开发笔记—— 前端工程化
java·前端
码农小灰4 小时前
Kafka消息持久化机制全解析:存储原理与实战场景
java·分布式·kafka
程序员鱼皮6 小时前
太香了!我连夜给项目加上了这套 Java 监控系统
java·前端·程序员
L2ncE7 小时前
高并发场景数据与一致性的简单思考
java·后端·架构