前面在实现完一个JAVA的面板之后,就想着继续实现一个相较于面板更复杂一点的程序――五子棋。但是我在在搜索资料的时候,发现网上很多关于五子棋实现的博文都是一步到位,直接给个源代码,不是很适合新手学习。所以我这里打算记录一下自己实现五子棋的整个过程,大致会分为三四个阶段。今天我们先来实现第一个阶段的五子棋。同样的我们在着手写代码之前我们需要先做一些准备。
一、我们需要实现哪些东西
1.一个15*15的五子棋界面;
2.能够在界面上下黑白棋子;
3.需要把棋子下在交叉点上;
4.实现棋子重绘;――作用:当界面大小被改变时能够保持棋盘和棋盘上面的棋子消失;
5.相同位置不能下多个棋子;
6.只有当“开始新游戏”的按钮被点击时,才能开始下棋;
(判断输赢等其他部分我们放到后面实现)
二、根据我们要实现的功能确定我们所需要的API类
五子棋的大致界面如下:
实现五子棋界面需要的API类:
JFrame和JPanel的区别:JFrame是最底层的容器,JPanel放在它上面,同一个界面只有一个JFrame,但是一个JFrame上面可以有多个JPanel。相当于JFrame是一个大餐桌,JPanel是盘子,如果我们要对餐桌JFrame上的东西进行管理分类等,我们就需要使用这些JPanel盘子。
事件监听机制的类:
绘画所需要的类:
三、各个功能实现的思路
1.一个15*15的五子棋界面;
需要重写GoBangframe的重绘方法,使其在界面刚生成时就产生一个15*15的棋盘。(重绘方法会在每次页面大小被改变时执行,因此当你改变大小时,棋盘不会消失)。这里我们可以通过两个for循环画出15条行线和15条列线。
2.能够在界面上下黑白棋子;
实现这个功能需要定义一个变量turn表名当前轮到哪方下棋。如果turn=1表明轮到黑方,turn=2表明轮到白方。每次黑方下完棋之后turn+1,白方下完棋之后turn-1。
3.需要把棋子下在交叉点上;
如果鼠标点击在交叉点处的某个范围内,我们都把棋子下在这个交叉点上。在这个由于各自大小是40,我们定义的范围时以交叉点为中心的上下20的正方形。这样我们就能确保每一次点击都会落到某一个交叉点处,不会覆盖也不会出现没有落子的情况。
4.实现棋子重绘;――作用:当界面大小被改变时能够保持棋盘上面的棋子不消失;
前面的棋盘重绘,虽然15*15的棋盘不会消失,但是棋子会消失。因此我们在重绘出棋盘以后还要把棋子也重新画上去。这里用了一个二维数组isAvail[15][15]来保存棋子信息,如果isArray[i][j]=0,表明这个位置没有棋子,等于1表明是一个黑棋子,等于2表明是一个白棋子。我们根据这些信息,遍历isArray数组,把棋子重新绘制上去。
5.相同位置不能下多个棋子;
判断isArray[i][j]是不是等于0,如果是0就可以下,如果不是则提示玩家下在其它地方。
6.只有当“开始新游戏”的按钮被点击时,才能开始下棋;
下棋是通过监听棋盘界面来实现的,因此我们只需要监听“开始新游戏”这个按钮,如果这个按钮被点击了,我们在给棋盘界面添加监听方法,否则不添加。
四、代码部分
//构建五子棋界面GoBangframe类 import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton; import javax.swing.JComboBox; import java.awt.Dimension; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.event.ActionListener; import java.awt.event.MouseListener; public class GoBangframe extends JPanel implements GoBangconfig{ public Graphics g;//定义一支画笔 public int[][] isAvail=new int [15][15];//定义一个二维数组来储存棋盘的落子情况 //主函数入口 public static void main(String args[]) { GoBangframe gf=new GoBangframe();//初始化一个五子棋界面的对象 gf.initUI();//调用方法进行界面的初始化 } public void initUI() { //初始化一个界面,并设置标题大小等属性 JFrame jf=new JFrame(); jf.setTitle("五子棋"); jf.setSize(800,650); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(3); jf.setLayout(new BorderLayout());//设置顶级容器JFrame为框架布局 Dimension dim1=new Dimension(150,0);//设置右半部分的大小 Dimension dim3=new Dimension(550,0);//设置左半部分的大小 Dimension dim2=new Dimension(140,40);//设置右边按钮组件的大小 //实现左边的界面,把GoBangframe的对象添加到框架布局的中间部分 GoBangframe jpwest=new GoBangframe(); jpwest.setPreferredSize(dim3);//设置下棋界面的大小 jpwest.setBackground(Color.LIGHT_GRAY);//设置下棋界面的颜色 //这里的话直接把左边的画板添加上去,指明是在框架布局的中间版块 //若放在其他版块会有一些小问题 jf.add(jpwest,BorderLayout.CENTER);//添加到框架布局的中间部分 //实现右边的JPanel容器界面 JPanel jp=new JPanel(); jp.setPreferredSize(dim1);//设置JPanel的大小 jp.setBackground(Color.white);//设置右边的界面颜色为白色 jf.add(jp,BorderLayout.EAST);//添加到框架布局的东边部分 jp.setLayout(new FlowLayout());//设置JPanel为流式布局 //接下来我们需要把按钮等组件依次加到那个JPanel上面 //设置按钮数组 String[] butname= {"开始新游戏","悔棋","认输"}; JButton[] button=new JButton[3]; //依次把三个按钮组件加上去 for(int i=0;i<butname.length;i++) { button[i]=new JButton(butname[i]); button[i].setPreferredSize(dim2); jp.add(button[i]); } //按钮监控类 ButtonListener butListen=new ButtonListener(jpwest); //对每一个按钮都添加状态事件的监听处理机制 for(int i=0;i<butname.length;i++) { button[i].addActionListener(butListen);//添加发生操作的监听方法 } //设置选项按钮 String[] boxname= {"人人对战","人机对战"}; JComboBox box=new JComboBox(boxname); jp.add(box); jf.setVisible(true); } //重写重绘方法,这里重写的是第一个大的JPanel的方法 public void paint(Graphics g) { super.paint(g); //重绘出棋盘 g.setColor(Color.black); for(int i=0;i<row;i++) { g.drawLine(x, y+size*i, x+size*(column-1), y+size*i); } for(int j=0;j<column;j++) { g.drawLine(x+size*j, y, x+size*j, y+size*(row-1)); } //重绘出棋子 for(int i=0;i<row;i++) { for(int j=0;j<column;j++) { if(isAvail[i][j]==1) { int countx=size*i+20; int county=size*j+20; g.setColor(Color.black); g.fillOval(countx-size/2, county-size/2, size, size); } else if(isAvail[i][j]==2) { int countx=size*i+20; int county=size*j+20; g.setColor(Color.white); g.fillOval(countx-size/2, county-size/2, size, size); } } } } }
//设置按钮监听方法ButttonLitener类 import java.awt.event.ActionListener; import java.awt.event.ActionEvent; //实现对JPanel的监听接口处理 public class ButtonListener implements GoBangconfig,ActionListener{ public GoBangframe gf; public ButtonListener(GoBangframe gf) { this.gf=gf;//获取左半部分的画板 } //当界面发生操作时进行处理 public void actionPerformed(ActionEvent e) { //获取当前被点击按钮的内容,判断是不是"开始新游戏"这个按钮 if(e.getActionCommand().equals("开始新游戏")) { //如果是开始新游戏的按钮,再为左半部分设置监听方法 frameListener fl=new frameListener(); fl.setGraphics(gf);//获取画笔对象 gf.addMouseListener(fl); } } }
//定义GoBangconfig接口 //定义与棋盘数据相关的接口,保存棋盘的起点,格子大小,行数列数等信息 public interface GoBangconfig { int x=20,y=20,size=40,row=15,column=15; }
import java.awt.event.ActionListener; import java.awt.event.MouseListener; import java.awt.event.ActionEvent; import java.awt.Graphics; import java.awt.Color; //实现对GoBangframe下棋界面的监听接口处理 public class frameListener implements GoBangconfig,MouseListener{ public GoBangframe gf; public int turn=1;//判断当前轮到谁了,1表示黑方,2表示白方 public void setGraphics(GoBangframe gf) { this.gf=gf; } public void mouseClicked(java.awt.event.MouseEvent e) { int x=e.getX(); int y=e.getY(); //计算棋子要落在棋盘的哪个交叉点上 int countx=(x/40)*40+20; int county=(y/40)*40+20; Graphics g=gf.getGraphics(); if(gf.isAvail[(countx-20)/40][(county-20)/40]!=0) { System.out.println("此处已经有棋子了,请下在其它地方"); } else { //当前位置可以落子,先计算棋盘上棋子在数组中相应的位置 int colu=(countx-20)/40; int ro=(county-20)/40; if(turn==1) { //先设置颜色 g.setColor(Color.black); //落子 g.fillOval(countx-size/2, county-size/2, size, size); //设置当前位置已经有棋子了,棋子为黑子 gf.isAvail[colu][ro]=1; turn++; } else { g.setColor(Color.white); g.fillOval(countx-size/2, county-size/2, size, size); //设置当前位置已经有棋子了,棋子为白子 gf.isAvail[colu][ro]=2; turn--; } } } // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V public void mousePressed(java.awt.event.MouseEvent e) { } // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V public void mouseReleased(java.awt.event.MouseEvent e) { } // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V public void mouseEntered(java.awt.event.MouseEvent e) { } // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V public void mouseExited(java.awt.event.MouseEvent e) { } }
至此,我们已经初步实现了五子棋的界面和落子,后面我们会继续完善这个五子棋,包括输赢的判断和悔棋等操作。