功能分析
Shoot是一款非常有名的射击类小游戏—飞机大战,游戏中,玩家可以驾驶英雄机,在空中进行战斗,通过鼠标点击并移动英雄机,可以发射子弹,打掉飞机或者蜜蜂,从而来获得分数和奖励(分数默认为0,性命为3),打掉一架敌飞机可以获得5分,打掉一个小蜜蜂可以获得20次双倍火力或者增加一条性命的奖励,如果英雄机撞上敌机或者小蜜蜂,则会减少一条性命,并且双倍火力清零,当性命为0时,游戏结束。
1.飞行物父类(后期主要做敌人父类)
import java.awt.image.BufferedImage;
/**飞行物父类*/
public abstract class FlyingObject {
protected BufferedImage image;
protected int width; //宽
protected int height; //高
protected int x; //x坐标
protected int y; //y坐标
public abstract void step(); //走步 10毫秒一次
public abstract boolean outOfBounds(); //检查飞行物是否出界
public boolean shootBy(Bullet bullet){ //检查当前敌人是否被子弹击中
int x1=this.x; //x1:敌人的X
int x2=this.x+this.width; //y1:敌人的x+敌人的宽
int y1=this.y; //敌人的宽
int y2=this.y+this.height; //敌人的y+敌人的高
int x=bullet.x; //子弹的x
int y=bullet.y; //子弹的y
return x>x1&&x<x2&&y>y1&&y<y2; //x在x1和x2之间并且y在y1和y2之间,就是子弹击中敌人
}
}
2.敌人接口,主要体现得分功能
/**接口---敌人得分*/
public interface Enemy {
public int getScore(); //得分
}
3.奖励接口,主要体现奖励类型
/**接口---奖励*/
public interface Award {
public static final int DOUBLE_FIRE=0; //火力
public static final int LIFE=1; //生命
public int getType(); //获取奖励类型(0火力 1生命)
}
4.敌机子类
import java.util.Random;
/**敌机---是飞行物,也是敌人*/
public class Airplane extends FlyingObject implements Enemy {
private int speed=2; //下落步数
public Airplane(){ //构造方法
image=ShootGame.airplane; //图片-静态块已加载
width=image.getWidth(); //写活 可扩展性高
height=image.getHeight();
Random ra=new Random();
x=ra.nextInt(ShootGame.WIDTH-this.width); //屏幕宽到敌机宽之间的随机数
y=-this.height; //负的敌机高
}
public int getScore(){ //重写接口抽象方法
return 5;
}
public void step(){ //重写父类走步方法
y+=speed; //敌机仅仅是向下走 y改变 x不变
}
public boolean outOfBounds(){ //重写父类越界方法
return this.y>=ShootGame.HEIGHT; //若敌机的y坐标大于窗口的高 则出界
}
}
5.蜜蜂子类
import java.util.Random;
/**小蜜蜂---是飞行物,也是奖励*/
public class Bee extends FlyingObject implements Award {
private int xSpeed=1; //x坐标走步步数
private int ySpeed=2; //y坐标走步步数
private int awardType; //奖励类型
public Bee(){ //构造方法
image=ShootGame.bee; //图片-静态块已加载
width=image.getWidth();
height=image.getHeight();
Random ra=new Random();
x=ra.nextInt(ShootGame.WIDTH-this.width); //屏幕宽到蜜蜂宽之间的随机数
y=-this.height; //负的蜜蜂高
awardType=ra.nextInt(2); //奖励类型 0火力,1生命
}
public int getType(){ //重写接口抽象方法
awardType=(int)(Math.random()*2);
return awardType;
}
public void step(){ //重写父类走步方法
y+=ySpeed; //向左或者向右
x+=xSpeed; //向下
if(x>=ShootGame.WIDTH-this.width){
xSpeed=-1;
}
if(x<=0){
xSpeed=1;
}
}
public boolean outOfBounds(){
return this.y>=ShootGame.HEIGHT; //若蜜蜂的y坐标大于窗口的高 则出界
}
}
6.英雄机子类
import java.awt.image.BufferedImage;
import java.util.Random;
/**英雄机---是飞行物*/
public class Hero extends FlyingObject {
private int life; //生命
private int doubleFire; //火力值
private BufferedImage[] images; //图片切换数
private int index; //协助切换图片
public Hero(){ //构造方法
image=ShootGame.hero0; //图片-静态块已加载
width=image.getWidth();
height=image.getHeight();
x=150; //x坐标固定
y=400; //y坐标固定
life=3; //命数为3
doubleFire=0; //火力值为0(单倍火力)
images=new BufferedImage[]{ShootGame.hero0,ShootGame.hero1};//两张图片切换键
index=0; //协助图片切换
}
public void step(){ //重写父类走步方法
/** * index++; * int a=index/10; * int b=a%2; * image=images[b]; */
image=images[index++/10%images.length];//每100毫秒切换一次图片
}
public Bullet[] shoot(){ //发射子弹
int xStep=this.width/4;
int yStep=20;
if(doubleFire>0){ //双倍火力
Bullet[] bs=new Bullet[2]; //双倍火力有俩个火力发射点
bs[0]=new Bullet(this.x+xStep*1,this.y-yStep); //双倍火力的第一发子弹发出位置
bs[1]=new Bullet(this.x+xStep*3,this.y-yStep); //双倍火力的第二发子弹发出位置
doubleFire-=2; //发射一次双倍火力,火力值-2(每个双倍火力可持续发20次)
return bs;
}else{ //单倍火力
Bullet[] bs=new Bullet[1];
bs[0]=new Bullet(this.x+xStep*2,this.y-yStep); //单倍火力的子弹发出位置
return bs;
}
}
public void moveTo(int x,int y){ //英雄机随着鼠标移动
this.x=x-this.width/2;
this.y=y-this.height/2;
}
public boolean outOfBounds(){ //英雄机永不越界
return false;
}
public void addLife(){ //生命+1
life++;
}
public int getLife(){ //获取生命数
return life;
}
public void subtractLife(){ //英雄机命数-1
life--;
}
public void addDoubleFir(){ //火力值增40
doubleFire+=40;
}
public void clearDoubleFire(){ //清火力(火力归零)
doubleFire=0;
}
public boolean hit(FlyingObject other){ //英雄机和敌人的碰撞 this英雄机 other敌人
int x1=other.x-this.width/2; //敌人的x-英雄机宽的一半
int x2=other.x+other.width+this.width/2; //敌人的x+敌人的宽+英雄机宽的一半
int y1=other.y-this.height/2; //敌人的y-英雄机高的一半
int y2=other.y+other.height+this.height/2; //敌人的y+敌人的高+英雄机高的一半
int x=this.x+this.width/2; //中心点x坐标 英雄机的x+英雄机宽的一半
int y=this.y+this.height/2; //中心点y坐标 英雄机的y+英雄机高的一半
return x>x1&&x<x2&&y>y1&&y<y2;
}
}
7.子弹子类
/**子弹---是飞行物*/
public class Bullet extends FlyingObject {
private int speed=3; //走的步数
public Bullet(int x,int y){ //构造方法
image=ShootGame.bullet; //图片-静态块已加载
width=image.getWidth();
height=image.getHeight();
this.x=x; //与英雄机有关
this.y=y;
}
public void step(){ //重写父类走步方法
y-=speed; //y- 向上走 x
}
public boolean outOfBounds(){ //判断子弹是否越界
return this.y<=-this.height;
}
}
8.主程序类
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame; //框架
import javax.swing.JPanel; //画板
import java.awt.Graphics; //paint()中的参数
import java.util.Random;
import java.util.Timer; //定时器
import java.util.TimerTask; //定时
import java.util.Arrays;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Color; //颜色
import java.awt.Font; //字体
/**主程序类*/
public class ShootGame extends JPanel{
public static final int WIDTH=400; //窗口的宽
public static final int HEIGHT=690; //窗口的高
public static BufferedImage background; //背景图片
public static BufferedImage start; //开始图片
public static BufferedImage pause; //暂停图片
public static BufferedImage gameover; //游戏结束图片
public static BufferedImage airplane; //敌机图片
public static BufferedImage bee; //蜜蜂图片
public static BufferedImage bullet; //子弹图片
public static BufferedImage hero0; //英雄机0图片
public static BufferedImage hero1; //英雄机1图片
private Hero hero=new Hero(); // 英雄机
private Bullet[] bullets={}; // 子弹数组
private FlyingObject[] flyings={}; // 敌机数组
public static final int START=0; //启动状态
public static final int RUNING=1; //运行状态
public static final int PAUSE=2; //暂停状态
public static final int GAME_OVER=3; //结束状态
private int state=START; //当前状态(启动状态)
static{ // 静态代码块,初始化图片资源
try{ //捕获异常语句
background=ImageIO.read(ShootGame.class.getResource("background.png"));
start=ImageIO.read(ShootGame.class.getResource("start.png"));
pause=ImageIO.read(ShootGame.class.getResource("pause.png"));
gameover=ImageIO.read(ShootGame.class.getResource("gameover.png"));
airplane=ImageIO.read(ShootGame.class.getResource("airplane.png"));
bee=ImageIO.read(ShootGame.class.getResource("bee.png"));
bullet=ImageIO.read(ShootGame.class.getResource("bullet.png"));
hero0=ImageIO.read(ShootGame.class.getResource("hero0.png"));
hero1=ImageIO.read(ShootGame.class.getResource("hero1.png"));
}catch(Exception e){
e.printStackTrace();
}
}
public FlyingObject nextOne(){ //创建敌人(敌机+小蜜蜂)对象
Random ra=new Random();
int type=ra.nextInt(20); //生成0~19的随机数
if(type==0){
return new Bee(); //若随机数为0生成蜜蜂
}else{
return new Airplane(); //否则生成敌机
}
}
int flyEnteredIndex=0; //飞行物入场计数
public void enterAction(){ //10毫秒走一次 控制入场时间
flyEnteredIndex++; //每10毫秒增1
if(flyEnteredIndex%40==0){ //400(10*40)毫秒走一次
FlyingObject obj=nextOne();
flyings=Arrays.copyOf(flyings,flyings.length+1);
flyings[flyings.length-1]=obj;
}
}
public void stepAction(){ //走步方法
hero.step(); //英雄机走一步
for(int i=0;i<flyings.length;i++){ //遍历所有敌人
flyings[i].step(); //每个敌人走一步
}
for(int i=0;i<bullets.length;i++){ //遍历所有子弹
bullets[i].step(); //每个子弹走一步
}
}
int shootIndex=0; //射击计数
public void shootAction(){ //子弹入场 因为在run方法里边 所以也是10毫秒走一次
shootIndex++; //每10毫秒自增1
if(shootIndex%20==0){ //每300毫秒走一次
Bullet[] bs=hero.shoot();
bullets=Arrays.copyOf(bullets, bullets.length+bs.length);
System.arraycopy(bs, 0, bullets,bullets.length-bs.length, bs.length); //数组的追加
}
}
public void outOfBoundsAction(){ //删除越界飞行物
int index=0; //1.不越界敌人数组下标 2.不越界敌人个数
FlyingObject[] flyingLives=new FlyingObject[flyings.length]; //不越界敌人数组
for(int i=0;i<flyings.length;i++){ //遍历所有敌人
FlyingObject f=flyings[i]; //获取每一个敌人
if(!f.outOfBounds()){ //不越界
flyingLives[index]=f; //将不越界敌人添加到不越界敌人数组
index++; //1.下标+1 2.不越界敌人个数增1
}
}
flyings=Arrays.copyOf(flyingLives,index); //将不越界敌人数组里的元素复制到飞行物数组中
}
public void bangAction(){ //所有子弹与所有敌人的碰撞
for(int i=0;i<bullets.length;i++){ //遍历每一个子弹
Bullet b=bullets[i]; //获取每一个子弹
bang(b); //一个子弹与所有敌人碰撞
}
}
int score=0; //玩家得分
public void bang(Bullet b){ //一个子弹与所有敌人碰撞
int index=-1; //被撞敌人的下标
for(int i=0;i<flyings.length;i++){ //遍历每一个敌人
FlyingObject f=flyings[i]; //获取每一个敌人
if(f.shootBy(b)){ //撞上了
index=i; //记录被撞敌人的下标(index值发生改变,不会再是-1)
break;
}
}
if(index!=-1){ //被撞上的飞行物
FlyingObject one = flyings[index]; //获取被撞的敌人
if(one instanceof Enemy){ //判断被撞敌人的类型 是敌人的话 得分
Enemy e=(Enemy)one; //将被撞敌人强转为敌人类型
score+=e.getScore(); //累加分数
}
if(one instanceof Award){ //判断被撞敌人的类型 是蜜蜂的话 得奖励
Award e=(Award)one; //将被撞敌人强转为奖励类型
int type=e.getType();
switch(type){ //判断获得奖励类型
case Award.DOUBLE_FIRE: //奖励双倍火力
hero.addDoubleFir(); //调用英雄机增火力方法
break;
case Award.LIFE: //奖励一条命
hero.addLife(); //调用英雄机增命力方法
break;
}
}
FlyingObject t=flyings[index]; //被撞元素
flyings[index]=flyings[flyings.length-1];
flyings[flyings.length-1]=t; //交换被撞敌人与数组中的最后一个元素 即将被撞敌人挪到数组中最后一个位置
flyings=Arrays.copyOf(flyings,flyings.length-1); //缩容默认去掉最后一个 去掉最后一个元素也就是被撞的元素
}
}
public void checkGameOverAction(){ //检测游戏是否结束
if(isGameOver()){ //游戏结束时
state=GAME_OVER;
}
}
public boolean isGameOver(){ //判断游戏是否结束,返回true表示游戏结束
for(int i=0;i<flyings.length;i++){ //遍历每一个敌人
FlyingObject f=flyings[i]; //获取每一个敌人
if(hero.hit(f)){ //如果撞上了
hero.subtractLife(); //英雄机减命
hero.clearDoubleFire(); //英雄机清火力
FlyingObject t=flyings[i];
flyings[i]=flyings[flyings.length-1];
flyings[flyings.length-1]=t; //交换被撞敌人和数组的最后一个元素
flyings=Arrays.copyOf(flyings, flyings.length-1); //缩容
}
}
return hero.getLife()<=0; //命数<=0 游戏结束
}
public void action(){ //启动程序的执行
MouseAdapter l=new MouseAdapter(){
public void mouseMoved(MouseEvent e){ //鼠标移动事件
if(state==RUNING){ //当前状态为运行状态时执行
int x=e.getX(); //获取鼠标x坐标
int y=e.getY(); //获取鼠标y坐标
hero.moveTo(x, y); //英雄机随着鼠标移动
}
}
public void mouseClicked(MouseEvent e){ //鼠标点击事件
switch(state){ //不同状态时点击后有不同反应
case START: //启动状态时
state=RUNING; //当前状态变为运行状态
break;
case GAME_OVER: //游戏结束状态时
score=0; //清理现场
hero=new Hero();
flyings=new FlyingObject[0];
bullets=new Bullet[0];
state=START; //当前状态为启动状态
break;
}
}
public void mouseExited(MouseEvent e){ //鼠标移出事件
if(state==RUNING){ //当前状态为运行状态时
state=PAUSE; //当前状态改为暂停状态
}
}
public void mouseEntered(MouseEvent e){ //鼠标移入事件
if(state==PAUSE){ //当前状态为暂停状态时
state=RUNING; //当前状态改为运行状态
}
}
};
this.addMouseListener(l); //处理鼠标的操作事件
this.addMouseMotionListener(l); //处理鼠标的滑动事件
Timer timer=new Timer(); //定时器对象 定时执行的一些事件
int intervel=10; //时间间隔(毫秒)
timer.schedule(new TimerTask(){ //匿名内部类 重写run方法
public void run(){ //10毫秒走一次 run方法里边的都是10毫秒走一次
if(state==RUNING){ //当前状态为运行状态时执行
enterAction(); //敌人入场
stepAction(); //飞行物走步
shootAction(); //子弹入场
outOfBoundsAction(); //删除越界飞行物
bangAction(); //子弹与敌人的碰撞
checkGameOverAction(); //检测游戏是否结束
}
repaint(); //重画,调用paint方法
}
},intervel,intervel); //做定时计划任务
}
public void paint(Graphics g){ //重写父类paint()方法 g:画笔
g.drawImage(background,0,0,null); //画背景图
paintHero(g); //画英雄机
paintFlyingObjects(g); //画敌机(敌机+小蜜蜂)
paintBullets(g); //画子弹
paintScoreAndLife(g); //画分和生命
paintState(g); //画状态
}
public void paintHero(Graphics g){ //画英雄机对象
g.drawImage(hero.image,hero.x,hero.y,null);
}
public void paintFlyingObjects(Graphics g){ //画敌机(敌机+小蜜蜂)对象
for(int i=0;i<flyings.length;i++){ //遍历flyings数组
FlyingObject f=flyings[i]; //把每个敌人存到f中 为了代码的简洁
g.drawImage(f.image,f.x,f.y,null);
}
}
public void paintBullets(Graphics g){ //画子弹对象
for(int i=0;i<bullets.length;i++){ //遍历bullets数组
Bullet b=bullets[i]; //把每个敌人存到f中 为了代码的简洁
g.drawImage(b.image,b.x,b.y,null);
}
}
public void paintScoreAndLife(Graphics g){ //画分和生命
g.setFont(new Font(Font.SERIF,Font.BOLD,20));//改变画笔字体
g.setColor(new Color(0xFF0000)); //改变画笔颜色 (0xFF0000纯红 0x00FF00纯绿 0x0000FF纯蓝)
g.drawString("SCORE:"+score,10,25);
g.drawString("LIFE:"+hero.getLife(), 10, 45);
}
public void paintState(Graphics g){ //画状态
switch(state){
case START: //启动状态画启动图
g.drawImage(start,0,0,null);
break;
case PAUSE: //暂停状态画暂停图
g.drawImage(pause,0,0,null);
break;
case GAME_OVER: //结束状态画结束图
g.drawImage(gameover,0,0,null);
break;
}
}
public static void main(String[] args) {
JFrame frame=new JFrame("Fly"); //创建一个JFrame对象
ShootGame game=new ShootGame(); //创建一个JPanel对象
frame.add(game); //将面板添加到框架上
frame.setSize(WIDTH,HEIGHT); //设置窗口大小
frame.setAlwaysOnTop(true); //设置总是在最上边
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置默认关闭操作,窗口关闭,程序也关闭
frame.setLocationRelativeTo(null); //设置窗体初始位置(居中显示)
frame.setVisible(true); //1.设置窗口可见 2.尽快调用paint()
game.action(); //启动程序的执行
}
}