你是否想过,作为Java初学者就能开发出可运行的小游戏? 今天就带大家从零开始,拆解五子棋小游戏的实现过程,哪怕没有学过Java,跟着步骤走也能看懂原理哦~ 现在让我们正式开始学习如何实现五子棋小游戏吧
一、核心原理:五子棋需要哪些“零件”?
在写代码之前,我们先想清楚五子棋的基础功能:要有一个游戏窗口、能看到棋盘、能落黑白两色的棋子、判断谁赢了,还要有开始游戏/进行悔棋/重新开始等功能按钮以及计时计步等功能。对应到Java里,我们会用到Swing组件(做窗口和按钮)、Graphics类(画棋盘棋子)、数组(存棋子位置和判断输赢)……这些知识点在后面的教学中都会边做边进行讲解。
二、创建游戏窗口(窗体)
窗体是游戏的“容器”,就像装棋盘的盒子一样。Java里常用JFrame类来做窗口,下面一步步看代码怎么编写。
1.核心代码(GameUI类)
import javax.swing.*;
import java.awt.*;
public class GameUI {
//显示游戏界面的方法
public void initUI() {
//1.创建窗口对象
JFrame jf = new JFrame();
//2.设置窗口大小:宽500像素,长400像素
jf.setSize(500, 440);
//3.设置窗口标题
jf.setTitle("五子棋游戏");
//4.关闭窗口时退出程序
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//5.让窗口显示在屏幕的正中间
jf.setLocationRelativeTo(null);
//6.设置窗口布局
jf.setLayout(new BorderLayout());
//7.让窗口显示出来
jf.setVisible(true);
}
//程序入口(主函数)
public static void main(String[] args) {
GameUI ui = new GameUI();//创建GameUI对象
ui.initUI();//调用方法显示窗口
}
}
2.运行效果
此时运行代码,会弹出一个空白的窗口,这就是我们的游戏容器啦~
三、绘制棋盘和棋子
有了窗口之后,接下来就需要画棋盘(横线+竖线)和棋子(黑白色)。这里会用到 JPanel(画板)和 Graphics(画笔)。
1.先做棋盘:创建GamePanel类
棋盘要先画在“画板”上,JPanel 就是专门用来绘图的面板,我们需要重写它自带的 paint 方法(相当于在画板上画画的步骤)
核心代码(GamePanel类)
import javax.swing.*;
import.java.awt.*;
public class GamePanel extends JPanel {
//棋盘参数:X0/Y0是棋盘左上角坐标,SIZE是每格边长,LINE是棋盘行数(15列15行)
public int X0=25 , Y0=25 , SIZE=25 , LINE=15;
//关联监听器(用于获取棋子信息)
public GameListener listener;
//重写paint方法
public void paint(Graphics g ){
//1.保留父类功能(必须加,不然画板会乱)
super.paint(g);
//2.设置画笔颜色为黑色(用于画棋盘线条)
g.setColor(Color.BLACK);
//3.画棋盘线
for (int i=0;i<LINE;i++){
//4.画横线:从左到右,共LINE条
g.drawLine(X0,Y0+i*SIZE,(LINE-1)*SIZE+X0,Y0+i*SIZE);
//5.画竖线:从 上到下,共LINE条
g.drawLine(X0+i*SIZE,Y0,X0+i*SIZE,(LINE-1)*SIZE+Y0);
}
}
}
2.再画棋子:通过鼠标点击落子
棋子不是一开始就画好的,需要点击棋盘落子。这里就需要用到 MouseListener(鼠标监听器),监听鼠标的“点击”动作,然后在点击的位置画出棋子。
核心代码( GameListener 类:处理鼠标点击和棋子绘制)
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class GameLIstener implements MouseListener {
//棋子参数:CHESS是棋子的直径
public int X0 = 25 , Y0 = 25 , SIZE = 25 , LINE = 15 , CHESS = 15;
//画笔对象(用来画棋子)
public Graphics g;
//棋盘画板(关联到GamePanel,方便重绘)
private GamPanel panel;
//棋子计数器(判断黑白棋:偶数画黑棋,奇数画白棋)
public int count = 0;
//二维数组:存所有棋子信息
public int[][] chessArray = new int[LINE * LINE][3];
//二维数组:存棋盘落子情况
public int[][] broad = new int[LINE][LINE];
//控制是否可以下棋
private boolean canPlay = flase;
//构造方法:初始化属性
public GameListener(Graphics g, GamePanel panel) {
this.g = g;
this.panel = panel;
}
//设置是否可以下棋的方法
public void setCanPlay(boolean canPlay){
this.canPlay = canPlay;
}
//鼠标点击时执行的方法(控制落子)
public void mouseClicked(MouseEvent e) {
//没点“开始”就点击棋盘,弹出提示
if (!canPlay) {
JOptionPane.showMessageDialog(panel,"点击【开始游戏】开始对局!");
return;
}
//1.获取鼠标点击的坐标
int x = e.getX();
int y = e.getY();
//2.计算 棋子落在棋盘的哪个交叉点
//计算棋子X轴交点值
if ((x - X0) % SIZE > SIZE / 2) { //靠右
chessX = (x - X0) / SIZE + 1;
} else { //靠左
chessX = (x - X0) / SIZE;
}
//计算棋子Y轴交点值
if ((y - Y0) % SIZE > SIZE / 2) { //靠下
chessY = (y - Y0) / SIZE + 1;
} else { //靠上
chessY = (y - Y0) / SIZE;
}
//3.限制棋子在棋盘内
chessX = Math.min(LINE - 1, Math.max(0, chessX));
chessY = Math.min(LINE - 1, Math.max(0, chessY));
//4.防止重复落子
//判断当前位置是否已有棋子
if (board[chessX][chessY] != 0) {
return;//已有棋子则不处理
}
//5.记录棋子颜色(1=黑棋,2=白棋)
int colorType = count % 2;
int boardColor = colorType + 1;
board[chessX][chessY] = boardColor;
//6.保存棋子信息到数组
chessArray[count][0] = chessX;
chessArray[count][1] = chessY;
chessArray[count][2] = count % 2;
count++;//每落一子,计数器+1
//触发重绘
panel.repaint();
}
3.把棋盘和监听器加到窗口里
完成上面的步骤之后,就差最后一步就能正常下棋啦。我们需要回到 GameUI 的 initUI 方法,添加下列代码(将棋盘面板和监听器关联起来)
核心代码
//在jf.setLayout(new BorderLayout());后面添加下列代码
//1.创建棋盘面板
GamePanel chessPanel = new GamePanel();
chessPanel.setBackground(Color.ORANGE); //棋盘背景设为橙色
chessPanel.setPreferredSize(new Dimension(425,425); //棋盘大小
jf.add(chessPanel,BorderLayout.CENTER); //把棋盘放在窗口中间
//2.创建画笔对象(从棋盘面板获取对象)
Graphics g = chessPanel.getGraphics();
//3.创建监听器对象(把画笔和棋盘传递进去)
GameListener listener = new GameListener(g,chessPanel);
chessPanel.listener = listener; //让棋盘监听器
chessPanel.addMouseListener(listener); //给鼠标加鼠标监听器
4.重要知识点
二维数组
在五子棋游戏中,棋盘通常用二维数组表示。一维表示行数,二维表示列数。二维数组board相当于“棋盘地图”,用0、1、2标记每个位置是否有棋子,避免在同一个地方重复落子。
public int[][] board = new int[15][15]; // 15x15的棋盘
二维数组的遍历与棋子检测
通过嵌套循环遍历二维数组,可以检测棋子的位置所在或判断输赢:
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if (board[i][j] == 1) {
System.out.print(" 黑棋 "); // 打印黑子
} else if (board[i][j] == 2) {
System.out.print(" 白棋 "); // 打印白子
} else {
System.out.print(" 空位 "); // 空位
}
}
System.out.println();
}
5.运行效果
点击“开始游戏”之后,在棋盘上点击会落下黑白交替的棋子,且不能在同一位置重复落子
四、实现棋盘和棋子的重绘
为什么需要“重绘”呢?比如当用户在玩游戏时将窗口最小化后再打开,之前下的棋子会消失不见,甚至棋盘也会消失不见,这时候就需要重新把所有棋子画出来。这就是“重绘”了。
1.核心代码
import javax.swing.*;
import java.awt.*;
public class GamePanel extends JPanel {
public int X0=25 , Y0=25 , SIZE=25 , LINE=15;
public GameListener listener;
//重写paint方法
public void paint(Graphics g ){
//保留绘制组件的功能
super.paint(g);
//绘制棋盘
for (int i=0;i<LINE;i++){
g.setColor(Color.black);//黑色棋盘线
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);
}
//绘制所有棋子(实现交替重绘)
if (listener!=null){
int radius = listener.CHESS/2;
//遍历所有已落下的棋子(i从0到listener.count-1)
for (int i=0;i<listener.count;i++){
//从数组中获取棋子的坐标和颜色
int x = listener.chessArray[i][0];
int y = listener.chessArray[i][1];
int color = listener.chessArray[i][2];
//计算棋子绘制坐标
int drawX = X0 + x*SIZE - radius;
int drawY = Y0 + y*SIZE - radius;
if(color == 0){ //黑棋
g.setColor(Color.BLACK);
}else{ //白棋
g.setColor(Color.WHITE);
}
//黑棋填充
g.fillOval(drawX,drawY,listener.CHESS,listener.CHESS);
//白棋填充加黑框
if (color == 1){
g.setColor(Color.BLACK);
g.drawOval(drawX,drawY,listener.CHESS,listener.CHESS);
}
}
}
}
}
2.重要知识点
- 我们把所有棋子的信息存在 chessArray 数组里,重绘时只要遍历这个数组,把每个棋子重新画一遍就行。
- fillOval 是画填充圆,drawOval 是画圆边框,白棋加黑边框是为了和橙色棋盘进行区分,使棋子颜色更为明显。
五、用二维数据判断游戏的输赢
我们都知道五子棋获胜的条件是“同一颜色的棋子在横、竖、斜四个方向中,有任意一个方向连成5颗”。在这里我们可以用二维数组遍历来判断,即每次落子之后,检查落子位置的四个方向(水平、竖直、两个对角线)有没有连续5颗相同颜色的棋子。
1.核心代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class GameLIstener implements MouseListener {
//棋子参数:CHESS是棋子的直径
public int X0 = 25 , Y0 = 25 , SIZE = 25 , LINE = 15 , CHESS = 15;
//画笔对象(用来画棋子)
public Graphics g;
//棋盘画板(关联到GamePanel,方便重绘)
private GamPanel panel;
//棋子计数器(判断黑白棋:偶数画黑棋,奇数画白棋)
public int count = 0;
//二维数组:存所有棋子信息
public int[][] chessArray = new int[LINE * LINE][3];
//二维数组:存棋盘落子情况
public int[][] broad = new int[LINE][LINE];
//控制是否可以下棋
private boolean canPlay = flase;
//构造方法:初始化属性
public GameListener(Graphics g, GamePanel panel) {
this.g = g;
this.panel = panel;
}
//设置是否可以下棋的方法
public void setCanPlay(boolean canPlay){
this.canPlay = canPlay;
}
//鼠标点击时执行的方法(控制落子)
public void mouseClicked(MouseEvent e) {
//没点“开始”就点击棋盘,弹出提示
if (!canPlay) {
JOptionPane.showMessageDialog(panel,"点击【开始游戏】开始对局!");
return;
}
//1.获取鼠标点击的坐标
int x = e.getX();
int y = e.getY();
//2.计算 棋子落在棋盘的哪个交叉点
//计算棋子X轴交点值
if ((x - X0) % SIZE > SIZE / 2) { //靠右
chessX = (x - X0) / SIZE + 1;
} else { //靠左
chessX = (x - X0) / SIZE;
}
//计算棋子Y轴交点值
if ((y - Y0) % SIZE > SIZE / 2) { //靠下
chessY = (y - Y0) / SIZE + 1;
} else { //靠上
chessY = (y - Y0) / SIZE;
}
//3.限制棋子在棋盘内
chessX = Math.min(LINE - 1, Math.max(0, chessX));
chessY = Math.min(LINE - 1, Math.max(0, chessY));
//4.防止重复落子
//判断当前位置是否已有棋子
if (board[chessX][chessY] != 0) {
return;//已有棋子则不处理
}
//5.记录棋子颜色(1=黑棋,2=白棋)
int colorType = count % 2;
int boardColor = colorType + 1;
board[chessX][chessY] = boardColor;
//6.保存棋子信息到数组
chessArray[count][0] = chessX;
chessArray[count][1] = chessY;
chessArray[count][2] = count % 2;
count++;//每落一子,计数器+1
//7.判断是否赢了
if (checkWin(chessX, chessY, boardColor)) {
String winner = (colorType == 0) ? "黑棋" : "白棋";
//获胜后不让继续下棋
canPlay = false;
//获胜后弹出窗口提示
JOptionPane.showMessageDialog(panel,(winner + "五子连珠,取得胜利!" ));
isGameOver = true;
}
//触发重绘
panel.repaint();
}
//判断输赢
private boolean checkWin(int x, int y, int color) {
//方向数组:水平,竖直,两个对角线
int[][] dirs = {
{1, 0}, //向右检查
{0, 1}, //向下检查
{1, 1}, //向右下检查
{1, -1} //向左下检查
};
//遍历每个检查方向
for (int[] dir : dirs) {
int sameCount = 1;//当前位置已经有1颗目标颜色棋子,初始为1
int dx = dir[0];//x方向每次走几步
int dy = dir[1];//y方向每次走几步
//1.正向检查(顺着当前方向走,最多查4步,因为起始1颗共五颗)
for (int i = 1; i < 5; i++) {
int nx = x + dx * i;//新的x坐标
int ny = y + dy * i;//新的y坐标
//检查新坐标是否在棋盘内,且棋子颜色相同
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;//超出棋盘,停止检查
}
if (board[nx][ny] != color) {
break;//颜色不同,停止检查
}
sameCount++;//颜色相同,计数+1
}
//2.反向检查(逆着当前方向走,最多查4步)
for (int i = 1; i < 5; i++) {
int nx = x - dx * i;
int ny = y - dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//3.每个方向检查完后,立即判断是否连成5颗
if (sameCount >= 5) {
return true;
}
}
//所有方向都没有到5颗,则没有获胜
return false;
}
}
六、功能按钮的添加
上面实现了基本的五子棋基本功能,在上面所述的基础上可以进一步完善五子棋游戏的功能。我们可以知道五子棋游戏需要3个关键按钮:开始游戏、重新开始、进行悔棋。
1.把按钮“放”到界面上
要让按钮显示在屏幕上,需要先给它们在窗体上安排位置,这就需要用到 Java Swing 的布局管理器。这里需要用到两种布局管理器(具体运用可参考【Java编程入门:Java Swing简易画图工具的优化指南(1)和(2)】
- BorderLayout(边框布局):把整个游戏窗口分为“东、西、南、北、中”五个部分,我们把“棋盘”放在中间(CENTER),“按钮面板”放在右边(EAST)。
- FlowLayout(流式布局):让按钮在面板里居中排列,还可以设置按钮之间的间距,使按钮与按钮之间看起来更整齐。
核心代码
//在GameUI的initUI中添加
//1.创建按钮面板
JPanel buttonPanel = new JPanel();
//将面板改为橙色
buttonPanel.setBackground(Color.ORANGE);
//按钮面板里的内容垂直排列
buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.Y_AXIS));
buttonPanel.setPreferredSize(new Dimension(100,0));
//2.创建一个子面板专门放置按钮,保持按钮原有居中排列样式
JPanel btnSubPanel = new JPanel();
//将面板统一颜色
btnSubPanel.setBackground(Color.ORANGE);
//居中排列,上下间距为10
btnSubPanel.setLayout(new FlowLayout(FlowLayout.CENTER,5,10));
btnSubPanel.setPreferredSize(new Dimension(100,50));
//3.创建三个按钮,设置文字和大小
//“开始游戏”按钮
JButton startBtn = new JButton("开始游戏");
startBtn.setPreferredSize(new Dimension(90,30));
//“进行悔棋”按钮
JButton undoBtn = new JButton("进行悔棋");
undoBtn.setPreferredSize(new Dimension(90,30));
//“重新开始”按钮
JButton restartBtn = new JButton("重新开始");
restartBtn.setPreferredSize(new Dimension(90,30));
//4.把按钮放置在子面板,再将子面板放置在右边的大面板里
btnSubPanel.add(startBtn);
btnSubPanel.add(restartBtn);
btnSubPanel.add(undoBtn);
//将按键子面板和计时框添加到总按钮面板上(垂直排列)
buttonPanel.add(btnSubPanel);
buttonPanel.add(Box.createHorizontalStrut(1));//添加垂直间距,让按钮与计时框之间有空隙
//5.将按钮面板放置在主窗体上
jf.add(buttonPanel,BorderLayout.EAST);
2.给按钮添加动作监听器
根据上面的操作可以使按钮正常显示出来,但要使其达到我们的目的,就需要添加动作监听器,使用户点击按钮之后就执行对应的操作。
Java中用 addActionListener 实现监听,点击时触发 actionPerformed 方法里的代码。
(1)“开始游戏”按钮
点击“开始游戏”之后,可以实现的目的:一是允许玩家进行游戏,二是启动计时器(后面进行补充)。
//在GameUI的jf.add(buttonPanel,BorderLayout.EAST);后面补充添加
//开始按钮
startBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//1.告诉程序可以开始运行(listener是控制下棋逻辑的对象)
listener.setCanPlay(true);
//2.如果计时器没有启动,就对其进行启动(避免重复计时)
if (!timeUpdateTimer.isRunning()){
timeUpdateTimer.start();
}
}
});
重要知识点:
setCanPlay(true) 就像按钮的开关,如果没点这个按钮就点棋盘的话,程序无法正常运行,会提示用户点击开始游戏按钮之后才能开始游戏(后面会对其进行补充)。
(2)“进行悔棋”按钮
点击“进行悔棋”按钮之后,可以实现的目的:判断有没有棋子可以悔、确认用户是否要进行悔棋、以及清空最后一步的记录并重绘棋盘。
//悔棋按钮事件
undoBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//调用listener里的悔棋方法
listener.undoLastStep();
//重新绘制棋盘,让撤回的棋子清空
chessPanel.repaint();
}
});
这里要重点看 undoLastStep() 方法里的代码(在 GameListener 类中)
//在 GameListener的public void mouseClicked(MouseEvent e)后面进行补充
//悔棋功能
public void undoLastStep() {
//1.检查是否能悔棋,如果还没有下过棋,就弹出提示“无棋可悔”
if (count <= 0) {
//如果已经没有棋可以悔,弹出提示
JOptionPane.showMessageDialog(null, "没有可悔的棋了哦~");
return;
}
//2.显示悔棋确认弹窗,弹出确认框,让用户进行确认(避免误选)
int choice = JOptionPane.showConfirmDialog(null, "是否悔棋?", "确认悔棋", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
//用户选择"否"时,就不进行悔棋
if (choice == JOptionPane.NO_OPTION) {
return;
}
//3.减少总步数:将最后一步的记录清空
//棋子计数器减1
count--;
//步数减1
stepCount--;
//获取最后一步棋的坐标
int lastX = chessArray[count][0];//获取x坐标
int lastY = chessArray[count][1];//获取y坐标
//清空最后一步记录
board[lastX][lastY] = 0;
chessArray[count][0] = 0;
chessArray[count][1] = 0;
chessArray[count][2] = 0;
//4.提示悔棋成功,指明下一个该是谁下
String nextPlayer = (count % 2 == 0) ? "黑方" : "白方";
JOptionPane.showMessageDialog(null, "悔棋成功,请" + nextPlayer + "继续下棋");
//确保游戏状态为未结束
isGameOver = false;
//重绘棋盘
panel.repaint();
}
重要知识点
- JOptionPane 类 :使用此类可以弹出提示框、确认框,不用自己写弹窗,直接调用现成的方法。这样使代码更为简洁与方便。
- %:此符号代表取余的意思,可以用于判断多个项目的选择。如:当面临悔棋后需判断该下黑棋还是白棋时可以使用 count %2 (count是棋子的总数,偶数时下黑棋,奇数时下白棋)。
(3)“重新开始”按钮
点击“重新开始”按钮之后,可以实现的目的:把所有数据全部恢复到初始值,在点击“开始游戏”按钮之后才能继续游戏。
//重新开始按钮
restartBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//调用listener里的重置方法,清空棋盘和所有步数
listener.restartGame();
//重新绘制棋盘,去掉所有棋子
chessPanel.repaint();
//重置计时框显示为初始状态
timeLabel.setText("当前用时:0分0秒");
//停止计时更新定时器
timeUpdateTimer.stop();
}
});
restartGame() 方法的核心逻辑(GameListener 类中)
//重新开始功能
public void restartGame() {
//1.清空棋盘:把棋盘数组里的所有位置设为0
for (int i = 0; i < LINE; i++) {
for (int j = 0; j < LINE; j++) {
board[i][j] = 0;
}
}
//2.清空棋子记录
count = 0;
//清空步数
stepCount = 0;
//3.游戏结束判断
isGameOver = false;
//重新开始时设置为不可下棋状态
canPlay = false;
//4.停止计时器
timer.stop();
panel.repaint();
}
七、计时器和计步器的实现
在游戏过程中,用户需要知道对局时长多久以及下了多少步棋子,进而思考下一步棋应该怎么下。所以计时器与计步器的实现在极大程度上能够提升玩家的游戏体验。我们要实现的两个功能:实时显示用时、记录总步数。
1.计时器
计时器的核心是 Timer 类,它能做到每隔1秒,就让总时长加1秒,再更新界面上的时间。
(1)创建计时器和时间显示框
首先,需要在界面上创建一个时间显示框( JLabel ),再创建一个 Timer ,让其能够实时更新时间
//在GameUI类的btnSubPanel.add(undoBtn);添加
//1.创建时间显示框
timeLabel = new JLabel("当前用时:0分0秒");
//设置文字居中
timeLabel.setHorizontalAlignment(SwingConstants.CENTER);
//设置背景颜色
timeLabel.setBackground(Color.WHITE);
//让计时框支持显示颜色
timeLabel.setOpaque(true);
//垂直方向居中对齐
timeLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
//2.创建计时器:1000毫秒触发1次
//1秒触发一次,实现实时效果
Timer timeUpdateTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int totalSec = listener.totalSeconds;
//计算分钟和秒
int minute = totalSec / 60;
int second = totalSec % 60;
//更新计时框文本,显示实时数据
timeLabel.setText("当前用时:" + minute + "分" + second + "秒");
}
});
//3.把时间显示框放到右边的按钮面板上
buttonPanel.add(timeLabel);
(2)控制计时器的启动与停止
- 点击“开始游戏”按钮:计时开始,且不重复计时
- 点击“重新开始”按钮:计时暂停,并将时间显示框中文字恢复为“0分0秒”
- 有一方获胜:计时暂停,显示获胜总时长
(3)totalSeconds 变量
totalSeconds 是 GameListener 里的变量,用来记录总秒数。
public class GameListener extends MouseAdapter{
//棋子数据
public int X0 = 25, Y0 = 25, SIZE = 25, LINE = 15, CHESS = 15;
//保存传递过来的画笔对象
public Graphics g;
//保存当前棋子在棋盘上的交点值
public int chessX, chessY;
//棋子计数器
public int count = 0;
//用二维数组保留所有棋子信息
public int[][] chessArray = new int[LINE * LINE][3];
//用二维数组记录落子情况:0=空,1=黑棋,2=白棋
public int[][] board = new int[LINE][LINE];
//关联棋盘画板
private GamePanel panel;
//标记游戏是否结束
private boolean isGameOver = false;
//控制是否可以下棋
private boolean canPlay = false;
//计时器(每秒走1次)
private Timer timer;
//总用时(单位:秒)
public int totalSeconds = 0;
//总步数(每下一颗棋+1)
public int stepCount = 0;
//构造方法初始化属性
public GameListener(Graphics g, GamePanel panel) {
this.g = g;
this.panel = panel;
//初始计时器(1000毫秒 = 1秒,每秒触发1次事件)
timer = new Timer(1000,e -> totalSeconds++);
}
//设置是否可以下棋的方法
public void setCanPlay(boolean canPlay){
this.canPlay = canPlay;
//开始游戏是启动计时器(避免重复多次计时)
if(canPlay && !timer.isRunning()){
timer.start();
}
}
//点击鼠标落子
public void mouseClicked(MouseEvent e) {
//未点击开始按钮,则不处理点击
if (!canPlay) {
JOptionPane.showMessageDialog(panel,"点击【开始游戏】开始对局!");
return;
}
//如果游戏已结束,则不处理点击
if (isGameOver){
JOptionPane.showMessageDialog(panel,"对局已结束,请重新开始哦!");
return;
}
System.out.println("点击!!");
int x = e.getX();
int y = e.getY();
//计算棋子X轴交点值
if ((x - X0) % SIZE > SIZE / 2) { //靠右
chessX = (x - X0) / SIZE + 1;
} else { //靠左
chessX = (x - X0) / SIZE;
}
//计算棋子Y轴交点值
if ((y - Y0) % SIZE > SIZE / 2) { //靠下
chessY = (y - Y0) / SIZE + 1;
} else { //靠上
chessY = (y - Y0) / SIZE;
}
//限制在棋盘范围内
chessX = Math.min(LINE - 1, Math.max(0, chessX));
chessY = Math.min(LINE - 1, Math.max(0, chessY));
//判断当前位置是否已有棋子
if (board[chessX][chessY] != 0) {
return;//已有棋子则不处理
}
//记录棋子颜色(1=黑棋,2=白棋)
int colorType = count % 2;
int boardColor = colorType + 1;
board[chessX][chessY] = boardColor;
//保存棋子信息
chessArray[count][0] = chessX;
chessArray[count][1] = chessY;
chessArray[count][2] = count % 2;
count++;
stepCount++;
//判断是否赢了
if (checkWin(chessX, chessY, boardColor)) {
String winner = (colorType == 0) ? "黑棋" : "白棋";
//获胜后停止计时
timer.stop();
//获胜后不让继续下棋
canPlay = false;
//计算用时
int minute = totalSeconds / 60 ;
int second = totalSeconds % 60 ;
String timeStr = minute + "分" + second + "秒";
//弹出窗口显示胜负、步数、用时
JOptionPane.showMessageDialog(panel,(winner + "五子连珠,取得胜利!" + "总共走了:"+ stepCount + "步!" +"总用时" + timeStr));
isGameOver = true;
}
//触发重绘
panel.repaint();
}
//悔棋功能
public void undoLastStep() {
//检查是否能悔棋
if (count <= 0) {
//如果已经没有棋可以悔,弹出提示
JOptionPane.showMessageDialog(null, "没有可悔的棋了哦~");
return;
}
//显示悔棋确认弹窗
int choice = JOptionPane.showConfirmDialog(null, "是否悔棋?", "确认悔棋", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
//用户选择"否"时才返回
if (choice == JOptionPane.NO_OPTION) {
return;
}
//减少总步数
count--;
stepCount--;
//获取最后一步棋的坐标
int lastX = chessArray[count][0];//获取x坐标
int lastY = chessArray[count][1];//获取y坐标
//清空最后一步记录
board[lastX][lastY] = 0;
chessArray[count][0] = 0;
chessArray[count][1] = 0;
chessArray[count][2] = 0;
//提示悔棋成功,指明下一方
String nextPlayer = (count % 2 == 0) ? "黑方" : "白方";
JOptionPane.showMessageDialog(null, "悔棋成功,请" + nextPlayer + "继续下棋");
//确保游戏状态为未结束
isGameOver = false;
//重绘棋盘
panel.repaint();
}
//重新开始功能
public void restartGame() {
//清空棋盘
for (int i = 0; i < LINE; i++) {
for (int j = 0; j < LINE; j++) {
board[i][j] = 0;
}
}
//清空棋子记录
count = 0;
//步数重置
stepCount = 0;
//游戏结束判断
isGameOver = false;
//重新开始时设置为不可下棋状态
canPlay = false;
//停止计时器
timer.stop();
panel.repaint();
}
//判断输赢
private boolean checkWin(int x, int y, int color) {
//方向数组:水平,竖直,两个对角线
int[][] dirs = {
{1, 0}, //向右检查
{0, 1}, //向下检查
{1, 1}, //向右下检查
{1, -1} //向左下检查
};
//遍历每个检查方向
for (int[] dir : dirs) {
int sameCount = 1;//当前位置已经有1颗目标颜色棋子,初始为1
int dx = dir[0];//x方向增量
int dy = dir[1];//y方向增量
//1.正向检查(顺着当前方向走,最多查4步,因为起始1颗共五颗)
for (int i = 1; i < 5; i++) {
int nx = x + dx * i;
int ny = y + dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//2.反向检查(逆着当前方向走,最多查4步)
for (int i = 1; i < 5; i++) {
int nx = x - dx * i;
int ny = y - dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//3.每个方向检查完后,立即判断是否连成5颗
if (sameCount >= 5) {
return true;
}
}
return false;
}
}
(4)重要知识点
e -> totalSeconds++ 是Java中的 Lambda 表达式。表达式结构:(参数)-> {表达式或代码块}。此表达式的意思是“每次计时器触发时,就执行 totalSeconds 加1”,这样比写完整方法更为简洁。
2.计步器
计步器的原理在于每成功下出一颗棋子,就让步数加1,最终在获胜时显示出来。
核心代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GameListener extends MouseAdapter{
//棋子数据
public int X0 = 25, Y0 = 25, SIZE = 25, LINE = 15, CHESS = 15;
//保存传递过来的画笔对象
public Graphics g;
//保存当前棋子在棋盘上的交点值
public int chessX, chessY;
//棋子计数器
public int count = 0;
//用二维数组保留所有棋子信息
public int[][] chessArray = new int[LINE * LINE][3];
//用二维数组记录落子情况:0=空,1=黑棋,2=白棋
public int[][] board = new int[LINE][LINE];
//关联棋盘画板
private GamePanel panel;
//标记游戏是否结束
private boolean isGameOver = false;
//控制是否可以下棋
private boolean canPlay = false;
//计时器(每秒走1次)
private Timer timer;
//总用时(单位:秒)
public int totalSeconds = 0;
//总步数(每下一颗棋+1)
public int stepCount = 0;
//构造方法初始化属性
public GameListener(Graphics g, GamePanel panel) {
this.g = g;
this.panel = panel;
//初始计时器(1000毫秒 = 1秒,每秒触发1次事件)
timer = new Timer(1000,e -> totalSeconds++);
}
//设置是否可以下棋的方法
public void setCanPlay(boolean canPlay){
this.canPlay = canPlay;
//开始游戏是启动计时器(避免重复多次计时)
if(canPlay && !timer.isRunning()){
timer.start();
}
}
//点击鼠标落子
public void mouseClicked(MouseEvent e) {
//未点击开始按钮,则不处理点击
if (!canPlay) {
JOptionPane.showMessageDialog(panel,"点击【开始游戏】开始对局!");
return;
}
//如果游戏已结束,则不处理点击
if (isGameOver){
JOptionPane.showMessageDialog(panel,"对局已结束,请重新开始哦!");
return;
}
System.out.println("点击!!");
int x = e.getX();
int y = e.getY();
//计算棋子X轴交点值
if ((x - X0) % SIZE > SIZE / 2) { //靠右
chessX = (x - X0) / SIZE + 1;
} else { //靠左
chessX = (x - X0) / SIZE;
}
//计算棋子Y轴交点值
if ((y - Y0) % SIZE > SIZE / 2) { //靠下
chessY = (y - Y0) / SIZE + 1;
} else { //靠上
chessY = (y - Y0) / SIZE;
}
//限制在棋盘范围内
chessX = Math.min(LINE - 1, Math.max(0, chessX));
chessY = Math.min(LINE - 1, Math.max(0, chessY));
//判断当前位置是否已有棋子
if (board[chessX][chessY] != 0) {
return;//已有棋子则不处理
}
//记录棋子颜色(1=黑棋,2=白棋)
int colorType = count % 2;
int boardColor = colorType + 1;
board[chessX][chessY] = boardColor;
//保存棋子信息
chessArray[count][0] = chessX;
chessArray[count][1] = chessY;
chessArray[count][2] = count % 2;
count++;
stepCount++;
//判断是否赢了
if (checkWin(chessX, chessY, boardColor)) {
String winner = (colorType == 0) ? "黑棋" : "白棋";
//获胜后停止计时
timer.stop();
//获胜后不让继续下棋
canPlay = false;
//计算用时
int minute = totalSeconds / 60 ;
int second = totalSeconds % 60 ;
String timeStr = minute + "分" + second + "秒";
//弹出窗口显示胜负、步数、用时
JOptionPane.showMessageDialog(panel,(winner + "五子连珠,取得胜利!" + "总共走了:"+ stepCount + "步!" +"总用时" + timeStr));
isGameOver = true;
}
//触发重绘
panel.repaint();
}
//悔棋功能
public void undoLastStep() {
//检查是否能悔棋
if (count <= 0) {
//如果已经没有棋可以悔,弹出提示
JOptionPane.showMessageDialog(null, "没有可悔的棋了哦~");
return;
}
//显示悔棋确认弹窗
int choice = JOptionPane.showConfirmDialog(null, "是否悔棋?", "确认悔棋", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
//用户选择"否"时才返回
if (choice == JOptionPane.NO_OPTION) {
return;
}
//减少总步数
count--;
stepCount--;
//获取最后一步棋的坐标
int lastX = chessArray[count][0];//获取x坐标
int lastY = chessArray[count][1];//获取y坐标
//清空最后一步记录
board[lastX][lastY] = 0;
chessArray[count][0] = 0;
chessArray[count][1] = 0;
chessArray[count][2] = 0;
//提示悔棋成功,指明下一方
String nextPlayer = (count % 2 == 0) ? "黑方" : "白方";
JOptionPane.showMessageDialog(null, "悔棋成功,请" + nextPlayer + "继续下棋");
//确保游戏状态为未结束
isGameOver = false;
//重绘棋盘
panel.repaint();
}
//重新开始功能
public void restartGame() {
//清空棋盘
for (int i = 0; i < LINE; i++) {
for (int j = 0; j < LINE; j++) {
board[i][j] = 0;
}
}
//清空棋子记录
count = 0;
//步数重置
stepCount = 0;
//游戏结束判断
isGameOver = false;
//重新开始时设置为不可下棋状态
canPlay = false;
//停止计时器
timer.stop();
panel.repaint();
}
//判断输赢
private boolean checkWin(int x, int y, int color) {
//方向数组:水平,竖直,两个对角线
int[][] dirs = {
{1, 0}, //向右检查
{0, 1}, //向下检查
{1, 1}, //向右下检查
{1, -1} //向左下检查
};
//遍历每个检查方向
for (int[] dir : dirs) {
int sameCount = 1;//当前位置已经有1颗目标颜色棋子,初始为1
int dx = dir[0];//x方向增量
int dy = dir[1];//y方向增量
//1.正向检查(顺着当前方向走,最多查4步,因为起始1颗共五颗)
for (int i = 1; i < 5; i++) {
int nx = x + dx * i;
int ny = y + dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//2.反向检查(逆着当前方向走,最多查4步)
for (int i = 1; i < 5; i++) {
int nx = x - dx * i;
int ny = y - dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//3.每个方向检查完后,立即判断是否连成5颗
if (sameCount >= 5) {
return true;
}
}
return false;
}
}
八、完整代码展示
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class GameUI {
//计时框组件,用于显示实时时间
private JLabel timeLabel;
//保存GameListener 对象,用于访问totalSeconds
private jh0826.GameListener listener;
//显示游戏界面
public void initUI() {
//JFrame 默认是边框布局
JFrame jf = new JFrame();
jf.setSize(500, 440);
jf.setTitle("五子棋游戏");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
jf.setLayout(new BorderLayout());
//创建棋盘面板
GamePanel chessPanel = new GamePanel();
chessPanel.setBackground(Color.ORANGE);
chessPanel.setPreferredSize(new Dimension(425,425));
jf.add(chessPanel,BorderLayout.CENTER);
//创建按钮面板
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(Color.ORANGE);
buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.Y_AXIS));
buttonPanel.setPreferredSize(new Dimension(100,0));
//创建一个子面板专门放按钮,保持按钮原有居中排列样式
JPanel btnSubPanel = new JPanel();
btnSubPanel.setBackground(Color.ORANGE);
btnSubPanel.setLayout(new FlowLayout(FlowLayout.CENTER,5,10));
btnSubPanel.setPreferredSize(new Dimension(100,50));
//开始按钮
JButton startBtn = new JButton("开始游戏");
startBtn.setPreferredSize(new Dimension(90,30));
//悔棋按钮
JButton undoBtn = new JButton("进行悔棋");
undoBtn.setPreferredSize(new Dimension(90,30));
//重新开始按钮
JButton restartBtn = new JButton("重新开始");
restartBtn.setPreferredSize(new Dimension(90,30));
//按钮添加到按钮子面板
btnSubPanel.add(startBtn);
btnSubPanel.add(restartBtn);
btnSubPanel.add(undoBtn);
//计时框
//设置初始显示文本
timeLabel = new JLabel("当前用时:0分0秒");
//设置文字居中
timeLabel.setHorizontalAlignment(SwingConstants.CENTER);
//设置背景颜色
timeLabel.setBackground(Color.WHITE);
//让计时框支持显示颜色
timeLabel.setOpaque(true);
//垂直方向居中对齐
timeLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
//将按键子面板和计时框添加到总按钮面板上(垂直排列)
buttonPanel.add(btnSubPanel);
buttonPanel.add(Box.createHorizontalStrut(1));//添加垂直间距,让按钮与计时框之间有空隙
buttonPanel.add(timeLabel);
//按钮面板添加到主窗体
jf.add(buttonPanel,BorderLayout.EAST);
//获取画笔对象
Graphics g = chessPanel.getGraphics();
this.listener = new GameListener(g,chessPanel);
//GameListener listener = new GameListener(g,chessPanel);
chessPanel.listener = listener;
chessPanel.addMouseListener(this.listener);
//实时更新定时器
//1秒触发一次,实现实时效果
Timer timeUpdateTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int totalSec = listener.totalSeconds;
//计算分钟和秒
int minute = totalSec / 60;
int second = totalSec % 60;
//更新计时框文本,显示实时数据
timeLabel.setText("当前用时:" + minute + "分" + second + "秒");
}
});
//开始按钮
startBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.setCanPlay(true);
//启动计时更新定时器
if (!timeUpdateTimer.isRunning()){
timeUpdateTimer.start();
}
}
});
//悔棋按钮事件
undoBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.undoLastStep();
chessPanel.repaint();
}
});
//重新开始按钮
restartBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.restartGame();
chessPanel.repaint();
//重置计时框显示为初始状态
timeLabel.setText("当前用时:0分0秒");
//停止计时更新定时器
timeUpdateTimer.stop();
}
});
//设置可见
jf.setVisible(true);
}
public static void main(String[] args) {
GameUI ui = new GameUI();
ui.initUI();
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GameListener extends MouseAdapter{
//棋子数据
public int X0 = 25, Y0 = 25, SIZE = 25, LINE = 15, CHESS = 15;
//保存传递过来的画笔对象
public Graphics g;
//保存当前棋子在棋盘上的交点值
public int chessX, chessY;
//棋子计数器
public int count = 0;
//用二维数组保留所有棋子信息
public int[][] chessArray = new int[LINE * LINE][3];
//用二维数组记录落子情况:0=空,1=黑棋,2=白棋
public int[][] board = new int[LINE][LINE];
//关联棋盘画板
private GamePanel panel;
//标记游戏是否结束
private boolean isGameOver = false;
//控制是否可以下棋
private boolean canPlay = false;
//计时器(每秒走1次)
private Timer timer;
//总用时(单位:秒)
public int totalSeconds = 0;
//总步数(每下一颗棋+1)
public int stepCount = 0;
//构造方法初始化属性
public GameListener(Graphics g, GamePanel panel) {
this.g = g;
this.panel = panel;
//初始计时器(1000毫秒 = 1秒,每秒触发1次事件)
timer = new Timer(1000,e -> totalSeconds++);
}
//设置是否可以下棋的方法
public void setCanPlay(boolean canPlay){
this.canPlay = canPlay;
//开始游戏是启动计时器(避免重复多次计时)
if(canPlay && !timer.isRunning()){
timer.start();
}
}
//点击鼠标落子
public void mouseClicked(MouseEvent e) {
//未点击开始按钮,则不处理点击
if (!canPlay) {
JOptionPane.showMessageDialog(panel,"点击【开始游戏】开始对局!");
return;
}
//如果游戏已结束,则不处理点击
if (isGameOver){
JOptionPane.showMessageDialog(panel,"对局已结束,请重新开始哦!");
return;
}
System.out.println("点击!!");
int x = e.getX();
int y = e.getY();
//计算棋子X轴交点值
if ((x - X0) % SIZE > SIZE / 2) { //靠右
chessX = (x - X0) / SIZE + 1;
} else { //靠左
chessX = (x - X0) / SIZE;
}
//计算棋子Y轴交点值
if ((y - Y0) % SIZE > SIZE / 2) { //靠下
chessY = (y - Y0) / SIZE + 1;
} else { //靠上
chessY = (y - Y0) / SIZE;
}
//限制在棋盘范围内
chessX = Math.min(LINE - 1, Math.max(0, chessX));
chessY = Math.min(LINE - 1, Math.max(0, chessY));
//判断当前位置是否已有棋子
if (board[chessX][chessY] != 0) {
return;//已有棋子则不处理
}
//记录棋子颜色(1=黑棋,2=白棋)
int colorType = count % 2;
int boardColor = colorType + 1;
board[chessX][chessY] = boardColor;
//保存棋子信息
chessArray[count][0] = chessX;
chessArray[count][1] = chessY;
chessArray[count][2] = count % 2;
count++;
stepCount++;
//判断是否赢了
if (checkWin(chessX, chessY, boardColor)) {
String winner = (colorType == 0) ? "黑棋" : "白棋";
//获胜后停止计时
timer.stop();
//获胜后不让继续下棋
canPlay = false;
//计算用时
int minute = totalSeconds / 60 ;
int second = totalSeconds % 60 ;
String timeStr = minute + "分" + second + "秒";
//弹出窗口显示胜负、步数、用时
JOptionPane.showMessageDialog(panel,(winner + "五子连珠,取得胜利!" + "总共走了:"+ stepCount + "步!" +"总用时" + timeStr));
isGameOver = true;
}
//触发重绘
panel.repaint();
}
//悔棋功能
public void undoLastStep() {
//检查是否能悔棋
if (count <= 0) {
//如果已经没有棋可以悔,弹出提示
JOptionPane.showMessageDialog(null, "没有可悔的棋了哦~");
return;
}
//显示悔棋确认弹窗
int choice = JOptionPane.showConfirmDialog(null, "是否悔棋?", "确认悔棋", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
//用户选择"否"时才返回
if (choice == JOptionPane.NO_OPTION) {
return;
}
//减少总步数
count--;
stepCount--;
//获取最后一步棋的坐标
int lastX = chessArray[count][0];//获取x坐标
int lastY = chessArray[count][1];//获取y坐标
//清空最后一步记录
board[lastX][lastY] = 0;
chessArray[count][0] = 0;
chessArray[count][1] = 0;
chessArray[count][2] = 0;
//提示悔棋成功,指明下一方
String nextPlayer = (count % 2 == 0) ? "黑方" : "白方";
JOptionPane.showMessageDialog(null, "悔棋成功,请" + nextPlayer + "继续下棋");
//确保游戏状态为未结束
isGameOver = false;
//重绘棋盘
panel.repaint();
}
//重新开始功能
public void restartGame() {
//清空棋盘
for (int i = 0; i < LINE; i++) {
for (int j = 0; j < LINE; j++) {
board[i][j] = 0;
}
}
//清空棋子记录
count = 0;
//步数重置
stepCount = 0;
//游戏结束判断
isGameOver = false;
//重新开始时设置为不可下棋状态
canPlay = false;
//停止计时器
timer.stop();
panel.repaint();
}
//判断输赢
private boolean checkWin(int x, int y, int color) {
//方向数组:水平,竖直,两个对角线
int[][] dirs = {
{1, 0}, //向右检查
{0, 1}, //向下检查
{1, 1}, //向右下检查
{1, -1} //向左下检查
};
//遍历每个检查方向
for (int[] dir : dirs) {
int sameCount = 1;//当前位置已经有1颗目标颜色棋子,初始为1
int dx = dir[0];//x方向增量
int dy = dir[1];//y方向增量
//1.正向检查(顺着当前方向走,最多查4步,因为起始1颗共五颗)
for (int i = 1; i < 5; i++) {
int nx = x + dx * i;
int ny = y + dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//2.反向检查(逆着当前方向走,最多查4步)
for (int i = 1; i < 5; i++) {
int nx = x - dx * i;
int ny = y - dy * i;
if (nx < 0 || nx >= LINE || ny < 0 || ny >= LINE) {
break;
}
if (board[nx][ny] != color) {
break;
}
sameCount++;
}
//3.每个方向检查完后,立即判断是否连成5颗
if (sameCount >= 5) {
return true;
}
}
return false;
}
}
import javax.swing.*;
import java.awt.*;
public class GamePanel extends JPanel {
public int X0=25 , Y0=25 , SIZE=25 , LINE=15;
public GameListener listener;
//重写paint方法
public void paint(Graphics g ){
//保留绘制组件的功能
super.paint(g);
//绘制棋盘
for (int i=0;i<LINE;i++){
g.setColor(Color.black);//黑色棋盘线
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);
}
//绘制所有棋子(实现交替重绘)
if (listener!=null){
int radius = listener.CHESS/2;
for (int i=0;i<listener.count;i++){
int x = listener.chessArray[i][0];
int y = listener.chessArray[i][1];
int color = listener.chessArray[i][2];
//计算棋子绘制坐标
int drawX = X0 + x*SIZE - radius;
int drawY = Y0 + y*SIZE - radius;
if(color == 0){ //黑棋
//g.setColor(Color.BLACK);
GradientPaint black3d = new GradientPaint(drawX,drawY,new Color(80,80,80),drawX + listener.CHESS,drawY + listener.CHESS,new Color(0,0,0));
((Graphics2D)g).setPaint(black3d);
}else{ //白棋
//g.setColor(Color.WHITE);
GradientPaint white3d = new GradientPaint(drawX,drawY,new Color(255,255,255),drawX + listener.CHESS, drawY + listener.CHESS,new Color(220,220,220));
((Graphics2D)g).setPaint(white3d);
}
//g.fillOval(drawX,drawY,listener.CHESS,listener.CHESS);
((Graphics2D)g).fillOval(drawX,drawY,listener.CHESS,listener.CHESS);
//白棋加黑框
if (color == 1){
g.setColor(Color.BLACK);
g.drawOval(drawX,drawY,listener.CHESS,listener.CHESS);
}
}
}
}
}
九、运行效果展示
十、补充拓展建议
1.将登录界面和五子棋游戏结合起来
2.将五子棋棋子进行优化使其更有立体感
3.添加人机对战
4.游戏窗体进一步装饰使其更为美观