前言:在一定的java基础上就可以进行飞机大战小游戏的编写了。整个小游戏主要涉及到的基础知识为:类与对象,鼠标事件监听,线程、重绘等。

整体思路框架

设计一个初始界面,在开始后出现自己的战机和敌机且自己的战机不断地发射子弹。当子弹碰到敌机后加分,若自己碰到敌机则游戏结束。

需要的类
1.入口类 2.自己的战机类 3.敌机类 4.子弹类 5.背景类 (6.抽象类 7.数据类)
在设计类的时候可以注意到,战机、敌机和子弹都属于飞行物体,它们都拥有几个相同的属性和方法。例如XY坐标,长度、宽度、速度,移动方法,绘制方法等。因此可以建立一个抽象的飞行类作为父类,战机类、敌机类、子弹类继承父类的属性和方法,并重写其中的抽象方法,为我们的代码编写提供方便。

一、编写入口类(初始化界面)

首先,建立一个入口类(MainFrame)继承JPanel.实例化一个窗体,并设置窗体的各种属性。

public static void main(String[] args) {// TODO Auto-generated method stubJFrame jf = new JFrame();MainFrame mf = new MainFrame();         //入口类名为MainFramejf.add(mf);jf.setTitle("飞机大战");jf.setDefaultCloseOperation(3);jf.setSize(700,1000);jf.setLocationRelativeTo(null);jf.setVisible(true);mf.iniUI();}

这时候,我们已经拥有了一个界面。其中,iniUI是一个初始化的方法,其中包括了监听和线程两个内容。当然,可以将监听和线程分开来写成监听类和线程类,但因为这个小游戏中用到的内容不多便写在了一起,避免传参出现不便和错误。

二、背景类(天空类)

根据思路,在初始化了界面之后我们需要一个背景类。即在界面中绘制一张背景图,通过图像坐标的改变,实现背景不断的移动,造成战机不断前进的状态景象。可以想象,只有一张图片很难在移动后实现循环的绘制,因此采用两张图片。当一张图片移出画面后另一张接上,同时改变前一张图片的坐标。如此形成循环往复。

//读取图片static {try {bgimg1 = ImageIO.read(Sky.class.getResourceAsStream("/picture/背景.jpg"));//相对位置bgimg2 = ImageIO.read(Sky.class.getResourceAsStream("/picture/背景.jpg"));}catch(IOException e){e.printStackTrace();}}//构造方法public Sky(){height = 1000;speed =1;y1 = 0;y2 = -height;        //从画面外开始画}//绘制背景图public void paint(Graphics g) {g.drawImage(bgimg1, 0, y1, null);g.drawImage(bgimg2, 0, y2, null);}//背景图的移动public void move() {y1+=speed;y2+=speed;if(y1>=height) {y1=-height+1;//此处的1为微小的调整,可根据实际情况修改}else if(y2>=height) {y2=-height+1;}}

在写完这个天空类之后,将其在入口类中实例化后调用它的move方法,就可以看到滚动的天空背景图片了。

三、抽象飞行体类

根据思路,总结出战机、敌机和子弹类都具有的几个共同的属性和方法为:

  • X和Y的坐标
  • 图片的宽度和高度
  • 绘画方法(根据需要重写)
  • 移动方法
  • 是否碰撞方法
  • 获取图片的方法(抽象方法需要重写)
  • 飞行体是否死亡和设置的方法
  • 飞行体是否出界的方法

注意抽象类的声明关键字为abstract

 public int x;public int y;//横纵坐标public int width;public int height;//宽度高度
 //绘画方法
public void paint(Graphics g) {g.drawImage(getImage(), x, y, null);
}//判断碰撞方法
public boolean isHit(Flyobject fly) {int x = obj.x-this.x;int y = obj.y-this.y;int r = 100;//判断半径return (x*x+y*y<r*r);//判断碰撞的方法可以自己定义 可以设置更为宽松或严格的标准
}//是否死亡
public boolean isdead() {return state == dead;
}//设置为死亡状态
public void setdead() {state = dead;
}//获取图片public abstract BufferedImage getImage();//因为各自的移动方法和判断出界有较大不同 写在各自的类里了 当然也可以在这里定义
四、战机类、敌机类、子弹类

战机类、敌机类和子弹类都是飞行体这一抽象类的子类。因此继承飞行体这一父类之后可以直接使用里面定义好的属性值,重写里面的方法即可。

战机类

因为子弹的初始位置在战机的机头位置,因此将创建子弹的方法写在战机类里。实例化战机后调用该方法创建子弹。
继承飞行体抽象类。

private static BufferedImage heroimage;static {try {heroimage = ImageIO.read(Hero.class.getResourceAsStream("/picture/LiPlane.png"));} catch (IOException e) {e.printStackTrace();}}//读取图片
 //构造方法public Hero() {x = 280;y = 800;width = heroimage.getWidth();height = heroimage.getHeight();}public void paint(Graphics g) {g.drawImage(getImage(), x, y, null);}public void move(int x,int y) {this.x = x - width/2;this.y = y - height/2;//此处是左右边界判断if(this.x >= 700-width) {this.x = 700-width;}else if(this.x <= 0){this.x = 0;}//此处是上下边界判断if(this.y >= 1000-height) {this.y = 1000-height;}else if(this.y<=0){this.y = 0;}}//创建子弹方法public Bullet creatBullet() {Bullet b = new Bullet(this.x+this.width/2-2,this.y-2);//调用Bullet构造方法 具体可见下文中bullet类return b;}@Overridepublic BufferedImage getImage() {return heroimage;}
敌机类

在创建敌机类之前应该想到,敌机可以用数组或者用ArrayList来存储,且敌机出现的位置应该是纵坐标一致,横坐标随机。
继承飞行体抽象类。

private static BufferedImage enemyimage;static {try {enemyimage = ImageIO.read(Enemy.class.getResourceAsStream("/picture/敌机1.png"));} catch (IOException e) {e.printStackTrace();}}//创建图片private int enemyspeed;      //飞行速度//构造方法public Enemy() {width = enemyimage.getWidth();height = enemyimage.getHeight();Random rand = new Random();x = (int)(rand.nextInt(700-width));        //随机产生横坐标y = -height;enemyspeed = 2;                          //给定飞行速度为2}public void move() {y+=enemyspeed;}//是否出界public boolean outofbound() {return y > 1000;}@Overridepublic BufferedImage getImage() {return enemyimage;}
子弹类

子弹类的创建与敌机类是大同小异的,同样需要数组或ArrayList来存储子弹对象。
继承飞行体抽象类。

private static BufferedImage bulletImage;private int bulletspeed;        //子弹速度//构造方法public Bullet(int x,int y) {x=x;y=y;height=bulletImage.getHeight();width=bulletImage.getWidth();bulletspeed = 3;}static {try {bulletImage = ImageIO.read(Bullet.class.getResourceAsStream("/picture/zd.png"));} catch (IOException e) {e.printStackTrace();}}public void paint(Graphics g) {g.drawImage(getImage(), x, y, null);}public void move() {this.y-=bulletspeed;}public boolean outofbound() {return this.y <height;}public BufferedImage getImage() {return bulletImage;}
五、回到入口类中

准备工作已经完成,现在可以回到入口类中完成整个代码了。梳理整个过程我们应该可以得知,我们还应该解决至少以下几个疑问:

  1. 如何创建子弹和敌机?
  2. 如何绘制战机、敌机和子弹?
  3. 怎样调用它们的移动方法?
  4. 在子弹碰撞敌机后,如何将它们俩从各自的ArrayList中去除?
  5. 如何让它们周而复始地自己完成运动?
  6. 得分机制如何?

因此我们需要以下方法来解决这些问题,并通过这些方法的调用,最终完成我们的飞机大战小游戏。

———————————————————————————————————
实例化sky 和 hero(战机)
创建子弹和敌机的动态数组
定义得分 和 子弹与敌机的计数器
定义战机的状态

private Hero hero = new Hero();
private Sky sky = new Sky();public ArrayList<Bullet> bullets = new ArrayList<Bullet>();
public ArrayList<Enemy> enemies = new ArrayList<Enemy>();//飞机的得分
private int score = 0;//子弹和敌机的计数器,初始化设定为0
private int bulletCount = 0;
private int enemiesCount = 0;//定义战机的状态 开始为0运行为1死亡为2 STATE为当前状态
public static int BEGIN = 0;
public static int RUNNING = 1;
public static int OVER = 2;
public static int STATE = 0;

在这里将移动的方法都写到一个方法里,通过sky对象调用sky类里的move方法。将动态数组中的每个子弹和敌机都取出来,分别调用它们各自的移动方法。

//移动方法public void moveAction() {//背景移动sky.move();//发射子弹for(int i=0;i<bullets.size();i++) {bullets.get(i).move();}//敌机飞行for(int i=0;i<enemies.size();i++) {enemies.get(i).move();}}

通过已经实例化的对象调用各种绘画方法

public void paint(Graphics g) {super.paint(g);sky.paint(g);//开始状态时绘制背景图(logoImage)if(STATE == 0) {g.drawImage(logoImage, 0, 0, null);}//绘制己方飞机hero.paint(g);//绘画得分if(STATE!=0) {paintScore(g);}for(int i= 0 ;i< bullets.size();i++) {bullets.get(i).paint(g);}for(int i= 0;i<enemies.size();i++) {enemies.get(i).paint(g);}//overImage为结束时候的画面 即如果战机状态为死亡则绘制结束画面if(STATE==2) {g.drawImage(overImage, 6, 400, null);}}//绘制分数的方法public void paintScore(Graphics g) {Font font = new Font(Font.SANS_SERIF,Font.BOLD,30);    //设置字体g.setColor(Color.RED);    //设置颜色g.setFont(font);g.drawString("SCORE:"+score, 15, 45);       //绘画文字}

创建和移除子弹的方法
因为创建和移除敌机的方法几乎完全相同,因此不再赘述

public void createBullet() {bulletCount++;//此处的50即为控制子弹生成的速率的参数if(bulletCount % 50==0) {Bullet b = hero.creatBullet();bullets.add(b);       //在动态数组中加入新的子弹bulletCount=0;}}public void removeBullet() {for(int i=0;i<bullets.size();i++) {if(bullets.get(i).outofbound()==true || bullets.get(i).isdead()==true) {bullets.remove(i);}}//如果子弹超过边界或者子弹已经判定为死亡状态则移除子弹}

处理碰撞情况的方法(自己和敌机碰撞、敌机和子弹碰撞)

//子弹和敌机碰撞
public void bulletHitEnemy() {for(int i=0;i<bullets.size();i++) {Bullet bullet = bullets.get(i);for(int j = 0;j<enemies.size();j++) {Flyobject enemy = enemies.get(j);if(bullet.isHit(enemy)) {//设置子弹和敌机的状态为死亡bullet.setdead();enemy.setdead();//加分score += 2;}}}}//自己和敌机碰撞public void herohitenemy() {for(int i=0;i<enemies.size();i++) {Flyobject enemy = enemies.get(i);if(hero.isHit(enemy)) {//设置状态为死亡  enemy.setdead();hero.setdead();STATE = OVER;}}}

最后的准备工作也都已经完成了,现在只需要增加界面的监听并在线程中调用它们即可,在文章最前提到的iniUI 方法将实现它们。

public void iniUI() {MouseAdapter adapter = new MouseAdapter() {public void mouseClicked(MouseEvent e) {if(STATE==BEGIN) {STATE = RUNNING;}else if(STATE==OVER) {STATE = BEGIN;score = 0;//重新初始化自己的飞机hero = new Hero();//重新初始化子弹数组bullets = new ArrayList<Bullet>();//重新初始化敌机数组enemies = new ArrayList<Enemy>();}}public void mouseMoved(MouseEvent e) {if(STATE==RUNNING) {int x1 = e.getX();int y1 = e.getY();hero.move(x1,y1);}}};this.addMouseListener(adapter);this.addMouseMotionListener(adapter);//定义游戏的定时器//所要执行的任务 延迟时间(毫秒) 执行各后续任务的时间间隔(毫秒)Timer timer = new Timer();timer.schedule(new TimerTask(){public void run() {if(STATE == RUNNING) {moveAction();createBullet();removeBullet();createEnemy();removeEnemy();//子弹和敌机碰撞的方法bulletHitEnemy();//自己和敌机碰撞的方法herohitenemy();}repaint();}},10,10);}

此处有两点需要特别提示的地方。一是没有新建监听类进行监听。adapter是适配器的意思,也就是使用了MouseAdapter类了以后只需要重写自己需要的监听方法即可,不需要将所有的方法都进行重写。二是没有新建线程类,而是使用了Timer类。其实,Timer类实现了Runnable的接口,使用时相当于一个线程。schedule的具体意义在代码段里的注释中已经注明。
此时,已经完成了所有的编写。当然还有很多值得改进的地方,可以将一些常用的方法和数据都写到各自的类里,使入口类的书写更为清晰简洁。

改进思路
  1. 增加敌机被击中后的爆炸动画效果
  2. 抽象类实例化不同的敌机类,得分也设置为不同
  3. 设置奖励系统
  4. 设置生命系统和等级闯关系统,达到一定分数后增加难度
  5. 提供界面使玩家自选难度

如有问题,希望各位大佬指正!

JAVA小游戏之飞机大战(超详细)相关推荐

  1. java小游戏之飞机大战

    飞机大战脚本组成 1.有一个所有物体的父物体GameObject,然后就是一堆物体继承于他,等到他的属性于函数. 2.窗口组件 3.工具脚本(负责做一些杂事和存放图片) 代码 GameObject p ...

  2. Java Swing 经典小游戏《飞机大战》———— (四)碰撞检测 游戏状态与得分 玩家升级

    前期回顾 Java Swing 经典小游戏<飞机大战>---- (一)获取素材,创建窗口,添加滚动背景,双缓冲 Java Swing 经典小游戏<飞机大战>---- (二)玩家 ...

  3. 安卓小游戏:飞机大战

    安卓小游戏:飞机大战 前言 前面写了十二篇自定义view的博客,说实话写的还是有点无聊了,最近调整了一下,觉得还是要对开发有热情,就写了点小游戏,现在抽时间把博客也写一写,希望读者喜欢. 需求 这里就 ...

  4. 微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)

    微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞 ...

  5. java小游戏:五子棋人机大战

    一.java小游戏:五子棋人机大战 1.绘制窗口 package wuziqi;import javax.swing.*; import java.awt.event.MouseAdapter; im ...

  6. python飞机大战加背景音乐_python实现飞机大战小游戏 python飞机大战中的音频文件怎么改成MP3...

    怎么样用Python写飞机大战游戏 python开发飞机大战外星人游戏怎么弄双人模式新的一年,哪怕仍是一个人,也要活得像一支队伍,为自己的头脑和心灵招兵买马,不气馁,有召唤,爱自由. 主函数 impo ...

  7. Python小游戏之 - 飞机大战 !

    用Python写的"飞机大战"小游戏 源代码如下: # coding=utf-8 import random import os import pygame# 用一个常量来存储屏幕 ...

  8. JAVA小游戏有源代码,非常详细的注释,以及自己做的答辩PPT

    JAVA小游戏--阴阳师版三国战纪 阴阳战纪 具体的实现方法 程序 效果 阴阳战纪 由于疫情原因,我们班的生产实习被告知要在线上举行,然后老师给我们上了三天的网课,教我们用JAVA来制作一个小游戏,因 ...

  9. 手把手教你使用Pygame制作飞机大战小游戏,4万字超详细讲解!

    点击上方"早起Python",关注并"星标" 每日接收原创Python干货! 大家好,偷学Python系列是由小甜同学从初学者的角度学习Python的笔记,其特 ...

最新文章

  1. 好想学python怎么猜人_学手艺我好想学个手艺哦可是脑子怎么想也想 – 手机爱问...
  2. mysql 多表查询or_MySQL 多表查询
  3. Android Intent 大全[转载]
  4. Android数据存储——2.文件存储_C_DOM解析XML文档
  5. 每个叶子节点(nil)是黑色。_填充每个节点的下一个右侧节点指针
  6. (转)Vue-初步了解vue-router的三要素:路由map 、路由视图、路由导航
  7. 中国民生银行借助Kubernetes的转型 | 凌云时刻
  8. 0501 0503 模块区别_深圳兼容思科光模块市场
  9. 影视后期制作学习(AE)(父子级链接-表达式)
  10. Android强制竖屏
  11. wxj platte
  12. 微信小程序共享元素+ page-container假页实现弹出效果
  13. Windows下Ubuntu子系统,开启复制粘贴功能方法
  14. 日本亚马逊海淘经验-2015
  15. docker:IPv4 forwarding is disabled. Networking will not work. 解决方法
  16. 微服务架构之:Redis的分布式锁---搭建生产可用的Redis分布式锁
  17. Silverlight开发实践--My Silverlight Clock (源)
  18. vue使用antv-G2
  19. 怎么把PDF转换成CAD文件呢?分享两种转换小技巧
  20. Java 匿名类(通俗易懂)

热门文章

  1. 购物兔最新版本3.2.26.100,上小众推荐和搜狐新闻啦~
  2. ExtAspNet应用技巧(十一) - Subsonic配置与使用
  3. 解决SELinux is preventing *** 的问题
  4. 路由器能运行linux程序么,用Linux系统做路由器
  5. 【C语言】反弹球消砖块游戏
  6. 画图专用平板Cintiq 13HD评测
  7. 用TreeSet存储以下数据: “xiaoqiang”、 “zhangsan”、“lisi”、“xiaohua”、 “ruhua”、 “wangcai” 要求按照字符串的长度进行排序后再存储(如
  8. Android内存管理机制官方详解文档
  9. C# 解决定时器本周期没执行完,又开始下一次调用
  10. TOTAL COMMANDER 7 ( TC7 ) 的遺憾