前面在实现完一个JAVA的面板之后,就想着继续实现一个相较于面板更复杂一点的程序——五子棋。但是我在在搜索资料的时候,发现网上很多关于五子棋实现的博文都是一步到位,直接给个源代码,不是很适合新手学习。所以我这里打算记录一下自己实现五子棋的整个过程,大致会分为三四个阶段。今天我们先来实现第一个阶段的五子棋。同样的我们在着手写代码之前我们需要先做一些准备。

完整代码已上传到github上,地址:https://github.com/Alexlingl/GoBang。有需要的可以自取。

五子棋系列博客(总共三篇,从简单功能简单界面到人机对战,以及较美观的登录及对战界面。第三篇博客中有最终实现的界面效果):

JAVA五子棋的实现(二)

JAVA五子棋的实现(三)——人机对战(权值法)

一、我们需要实现哪些东西

  1. 一个15*15的五子棋界面;
  2. 能够在界面上下黑白棋子;
  3. 需要把棋子下在交叉点上;
  4. 实现棋子重绘;——作用:当界面大小被改变时能够保持棋盘和棋盘上面的棋子不会消失;
  5. 相同位置不能下多个棋子;
  6. 只有当“开始新游戏”的按钮被点击时,才能开始下棋;

(判断输赢等其他部分我们放到后面实现)

二、根据我们要实现的功能确定我们所需要的API类

五子棋的大致界面如下:

2.1 实现五子棋界面需要的API类

JFrame  顶级容器类

BorderLayout  框架布局类——把容器分为上北下南左西右东中间五个部分,每个部分只能添加一个组件。在这里我们需要把整个界面分为左右两部分。选择在框架布局的中间和右边两个部分。

这里我们还需要另一个容器类——JPanel  面板容器类。主要用来实现右边的部分,因为原本框架布局的右边部分只能添加一个组件,但是我们需要添加的组件不只一个,因此就先这个部分加上一个JPanel,然后再在JPanel上加我们需要的组件,包括按钮等等。

JFrame和JPanel的区别:JFrame是最底层的容器,JPanel放在它上面,同一个界面只有一个JFrame,但是一个JFrame上面可以有多个JPanel。相当于JFrame是一个大餐桌,JPanel是盘子,如果我们要对餐桌JFrame上的东西进行管理分类等,我们就需要使用这些JPanel盘子。

FlowLayout   流式布局类。因为我们右边的JPanel部分需要将按钮等组件由上到下进行排列。

JButton  按钮组件类

JComboBox   下拉列表框类——实现那个人人对战和人机对战的选择

Dimension  封装组件宽度和高度的类——组件类不能直接用setSize方法来设置

2.2 事件监听机制的类

ActionListener  状态监听处理类

ActionEvent   状态监听事件,监听某个组件的状态是否有改变

MouseListener  鼠标监听处理类

MouseEvent  鼠标监听事件,里面包含鼠标被点击等事件的处理方法。当界面被点击时就放下一个棋子。

2.3 绘画所需要的类

Graphics|Graphics2D   画笔类

Color  颜色类,需要设置画笔的颜色,实现黑白棋子

三、各个功能实现的思路

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的对象添加到框架布局的中间部分
		this.setPreferredSize(dim3);//设置下棋界面的大小
		this.setBackground(Color.LIGHT_GRAY);//设置下棋界面的颜色
		//这里的话直接把左边的画板添加上去,指明是在框架布局的中间版块
		//若放在其他版块会有一些小问题
		jf.add(this,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(this);
		//对每一个按钮都添加状态事件的监听处理机制
		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) {
		  
	  }
}

五、实现的界面

至此,我们已经初步实现了五子棋的界面和落子,后面我们会继续完善这个五子棋,包括输赢的判断和悔棋等操作。

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