java实现飞机大战案例详解

前言

飞机大战是一个非常经典的案例,因为它包含了多种新手需要掌握的概念,是一个非常契合面向对象思想的入门练习案例

程序分析:

在此游戏中共有六个对象:
小敌机Airplane,大敌机BigAirplane,小蜜蜂Bee,天空Sky,英雄机Hero,子弹Bullet

其次我们还需要三个类:

超类Flyer,图片类Images,测试类World

还需:

英雄机2张,小敌机,大敌机,小蜜蜂,子弹,天空各1张,爆炸图4张,游戏开始,暂停,游戏结束各1张,共14张图片放入与图片类Images同包中

超类Flyer:

此类是用来封装所有对象共有的行为及属性的
不管是写什么程序,都建议遵循两点:数据私有化,行为公开化

import java.util.Random;
import java.awt.image.BufferedImage;

public abstract class Flyer {
 //所有对象都有三种状态:活着的,死了的,及删除的
 //这里之所以选择用常量表示状态是因为首先状态是一个不需要去修改的值
 //其次状态需要反复使用所以结合这两个特点,我选择了使用常量表示
 //state是用来表示当前状态的,每个对象都有一个实时的状态,此状态是会改变的,且初始状态都是活着的
 public static final int LIVE = 0;//活着的
 public static final int DEAD = 1;//死了的
 public static final int REMOVE = 2;//删除的
 protected int state = LIVE;//当前状态(默认状态为活着的)

 每个对象都是一张图片,既然是图片那么就一定有宽高,其次因为每个对象都是会随时移动的 即为都有x,y坐标
 protected int width;//宽
 protected int height;//高
 protected int x;//左右移动(x坐标)
 protected int y;//上下移动(y坐标)

 /**
 * 飞行物移动(抽象)
 * 每个飞行物都是会移动的,但是移动方式不同
 * 所以这里就将共有的行为抽到了超类中
 * 但是设置成了抽象方法,实现了多态的效果
 */
 public abstract void step();

 /**
 * 获取图片(抽象)
 * 所有对象都是图片但图片不相同所以抽象化了
 */
 public abstract BufferedImage getImage();

 /**
 * 判断对象是否是活着的
 */
 public boolean isLive(){
 return state == LIVE;
 }
 /**
 * 判断对象是否是死了的
 */
 public boolean isDead(){
 return state == DEAD;
 }
 /**
 * 判断对象是否删除了
 */
 public boolean isRemove(){
 return state == REMOVE;
 }

 /**
 * 判断对象(大敌机,小敌机,小蜜蜂)是否越界
 * 当敌人越界我们就需要删除它否则程序越执行越卡,会出现内存泄露的问题,此方法就是为后续删除越界对象做铺垫的
 * @return
 */
 public boolean isOutOfBounds(){
 return y >= World.HEIGHT;
 }
 /**
 * 给小/大敌机,小蜜蜂提供的
 * 因为三种飞行物的宽,高不同所以不能写死。
 * 若三种飞行物的宽,高相同,那么就可以将宽,高写死
 */
 public Flyer(int width,int height){
 Random rand = new Random();
 this.width = width;
 this.height = height;
 x = rand.nextInt(World.WIDTH-width);//x:0到负的width长度的之间的随机数
 y = -height;//y:负的height高度
 }

 /**
 * 给天空,子弹,英雄机提供的
 * 因为英雄机,子弹,天空的宽,高,x,y都是不同的,所以数据不能写死,需要传参
 */
 public Flyer(int width,int height,int x,int y){
 this.width = width;
 this.height = height;
 this.x = x;
 this.y = y;
 }

 /**
 *检测碰撞
 * this:敌人(小敌机/小蜜蜂/大敌机)
 * other:子弹/英雄机
 *@return
 */
 public boolean isHit(Flyer other){
 int x1 = this.x - other.width;//x1:敌人的x-英雄机/子弹的宽
 int x2 = this.x + this.width;//x2:敌人的x加上敌人的宽
 int y1 = this.y - other.height;//y1:敌人的y-英雄机/子弹的高
 int y2 = this.y + this.height;//y2:敌人的y加上敌人的高
 int x = other.x;//x:英雄机/子弹的x
 int y = other.y;//y:英雄机/子弹的y
 /*
 x在x1与x2之间 并且 y在y1与y2之间,即为撞上了
 */
 return x>x1 && x<=x2 && y>=y1 && y<=y2;
 }

 /**
 * 飞行物死亡
 */
 public void goDead(){
 state = DEAD;//将当前状态修改为死了的
 }
}

图片工具类Images:

此类用来获取每个对象对应的图片

import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
/**
 * 图片工具类
 */
