接上一篇,继续完善功能。
画个大纲(跟随慢慢开发过程不断完善,今天分类方法改一改)
1.用户
两个用户对战 一黑一白
用户可以是人,也可以是AI。对战模式支持人人,人机,机机。
- 属性
执棋颜色
用户名
游戏得分
-
方法
下棋
输赢
2.比赛规则
一黑一白交替轮流下棋
可以决定哪个玩家先手
不可以重复下棋到同一个位置
不可以将棋子下到边界外
可以撤回刚刚下的棋,不可以撤回上一步的棋
哪一方横竖斜到达5个棋子赢一局
3.界面
- 登录界面
用户登录:
用户名、密码输入栏(带提示,用户密码匹配检验),登录按键;
登录图像。
- 菜单界面
选择功能:
新游戏
游戏积分——获胜局数
对战模式 —— 人人,人机,机机
- 游戏界面
下棋主界面
设置棋盘、背景板、菜单栏、棋子计数板、计分板、棋子、下棋指示器,刷新窗体后这些都不会消失。
背景板、棋盘:17*17,棋盘在背景板之上。
菜单栏:撤回、清空界面——撤回/清空时,计数器要跟着变化。
棋子计数板:记录当前棋盘上黑白棋子个数。(图像不重叠,随撤回清空等操作实时刷新)。
计分板:记录目前双方赢得局数。
棋子:下到交叉线(棋子校准)、不重复、不越出棋盘、刷新保存,可以撤回,可以识别获胜。
当前局数计时器:距离游戏开始的耗时。
- 获胜界面
当有一方获胜后弹出
显示哪方获胜
显示获胜图片
菜单:
再战一局:触发游戏界面
退出游戏:回到菜单界面
回顾棋局:显示重新下棋步骤(撤回步骤显示?)
乱七八糟的功能
存档
读档
软件使用日志
第二天 —— 实现登录界面、菜单界面、获胜界面、
实现棋子不重复、撤销、清空棋盘、棋子计数、判定谁赢
实现方式
1. 棋子不重复:设置一个二维int数组储存棋盘对应位置上放的是黑、白、空棋(2、1、0)代替。
当算出当前位置时,先判断这个位置是否为空也就是0,如果不是则不放(后续一
系列画棋子,计数板变化等不做)。
注意:
1.在窗体(面板)上x为横轴,y为纵轴。二位数数组chess[列][行],对应的是chess[y][x]。
2.当发生撤销、清空、新游戏等情况是,要对这个二维数组做更新。
2. 撤销:撤回上一步。除了二维数组,还需要一个ArrayList<chess>来储存棋子放置顺序,设置
棋子类,属性有x、y坐标,棋子颜色,设置构造方法可以直接传3个参数进去,设置 get
方法获得三个属性值,最好再设置set方法设置3个属性。(在下棋的时候,将棋子加入到
ArrayList,加入的坐标x,y是棋子的圆心坐标,重画时自己偏移)。当点击撤销时,调
用界面重写的paint方法,将最后一个加入的棋子删去,然后for循环重画棋子。
ArrayList目前用到的方法 (序号排序从0开始)
.add(chessShape obj);加入一个目标类型对象
.remove(index); 根据序号index移除列表内的对象
.size(); 得出数组目前的长度
.clear(); 清除所有对象
.get(index); 获取序号为index的对象
注意:
当撤销时,ArrayList要移除最后一个对象,二维数组改棋盘值。
3. 清空:清空棋盘上所有棋子。调用界面的 repaint 函数。
遇到的问题
1.repaint函数好像是调用的paint函数。当想要设置刷新窗口棋子不丢失时,就把棋子重写放在了重
写的paint函数中。但当你想要清空棋盘时,调用了repaint函数,就会让棋子仍然重画无法消掉。
解决办法:
1. 棋子重写仍然写在paint函数里,但是清空的时候先将ArrayList清空,chesses二维数组更
新,再调用repaint方法。
2.棋子重写在撤回方法实现的时候写,不写入paint函数。在清空时,直接调用repaint方法。
综上而言第一个方法更好。
2.界面布局有点麻烦,想要设置坐标布局好像不太管用,(边框布局只能设置一次,重复只会保留
最后的)(流式布局只会横行排列,然后排到下面,不灵活)。面板、按键大小和布局调整时设
置xy坐标貌似不管用,还未解决,有待多加尝试。以及字体大小设置虽不是问题但有待改进。
3.在设置判断胜方之后弹出winner界面,要实现在这个界面画照片和写字,但是界面无法给监听器
传画笔、监听器传给界面的画笔是画在主界面棋盘格上的。矛盾暂无法解决。有待更新解决方
法。
代码
//有些类的注释还没全,后续补齐
//BiangGoBangUI.java
/**
* GoBang界面
* 继承JFRame ,重写paint方法
*
* 属性:
* 1.bgimg 背景图
* 2.mylis GoBang棋盘操作监听器 ———— BiangGoBangListener mylis
* 3.loginListener GOBang 登录页面监听器 ————— LoginListener loginListener
* 4.二维int 数组 chessIf 存放棋盘放棋子 黑 白 空 棋情况 2 1 0 0-16 共 17*17个点
* 5.一维int 数组 chessIndexX 按顺序存放棋子横坐标
* 6.一维int 数组 chessIndexY 按顺序存放棋子纵坐标
*
*
* 方法:
* 1.initBiangGoBangUI() 初始化界面方法
* 2.startGame() 开始游戏方法 点击开始游戏跳转棋盘界面
* 3.loginGame() 登录游戏方法 开始实现用户名登录 跳转开始游戏界面
*
* 重写方法:
* 1.paint() 实现窗体刷新之后重画 背景图 棋盘
*/
public class BiangGoBangUI extends JFrame implements BiangGoBangInterface {
public static final Image bgimg = new ImageIcon("img/bg50.jpg").getImage();
public static final Image winImg = new ImageIcon("img/winne2" +
".jpg").getImage();
public BiangGoBangListener mylis = new BiangGoBangListener();
public LoginListener loginListener = new LoginListener();
public int[][] chesses = new int[17][17];
public ArrayList<chessShape> chessIndex = new ArrayList<chessShape>(300);
// public int[] chessIndexX = new int [];
// public int[] ChessIndexY = new int [];
public void initBiangGoBangUI() {
for(int i=0;i<17;i++){
for(int j=0;j<17;j++)
System.out.print(chesses[i][j]);
System.out.println();
}
//JFrame bgb = new JFrame("Biang's五子棋v 1.0");
this.setTitle("Biang's 五子棋 v1.0");
this.setSize(SIZE * LINE+X*2, SIZE * ROW+Y*3);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mylis.setGoBangUi(this);
JPanel downBar = new JPanel();
downBar.setBackground(Color.GRAY);
Dimension dim = new Dimension(downWidth,downWidth);
downBar.setPreferredSize(dim);
JButton BeginBtn = new JButton("开始");
BeginBtn.setBackground(Color.WHITE);
BeginBtn.setSize(btnWidth,btnHeight);
downBar.add(BeginBtn);
JButton ClearBtn = new JButton("清空");
ClearBtn.setBackground(Color.WHITE);
ClearBtn.setSize(btnWidth,btnHeight);
downBar.add(ClearBtn);
JButton backBtn = new JButton("撤回");
backBtn.setBackground(Color.WHITE);
backBtn.setSize(btnWidth,btnHeight);
downBar.add(backBtn);
ClearBtn.addActionListener(mylis);
backBtn.addActionListener(mylis);
this.add(downBar,BorderLayout.SOUTH);
//BeginBtn.addActionListener(mylis);
// 实现下棋子下在面板之内:特别为棋盘设置面板画笔不一致,需要重新写继承JPanel的paint方法,所以换在监听器里实现
// this.setLayout(null);
// JPanel chessBoard = new JPanel();
// chessBoard.setLocation(X,Y);
// Dimension dim = new Dimension(SIZE*ROW,SIZE*LINE);
// //chessBoard.setPreferredSize(dim);
// chessBoard.setSize(SIZE*ROW,SIZE*LINE);
// chessBoard.setBackground(Color.BLUE);
// this.add(chessBoard);
this.setVisible(true);
this.addMouseListener(mylis);
Graphics pen = this.getGraphics();
mylis.setGraphics(pen);
}
@Override
public void paint(Graphics g){
super.paint(g);
g.drawImage(bgimg,X-SIZE,Y-SIZE,(ROW+2)*SIZE,(LINE+2)*SIZE,null);
g.setColor(Color.BLACK);
for(int i=0;i<=ROW;i++){
g.drawLine(X,Y+i*SIZE,X+SIZE*LINE,Y+i*SIZE); //y不变,画横线
g.drawLine(X+i*SIZE,Y,X+i*SIZE,Y+SIZE*ROW); //x不变,画竖线
}
// for(int i=0;i<17;i++){
// for(int j=0;j<17;j++){
// if(chesses[i][j]==2){
// g.setColor(Color.BLACK);
// g.fillOval(X+j * SIZE-SIZE/2,Y+i*SIZE-SIZE/2,SIZE,SIZE);
// }
// else if(chesses[i][j]==1) {
// g.setColor(Color.WHITE);
// g.fillOval(X + j * SIZE - SIZE / 2, Y + i * SIZE - SIZE / 2, SIZE, SIZE);
// }
// }
// }
}
public void startGame(){
JFrame startjf = new JFrame("Biang 's 五子棋v2.0");
startjf.setSize(700,700);
startjf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JButton btn1 = new JButton("开始游戏");
btn1.addActionListener(mylis);
startjf.add(btn1,BorderLayout.NORTH);
startjf.setVisible(true);
mylis.setGoBangUi(this); //全局??
mylis.setStartJf(startjf);
}
public void loginGame(){
JFrame loginJf = new JFrame("欢迎登录 Biang 's 五子棋v2.0");
loginJf.setSize(800,800);
loginJf.setLayout(null);
loginJf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
FlowLayout fl = new FlowLayout();
loginJf.setLayout(fl);
JTextField jTFLoginUser = new JTextField(12);
JTextField jTFLoginPassword = new JTextField(12);
JButton btn0 = new JButton("登录");
btn0.addActionListener(loginListener);
loginJf.add(jTFLoginUser);
loginJf.add(jTFLoginPassword);
loginJf.add(btn0);
// btn0.setLocation(100,200);
// jTFLoginUser.setLocation(100,100);
// jTFLoginPassword.setLocation(100,150);
// btn0.setSize();
// loginJf.add(jTFLoginUser,BorderLayout.SOUTH);
// loginJf.add(jTFLoginPassword,BorderLayout.SOUTH);
// loginJf.add(btn0,BorderLayout.SOUTH);
loginJf.setVisible(true);
loginListener.setGoBangUi(this);
loginListener.setLoginJf(loginJf);
loginListener.setjTFUser(jTFLoginUser);
loginListener.setjTFPassword(jTFLoginPassword);
}
public void WinGameUI(int Winner,Graphics wpen){
JFrame winJf = new JFrame("兄弟!太厉害了");
winJf.setSize(400,500);
winJf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
if(Winner==2)
wpen.drawString("恭喜黑方获胜!",100,100);
else
wpen.drawString("恭喜白方获胜!",100,100);
wpen.drawImage(winImg,100,200,100,100,null);
JButton btn1 = new JButton("再战一局");
btn1.addActionListener(mylis);
winJf.add(btn1,BorderLayout.SOUTH);
winJf.setVisible(true);
// Graphics wpen = winJf.getGraphics();
// mylis.setGraphics(wpen);
// JButton btn2 = new JButton("结束游戏");
// btn2.addActionListener(mylis);
// WinJf.add(btn2,BorderLayout.NORTH);
//
// JButton btn3 = new JButton("回顾棋局");
// btn3.addActionListener(mylis);
// WinJf.add(btn3,BorderLayout.NORTH);
mylis.setGoBangUi(this); //全局??
mylis.setWinJf(winJf);
}
}
//BiangGoBangInterface.java
/**
* 五子棋接口 设置固定参数
* 属性
* 1.SIZE:棋盘间距 & 棋子直径
* 2.棋盘左上角坐标 X
* 3.棋盘左上角坐标 Y
* 4.棋盘行列数 ROW LINE
* 5.下拉栏宽度
* 6.按钮的宽度 btnWidth
* 7.按钮的高度 btnHeight
*/
public interface BiangGoBangInterface {
int SIZE = 30;
int X = 100;
int Y = 100;
int ROW = 16;
int LINE = 16;
int downWidth = 100;
int btnWidth = 100;
int btnHeight = 50;
}
//BiangGoBangLinstener.java
/**
* 棋盘操作下棋监听器 (实现鼠标 鼠标拖动接口)
* 属性:
* 1.chessX chessY当前下棋的坐标
* 2.pen 绘制棋子的画笔组件
* 3.controlColor 控制棋子颜色交替 0 黑 1 白
* 4.countBlack countWhite 计数黑棋 白棋个数
* 5.goBangUi ———— BiangGoBangUI 棋盘界面对象
* 6.startJf ———— JFrame 开始游戏界面对象
* 7.
*
* 方法:
* 1.void setGraphics(Graphics pen) 向监听器传入界面的画笔对象
* 2.void setGoBangUi(BiangGoBangUI goBangUi) 向监听器传入棋盘界面对象
* 3.void setStartJf(JFrame startJf) 向监听器传入开始界面对象
* 4.
*
* 重写的方法:
* 1.mousePressed 实现点击下棋 棋子校正 控制下棋范围 棋盘保存 不重复下棋
* 2.actionPerformed 实现开始游戏按钮——跳转棋盘 清空 撤回
*
*/
public class BiangGoBangListener implements MouseListener , ActionListener, MouseMotionListener ,BiangGoBangInterface{
int chessX,chessY;
Graphics pen;
int controlColor=0;
int countBlack=0, countWhite=0;
BiangGoBangUI goBangUi;
JFrame startJf ;
JFrame winJf;
int[][] changeLocation =new int[][]{{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};//04 15 26 37
public void setGraphics(Graphics pen){
this.pen = pen;
}
public void setGoBangUi(BiangGoBangUI goBangUi) {this.goBangUi = goBangUi; }
public void setStartJf(JFrame startJf) {this.startJf = startJf; }
public void setWinJf(JFrame winJf) {this.winJf = winJf; }
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if(x>X-SIZE && x<X+SIZE*ROW+SIZE && y>Y-SIZE && y<Y+SIZE*LINE+SIZE ){
//棋子校正:点中间线归上左
int baseX = X + ((x - X) / SIZE) * SIZE;
int baseY = Y + ((y - Y) / SIZE) * SIZE;
if ((x - baseX) > (SIZE / 2))
chessX = baseX + SIZE;
else
chessX = baseX;
if ((y - baseY) > (SIZE / 2))
chessY = baseY + SIZE;
else
chessY = baseY;
if(goBangUi.chesses[(chessY-Y)/SIZE][(chessX-X)/SIZE]!=0)
return;
if((controlColor%2)==0) {
countBlack++;
pen.setColor(Color.BLACK);
goBangUi.chesses[(chessY-Y)/SIZE][(chessX-X)/SIZE]=2;
}
else {
countWhite++;
pen.setColor(Color.WHITE);
goBangUi.chesses[(chessY-Y)/SIZE][(chessX-X)/SIZE]=1;
}
System.out.println(controlColor);
controlColor=(controlColor+1)%2;
goBangUi.chessIndex.add(new chessShape(chessX,chessY,pen.getColor())); //存的是棋子圆心,画的时候要偏移
pen.fillOval(chessX - SIZE / 2, chessY - SIZE / 2, SIZE, SIZE);
pen.setColor(Color.WHITE);
pen.fillRect(100,30,500,30);
pen.setColor(Color.BLACK);
pen.drawString("黑色棋子计数: "+countBlack,150,50);
pen.drawString("黑色棋子计数: "+countWhite,450,50);
if(ifWin(((chessY-Y)/SIZE),((chessX-X)/SIZE),goBangUi.chesses[(chessY-Y)/SIZE][(chessX-X)/SIZE])!=0){
if(goBangUi.chesses[(chessY-Y)/SIZE][(chessX-X)/SIZE]==2){
System.out.println("黑方获胜!");
goBangUi.WinGameUI(2,pen);
}
else {
System.out.println("白方获胜!");
goBangUi.WinGameUI(1,pen);
}
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
String btnStr = e.getActionCommand();
if(btnStr.equals("开始游戏")){
startJf.setVisible(false);
goBangUi.initBiangGoBangUI();
}
else if(btnStr.equals("再战一局")){
winJf.setVisible(false);
goBangUi.initBiangGoBangUI();
}
else if(btnStr.equals("清空")){
System.out.println("我感受到了清空的召唤!");
goBangUi.repaint();
//棋盘记录改
for(int i=0;i<17;i++){
for(int j=0;j<17;j++)
goBangUi.chesses[i][j]=0;
}
//步数记录改
goBangUi.chessIndex.clear();
}
else if(btnStr.equals("撤回")){
goBangUi.paint(pen);
if(goBangUi.chessIndex.get(goBangUi.chessIndex.size()-1).getColor().equals(Color.BLACK))
countBlack--;
else
countWhite--;
//棋盘记录改
goBangUi.chesses[goBangUi.chessIndex.get(goBangUi.chessIndex.size()-1).getChessX()/SIZE][goBangUi.chessIndex.get(goBangUi.chessIndex.size()-1).getChessY()/SIZE]=0;
//System.out.println("撤回前:"+goBangUi.chessIndex.size());
//步数记录改
goBangUi.chessIndex.remove(goBangUi.chessIndex.size()-1);
//System.out.println("撤回后:"+goBangUi.chessIndex.size());
for(int i=0;i<goBangUi.chessIndex.size();i++){
pen.setColor(goBangUi.chessIndex.get(i).getColor());
System.out.println(i);
System.out.println(goBangUi.chessIndex.get(i).getChessX()+" "+goBangUi.chessIndex.get(i).getChessY());
pen.fillOval(goBangUi.chessIndex.get(i).getChessX()-SIZE/2, goBangUi.chessIndex.get(i).getChessY()-SIZE/2,SIZE,SIZE);
}
//计数板改变
pen.setColor(Color.WHITE);
pen.fillRect(100,30,500,30);
pen.setColor(Color.BLACK);
pen.drawString("黑色棋子计数: "+countBlack,150,50);
pen.drawString("白色棋子计数: "+countWhite,450,50);
//步数记录改
//goBangUi.chessIndex.remove(goBangUi.chessIndex.size()-1);
}
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
public int ifWin(int x,int y,int colorNum){
int countNumFive=4;
int currX=x,currY=y;
int flag=1;
for(int i=0;i<4;i++) { //左上到右下 竖 右上到左下 横
countNumFive=4;
while (countNumFive > 0) {
if (flag == 1 && currX + changeLocation[i][0] >= 0 && currX + changeLocation[i][0] <= 16 && currY + changeLocation[i][1] >= 0 && currY + changeLocation[i][1] <= 16) {
if (goBangUi.chesses[currX + changeLocation[i][0]][currY + changeLocation[i][1]] == colorNum) {
currX += changeLocation[i][0];
currY += changeLocation[i][1];
countNumFive--;
}
else { //换方向
flag = 2;
currX = x;
currY = y;
}
}
else if (flag == 2 && currX + changeLocation[i+4][0] >= 0 && currX + changeLocation[i+4][0] <= 16 && currY + changeLocation[i+4][1] >= 0 && currY + changeLocation[i+4][1] <= 16) {
if (goBangUi.chesses[currX + changeLocation[i+4][0]][currY + changeLocation[i+4][1]] == colorNum) {
currX += changeLocation[i+4][0];
currY += changeLocation[i+4][1];
countNumFive--;
}
else { //这个方向没连够
flag = 1;
currX = x;
currY = y;
break;
}
}
if (countNumFive == 0)
return colorNum;
}
}
//if(countNumFive!=0)
return 0;
}
}
//loginListener.java
public class LoginListener implements ActionListener {
private JTextField jTFUser;
private JTextField jTFPassword;
BiangGoBangUI goBangUi;
//JFrame startJf ;
JFrame loginJf ;
public void setjTFUser(JTextField jTFUser) {this.jTFUser = jTFUser; }
public void setjTFPassword(JTextField jTFPassword) {this.jTFPassword = jTFPassword; }
public void setGoBangUi(BiangGoBangUI goBangUi) {this.goBangUi = goBangUi; }
//public void setStartJf(JFrame startJf) {this.startJf = startJf; }
public void setLoginJf(JFrame loginJf) {this.loginJf = loginJf; }
@Override
public void actionPerformed(ActionEvent e) {
String btnStr = e.getActionCommand();
String jTFUserStr = jTFUser.getText();
String jTFPasswordStr = jTFPassword.getText();
if(btnStr.equals("登录")){
if(jTFUserStr.equals("Stefan") && jTFPasswordStr.equals("0410")){
loginJf.setVisible(false);
goBangUi.startGame();
}
}
}
}
//chessShape.java
public class chessShape {
int chessX;
int chessY;
Color color;
public chessShape(int x,int y,Color cc){
this.chessX=x;
this.chessY=y;
this.color=cc;
}
public int getChessX(){
return chessX;
}
public int getChessY(){
return chessY;
}
public Color getColor() {
return color;
}
}
//StartGameUI.java
public class StartGameUI {
public static void main(String[] args){
BiangGoBangUI goBangUI = new BiangGoBangUI();
goBangUI.loginGame();
}
}