由于这是在优化结束后再写的博客,初期版本的代码已经不在,这里用最终版的代码来讲解思路。
V1.0 登录跳转主页 与 棋盘开发&下棋
-界面类:
关于基础的JFrame 知识就不细讲了,看往期,这里主要提供如何在登录页面和棋盘页面之间跳转
-登录/注册页面
-这里没有写注册功能的代码,仅写了登录功能
public class GameUI implements GoData {
JFrame loginJF = new JFrame();
GameJFrame gameJF = new GameJFrame();
GameListener gameListener = new GameListener();
Mypanel jp1 = new Mypanel();
JPanel jp2 = new JPanel();
public void initLoginUI() {
loginJF.setTitle("登录");
loginJF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
loginJF.setLayout(new FlowLayout());
loginJF.setSize(400, 400);
loginJF.setLocationRelativeTo(null);
JLabel nameInLa = new JLabel("账号");
JTextField nameIn = new JTextField(30);
JButton jButton = new JButton("登录");
loginJF.add(nameInLa);
loginJF.add(nameIn);
loginJF.add(jButton);
loginJF.setVisible(true);
jButton.addActionListener(gameListener);
gameListener.nameIn = nameIn;
gameListener.gameUI = this;
}
}
比较简单,就讲解最后一句,this指的是GameUI,将GameUI传进监听器后,就能在监听器内调用GameUI内的函数,实现后面的显示和隐藏页面。
-五子棋主页
-直接提供优化后的方法,利用面板
-此处将窗口设置为边框布局(BorderLayout( ) ),将窗口分为南北 东西 中五个区域,这里将按钮区放在东区(EAST),将宽度固定,下棋区放在中心(CENTER),会随着窗口大小改变尺寸。
-关于监听器,只给面板1(下棋区)设置鼠标监听器,给面板二中的按钮设置动作监听器。
-关于按钮,这里用了单选按钮,通过按钮组实现多个按钮的单选
-在进入五子棋页面后,要直接展示棋盘,所以要重写Mypanel的paint()方法,棋盘的绘制就不细讲了就是填充一个矩形后,在上面画上数根(这里为16)直线即可
//此处创建了两个面板,面板是一个容器,其中的Mypanel中重写了重载的代码
Mypanel jp1 = new Mypanel();
JPanel jp2 = new JPanel();
public void initGameUI() {
gameJF.setTitle("五子棋");
gameJF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameJF.setLayout(new BorderLayout()); //边框布局,一种布局方式
gameJF.setSize(900, 900);
gameJF.setLocationRelativeTo(null);
jp1.setBackground(Color.white);
jp2.setPreferredSize(new Dimension(120, 0));
jp2.setBackground(Color.darkGray);
gameJF.add(jp1, BorderLayout.CENTER);
gameJF.add(jp2, BorderLayout.EAST);
String[] strs = {"开始游戏", "人人对战", "人机对战", "悔棋", "退出"};
JButton bt1 = new JButton(strs[0]);
jp2.add(bt1);
bt1.addActionListener(gameListener);
ButtonGroup group = new ButtonGroup();
JRadioButton jRadioButton1 = new JRadioButton(strs[1], true);
JRadioButton jRadioButton2 = new JRadioButton(strs[2], false);
group.add(jRadioButton1);
group.add(jRadioButton2);
jp2.add(jRadioButton1);
jp2.add(jRadioButton2);
jRadioButton1.addActionListener(gameListener);
jRadioButton2.addActionListener(gameListener);
gameListener.bt1 = jRadioButton1;
gameListener.bt2 = jRadioButton2;
JButton bt2 = new JButton(strs[3]);
jp2.add(bt2);
bt2.addActionListener(gameListener);
JButton bt3 = new JButton(strs[4]);
jp2.add(bt3);
bt3.addActionListener(gameListener);
jp1.gameListener = gameListener;
jp1.addMouseListener(gameListener);
gameListener.g = jp1.getGraphics();
}
@Override
public void paint(Graphics g) {
super.paint(g);
BufferedImage img = new BufferedImage(X + COL * SIZE + SIZE, Y + ROW * SIZE + SIZE, 2);
Graphics imgG = img.getGraphics();
imgG.setColor(Color.white);
GoData.drawChessPad(imgG);
GoData.drawChesses(imgG, gameListener.chessList);
g.drawImage(img, 0, 0, null);
}
public static void drawChessPad(Graphics g) { //绘制棋盘
g.setColor(Color.orange);
g.fillRect(X - 20, Y - 20, SIZE * (COL + 1), SIZE * (ROW + 1));
g.setColor(Color.black);
for (int i = 0; i <= 15; i++) {
g.drawLine(X, Y + (i * SIZE), X + (ROW * SIZE), Y + (i * SIZE));
g.drawLine(X + (i * SIZE), Y, X + (i * SIZE), Y + (COL * SIZE));
}
}
-显示/隐藏页面
-其实就是利用 setVisible(true/false) 来实现显示和隐藏,不可视(即隐藏)时是无法操作的。
public void showLoginUI() {
loginJF.setVisible(true);
}
public void showGameUI() {
gameJF.setVisible(true);
Graphics g = jp1.getGraphics();
gameListener.g = g;
}
public void hideLoginUI() {
loginJF.setVisible(false);
}
public void hideGameUI() {
gameJF.setVisible(false);
}
-主函数 启动程序
-因为五子棋页面的可视化没放在init中,所以只显示登录页面,但五子棋页面的初始化已经完成。
public static void main(String[] args) {
GameUI gameUI = new GameUI();
gameUI.initLoginUI();
gameUI.initGameUI();
}
-监听器类:按钮监听 动作监听
-按钮与落子之间逻辑的处理
-1.点击“开始游戏”后才可以落子,并将“开始游戏”转变为“结束游戏”
isGoing 判断对局是否开始,此时cl里为“开始游戏”,后续通过cl实现对开始结束的判断
-2.落子期间无法切换对战模式
两个按钮设置为false时是不可操作的
public void actionPerformed(ActionEvent e) {
String ac = e.getActionCommand();
cl = ac;
switch (ac) {
case "开始游戏" -> {
isGoing = true;
GoData.drawChessPad(g);
bt1.setEnabled(!isGoing);
bt2.setEnabled(!isGoing);
//获取事件对象,因为是按钮,所以强制转换成按钮
JButton button = (JButton) e.getSource();
button.setText("结束游戏");
}
}
}
-3.悔棋后可以直接落子,不用额外操作
再写一个悔棋的判断即可,此时cl里为“悔棋”
if (cl.equals("开始游戏") && !isAI || cl.equals("悔棋") && !isAI)
-4.一方获胜后,无法继续落子,除非悔棋
设置一个参数判断是否已经胜利,默认false,在任意一方胜利后,改为true,落子时先判断,如果为true,此次落子无效
Boolean isWin = false;
case "悔棋" -> {
if (isWin){
isWin = false;
}
}
if (GoData.win(chessList, GoData.calC(x), GoData.calR(y))) {
JOptionPane.showMessageDialog(null, chessFlag == 1 ? "白棋胜利~" : "黑棋胜利~");
chessFlag = 0;
isWin = true;
}
if(isWin){
return;
}
-5.点击结束对局不会立即清空棋盘,而是再次点击“开始游戏”才清空棋盘,方便复盘
drawChessPad(g) 是绘制棋盘的代码,在点击“开始游戏”后才会调用,还要将单选按钮恢复
case "开始游戏" -> {
isGoing = true;
GoData.drawChessPad(g);
bt1.setEnabled(!isGoing);
bt2.setEnabled(!isGoing);
JButton button = (JButton) e.getSource();
button.setText("结束游戏");
}
case "结束游戏" -> {
isGoing = false;
bt1.setEnabled(true);
bt2.setEnabled(true);
JButton button = (JButton) e.getSource();
button.setText("开始游戏");
}
-数据接口
-定义存储 棋盘上的固定数据
-初始仅存储棋盘的大小之类的数据
-后续将各种通用方法放在其中,可以通过GoData. 来直接调用其中的静态方法,上面有使用到
public interface GoData {
int X = 70, Y = 70, SIZE = 40, COL = 15, ROW = 15;
}