public class Images {
// 公开的 静态的 图片数据类型 变量名
 /**
 * 对象图片
 */
 public static BufferedImage sky;//天空
 public static BufferedImage bullet;//子弹
 public static BufferedImage[] heros;//英雄机
 public static BufferedImage[] airs;//小敌机
 public static BufferedImage[] bairs;//大敌机
 public static BufferedImage[] bees;//小蜜蜂

 /**
 * 状态图片
 */
 public static BufferedImage start;//启动状态图
 public static BufferedImage pause;//暂停状态图
 public static BufferedImage gameover;//游戏结束状态图

 static {//初始化静态图片
 sky = readImage("background01.png");//天空
 bullet = readImage("bullet.png");//子弹
 heros = new BufferedImage[2];//英雄机图片数组
 heros[0] = readImage("hero0.png");//英雄机图片1
 heros[1] = readImage("hero1.png");//英雄机图片2
 airs = new BufferedImage[5];//小敌机图片数组
 bairs = new BufferedImage[5];//大敌机图片数组
 bees = new BufferedImage[5];//小蜜蜂图片数组
 airs[0] = readImage("airplane.png");//小敌机图片读取
 bairs[0] = readImage("bigairplane.png");//大敌机图片读取
 bees[0] = readImage("bee01.png");//小蜜蜂图片读取

 /**爆炸图迭代读取*/
 for (int i=1;i<5;i++){//遍历/迭代赋值
 airs[i] = readImage("bom"+i+".png");//小敌机图片数组其余元素赋值爆炸图
 bairs[i] = readImage("bom"+i+".png");//大敌机图片数组其余元素赋值爆炸图
 bees[i] = readImage("bom"+i+".png");//小蜜蜂图片数组其余元素赋值爆炸图
 }
 start = readImage("start.png");//启动状态图
 pause = readImage("pause.png");//暂停状态图
 gameover = readImage("gameover.png");//游戏结束状态图
 }

 /**
 * 读取图片
 * 此处的fileName:图片文件名
 *
 * try.....catch:异常的一种处理方法
 */
 public static BufferedImage readImage(String fileName){
 try{
 BufferedImage img = ImageIO.read(Flyer.class.getResource(fileName)); //读取与Flyer在同一个包中的图片
 return img;
 }catch(Exception e){
 e.printStackTrace();
 throw new RuntimeException();
 }
 }
}

世界窗口类/测试类 World:

此类用来集合所有类进行排序及具体的操作,和程序的最终运行

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.nio.Buffer;
//定时器
import java.util.Timer;
//定时器任务
import java.util.TimerTask;
//打开随机类
import java.util.Random;
//扩容类
import java.util.Arrays;
/**
 * 世界测试类(整个游戏窗口)
 */
public class World extends JPanel{
 public static final int WIDTH = 400;//窗口宽
 public static final int HEIGHT = 700;//窗口高

 public static final int START = 0;//启动状态
 public static final int RUNNING = 1;//运行状态
 public static final int PAUSE = 2;//暂停状态
 public static final int GAME_OVER = 3;//游戏结束状态
 private int state = START;//当前状态默认是启动状态

 /**
 * 声明每个类具体的对象
 * 如下为:窗口中所看到的对象
 */
 private Sky s = new Sky();//天空对象
 private Hero h = new Hero();//英雄机对象
 private Flyer[] enemies ={};//敌人对象,分别是大敌机,小敌机,小蜜蜂所以写成了数组
 private Bullet[] bt ={};//子弹也是有很多的所以写成了数组

 /**
 * 生成敌人对象(小敌机,大敌机,小蜜蜂)
 */
 public Flyer nextOne(){
 Random rand = new Random();
 int type = rand.nextInt(20);//0-19之间的随机数
 if (type < 5){//当随机数小于5
 return new Bee();//返回小蜜蜂
 }else if (type < 13){//当随机数小于13
 return new Airplane();//返回小敌机
 }else{//大于十三则
 return new BigAirplane();//返回大敌机
 }
 }

 private int enterIndex = 0;
 /**
 * 敌人(大敌机,小敌机,小蜜蜂)入场
 */
 public void enterAction() {//每10毫秒走一次
 enterIndex++;
 if (enterIndex%40 == 0 ){//四百毫秒走一次
 Flyer fl = nextOne();//获取敌人对象
 enemies = Arrays.copyOf(enemies,enemies.length+1);//扩容(每产生一个敌人数组就扩容1)
 enemies[enemies.length-1] = fl;//将生成的敌人fl放置enemies数组的末尾
 }
 }

 int shootIndex = 0;
 /**
 * 子弹入场
 */
 public void shootAction(){//10毫秒走一次
 shootIndex++;
 if (shootIndex%30 == 0){//每300毫秒走一次
 Bullet[] bs = h.shoot();//获取子弹数组对象
 bt = Arrays.copyOf(bt,bt.length+bs.length);//扩容子弹数组(每入场一个子弹就加一个元素)
 System.arraycopy(bs,0,bt,bt.length-bs.length,bs.length);//数组的追加
 }
 }

