目录
(标记:1.0 初级版)
今天将利用Java语言来实现一个五子棋小游戏,我将演示从界面到棋子再到内在逻辑,如何实现从无到有的逐步搭建过程。
一、从“解构思维”到五子棋
1.项目中的“解构”思维
1)从哲学视角上来看
解构指对稳固性的结构及其中心进行消解,每一次解构都表现为结构的中断、分裂或解体,但是每一次解构的结果又都是产生新的结构。理性将其拆解,同时建立了自己的结构。
2)从艺术视角上来看
对构造解构的分解或拆除,把一个事物拆解,再重新构建。其中每一次解构都会产生新的结构,理性地将其拆解,同时再感性地建立一个自己的解构。
写到这发现其实都是差不多的,那就直接总结一下吧。
3)回到代码上做总结
解构 ≈ 结构分解
每一个项目都具有自己独特的结构,而五子棋这个小项目对于我们来说一开始也是一个陌生的结构,我们要做的就是运用自己熟悉的代码知识与编程能力,通过解构将它拆分成一个一个小的零部件,最后通过自身的思维能力将它们再次拼装回去,但在这个过程中也具有一定的挑战性:那就是发现即便解构了我们最终也拼不回去,但也妨碍这是一种思考问题的思维方式。而在解构后重组的过程之中我们可能也会有新的收获或者是对项目的突破。
解构的程度取决于我们的经验和对项目的把握,并非拆解的越零碎对我们越有益,解构的程度越深拼装的难度也越大。
2.对五子棋项目的解构
不妨先来看看初级版本的五子棋分了几个结构:
1)WuZiQiData 接口
在这个接口中我们放入一些在后续代码中会反复调用的参数,如:
棋盘在屏幕上出现的位置(x0,y0)
棋盘网格间距棋子直径SIZE
棋盘的行和列(row,col)
> 最终调用时只要通过类名实现接口,而不需要在每个类中都重复复制(或者重新定义)一遍常量
public interface WuZiQiData {
int x0 = 50;
int y0 = 50;
int SIZE = 30;
int ROW = 15;
int COL = 15;
}
我们为什么要使用接口? > 三个“实现”
> interface & implements
1.0 实现多重继承
Java中类是单继承的,但是一个类可以实现多个接口。
2.0 实现代码复用
接口可以定义一组相关方法的规范,而不涉及具体实现。其他类可以实现这些接口,从而达到代码复用的效果。接口可以帮助降低代码的耦合度,提高代码的可维护性。
3.0 实现多态
通过接口,可以定义一组共同的方法,不同类实现这个接口后可以根据需要实现这些方法,从而实现不同对象在同一个接口下的统一对待
2)WuZiQiUI 界面
1.0 界面的构成要素
首先思考一个五子棋盘应该具有哪些要素elements:棋盘、棋子、以及下棋整体逻辑(通过代码实现):先黑后白、黑白交替、不能覆盖棋子(即重复落子)、输赢条件(横纵斜四条线判断)
一些细节逻辑:如何让棋子精准地落在交叉点上、不能在棋盘外下棋、下棋逻辑按钮等等
在UI这里我们需要实现的就是如何绘制一个电子的棋盘
2.0 窗体的继承
比较继承窗体和非继承的差异和区别
当继承JFrame时候,可以直接在构造方法中用过this.窗体方法来调用,而不继承则需要创建窗体对象通过对象调用方法
3.0 实现画图功能的paint方法
对于Swing中的组件,更常见的做法是在JPanel paintComponent方法中进行绘制,而不是直接重写JFrame的paint方法。
通过将绘制逻辑放在 JPanel 的 paintComponent 方法中,可以更好地遵循面向对象设计的原则,实现责任分离、提高灵活性和组件复用性,同时考虑到界面性能。
在重写 paint 方法时,应该首先调用父类的 paint 方法,以确保组件的默认绘制操作能够正常进行。
public class WuZiQiUI extends JFrame implements WuZiQiData {
WuZiQiListener wzqListener ;
public void showUI(){
setSize(800,600);
setTitle("五子棋棋盘");
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLayout(null);
String[] btnText ={"开始下棋","悔棋","人机AI斗法","EXIT"};
for (int i = 0; i<btnText.length; i++){
JButton jbt = new JButton(btnText[i]);
jbt.addActionListener(wzqListener);
jbt.setBounds(x0+SIZE*COL+50,y0+30+30*i,100,30);
add(jbt);
}
setVisible(true);
addMouseListener(wzqListener);
wzqListener.g = getGraphics();
wzqListener.wzqUI=this;
}
public void paint(Graphics g){
super.paint(g);
//wzqListener.g=g;
for (int i = 0; i <= COL; i++){
g.drawLine(x0,y0+i*SIZE,x0+SIZE*COL,y0+i*SIZE);
g.drawLine(x0+i*SIZE,y0,x0+i*SIZE,y0+SIZE*ROW);
}
}
public void openUI(){
setVisible(true);
}
public void closeUI(){
setVisible(false);
}
/*public static void main(String[] args) {
new WuZiQiUI().showUI();
}*/
}
3)WuZiQiListener 监听器
这一模块的基本任务是:继承鼠标监听器,实现数据接口、新建棋子颜色标识
根据鼠标点击的位置获取坐标,进一步判断是否落在棋盘上、在棋盘上的x行x列等等
通过获取坐标、校对坐标、还原坐标,最后实现画棋子
public class WuZiQiListener extends MouseAdapter implements WuZiQiData, ActionListener {
int wzqColor = 2;//0:白棋,1:黑棋,2:游戏未开始
Graphics g;
@Override
public void mousePressed(MouseEvent e) {
int x1 = e.getX();
int y1 = e.getY();
System.out.println("x1:"+x1+"y1:"+y1);
int r0 = (x1+SIZE/2-x0)/SIZE;
int c0 = (y1+SIZE/2-y0)/SIZE;
System.out.println("行:"+r0+"列:"+c0);
if(x1<x0-SIZE/2||y1<y0-SIZE/2||x1>x0+SIZE*ROW+SIZE/2||y1>y0+SIZE*COL+SIZE/2){
JOptionPane.showMessageDialog(null,"在棋盘里下棋!");
return;
}
if(wzqColor==1){
g.setColor(Color.black);
wzqColor=0;
} else if (wzqColor==0) {
g.setColor(Color.white);
wzqColor=1;
}
int wzqX = x0-SIZE/2+r0*SIZE;
int wzqY = y0-SIZE/2+c0*SIZE;
g.fillOval(wzqX,wzqY,SIZE,SIZE);
}
}
二、完整代码与下期讲解
1.完整代码
这份整体代码在前面提供的基础上又做了改进与升级,为我们2.0中级版做准备
//代码块1:
public class WuZiQiListener extends MouseAdapter implements WuZiQiData, ActionListener {
int wzqColor = 2;//0:白棋,1:黑棋,2:游戏未开始
Graphics g;
WuZiQiUI wzqUI= new WuZiQiUI();
WuZiQiLoginUI wzqLoginUI;
int[][] WuZiQiArr = new int[ROW+1][COL+1];
//int loginCount = 0;
//String firstClick = "否";
//public JTextField nameIn;
//public JTextField pwdIn;
public void actionPerformed(ActionEvent e){
String ac = e.getActionCommand();
if (ac.equals("登录")){
System.out.println("点击的是:"+ac);
wzqUI.wzqListener=this;
wzqLoginUI.closeUI();
wzqUI.openUI();
wzqUI.showUI();
} else if (ac.equals("注册")) {
System.out.println("点击的是:"+ac);
}
if (ac.equals("开始下棋")){
wzqColor = 1;
System.out.println("0:白棋,1:黑棋"+"——"+wzqColor);
JButton jbt = (JButton) e.getSource();
jbt.setText("结束下棋");
jbt.setBackground(Color.red);
WuZiQiArr = new int[ROW+1][COL+1];
wzqUI.repaint();//点击结束后可以先保留棋盘,再次点击开始棋盘才会清空
} else if (ac.equals("结束下棋")) {
wzqColor = 2;
System.out.println("0:白棋,1:黑棋"+"——"+wzqColor);
JButton jbt = (JButton) e.getSource();
jbt.setText("开始下棋");
jbt.setBackground(Color.GREEN);
} else if (ac.equals("悔棋")) {
} else if (ac.equals("人机AI斗法")) {
} else if (ac.equals("EXIT")) {
wzqUI.closeUI();
wzqLoginUI.closeUI();
}
}
@Override
public void mousePressed(MouseEvent e) {
//super.mousePressed(e);
int x1 = e.getX();
int y1 = e.getY();
System.out.println("x1:"+x1+"y1:"+y1);
int r0 = (x1+SIZE/2-x0)/SIZE;
int c0 = (y1+SIZE/2-y0)/SIZE;
System.out.println("行:"+r0+"列:"+c0);
if(x1<x0-SIZE/2||y1<y0-SIZE/2||x1>x0+SIZE*ROW+SIZE/2||y1>y0+SIZE*COL+SIZE/2){
JOptionPane.showMessageDialog(null,"在棋盘里下棋!");
return;
}
if (wzqColor==2){
JOptionPane.showMessageDialog(null,"请开始游戏后再落子!");
return;
}
if(WuZiQiArr[r0][c0]!=0){
JOptionPane.showMessageDialog(null,"请选择空位落子!");
return;
}
if(wzqColor==1){
g.setColor(Color.black);
wzqColor=0;
} else if (wzqColor==0) {
g.setColor(Color.white);
wzqColor=1;
}
int wzqX = x0-SIZE/2+r0*SIZE;
int wzqY = y0-SIZE/2+c0*SIZE;
g.fillOval(wzqX,wzqY,SIZE,SIZE);
WuZiQiArr[r0][c0]=1;
}
}
//代码块2:
public class WuZiQiLoginUI {
JFrame jf = new JFrame();
// WuZiQiListener wzqListener = new WuZiQiListener();
// WuZiQiListener wzqListener;
public void initUI(){
jf.setTitle("五子棋游戏登录界面");
jf.setSize(420,500);
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jf.setLayout(new FlowLayout());
//jf.addMouseListener(wzqListener);
String picPath="C:\\Users\\紫川秀\\Desktop\\低像素.png";
ImageIcon icon = new ImageIcon(picPath);
JLabel label = new JLabel(icon);
jf.add(label);
JLabel nameLabel = new JLabel("账号");
JTextField nameJTF = new JTextField(35);
jf.add(nameLabel);
jf.add(nameJTF);
JLabel pwdLabel = new JLabel("密码");
JTextField pwdJTF = new JTextField(35);
jf.add(pwdLabel);
jf.add(pwdJTF);
JButton jbt1 = new JButton("登录");
JButton jbt2 = new JButton("注册");
jf.add(jbt1);
jf.add(jbt2);
JCheckBox jbox = new JCheckBox("开始前请阅读游戏规则");
jf.add(jbox);
WuZiQiListener wzqListener = new WuZiQiListener();
wzqListener.wzqLoginUI=this;
jbt1.addActionListener(wzqListener);
jbt2.addActionListener(wzqListener);
jf.setVisible(true);//根据要求进行可视化而不是一运行程序就可视化
}
public void openUI(){
jf.setVisible(true);
}
public void closeUI(){
jf.setVisible(false);
}
public static void main(String[] args) {
WuZiQiLoginUI wzqLoginUI= new WuZiQiLoginUI();
wzqLoginUI.initUI();
}
}
//代码块3:
public class WuZiQiUI extends JFrame implements WuZiQiData {
WuZiQiListener wzqListener ;
// WuZiQiListener wzqListener;
public void showUI(){
setSize(800,600);
setTitle("五子棋棋盘");
setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLayout(null);
String[] btnText ={"开始下棋","悔棋","人机AI斗法","EXIT"};
for (int i = 0; i<btnText.length; i++){
JButton jbt = new JButton(btnText[i]);
jbt.addActionListener(wzqListener);
jbt.setBounds(x0+SIZE*COL+50,y0+30+30*i,100,30);
add(jbt);
}
setVisible(true);
addMouseListener(wzqListener);
wzqListener.g = getGraphics();
wzqListener.wzqUI=this;
}
public void paint(Graphics g){
super.paint(g);
//wzqListener.g=g;
for (int i = 0; i <= COL; i++){
g.drawLine(x0,y0+i*SIZE,x0+SIZE*COL,y0+i*SIZE);
g.drawLine(x0+i*SIZE,y0,x0+i*SIZE,y0+SIZE*ROW);
}
}
public void openUI(){
setVisible(true);
}
public void closeUI(){
setVisible(false);
}
/*public static void main(String[] args) {
new WuZiQiUI().showUI();
}*/
}
2.下期讲解
下一篇可能会单独出一小短篇讲讲如何限制棋子落在棋盘交叉点上