有时候我们可以用java编写一些小游戏,比如井字棋,这是一个很简单的程序,如图效果;
我们可以将它分为棋子,棋盘,框架启动类表示;
首先我们来编写棋子类,棋子类里有棋子的坐标和形状的表示,
用1表示圆圈,2表示方框
public class Chess { private int x; private int y;//棋子的索引 private int form;//棋子的形状,1是圆圈,2是方框 public Chess(int x,int y,int form) {//构造函数 this.x=x; this.y=y; this.form=form; } public int getX() {//get函数 return x; } public int getY() { return y; } public int getForm() { return form; }}
接下来进行框架类的编写,创建一个类来继承JFrame类,用来启动游戏,放置面板对象,设置大小,设置标题,一个游戏少不了一些按钮,井字棋中有三个按钮,重新开始游戏按钮,悔棋按钮和退出游戏按钮,有按钮就有相应的事件处理,这时候我们需要一个类继承事件处理类,并将三个按钮和事件处理进行绑定;
import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.*;/* * 井字棋的主框架类 * */public class ChessJFrame extends JFrame { private Chessbord chessbord;//声明一个棋盘对象 private Button restartgame;//声明一个重新开始按钮 private Button backgame;//声明一个悔棋按钮 private Button exitgame;//声明一个退出游戏按钮 private JPanel jp;//面板对象,存放按钮 public ChessJFrame() {//构造函数,对参数初始化 setTitle("井字棋");//设置表题名称 MyActLister ma=new MyActLister();//事件处理对象 chessbord=new Chessbord();//棋盘对象 restartgame=new Button("重新开始");// backgame=new Button("悔棋"); exitgame=new Button("退出");//对三个按钮进行初始化 jp=new JPanel();//面板对象 jp.setLayout(new FlowLayout(FlowLayout.CENTER));//流式布局 jp.add(restartgame);// jp.add(backgame); jp.add(exitgame);//将三个按钮添加到面板中 add(jp,BorderLayout.SOUTH);//面板的位置 restartgame.addActionListener(ma); backgame.addActionListener(ma); exitgame.addActionListener(ma);//将三个按钮事件源进行监听 add(chessbord);//添加棋盘 pack();//框架大小的自适应 } public static void main(String[] args) { ChessJFrame jf=new ChessJFrame();//声明框架对象 jf.setVisible(true);//可见 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭方式 jf.setLocationRelativeTo(null);//将面板置中 } private class MyActLister implements ActionListener{//事件监听内部类 @Override public void actionPerformed(ActionEvent e) { Object obj=e.getSource();//获取事件源 if(obj==restartgame) {//重新开始事件 System.out.println("重新开始"); chessbord.restartgame(); } else if(obj==backgame) {//悔棋事件 System.out.println("悔棋"); chessbord.backgame(); }else if(obj==exitgame) {//退出游戏事件 System.exit(0); } } } }
最后一个棋盘类是最复杂的,也是最重要的一个类,它继承的JTable类和两个鼠标事件接口,棋盘类里有画图函数,判单输赢函数和鼠标处理事件,
先说画图函数,它对棋盘进行画图,对棋子进行画图,要想弄清楚其中的关系,画一个九宫格自己比划比划,
画图函数调用的画直线函数方法,画圆圈方法,画方框方法;
protected void paintComponent(Graphics g) {//画棋盘和棋子 super.paintComponent(g); for(int i=0;i<=ROWS;i ) {//画棋盘的行数 g.drawLine(MARGIN, MARGIN i*GRID_SPAN, MARGIN COLS*GRID_SPAN, MARGIN i*GRID_SPAN); } for(int i=0;i<=COLS;i ) {//画棋盘的列数 g.drawLine(MARGIN i*GRID_SPAN, MARGIN, MARGIN i*GRID_SPAN, MARGIN ROWS*GRID_SPAN); } for (int i = 0; i < chessCount; i ) {//画棋子 int form=chessList[i].getForm();//判断是什么形状 int xpos=chessList[i].getX()*GRID_SPAN MARGIN 20;//棋子的X的坐标 int ypos=chessList[i].getY()*GRID_SPAN MARGIN 20;//棋子的y的坐标 if(form==1) {//画圆圈 g.drawOval(xpos,ypos, GRID_SPAN/2, GRID_SPAN/2); }else if(form==2) {//画方框 g.drawRect(xpos, ypos, GRID_SPAN/2, GRID_SPAN/2); } } }
接下来就是鼠标事件,鼠标事件又分为鼠标移动事件和鼠标按下事件,
鼠标移动事件用来设置鼠标光标的,当光标在棋盘外,所在位置有棋子时或游戏结束时设置为默认形状;
代码如下;
public void mouseMoved(MouseEvent e) {//鼠标移动事件 int x1=(e.getX()-MARGIN)/GRID_SPAN;//棋子x的坐标 int y1=(e.getY()-MARGIN)/GRID_SPAN;//棋子y的坐标 if(x1<0||x1>ROWS-1||y1<0||y1>COLS-1||findchess(x1,y1)||gameover) {//判断鼠标光标是否在棋盘外,是否所在位置有棋子,是否游戏结束 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));//如果是,设置为默认形状 }else {//如果不是,则设置为手形 setCursor(new Cursor(Cursor.HAND_CURSOR)); } }
鼠标按下事件,是用来下棋子的,并判断棋子是否胜利,是否平局;
代码如下;
public void mousePressed(MouseEvent e) {//鼠标按下事件 if(gameover)//游戏结束不能下 return ; int form=start?1:2; int xindex=(e.getX()-MARGIN)/GRID_SPAN; int yindex=(e.getY()-MARGIN)/GRID_SPAN; if(xindex<0||xindex>ROWS-1||yindex<0||yindex>COLS-1||findchess(xindex, yindex)) { return ;//在棋盘外,所在位置有棋子,不能下 } Chess ch=new Chess(xindex,yindex,form);//棋子对象 chessList[chessCount ]=ch;//存放在棋子对象数组里 repaint();//重画 if(win(xindex,yindex)) {//如果游戏胜利,输出以下信息 String msg=String.format("恭喜 %s赢了",start?"圆圈":"方框"); JOptionPane.showMessageDialog(this, msg); gameover=true; }else if(chessCount==ROWS*COLS&&gameover==false) {//如果棋子下满,并且没有胜利,输出以下信息 String msg=String.format("旗鼓相当,加油"); JOptionPane.showMessageDialog(this, msg); gameover=true; } start=!start;//改变形状 }
最后是判断棋子是否胜利的代码,个人觉得这是最难的地方;
判断井字棋是否胜利时,当棋子个数小于5时不用判断棋子是否胜利,棋子数不够;
判断主对角线上的棋子时,根据棋子的坐标我们可以知道主对角线上的x坐标与y坐标是相等的;
接下来就是遍历棋子数组中的棋子是否有x坐标与Y坐标相等的;
有的话就判断是否和当前所下棋子是否相同,是的话,数目加一,当数目为三时,说明游戏中有棋子胜利;
根据这个关系写出可以下代码;
if(xindex==yindex) {//主对角线上的棋子 for(int i=0;i<chessCount;i ) { if(chessList[i].getX()==chessList[i].getY()) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子个数是否为三 return true; }else { Count=0; }
判断反对角线上的棋子,放对角线的棋子x坐标和y坐标相加等于同一个数;
判断反对角线上的棋子原理和主对角线上的原理差不多,这里就不多说了;
if(xindex yindex==2) {//反对角线上的棋子 for(int i=0;i<chessCount;i ) { if(chessList[i].getX() chessList[i].getY()==2) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子数是否为三 return true; }else { Count=0; } }
接下来就是判断棋子所在行和列是否有相同连续的三个棋子;
根据坐标关系我们可知;同一列的坐标,它们的x坐标值相等,同一行的坐标,它们的y坐标相等;
以列为例,我们可以根据所下棋子的索引来对棋子所在的列进行遍历;
拿出当前所下棋子的x索引值,然后遍历棋子对象中有没有和当前棋子x索引相同的值,有的话说明该棋子是和所下棋子在同一列
然后进行判断,同一列有没有连续相同的三个棋子;有的话则当前棋子胜利;
行和列的判断代码如下;
for(int i=0;i<chessCount;i ) {//判断所下棋子所在列有没有连续三个的棋子 if(xindex==chessList[i].getX()) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子数是否为三 return true; }else { Count=0; } for(int i=0;i<chessCount;i ) {//判断棋子所在行有没有连续的三个棋子 if(yindex==chessList[i].getY()) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子数是否为三 return true; }else { Count=0; }
棋盘类三个比较重要的部分已经说完了,接下来我会贴出完整的棋盘类代码,大家再接再厉;
import java.awt.*;import java.awt.event.*;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;import java.awt.event.MouseMotionListener;import javax.swing.*;/* * 井字棋的棋盘类 * 棋盘类继承了面板类和鼠标事件接口 */public class Chessbord extends JPanel implements MouseListener,MouseMotionListener{ private static int MARGIN=30;//面板与框架之间的间距 private static int ROWS=3;//行数 private static int COLS=3;//列数 private static int GRID_SPAN=80;//网格间距 Chess[] chessList=new Chess[ROWS*COLS];//棋子数组,存放棋子对象 int chessCount=0;//棋子个数 boolean start=true;//默认圆圈先下 boolean gameover=false;//游戏结束 public Chessbord() {//构造函数 setBackground(Color.GRAY);//设置背景颜色 addMouseListener(this); addMouseMotionListener(this);//添加鼠标事件 } @Override protected void paintComponent(Graphics g) {//画棋盘和棋子 super.paintComponent(g); for(int i=0;i<=ROWS;i ) {//画棋盘的行数 g.drawLine(MARGIN, MARGIN i*GRID_SPAN, MARGIN COLS*GRID_SPAN, MARGIN i*GRID_SPAN); } for(int i=0;i<=COLS;i ) {//画棋盘的列数 g.drawLine(MARGIN i*GRID_SPAN, MARGIN, MARGIN i*GRID_SPAN, MARGIN ROWS*GRID_SPAN); } for (int i = 0; i < chessCount; i ) {//画棋子 int form=chessList[i].getForm();//判断是什么形状 int xpos=chessList[i].getX()*GRID_SPAN MARGIN 20;//棋子的X的坐标 int ypos=chessList[i].getY()*GRID_SPAN MARGIN 20;//棋子的y的坐标 if(form==1) {//画圆圈 g.drawOval(xpos,ypos, GRID_SPAN/2, GRID_SPAN/2); }else if(form==2) {//画方框 g.drawRect(xpos, ypos, GRID_SPAN/2, GRID_SPAN/2); } } } @Override public void mouseDragged(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseMoved(MouseEvent e) {//鼠标移动事件 int x1=(e.getX()-MARGIN)/GRID_SPAN;//棋子x的坐标 int y1=(e.getY()-MARGIN)/GRID_SPAN;//棋子y的坐标 if(x1<0||x1>ROWS-1||y1<0||y1>COLS-1||findchess(x1,y1)||gameover) {//判断鼠标光标是否在棋盘外,是否所在位置有棋子,是否游戏结束 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));//如果是,设置为默认形状 }else {//如果不是,则设置为手形 setCursor(new Cursor(Cursor.HAND_CURSOR)); } } private boolean findchess(int x1, int y1) {//判断所在位置是否有棋子 for (Chess chess : chessList) { if(chess!=null&&chess.getX()==x1&&chess.getY()==y1) { return true; } } return false; } @Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mousePressed(MouseEvent e) {//鼠标按下事件 if(gameover)//游戏结束不能下 return ; int form=start?1:2; int xindex=(e.getX()-MARGIN)/GRID_SPAN; int yindex=(e.getY()-MARGIN)/GRID_SPAN; if(xindex<0||xindex>ROWS-1||yindex<0||yindex>COLS-1||findchess(xindex, yindex)) { return ;//在棋盘外,所在位置有棋子,不能下 } Chess ch=new Chess(xindex,yindex,form);//棋子对象 chessList[chessCount ]=ch;//存放在棋子对象数组里 repaint();//重画 if(win(xindex,yindex)) {//如果游戏胜利,输出以下信息 String msg=String.format("恭喜 %s赢了",start?"圆圈":"方框"); JOptionPane.showMessageDialog(this, msg); gameover=true; }else if(chessCount==ROWS*COLS&&gameover==false) {//如果棋子下满,并且没有胜利,输出以下信息 String msg=String.format("旗鼓相当,加油"); JOptionPane.showMessageDialog(this, msg); gameover=true; } start=!start;//改变形状 } private boolean win(int xindex,int yindex) {//判断游戏是否胜利 if(chessCount<5) {//当棋子小于5时不用判断 return false; } int x=start?1:2;//判断棋子形状 int Count=0;//统计连续棋子数 if(xindex==yindex) {//主对角线上的棋子 for(int i=0;i<chessCount;i ) { if(chessList[i].getX()==chessList[i].getY()) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子个数是否为三 return true; }else { Count=0; } } if(xindex yindex==2) {//反对角线上的棋子 for(int i=0;i<chessCount;i ) { if(chessList[i].getX() chessList[i].getY()==2) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子数是否为三 return true; }else { Count=0; } } for(int i=0;i<chessCount;i ) {//判断所下棋子所在列有没有连续三个的棋子 if(xindex==chessList[i].getX()) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子数是否为三 return true; }else { Count=0; } for(int i=0;i<chessCount;i ) {//判断棋子所在行有没有连续的三个棋子 if(yindex==chessList[i].getY()) { if(chessList[i].getForm()==x) { Count ; }else break; } } if(Count==3) {//判断连续棋子数是否为三 return true; }else { Count=0; } return false;//否则返回false } @Override public void mouseReleased(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub } public void restartgame() {//重新开始函数 for (int i = 0; i < chessList.length; i ) { chessList[i]=null; } chessCount=0; gameover=false; start=true;//一切恢复为初始状态 repaint();//重画 } public void backgame() {//悔棋函数 if(gameover) {//如果游戏结束,不能悔棋 return ; } if(chessCount==0) {//如果棋子个数为0,不能悔棋 return ; } chessList[chessCount-1]=null; chessCount--; start=!start;//改变形状 repaint();//重画 } public Dimension getPreferredSize() {//画矩形面板 return new Dimension(MARGIN*2 ROWS*GRID_SPAN,MARGIN*2 COLS*GRID_SPAN); } }
井字棋代码到此结束;
来源:http://www.icode9.com/content-1-135351.html联系客服