 /**
 * 让除去英雄机外的所有对象(小敌机,大敌机,小蜜蜂,子弹,天空)移动
 */
 public void setpAction() {//每10毫秒走一次
 s.step();//天空移动
 for (int i=0;i<enemies.length;i++) {//遍历所有敌人
 enemies[i].step();//敌人移动
 }
 for (int i=0;i<bt.length;i++){//遍历所有子弹
 bt[i].step();//子弹移动
 }
 }

 /**
 * 重写outOfBoundsAction(方法)
 */
 public void outOfBoundsAction() {//每10毫秒走一次
 for (int i=0;i<enemies.length;i++){//遍历所有敌人
 if (enemies[i].isOutOfBounds() || enemies[i].isRemove()){
 enemies[i] = enemies[enemies.length-1];//最后一个敌人和越界敌人替换
 enemies = Arrays.copyOf(enemies,enemies.length-1);//缩容
 }
 }
 for (int i=0;i<bt.length;i++){//迭代所有子弹
 if (bt[i].isOutOfBounds() || bt[i].isRemove()){
 bt[i] = bt[bt.length-1];//用最后一个子弹替换出界的子弹
 bt = Arrays.copyOf(bt,bt.length-1);//缩容
 }
 }
 }
 private int score = 0;//玩家的得分
 /**
 * 子弹与敌人的碰撞
 */
 public void bulletBangAction() {//每10毫秒走一次
 for (int i=0;i<bt.length;i++){//遍历所有子弹
 Bullet b = bt[i];//获取每一个子弹
 for (int j=0;j<enemies.length;j++){//迭代每一个敌人
 Flyer f = enemies[j];//获取每一个敌人
 if (b.isLive() && f.isLive() && f.isHit(b)){//若子弹活着的,敌人活着的,并且两个对象相撞
  b.goDead();//子弹当前状态修改为死亡
  f.goDead();//敌人当前状态修改为死亡
  if (f instanceof EnemyScore) {//判断死亡的敌人类型能否强转为得分接口类型
  EnemyScore es = (EnemyScore) f;//将死亡敌人向下造型
  score += es.getScore();//调用具体的敌人对象的得分接口的getScore()加分方法
  }
  if (f instanceof EnemyAward){//判断死亡的敌人类型能否强转为奖励值接口类型
  EnemyAward ea = (EnemyAward) f;//将死亡敌人强转为奖励值接口类型
  int type = ea.getAwardType();//将具体的奖励值赋值给type
  switch (type){
  case EnemyAward.FIRE://火力值
  h.addFier();//返回增加火力值
  break;
  case EnemyAward.LIFE://生命值
  h.addLife();//返回增加生命值
  break;
  }
  }
 }
 }
 }
 }

 /**
 * 英雄机与敌人的碰撞
 */
 private void heroBangAction() {//每10毫秒走一次
 for (int i=0;i<enemies.length;i++){//迭代所有敌人
 Flyer f = enemies[i];//获取每个敌人
 if (f.isLive() && h.isLive() && f.isHit(h)){//判断碰撞
 f.goDead();//敌人死亡
 h.subtractLife();//英雄机减生命值
 h.clearFier();//英雄机清空火力值
 }
 }
 }

