1. Java俄罗斯方块
2. 老Java程序员花2天写了个连连看
3. 老Java程序员花一天时间写了个飞机大战
4. Java五子棋人机版
5. Java植物大战僵尸
6. Java消消乐(天天爱消除)
7. Java贪吃蛇小游戏
8. Java扫雷小游戏
9. Java坦克大战
前两天我发现CSDN上有两篇飞机大战的文章异常火爆,各种指标都很高(阅读、点赞、评论、收藏等),但都是python写的,竟然不是我大Java,说实话作为老java选手,我心里是有那么一些失落的,难道我大java打飞机不行?就算大java打飞机不行,那我用单身30年的打飞机手速,我肯定行(反正我的代码我做主,就是玩!),于是我决定一展伸手,用java写了一个飞机大战。我就问你们我打飞机行不行,我媳妇都说行,你们呢?欢迎我亲爱的大Java选手们 点赞+评论+收藏!给我冲、冲、冲。。。
首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。
/* * 游戏窗体类 */public class GameFrame extends JFrame { public GameFrame() { setTitle('飞机大战');//设置标题 setSize(526, 685);//设定尺寸 setLayout(new BorderLayout()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序 setLocationRelativeTo(null); //设置居中 setResizable(false); //不允许修改界面大小 }}
创建面板容器GamePanel继承至JPanel
package main;import java.awt.Graphics;import javax.swing.JFrame;import javax.swing.JPanel;/* * 画布类 */public class GamePanel extends JPanel{ GamePanel gamePanel=this; private JFrame mainFrame=null; //构造里面初始化相关参数 public GamePanel(JFrame frame){ this.setLayout(null); mainFrame = frame; mainFrame.setVisible(true); } @Override public void paint(Graphics g) { }}
再创建一个Main类,来启动这个窗口,用来启动。
package main;public class Main { //主类 public static void main(String[] args) { GameFrame frame = new GameFrame(); GamePanel panel = new GamePanel(frame); frame.add(panel); frame.setVisible(true);//设定显示 }}
右键执行这个Main类,窗口建出来了
创建菜单
private void initMenu(){ // 创建菜单及菜单选项 jmb = new JMenuBar(); JMenu jm1 = new JMenu('游戏'); jm1.setFont(new Font('微软雅黑', Font.BOLD, 15));// 设置菜单显示的字体 JMenu jm2 = new JMenu('帮助'); jm2.setFont(new Font('微软雅黑', Font.BOLD, 15));// 设置菜单显示的字体 JMenuItem jmi1 = new JMenuItem('开始新游戏'); JMenuItem jmi2 = new JMenuItem('退出'); jmi1.setFont(new Font('微软雅黑', Font.BOLD, 15)); jmi2.setFont(new Font('微软雅黑', Font.BOLD, 15)); JMenuItem jmi3 = new JMenuItem('操作说明'); jmi3.setFont(new Font('微软雅黑', Font.BOLD, 15)); JMenuItem jmi4 = new JMenuItem('胜利条件'); jmi4.setFont(new Font('微软雅黑', Font.BOLD, 15)); jm1.add(jmi1); jm1.add(jmi2); jm2.add(jmi3); jm2.add(jmi4); jmb.add(jm1); jmb.add(jm2); mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上 jmi1.addActionListener(this); jmi1.setActionCommand('Restart'); jmi2.addActionListener(this); jmi2.setActionCommand('Exit'); jmi3.addActionListener(this); jmi3.setActionCommand('help'); jmi4.addActionListener(this); jmi4.setActionCommand('win');}
实现ActionListener并重写方法actionPerformed
@Overridepublic void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); UIManager.put('OptionPane.buttonFont', new FontUIResource(new Font('宋体', Font.ITALIC, 18))); UIManager.put('OptionPane.messageFont', new FontUIResource(new Font('宋体', Font.ITALIC, 18))); if ('Exit'.equals(command)) { Object[] options = { '确定', '取消' }; int response = JOptionPane.showOptionDialog(this, '您确认要退出吗', '', JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { System.exit(0); } }else if('Restart'.equals(command)){ if(startFlag){ Object[] options = { '确定', '取消' }; int response = JOptionPane.showOptionDialog(this, '游戏中,您确认要重新开始吗', '', JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); if (response == 0) { //需要先结束游戏 realGameEnd(1); restart(); } }else{ restart(); } }else if('help'.equals(command)){ JOptionPane.showMessageDialog(null, '游戏开始后,要先动鼠标到飞机处,触发移动效果,然后飞机就会跟随鼠标移动!', '提示!', JOptionPane.INFORMATION_MESSAGE); }else if('win'.equals(command)){ JOptionPane.showMessageDialog(null, '得分1000,获得胜利!', '提示!', JOptionPane.INFORMATION_MESSAGE); }}
在GamePanel类中重写paint方法,绘制背景图即可
//绘图方法@Overridepublic void paint(Graphics g) { gameHeight = this.getHeight(); gameWidth = this.getWidth(); //绘制背景 g.drawImage((BufferedImage)imageMap.get('bg'), 0, -150, null);}
主线程,用来重绘页面,重绘全部交给主线程,主线程调用 repaint方法就行,要产生动画就要靠这个repaint。
//刷新线程,用来重新绘制页面private class RefreshThread implements Runnable { @Override public void run() { while (startFlag) { repaint(); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } }}
在GamePanel的构造里面启动这个主线程
创建MyPlane类,属性有坐标x、y,宽高、图片、是否存活、是否可以移动等;方法主要有绘制、移动、射击等。
public class MyPlane { private int x = 0; private int y = 0; private int width = 0; private int height = 0; private BufferedImage image = null; private GamePanel panel=null; private HashMap imageMap=null; private boolean alive=true; private boolean canMove=false; private int key=1; private HashMap boomImageMap=null; private boolean hitFlag=false;//正在碰撞 public MyPlane(int x,int y,int width,int height,GamePanel panel) { this.x=x; this.y=y; this.width=width; this.height=height; this.panel=panel; this.imageMap=panel.imageMap; this.image=(BufferedImage)imageMap.get('myplane1'); this.boomImageMap=panel.mypalneBoomImageMap; } //绘制 public void draw(Graphics g) { g.drawImage(image, x, y, width,height, null); }}
创建(这里只是创建好了飞机对象,需要绘制)
//创建自己飞机private void initMyPlane() { myPlane = new MyPlane(200, 530, 132, 86, this);}
在paint方法中绘制
//绘图方法@Overridepublic void paint(Graphics g) { gameHeight = this.getHeight(); gameWidth = this.getWidth(); //绘制背景 g.drawImage((BufferedImage)imageMap.get('bg'), 0, -150, null); //绘制飞机 if(myPlane!=null){ myPlane.draw(g); }}
加入监听是为了让飞机跟随鼠标移动,我这里定的规则是第一次鼠标必须移动到飞机上,然后飞机才会跟随。
代码里面用一个属性canMove来控制,默认是false,只有鼠标第一次移入到飞机上时,这个属性设置为true,然后就可以跟随鼠标移动了。
//鼠标事件的创建private void createMouseListener() { MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mouseMoved(MouseEvent e) { int x = e.getX(); int y = e.getY(); if(myPlane==null) return ; //飞机第一次是不允许移动的,只有飞机的canMove为true才去跟随 if(myPlane.isCanMove()){ myPlane.move(x,y); return; } //判断鼠标的移入,如果移动到飞机上则canMove设置为true if(myPlane.isPoint(x,y)){ myPlane.setCanMove(true); } } }; addMouseMotionListener(mouseAdapter); addMouseListener(mouseAdapter);}
来实现一下MyPlane的move方法,这里处理了边界,保证飞机不出界,同时保证鼠标在飞机的中间位置
//飞机跟随鼠标移动public void move(int x,int y) { //判断范围,当横向移动在窗口范围内 if(x-width/2>=0 && x<=panel.getWidth()-width/2){ this.x=x-width/2; } //判断范围,当纵向移动在窗口范围内 if(y-height/2>=0 && y<=panel.getHeight()-height/2){ this.y=y-height/2; }}
属性也就是坐标、宽高这些,给子弹加入移动方法
//移动void move(){ new Thread(new Runnable() { @Override public void run() { while(alive){ y-=speed; if(y<=0){ clear(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();}
飞机类加入射击方法,200毫秒创建一发子弹
//射击void shoot() { new Thread(new Runnable() { @Override public void run() { while(alive){ //创建子弹 createBullet(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } private void createBullet() { Bullet bullet = new Bullet(x+width/2-10, y, 20, 30, panel); panel.bulletList.add(bullet); new MusicPlayer('/music/shoot.wav').play(); } }).start();}
别忘记在paint方法里面绘制子弹出来
//绘制子弹Bullet bullet=null;for (int i = 0; i < bulletList.size(); i++) { bullet = (Bullet)bulletList.get(i); bullet.draw(g);}
创建抽象类Plane
package main;import java.awt.Graphics;public abstract class Plane { public abstract void move(); public abstract void draw(Graphics g); public abstract void boom(); public abstract void clear(); abstract int getX(); abstract int getY(); abstract int getWidth(); abstract int getHeight();}
创建敌机子类
有个特殊一点的地方:因为有4种敌机,这里随机1、2、3、4这4个数字作为属性index,然后根据这个index来展现不同的飞机图片,当然也可以通过这个index来设置敌机不同的移动速度,但是我为了偷懒,就全部一样的移动速度,嘿嘿。
移动就是开启线程让y坐标增加,没什么好讲的,这里加一个飞机碰撞,就是当敌机跟我方飞机如何判断碰撞的问题。
撞机分析(敌机与我机的撞机)
1.判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了。
2.如果1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是表示碰撞了。
//判断飞机与子弹是否碰撞private boolean isPoint(MyPlane plane) { /* * * 两种情况 * 1.需要判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了 * 2.如果步骤1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是标志碰撞了 */ //方式1 //左上角 int x1 = x; int y1 = y; //右上角 int x2 = x+width; int y2 = y; //右下角 int x3 = x+width; int y3 = y+height; //左下角 int x4 = x; int y4 = y+height; //只要有一个点在范围内,则判断为碰撞 if(comparePointMyPlane(x1,y1,plane)|| comparePointMyPlane(x2,y2,plane)||comparePointMyPlane(x3,y3,plane)||comparePointMyPlane(x4,y4,plane) ){ return true; } //方式1没成立则用方式2判断 //方式2 x1 = plane.getX(); y1 = plane.getY(); //右上角 x2 = plane.getX()+plane.getWidth(); y2 = plane.getY(); //右下角 x3 = plane.getX()+plane.getWidth(); y3 =plane.getY()+plane.getHeight(); //左下角 x4 = plane.getX(); y4 = plane.getY()+plane.getHeight(); if(comparePoint(x1,y1)|| comparePoint(x2,y2)||comparePoint(x3,y3)||comparePoint(x4,y4) ){ return true; } return false;}//用敌机的坐标来判断private boolean comparePointMyPlane(int x,int y,MyPlane plane){ //大于左上角,小于右下角的坐标则肯定在范围内 if(x>plane.getX() && y >plane.getY() && x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight() ){ return true; } return false;}//用我机的坐标来判断private boolean comparePoint(int x,int y){ //大于左上角,小于右下角的坐标则肯定在范围内 if(x>this.x && y >this.y && x<this.x+this.width && y <this.y+this.height){ return true; } return false;}
测试一下效果
//判断击中敌机protected void hitEnemy() { EnemyPlane enemyPlane=null; List enemys = panel.enemyList; for (int i = 0; i < enemys.size(); i++) { try { enemyPlane = (EnemyPlane)enemys.get(i); } catch (Exception e) { } if(enemyPlane==null) continue; if(this.isPoint(enemyPlane)){ panel.curCount+=enemyPlane.getCount(); //删除当前子弹 clear(); //飞机爆炸 enemyPlane.boom(); if(panel.curCount>=panel.totalCount){ panel.myPlane.setCanMove(false); panel.gameWin(); } } }}//判断飞机与子弹是否碰撞private boolean isPoint(EnemyPlane plane) { //因为子弹比飞机小,所以只需要判断子弹的4个点是否在飞机范围内,如果有则表示碰撞了 //左上角 int x1 = x; int y1 = y; //右上角 int x2 = x+width; int y2 = y; //右下角 int x3 = x+width; int y3 = y+height; //左下角 int x4 = x; int y4 = y+height; //只要有一个点在范围内,则判断为碰撞 if(comparePoint(x1,y1,plane)|| comparePoint(x2,y2,plane)||comparePoint(x3,y3,plane)||comparePoint(x4,y4,plane) ){ return true; } return false;}private boolean comparePoint(int x,int y,EnemyPlane plane){ //大于左上角,小于右下角的坐标则肯定在范围内 if(x>plane.getX() && y >plane.getY() && x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight() ){ return true; } return false;}
最后加上计分的、胜利、失败等提示就完成了!
联系客服