 /**
 * 检测游戏结束
 */
 private void checkGameOverAction() {//每10毫秒走一次
 if (h.getLife() <= 0) {//若英雄机生命值为0或小于0
 state = GAME_OVER;//将状态修改为GAME_OVER游戏结束状态
 }
 }
 /**
 * 启动程序的执行
 */
 public void action() {//测试代码
 MouseAdapter m = new MouseAdapter() {
 /**
 * 重写mouseMoved()鼠标移动事件
 * @param e
 */
 @Override
 public void mouseMoved(MouseEvent e) {
 if (state == RUNNING){//仅在运行状态下执行
  int x = e.getX();//获取鼠标的x坐标
  int y = e.getY();//获取鼠标的y坐标
  h.moveTo(x,y);//接收鼠标具体坐标
 }
 }

 /**
 * 重写mouseClicked() 鼠标点击事件
 * @param e
 */
 public void mouseClicked(MouseEvent e){
 switch (state){//根据当前状态做不同的处理
  case START://启动状态时
  state = RUNNING;//鼠标点击后改成运行状态
  break;
  case GAME_OVER://游戏结束状态时
  /*清理战场(将所有数据初始化)*/
  score = 0;//总分归零
  s = new Sky();//天空初始化所有属性
  h = new Hero();//英雄机初始化所有属性
  enemies = new Flyer[0];//敌人初始化所有属性
  bt = new Bullet[0];//子弹初始化所有属性

  state = START;//鼠标点击后修改为启动状态
  break;
 }
 }

 /**
 * 鼠标移出窗口事件
 * @param e
 */
 public void mouseExited(MouseEvent e){
  if (state == RUNNING){//若状态为运行
  state = PAUSE;//则将当前状态修改为暂停
  }
 }

 /**
 * 鼠标的进入窗口事件
 * @param e
 */
 public void mouseEntered(MouseEvent e){
 if (state == PAUSE){//若当前状态为暂停
  state = RUNNING;//则将当前状态修改为运行
 }
 }
 };
 this.addMouseListener(m);
 this.addMouseMotionListener(m);
 Timer timer = new Timer();//定时器对象
 int interval = 10;//定时的间隔(此间隔是以毫秒为单位)
 timer.schedule(new TimerTask() {
 @Override
 public void run() {//定时干的事(每10毫秒自动执行此方法当中的所有方法)
 if (state == RUNNING){//只在运行状态下执行
  enterAction();//敌人(大敌机,小敌机,小蜜蜂)入场
  shootAction();//子弹入场
  setpAction();//飞行物移动
  outOfBoundsAction();//删除越界的敌人
  bulletBangAction();//子弹与敌人的碰撞
  heroBangAction();//英雄机与敌人的碰撞
  checkGameOverAction();//检测游戏结束
 }
 repaint();//重新调用paint()方法(重画)
 }
 }, interval, interval);//定时计划表
 }

 /**
 * 重写paint方法,在窗口中画图片
 * @param g:画笔
 */
 public void paint(Graphics g){//每10毫秒走一次
 g.drawImage(s.getImage(), s.x, s.y, null);//画天空
 g.drawImage(s.getImage(), s.x, s.getY1(), null);//画第二张天空
 g.drawImage(h.getImage(),h.x,h.y,null);//画英雄机
 for (int i=0;i<enemies.length;i++){//遍历所有敌人
 Flyer f = enemies[i];//获取每一个敌人
 g.drawImage(f.getImage(),f.x,f.y,null);//画敌人
 }
 for (int i = 0; i<bt.length; i++){//遍历所有子弹
 Bullet b = bt[i];//获取所有子弹
 g.drawImage(b.getImage(),b.x,b.y,null);//画子弹
 }
 g.drawString("SCORE:"+score,10,25);//在窗口右上角画分数
 g.drawString("HP:"+h.getLife(),10,45);//在窗口右上角画出英雄机的生命值
 switch (state){//画状态图
 case START:
 g.drawImage(Images.start,0,0,null);//启动状态图
 break;
 case PAUSE:
 g.drawImage(Images.pause,0,0,null);//暂停图
 break;
 case GAME_OVER:
 g.drawImage(Images.gameover,0,0,null);//游戏结束图
 break;
 }
 }

 /**
 * 主执行方法
 * @param args
 */
 public static void main(String[] args) {
 JFrame frame = new JFrame();
 World world = new World();
 frame.add(world);
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 frame.setSize(WIDTH,HEIGHT);
 frame.setLocationRelativeTo(null);
 frame.setVisible(true);//1)设置窗口可见 2)尽快调用paint()方法

 world.action();//启动程序的执行
 }
}
/**
 * 1.问:为什么要将引用设计再main方法的外面?
 * 答:因为若将引用设计在main中,则引用只能在main中使用,其他方法都不能访问,
 * 为了能在其他方法中也能访问这些引用,所以将引用设计在main外
 *
 * 2.问:为什么要单独创建action方法来测试?
 * 答:因为main方法时static的,在main方法中是无法访问引用的,
 * 所以需要单独创建非static的方法来测试
 *
 * 3.问:为什么在main中要先创建world对象,然后再调用action()方法?
 * 答:因为main方法是static的,再main中是无法调用action()方法的
 * 所以要先创建world对象,然后再调用action()方法
 */

小敌机类Airplane:

此类存储小敌机特有的属性及行为:
移动速度,分值,及图片的切换
继承超类,且实现得分接口

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 小敌机
 */
public class Airplane extends Flyer implements EnemyScore{
 // 移动速度
 private int speed;

 public Airplane(){
 super(66,89);
 speed = 2;//小敌机的下落速度
 }
 /**重写step方法(移动)*/
 public void step(){
 y += speed;//y+表示向下
 }
 int index = 1;
 /**
 * 重写getImage()获取对象图片
 * @return
 */
 public BufferedImage getImage() {
 if (isLive()){//若活着 则返回airs[0]图片
 return Images.airs[0];
 }else if (isDead()){//若死了 则返回airs[1~4]图片
 BufferedImage img = Images.airs[index++];//获取爆破图
 if (index == Images.airs.length){//若index到了5 则表示到了最后一张
 state = REMOVE;//将当前状态修改为REMOVE删除的
 }
 return img;//返回爆炸图
 /*
 index = 1
 10M isLive返回true 则 return返回airs[0]图片
 20M isLive返回false 则 执行isDead返回true img = airs[1] index = 2  返回airs[1]图片
 30M isLive返回false 则 执行isDead返回true img = airs[2] index = 3  返回airs[2]图片
 40M isLive返回false 则 执行isDead返回true img = airs[3] index = 4  返回airs[3]图片
 50M isLive返回false 则 执行isDead返回true img = airs[4] index = 5 state修改为REMOVE 返回airs[4]图片
 60M isLive返回false 则 执行isDead返回false return返回null空值(不返回图片)
 */
 }
 return null;
 }

 /**
 * 重写getScore()方法
 * @return:分值
 */
 public int getScore(){
 return 1;
 }
}

大敌机类BigAirplane:

大敌机与小敌机几乎无差别
同样要继承超类,且实现得分接口

package cn.tedu.shoot;

import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 大敌机
 */
public class BigAirplane extends Flyer implements EnemyScore{
// 移动速度
 private int speed;

 public BigAirplane(){//初始化默认属性
 super(203,211);//图片宽,高
 speed = 2;//移动速度
 }

 /**重写step方法(移动)*/
 public void step(){
 y += speed;//y+表示直线向下移动
 }

 int index = 1;
 @Override
 public BufferedImage getImage() {
 if (isLive()){//若活着 则返回airs[0]图片
 return Images.bairs[0];
 }else if (isDead()){//若死了 则返回airs[1~4]图片
 BufferedImage img = Images.bairs[index++];//获取爆破图
 if (index == Images.bairs.length){//若index到了5 则表示到了最后一张
 state = REMOVE;//将当前状态修改为REMOVE删除的
 }
 return img;
 }
 return null;
 }

 /**
 * 重写getScore()方法
 * @return:分值
 */
 public int getScore(){
 return 3;
 }
}

小蜜蜂类Bee:

此类虽也可以算作敌人类,但是与小/大敌机有所不同,它是实现奖励值接口

package cn.tedu.shoot;

import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 小蜜蜂
 */
public class Bee extends Flyer implements EnemyAward{
 // x坐标移动速度,y坐标移动速度,
 private int xSpeed;//x坐标移动速度
 private int ySpeed;//y坐标移动速度
 private int awardType;//奖励类型

 public Bee(){//初始化属性
 super(48,50);//图片宽,高
 Random rand = new Random();
 awardType = rand.nextInt(2);//随机奖励值类型0~2之间(不包括2)0表示火力值,1表示生命值
 xSpeed = 1;//平行移动
 ySpeed = 2;//垂直移动
 }
 /**重写step方法(移动)*/
 public void step() {
 y += ySpeed;//y+:向下移动
 x += xSpeed;//x+:随机向左或是向右移动
 if (x <= 0 || x >= World.WIDTH - width) {
 xSpeed *= -1;//到达边界后反方向移动(正负为负,负负为正)
 }
 }

 int index = 1;
 public BufferedImage getImage() {
 if (isLive()){//若活着 则返回airs[0]图片
 return Images.bees[0];//返回小蜜蜂图
 }else if (isDead()){//若死了 则返回airs[1~4]图片
 BufferedImage img = Images.bees[index++];//获取爆破图
 if (index == Images.bees.length){//若index到了5 则表示到了最后一张
 state = REMOVE;//将当前状态修改为REMOVE删除的
 }
 return img;//返回爆炸图
 }
 return null;
 }

 /**
 * 重写getAwardType()方法
 * @return
 */
 public int getAwardType(){
 return awardType;//返回奖励类型
 }
}

天空类Sky:

这里有一点需要强调,就是为了实现天空图片向下移动后会出现移动过的位置出现图片丢失的情况,就使用了两张图上下拼接起来,当第某张天空图完全移出窗口的时候会让它重新出现在窗口上方继续向下移动

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 天空
 */
public class Sky extends Flyer{
 // 移动速度,y1
 private int y1;//第二张图片的y坐标
 private int speed;//移动速度

 public Sky(){//设置初始值(默认值)
 //此处的宽高用常量是因为天空的宽高和窗口是一致的,x轴和y轴为若不为0就和窗口不匹配了
 super(World.WIDTH,World.HEIGHT,0,0);//初始化图片坐标及宽,高
 speed = 1;//初始化移动速度
 y1 = -World.HEIGHT;//第二张图片设置在第一张图片上方
 }
 /**重写step方法(移动)*/
 public void step(){
 y += speed;//第一张图向下移动
 y1 += speed;//第二张图向下移动
 if (y >= World.HEIGHT){//若y>=窗口的高
 y = -World.HEIGHT;//将移动出去的第一张天空挪到窗口上方
 }
 if (y1 >= World.HEIGHT){//若第二张天空挪出窗口
 y1 = -World.HEIGHT;//将第二张天空挪到窗口上方
 }
 }
 /**重写getImage()获取对象图片*/
 @Override
 public BufferedImage getImage() {//10毫秒走一次
 return Images.sky;//返回天空图片即可
 }

 /**
 * 获取y1坐标
 */
 public int getY1(){
 return y1;//返回y1
 }
}

英雄机类Hero:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;
/**
 * 英雄机
 */
public class Hero extends Flyer {
 // 命数,火力值
 private int life;//命数
 private int fire;//火力

 /**
 * 初始化英雄机坐标机具体数据
 */
 public Hero() {
 super(97,139,140,400);//宽,高,及初始坐标
 fire = 0;//初始火力值 0:单倍火力
 life = 3;//初始生命值
 }
 /**重写step方法(移动)*/
 public void step(){//每10毫秒走一次
 //因为英雄机是跟随鼠标移动的,而鼠标是在窗口上的所以这里就没有写具体的方法,而是在窗口类中去用鼠标的具体坐标计算出英雄机的移动位置
 }

 int index = 0;//下标
 /**重写getImage()获取对象图片*/
 @Override
 public BufferedImage getImage() {//每10毫秒走一次
 return Images.heros[index++ % Images.heros.length];//heros[0]和heros[1]来回切换
 /*过程
  index = 0
 10M 返回heros[0] index = 1
 20M 返回heros[1] index = 2
 30M 返回heros[0] index = 3
 40M 返回heros[1] index = 4
 50M 返回heros[0] index = 5
 60M 返回heros[1] index = 6
 ...........
 */
 }
 /**
 * 英雄机发射子弹(生成子弹对象)
 */
 public Bullet[] shoot(){
 int xStep = this.width/4;//子弹x坐标
 int yStep = 5;//子弹y坐标
 System.out.println(this.x+"\t"+this.y);
 if (fire>0){//双倍火力
 Bullet[] bs = new Bullet[3];//2发子弹
 bs[0] = new Bullet(this.x+1*xStep,this.y-yStep);//子弹坐标1
 bs[1] = new Bullet(this.x+3*xStep,this.y-yStep);//子弹坐标2
 bs[2] = new Bullet(this.x+2*xStep,this.y-yStep);
 fire -= 2;//发射一次双倍活力,则火力值-2
 return bs;
 } else {//单倍火力
 Bullet[] bs = new Bullet[1];//1发子弹
 bs[0] = new Bullet(this.x+2*xStep,this.y-yStep);//x:英雄机的x+2/4英雄机的宽,y:英雄机的y-
 return bs;
 }
 }

 /**
 * 英雄机移动
 */
 public void moveTo(int x,int y){//形参列表:鼠标的x坐标,y坐标
 this.x = x - this.width/2;//英雄机的x = 鼠标的x减1/2英雄机的宽
 this.y = y - this.height/2;//英雄机的y = 鼠标的y减1/2英雄机的高
 }
 /**
 * 英雄机增生命值
 */
 public void addLife(){
 life++;//生命值+1
 }

 /**
 * 获取英雄机生命值
 * @return
 */
 public int getLife(){
 return life;//返回生命值
 }

 /**
 * 英雄机减少生命值
 */
 public void subtractLife(){
 life--;//生命值减1
 }
 /**
 * 英雄机增火力值
 */
 public void addFier(){
 fire += 40;//火力值+40
 }

 /**
 * 清空火力值
 */
 public void clearFier(){
 fire = 0;//火力值归零
 }
}

子弹类Bullet:

package cn.tedu.shoot;

import java.awt.image.BufferedImage;

/**
 * 子弹
 */
public class Bullet extends Flyer {
 // 移动速度
 private int speed;
 public Bullet(int x,int y) {//子弹有多个,每个子弹的初始坐标都不同,所以要写活
 super(8,20,x,y);
 speed = 3;//初始移动速度
 }
 /**重写step方法(移动)*/
 public void step(){
 y -= speed;//y-:表示直线向上移动
 }

 /**
 * 重写getImage()获取对象图片
 * @return
 */
 @Override
 public BufferedImage getImage() {//10毫秒走一次
 if (isLive()){//若活着则返回bullet图片
 return Images.bullet;
 }else if (isDead()){//若死了则将state修改为REMOVE
 state = REMOVE;
 }
 return null;//死了的和删除的都返回null空值
 /**
 * 若活着 则返回bullet图片
 * 若死了 则修改REMOVE 再返回空值
 * 若删除 则返回空值
 */
 }

 /**
 * 判断子弹是否越界
 * @return
 */
 public boolean isOutOfBounds(){
 return y <= -height;若子弹的y轴坐标小于自己的高则说明移动到了窗口外部
 }
}

奖励值接口 EnemyAward:

package cn.tedu.shoot;

/**
 * 奖励值接口
 */
public interface EnemyAward {
 public int FIRE = 0;//火力
 public int LIFE = 1;//生命值
 /**
 * 获取奖励值类型
 * @return
 */
 int getAwardType();
}

得分接口 EnemyScore:

package cn.tedu.shoot;
/*得分接口*/
public interface EnemyScore {
 /*得分*/
 public int getScore();
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java实现飞机大战-连接数据库并把得分写入数据库

    编写飞行物类 package com.tarena.shoot; import java.awt.image.BufferedImage; /** * 飞行物(敌机,蜜蜂,子弹,英雄机) */ public abstract class FlyingObject { protected int x; protected int y; protected int width; protected int height; protected BufferedImage image; protecte

  • java版飞机大战实战项目详细步骤

    本文为大家分享了java版飞机大战实战项目,供大家参考,具体内容如下 分析 飞机大战 首先对这个游戏分析,在屏幕上的物体都是飞行物,我们可以把建一个类,让其他飞行物继承这个类.游戏中应有英雄机(也就是自己控制的飞机).敌人.而敌人应该分为打死给分的飞机(就是普通飞机),另一种就是打死有奖励的敌人.他们都应该是飞行物的子类,我们也可以为普通飞机和给奖励的敌人设一个接口让他们去实现接口,这样有利于以后的扩展,我在这里给的简化版的飞机大战,主要是为了让大家了解面向对象. 第一步建立飞行物类 impor

  • java实现飞机游戏代码

    本文实例为大家分享了java实现飞机游戏的具体代码,供大家参考,具体内容如下 MyGameFrame类: 主要的调用类 package sc.wh.game; import javax.swing.JFrame; import java.awt.Color; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import sc.wh.game.*; impor

  • java实现飞机大战游戏

    java实现飞机大战 用Java写个飞机大战游戏练习一下,实现可播放游戏背景音乐和游戏的基本功能 设计 1.准备好相应的图片和背景音乐(.wav文件): 2.直接看源码: 3.还有部分功能未实现. 源码 package forGame 加载游戏图片类 package forGame; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.image.BufferedImage; import java.io.Fil

  • java实现抖音飞机大作战

    本文实例为大家分享了java抖音飞机大作战的具体代码,供大家参考,具体内容如下 Airplane.java package zmf.game.shoot; import java.util.Random; /** * @author jcf * @Description: Airplane----敌机既是飞行物 * @date 2018-03-28 11:17:16 */ public class Airplane extends FlyingObject implements Enemy{ /

  • 微信游戏打飞机游戏制作(java模拟微信打飞机游戏)

    复制代码 代码如下: package com.beancore.ui; import java.awt.BorderLayout;import java.awt.Container;import java.awt.Dimension;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.IOException;import java

  • Java实现打飞机小游戏(附完整源码)

    写在前面 技术源于分享,所以今天抽空把自己之前用java做过的小游戏整理贴出来给大家参考学习.java确实不适合写桌面应用,这里只是通过这个游戏让大家理解oop面向对象编程的过程,纯属娱乐.代码写的很简单,也很容易理解,并且注释写的很清楚了,还有问题,自己私下去补课学习. 效果如下 完整代码 敌飞机 import java.util.Random; 敌飞机: 是飞行物,也是敌人 public class Airplane extends FlyingObject implements Enemy

  • Java实现飞机航班管理系统的思路详解

    学习Java实现飞机航班管理系统,本文有该系统的功能截图,和数据库设计SQL语句供大家参考 1.飞机航班管理系统背景 本系统模拟飞机航班管理业务开发. 2.飞机航班管理系统技术架构 主要技术 Spring.SpringMVC.Mybatis JSP.JSTL.jQuery.HTML.CSS.JS Mysql bootstrap 开发工具和环境 Eclipse Maven Tomcat 7 JDK 1.8 Mysql 5.6 Win10 操作系统 3.飞机航班管理系统数据库设计 CREATE TA

  • java实现飞机大战案例详解

    前言 飞机大战是一个非常经典的案例,因为它包含了多种新手需要掌握的概念,是一个非常契合面向对象思想的入门练习案例 程序分析: 在此游戏中共有六个对象: 小敌机Airplane,大敌机BigAirplane,小蜜蜂Bee,天空Sky,英雄机Hero,子弹Bullet 其次我们还需要三个类: 超类Flyer,图片类Images,测试类World 还需: 英雄机2张,小敌机,大敌机,小蜜蜂,子弹,天空各1张,爆炸图4张,游戏开始,暂停,游戏结束各1张,共14张图片放入与图片类Images同包中 超类F

  • Java之Jackson使用案例详解

    序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象. Json是什么? Jason是 JavaScript Object Notation-  JavaScript对象表示法,是一种轻量级数据交换格式.主要用于数据传输,比如说在后端写了一个Java对象,想在其他地方(前端)使用这个对象,就需要转换为Json这种形式进行传输. 1.

  • Java之Buffer属性案例详解

    一.前言 熟悉NIO的人想必一定不会陌生buffer中position,limit,capacity这三个属性吧,之前在学习的时候遇到一个问题:就是当你先往缓冲区写入一部分数据,然后调用flip()方法,再全部读取完数据,然后再调用flip()方法,此时这三个值的变化是怎样的,研究了一下,决定写下来分享一下. 二.正文 1.介绍 position: 它指的是下一次读取或写入的位置. limit: 指定还有多少数据需要写出(在从缓冲区写入通道时),或者还有多少空间可以读入数据(在从通道读入缓冲区时

  • Java Spring拦截器案例详解

    springmvc提供了拦截器,类似于过滤器,他将在我们的请求具体出来之前先做检查,有权决定接下来是否继续,对我们的请求进行加工. 拦截器,可以设计多个. 通过实现handlerunterceptor,这是个接口 定义了非常重要的三个方法: 后置处理 前置处理 完成处理 案例一: 通过拦截器实现方法耗时统计与警告 package com.xy.interceptors; import org.springframework.web.servlet.HandlerInterceptor; impo

  • Java Thread之Sleep()案例详解

    一.API简介 Thread.sleep()是Thread类的一个静态方法,使当前线程休眠,进入阻塞状态(暂停执行),如果线程在睡眠状态被中断,将会抛出IterruptedException中断异常..主要方法如下: [a]sleep(long millis)  线程睡眠 millis 毫秒 [b]sleep(long millis, int nanos)  线程睡眠 millis 毫秒 + nanos 纳秒 Api文档: 二.使用方法 注意:在哪个线程里面调用sleep()方法就阻塞哪个线程.

  • Java获取当前时间戳案例详解

    获取当前时间戳 //方法 一 System.currentTimeMillis(); //方法 二 Calendar.getInstance().getTimeInMillis(); //方法 三 new Date().getTime(); 获取时间戳三种方法执行效率比较: import java.util.Calendar; import java.util.Date; public class TimeTest { private static long _TEN_THOUSAND=1000

  • Java反射 PropertyDescriptor类案例详解

    JAVA中反射机制(JavaBean的内省与BeanUtils库) 内省(Introspector) 是Java 语言对JavaBean类属性.事件的一种缺省处理方法. JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则.如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为"值对象"(Value Object),或"VO".方法比较少.这些信息储存在类的私有变量中,通过set(

  • Java JVM编译策略案例详解

    解释器 当虚拟机启动时,解释器可以首先发挥作用,而不必等待编译器全部编译完成再执行,这样可以省去许多不必要的编译时间.并且随着程序运行时间的推移,编译器逐渐发挥作用,根据热点探测功能,,将有价值的字节码编译为本地机器指令,以换取更高的程序执行效率. hotspot中内嵌有2个JIT编译器,分别为Client Compiler,Server Compiler,但大多数情况下我们称之为C1编译器和C2编译器. C1编译器 client compiler,又称C1编译器,较为轻量,只做少量性能开销比较

  • Java reservedcodecachesize虚拟机参数案例详解

    一.reservedcodecachesize参数介绍 该参数是JvM虚拟机调优中调整内存大小的一个设置参数,值得大小设置直接影响到Code Cache的大小,而jvm编译的代码有常常存放在Code Cache中,而Code Cache的空间内存又支撑着jvm的正常运行,如果该空间不足jvm虚拟机将会发生问题,并且性能持续降低. Code Cache就是所谓的代码缓存,由于JVM虚拟机的内存默认是有大小限制的,因此代码缓存区域肯定也是有一定大小限制,一般的Windows电脑上64位系统下它的默认

  • Java 括号匹配问题案例详解

    目录 前言 例题 算法思想 算法举例 代码 栈类 括号匹配核心算法 完整代码 运行结果 前言 括号匹配问题算是栈应用中比较经典的问题了,在数据结构的书中还有各种考试中会出现.最近刷题的时候也遇到了,就想写一篇文章整理一下. 例题 题目来自Leetcode中国 给定一个只包括 (,),{,},[,] 的字符串,判断字符串是否有效. 有效字符串需满足: 1.左括号必须用相同类型的右括号闭合. 2.左括号必须以正确的顺序闭合. 注意空字符串可被认为是有效字符串. 示例 1: 输入: "()"

随机推荐