前言: 飞机大战可以说是我的第一个做着玩的项目,虽然只是好玩,但是确实也可以增加自身对java语言的理解,以下文章适合有一点点java基础的人儿,毕竟嘛,怎么创建项目,搭建jdk环境这些太寄基础了;
![dcc98651eeefb2143ec10dabd49cbf4e.png](https://i-blog.csdnimg.cn/blog_migrate/9cfb4e61a29975d1f80b76e4b4cff57f.jpeg)
对于飞机大战的初步认识
在写飞机大战前首先我们要了解飞机大战是个什么玩意吧,很简单的概括就是有一个英雄机(玩家操控的飞机)可以不断地发送子弹,通过击落敌机(系统生成的飞机)获取积分,因为是简易玩法,所以这里不设置boss机进行通关玩法;
准备工作
对飞机大战有了初步的了解后,我们要开始准备飞机大战需要的准备工作了,首先我们要准备的图片有:
- 游戏开始界面(start.png)
- 游戏暂停界面(gameover.png)
- 游戏结束界面(pause.png)
- 英雄机01(hero0.png)
- 英雄机02(hero1.png)
- 敌机01(airplane0.png)
- 敌机02(bee0.png)
- 子弹图标(bullet.png)
- 游戏背景图(background.png)
这些我们可以根据个人的喜好来做,英雄机设置1、2的图标是用来做一个动态效果,敌机就是两种不同的敌机,为了正经我还是选了些正经图片的,下面是图片包,不喜欢自制图片的可以拿走,我做的飞机大战会偏简单,但是其原理嘛基本差不多,有兴趣的可以继续拿图片包里的其他图片将飞机大战完善,增强游戏体验。
这里补充一下,我这里使用的使eclipse,环境用的使JDK1.8进行编写的;
图片包 提取码:yekapan.baidu.com工作开始了!工作开始了!
首先我们要写一个运行的主类ShootGame也就是主战场,为了使我们这个游戏图形化我们要使用JPanel这个图形化界面容器,顺便定义一个窗口的大小;
public
定义好了这个主类之后,我们要开始创建飞机大战中的各种对象的类了。
![ee227e7d5d3a76ad6ec83c5f8a055d6f.png](https://i-blog.csdnimg.cn/blog_migrate/fd30c98f05455dcc4d2ca469941183f1.png)
这里我由上至下一个个进行讲解。
特别讲解(挺重点的):
在这个JPanel的容器中,x轴的方向和我们正常学习中的x轴是一致的,但是y轴是反的,下图是我们展现的画布在x轴y轴中的位置;
![192ca3aaa39c8079297b1c06c6dd64d0.png](https://i-blog.csdnimg.cn/blog_migrate/27de89d0dc9c6bf3f1f461ee49132e1a.png)
在游戏中的主要对象就是各种各样的飞行物,什么英雄机啊,什么敌机啊什么的,我们为了统一标准,也就提取了FlyingObject这个虚拟类,飞行物的共性属性就是他们有各自的长宽,有图片,有速度、会离开画布范围,一下是提取共性的代码,属性很简答,两个抽象方法step和outOfBounds的作用分别是飞行物的爬行速度以及这个出画布后的处理方式;
import java.awt.image.BufferedImage;
public abstract class FlyingObject {
BufferedImage image;
protected int x;
protected int y;
protected int width;
protected int height;
public abstract void step();
public abstract boolean outOfBounds();
public boolean shootBy(Bullet b) {
int x1=this.x;
int x2=this.x+this.width;
int y1=this.y;
int y2=this.y+this.height;
int x=b.x;
int y=b.y;
return x>=x1&&x<=x2
&&
y>=y1 && y<=y2;
}
}
这里解释一下后面的这个shootBy方法,传进来的参数是子弹这个对象,然后内部的x1和x2就是飞行物的宽,y1、y2是飞行物的高,最后做一个判断,x和y就是子弹的宽和高,然后最后做一个判断,如果子弹射击到了飞行物则返回布尔值属性true;
统一了标准后我们首先要写的是这个英雄机,作为整个游戏的主角,当然要第一个写,因为主角才是游戏存在的意义;
![135f2b0bc63f8add6df7232cbddca177.png](https://i-blog.csdnimg.cn/blog_migrate/c4e0e0ec45f67c81ae52e4adee4313a3.png)
![9b4a9e36d05617d513ea7095c176421f.png](https://i-blog.csdnimg.cn/blog_migrate/ec2f6edc4f313bc556b618fa27f64d06.png)
为什么我要给英雄机上两张图,这里一个很小的细节,注意英雄机的火焰,这是增加游戏体验的一种手法,将英雄机动画化,这两张图来回的切换就可以让英雄机看起来在喷射火焰一样。
现在我们要给这个英雄机设计属性了,我们可以试着想想,英雄机在游戏中是一个怎么样的存在。
- 大小
- 坐标
- 火力
- 型号
- 血量
- 技能冷却
- 图片
- 速度
这里我们就按最简单的来,给他一个生命值、火力值(击落蜜蜂敌机后有几率获得双倍火力)、图片、以及辅助图片切换的下标index,代码如下;
import java.awt.image.BufferedImage;
public class Hero extends FlyingObject {
int life;//生命
int doubleFire;//火力值
BufferedImage[] images;//存两张要切换的英雄机图片;
int index;//辅助切换的下标
@Override
public void step() {
image=images[index++/10%2];
/*
* method stub index++;
* int a=index/10; int
* b=a%2; image=images[b];
*/
}
@Override
public boolean outOfBounds() {
return false;
}
}
作为英雄机的武器:子弹,也有着它的属性,一个是速度属性,它的速度在游戏中应该是最快的,还有就是图片;
![651686d371e99a199dc309a1d1b1f057.png](https://i-blog.csdnimg.cn/blog_migrate/40b51ea29846d4b2a99912e25affcf20.png)
public class Bullet extends FlyingObject{
BufferedImage image;
private int speed=5;
@Override
public void step() {
// TODO Auto-generated method stub
y-=speed;
}
@Override
public boolean outOfBounds() {
// TODO Auto-generated method stub
return this.y<=-this.height;
}
}
然后是Airplane的类,它所对应的对象是下图这台小飞机;
![37f68de324187cd6b449e8721a8eec72.png](https://i-blog.csdnimg.cn/blog_migrate/841b2cba4b74698b26912810ab12479e.png)
这台小飞机的属性我们赋予了一个speed(速度)的属性,小飞机从上至下行走,他的主要目的就是撞击我们操控的英雄机,他的行走路线是从上至下的直线,飞行路线如下。
![e06c99cc42f3c54262e8afcba5022179.png](https://i-blog.csdnimg.cn/blog_migrate/7b4d443252f2466ae9c237b080dbb45f.jpeg)
同样的我们给它写上速度属性,代码如下;
import java.awt.image.BufferedImage;
import java.util.Random;
public class Airplane extends FlyingObject implements{
private int speed=3;
@Override
public void step() {
// TODO Auto-generated method stub
y+=speed;
}
@Override
public boolean outOfBounds() {
// TODO Auto-generated method stub
return this.y>=ShootGame.HEIGHT;
}
}
再者我们讲下这个Bee的类,它所对应的对象是下图这太大一点的大飞机;
![ff66cf3dfb706b1f60a0826ec0338e45.png](https://i-blog.csdnimg.cn/blog_migrate/ac10b6b68bf7117b78c3698fc1322b31.png)
这台大飞机我们给三个属性,xSpeed、ySpeed、awardType,这里分别是指大飞机在x轴的速度,在y轴的速度以及打掉大飞机后的奖励类型,飞行路线如下;
![5dc32a855b6c7f66a775998660659cb2.png](https://i-blog.csdnimg.cn/blog_migrate/4da0750f395af18765946526cefbb062.jpeg)
import java.awt.image.BufferedImage;
import java.util.Random;
public class Bee extends FlyingObject implements Award{
private int xSpeed=50;
private int ySpeed=20;
private int awardType;//奖励类型
@Override
public int getType() {
// TODO Auto-generated method stub
return awardType;
}
@Override
public void step() {
// TODO Auto-generated method stub
x+=xSpeed;
y+=ySpeed;
//到达最后(最大)零界点;
if(x>=ShootGame.WIDTH-this.width) {
xSpeed=-50;
}
//到达最小零界点;
if(x<=0) {
xSpeed=50;
}
}
@Override
public boolean outOfBounds() {
// TODO Auto-generated method stub
return this.y>=ShootGame.HEIGHT;
}
}
在Bee中我们涉及到一个奖励机制,我们给出两种Bee敌机被击落后的奖励,一个是生命值+1,一个是双倍火力,两者随机进行奖励,于是我们还要加一个getType的奖励选择属性,具体代码如下;
public interface Award {
public static final int DOUBLE_FIRE=0;
public int LIFE=1;
public int getType();
}
我们击败这个Airplane的时候,要给一个分值,这里创建个Enemy的接口,使Airplane实现该接口的得分方法。
import java.awt.image.BufferedImage;
public interface Enemy{
public int getScore();
}
准备的类准备的差不多了,现在我们可以开始来主战场进行代码的编写来构建我们的游戏了;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ShootGame extends JPanel{
public static final int WIDTH=400;
public static final int HEIGHT=654;
public static BufferedImage background;
public static BufferedImage airplane0;
public static BufferedImage bee0;
public static BufferedImage gameover;
public static BufferedImage hero0;
public static BufferedImage hero1;
public static BufferedImage pause;
public static BufferedImage start;
public static BufferedImage bullet;
static {
try {
background=ImageIO.read(ShootGame.class.getResource("background.png"));
airplane0=ImageIO.read(ShootGame.class.getResource("airplane0.png"));
bee0=ImageIO.read(ShootGame.class.getResource("bee0.png"));
gameover=ImageIO.read(ShootGame.class.getResource("gameover.png"));
hero0=ImageIO.read(ShootGame.class.getResource("hero0.png"));
hero1=ImageIO.read(ShootGame.class.getResource("hero1.png"));
pause=ImageIO.read(ShootGame.class.getResource("pause.png"));
start=ImageIO.read(ShootGame.class.getResource("start.png"));
bullet=ImageIO.read(ShootGame.class.getResource("bullet.png"));
}catch(Exception e) {
System.out.println("图片加载错误...");
e.printStackTrace();
}
}
上面的代码是对图片的加载
public static void main(String[] args) {
//创建窗口对象;
JFrame frame=new JFrame("飞机大战");
//创建面板对象;
ShootGame game=new ShootGame();
//将面板添加到窗口;
frame.add(game);
//设置窗口大小
frame.setSize(WIDTH, HEIGHT);
//设置窗口总是在最上方
frame.setAlwaysOnTop(true);
//设置窗口默认关闭操作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口居中显示
frame.setLocationRelativeTo(null);
//设置窗口可见,尽快调用paint()方法;
frame.setVisible(true);
//启动程序执行;
game.action();
}
上面这段代码就是生成游戏窗口,并且生成游戏,可以说是启动方法。
![19dd902f4500b96246e5eb55d39ee8b3.png](https://i-blog.csdnimg.cn/blog_migrate/e926225993065c3506bdbc53af6fc71e.png)
在游戏编写的时候,我们要先编写好游戏的运行过程(通过注释的方式简述)
- 英雄机入场
- 敌人(敌机,密封)入场
- 子弹入场
- 飞行物移动(英雄机,子弹,蜜蜂敌)机移动
- 删除越界的飞行物
- 子弹和敌人碰撞
- 敌人和英雄机碰撞
- 结束游戏
- 游戏重新开始
以上是我们整个游戏进行的流程。
我们先把之前定义好的类,统统画到画布中,代码如下(这里还加了积分的显示以及生命值的剩余提示)。
//重写paint方法,g是画笔;
public void paint(Graphics 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++) {
FlyingObject f=flyings[i];
g.drawImage(f.image,f.x,f.y,null);
}
}
//画子弹对象;
public void paintBullets(Graphics g) {
for(int i =0; i<bullets.length;i++) {
Bullet b=bullets[i];
g.drawImage(b.image,b.x,b.y,null);
}
}
//画分和命;
public void paintScoreAndLife(Graphics g) {
//设置颜色
g.setColor(new Color(0x00FF00));
//设置字体样式(字体,样式,字号)
g.setFont(new Font(Font.MONOSPACED,Font.BOLD,24));
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 void action() {
//鼠标监听器
MouseAdapter l=new MouseAdapter() {
//重写鼠标移动事件;
public void mouseMoved(MouseEvent e) {
if(state==RUNNING) {
int x=e.getX();
int y=e.getY();
hero.moveTo(x,y);
}
}
//鼠标点击事件;
public void mouseClicked(MouseEvent e) {
switch (state) {
case START:
state=RUNNING;
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==RUNNING) {
state=PAUSE;
}
}
//鼠标移入事件;
public void mouseEntered(MouseEvent e) {
if(state==PAUSE) {
state=RUNNING;
}
}
};
//处理鼠标操作事件;
this.addMouseListener(l);
//处理鼠标滑动事件;
this.addMouseMotionListener(l);
//创建定时器对象
Timer timer=new Timer();
//设置时间间隔,单位是毫秒(ms);
int intervel=10;
//TimerTask为抽象类,里面的抽象方法是run(),用匿名内部类的形式创建;
timer.schedule(new TimerTask() {
//定时要做的那些事情;
@Override
public void run() {
/*******************************重点******************************/
if(state==RUNNING) {
// 敌人(敌机,密封)入场
enterAction();
//子弹入场
shootAction();
//飞行物移动(英雄机,子弹,蜜蜂敌)机移动;
stepAction();
//删除越界的飞行物;
outOfBoundsAction();
//子弹和敌人碰撞;
bangAction();
//敌人和英雄机碰撞;
hitAction();
//结束游戏
checkGameOverAction();
}
//重画(调用paint()方法)
repaint();
}
},intervel,intervel);
}
主干搭建好之后,我们要让敌机出现在画布中,流程是:
1.生成敌人对象(包含Bee和Airplane)
public FlyingObject nextOne() {
int type =new Random().nextInt(30);
if(type<1) {
return new Bee();
}else {
return new Airplane();
}
}
2.敌机入场
public void enterAction() {
flyIndex++;
//没调用40次调用一个集合对象;
if(flyIndex%20==0) {
FlyingObject obj=nextOne();
flyings=Arrays.copyOf(flyings, flyings.length+1);
flyings[flyings.length-1]=obj;
}
}
然后我们分别在Airplane和Bee的类中加入主要方法,让他们在画布外随机生成。
Airplane:
import java.awt.image.BufferedImage;
import java.util.Random;
public class Airplane extends FlyingObject implements Enemy{
private int speed=3;
public Airplane(){
image=ShootGame.airplane0;
width=image.getWidth();
height=image.getHeight();
x=new Random().nextInt(ShootGame.WIDTH-this.width);
y=-this.height;
}
@Override
public int getScore() {
// TODO Auto-generated method stub
return 5;
}
@Override
public void step() {
// TODO Auto-generated method stub
y+=speed;
}
@Override
public boolean outOfBounds() {
// TODO Auto-generated method stub
return this.y>=ShootGame.HEIGHT;
}
}
Bee:
import java.awt.image.BufferedImage;
import java.util.Random;
public class Bee extends FlyingObject implements Award{
private int xSpeed=50;
private int ySpeed=20;
private int awardType;//奖励类型
public Bee() {
image=ShootGame.bee0;
width=image.getWidth();
height=image.getHeight();
x=new Random().nextInt(ShootGame.WIDTH-this.width);
y=-this.height;
awardType=new Random().nextInt(2);
}
@Override
public int getType() {
// TODO Auto-generated method stub
return awardType;
}
@Override
public void step() {
// TODO Auto-generated method stub
x+=xSpeed;
y+=ySpeed;
//到达最后(最大)零界点;
if(x>=ShootGame.WIDTH-this.width) {
xSpeed=-50;
}
//到达最小零界点;
if(x<=0) {
xSpeed=50;
}
}
@Override
public boolean outOfBounds() {
// TODO Auto-generated method stub
return this.y>=ShootGame.HEIGHT;
}
}
在Airplane中重写得分方法,这两个敌机的类就算彻底完成了。
然后到英雄机在画面中的展现了,首先给英雄机的类中写一个构造方法,使其能够出现在画布中。
public Hero() {
image=ShootGame.hero0;
width=image.getWidth();
height=image.getHeight();
x=150;
y=450;
doubleFire=0;
life=3;
images=new BufferedImage[ ] {ShootGame.hero0,ShootGame.hero1};
index=0;
}
之后我们要对他的方法进行编写,首先是移动功能,在主类中我们给主类添加了鼠标监听的功能,我们只需要再给英雄机添加移动的功能跟随鼠标就完事了。
public void moveTo(int x, int y) {
// TODO Auto-generated method stub
this.x=x-this.width/2;
this.y=y-this.height/2;
}
我们的英雄机的主要攻击手段就是通过发射子弹来击毁敌机,这里我们要添加一个发射子弹的方法。
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+1*xStep,this.y-yStep);
bs[1]= new Bullet(this.x+3*xStep,this.y-yStep);
return bs;
}else {
Bullet[] bs=new Bullet[1];
bs[0]= new Bullet(this.x+2*xStep,this.y-yStep);
return bs;
}
}
然后我们的英雄机会受到敌人的攻击,要进行一个判定(如果想玩无敌无聊版可以不加)。
public boolean hit(FlyingObject f) {
// TODO Auto-generated method stub
int x1=f.x-this.width/2;
int x2=f.x+f.width+this.width/2;
int y1=f.y-this.height/2;
int y2=f.y+f.height+this.height/2;
int x=this.x+this.width/2;
int y=this.y+this.height/2;
return x>=x1&&x<=x2
&&
y>=y1&&y<=y2;
}
受击的英雄机会减去生命,所以我们也要给个记录给英雄机的生命值以及他的生命值变化的方法。
public int getLife() {
// TODO Auto-generated method stub
return life;
}
public void subtracLife() {
// TODO Auto-generated method stub
life--;
}
public void addLife() {
// TODO Auto-generated method stub
life++;
}
添加生命是通过击毁蜜蜂型号的敌机获得的,同时也有可能获得双倍火力,下面是双倍火力的添加方法。
public void addDoubleFire() {
// TODO Auto-generated method stub
doubleFire+=20;
}
通过上面的代码,我们已经将英雄机的所有方法和属性写完了,英雄机的类也就告一段落了,全部代码如下。
import java.awt.image.BufferedImage;
public class Hero extends FlyingObject {
int life;//生命
int doubleFire;//火力值
BufferedImage[] images;//存两张要切换的英雄机图片;
int index;//辅助切换的下标
public Hero() {
image=ShootGame.hero0;
width=image.getWidth();
height=image.getHeight();
x=150;
y=450;
doubleFire=0;
life=3;
images=new BufferedImage[ ] {ShootGame.hero0,ShootGame.hero1};
index=0;
}
@Override
public void step() {
image=images[index++/10%2];
/*
* method stub index++;
* int a=index/10; int
* b=a%2; image=images[b];
*/
}
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+1*xStep,this.y-yStep);
bs[1]= new Bullet(this.x+3*xStep,this.y-yStep);
return bs;
}else {
Bullet[] bs=new Bullet[1];
bs[0]= new Bullet(this.x+2*xStep,this.y-yStep);
return bs;
}
}
@Override
public boolean outOfBounds() {
return false;
}
public void addLife() {
// TODO Auto-generated method stub
life++;
}
public void addDoubleFire() {
// TODO Auto-generated method stub
doubleFire+=20;
}
public void moveTo(int x, int y) {
// TODO Auto-generated method stub
this.x=x-this.width/2;
this.y=y-this.height/2;
}
public void subtracLife() {
// TODO Auto-generated method stub
life--;
}
public void clearDoubleFire() {
// TODO Auto-generated method stub
doubleFire=0;
}
public boolean hit(FlyingObject f) {
// TODO Auto-generated method stub
int x1=f.x-this.width/2;
int x2=f.x+f.width+this.width/2;
int y1=f.y-this.height/2;
int y2=f.y+f.height+this.height/2;
int x=this.x+this.width/2;
int y=this.y+this.height/2;
return x>=x1&&x<=x2
&&
y>=y1&&y<=y2;
}
public int getLife() {
// TODO Auto-generated method stub
return life;
}
}
这样其他的类就写的七七八八了,我们回到这个主类。我们上面写到了这个敌机生成和入场,我们给一个属性记录敌机入场的方法调用次数,以及子弹入场的次数。
int
子弹的入场:
public void shootAction() {
shootIndex++;
if(shootIndex%15==0) {
Bullet[] bs=hero.shoot();
bullets=Arrays.copyOf(bullets,bullets.length+bs.length);
System.arraycopy(bs, 0,bullets, bullets.length-bs.length,bs.length);
}
}
然后我们给子弹写一个创建方法。
import java.awt.image.BufferedImage;
public class Bullet extends FlyingObject{
BufferedImage image;
private int speed=5;
public Bullet(int x,int y) {
image=ShootGame.bullet;
width=image.getWidth();
height=image.getHeight();
this.x=x;
this.y=y;
}
@Override
public void step() {
// TODO Auto-generated method stub
y-=speed;
}
@Override
public boolean outOfBounds() {
// TODO Auto-generated method stub
return this.y<=-this.height;
}
}
所有的飞行物的移动其实都是统一标准,这里我们给个飞行物的移动方法。
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();
}
}
然后除了英雄机以外飞行物出界之后会不对堆积,为了防止内存泄漏,当飞行物离开画布的时候我们要给出一个删除操作。
//删除越界的飞行物;
public void outOfBoundsAction() {
//不越界的敌人数组下标,不越界敌人个数;
int index=0;
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++;
}
}
flyings=Arrays.copyOf(flyingLives, index);
index=0;
Bullet[] bulletLives=new Bullet[bullets.length];
for (int i = 0; i < bullets.length; i++) {
Bullet b=bullets[i];
if(!b.outOfBounds()) {
bulletLives[index]=b;
index++;
}
}
bullets=Arrays.copyOf(bulletLives, index);
}
接下里就是子弹和敌人碰撞以及敌人和英雄机的碰撞。
//子弹和敌人碰撞;
public void bangAction() {
for (int i = 0; i < bullets.length; i++) {
Bullet b=bullets[i];
bang(b);
}
}
//一颗子弹和所有敌人的碰撞;
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;
break;
}
}
if(index!=-1) {
FlyingObject one = flyings[index];
if(one instanceof Enemy) {
Enemy e=(Enemy)one;
score+=e.getScore();
System.out.println("分数"+score);
}
if(one instanceof Award) {
Award a=(Award)one;
int type =a.getType();
switch (type) {
case Award.DOUBLE_FIRE:
hero.addDoubleFire();
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);
b.y=-b.height;
}
}
//敌人和英雄机碰撞;
public void hitAction() {
for (int i = 0; i < flyings.length; i++) {
FlyingObject f=flyings[i];
if(hero.hit(f)) {
hero.subtracLife();
hero.clearDoubleFire();
//交换被撞敌人和敌人最后一位数组中最后一位元素的位置;
FlyingObject t=flyings[i];
flyings[i]=flyings[flyings.length-1];
flyings[flyings.length-1]=t;
flyings=Arrays.copyOf(flyings, flyings.length-1);
}
}
}
这样,我们得所有代码就全部完成啦!!!以下是主类的全部代码展示。
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ShootGame extends JPanel{
public static final int WIDTH=400;
public static final int HEIGHT=654;
public static BufferedImage background;
public static BufferedImage airplane0;
public static BufferedImage bee0;
public static BufferedImage gameover;
public static BufferedImage hero0;
public static BufferedImage hero1;
public static BufferedImage pause;
public static BufferedImage start;
public static BufferedImage bullet;
static {
try {
background=ImageIO.read(ShootGame.class.getResource("background.png"));
airplane0=ImageIO.read(ShootGame.class.getResource("airplane0.png"));
bee0=ImageIO.read(ShootGame.class.getResource("bee0.png"));
gameover=ImageIO.read(ShootGame.class.getResource("gameover.png"));
hero0=ImageIO.read(ShootGame.class.getResource("hero0.png"));
hero1=ImageIO.read(ShootGame.class.getResource("hero1.png"));
pause=ImageIO.read(ShootGame.class.getResource("pause.png"));
start=ImageIO.read(ShootGame.class.getResource("start.png"));
bullet=ImageIO.read(ShootGame.class.getResource("bullet.png"));
}catch(Exception e) {
System.out.println("图片加载错误...");
e.printStackTrace();
}
}
private Hero hero = new Hero();
private FlyingObject[] flyings = {};
private Bullet[] bullets= {};
private int score;
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;
public int state=START;
//生成敌人对象
public FlyingObject nextOne() {
int type =new Random().nextInt(30);
if(type<1) {
return new Bee();
}else {
return new Airplane();
}
}
//记录enterAction()方法的调用次数;
int flyIndex=0;
//敌机入场
public void enterAction() {
flyIndex++;
//没调用40次调用一个集合对象;
if(flyIndex%20==0) {
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();
}
}
//记录shootAction方法执行次数
int shootIndex=0;
//子弹入场
public void shootAction() {
shootIndex++;
if(shootIndex%15==0) {
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;
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++;
}
}
flyings=Arrays.copyOf(flyingLives, index);
index=0;
Bullet[] bulletLives=new Bullet[bullets.length];
for (int i = 0; i < bullets.length; i++) {
Bullet b=bullets[i];
if(!b.outOfBounds()) {
bulletLives[index]=b;
index++;
}
}
bullets=Arrays.copyOf(bulletLives, index);
}
//子弹和敌人碰撞;
public void bangAction() {
for (int i = 0; i < bullets.length; i++) {
Bullet b=bullets[i];
bang(b);
}
}
//一颗子弹和所有敌人的碰撞;
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;
break;
}
}
if(index!=-1) {
FlyingObject one = flyings[index];
if(one instanceof Enemy) {
Enemy e=(Enemy)one;
score+=e.getScore();
System.out.println("分数"+score);
}
if(one instanceof Award) {
Award a=(Award)one;
int type =a.getType();
switch (type) {
case Award.DOUBLE_FIRE:
hero.addDoubleFire();
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);
b.y=-b.height;
}
}
//敌人和英雄机碰撞;
public void hitAction() {
for (int i = 0; i < flyings.length; i++) {
FlyingObject f=flyings[i];
if(hero.hit(f)) {
hero.subtracLife();
hero.clearDoubleFire();
//交换被撞敌人和敌人最后一位数组中最后一位元素的位置;
FlyingObject t=flyings[i];
flyings[i]=flyings[flyings.length-1];
flyings[flyings.length-1]=t;
flyings=Arrays.copyOf(flyings, flyings.length-1);
}
}
}
//结束游戏
public void checkGameOverAction() {
if(hero.getLife()<=0) {
state=GAME_OVER;
}
}
/* 程序的执行 */
public void action() {
//鼠标监听器
MouseAdapter l=new MouseAdapter() {
//重写鼠标移动事件;
public void mouseMoved(MouseEvent e) {
if(state==RUNNING) {
int x=e.getX();
int y=e.getY();
hero.moveTo(x,y);
}
}
//鼠标点击事件;
public void mouseClicked(MouseEvent e) {
switch (state) {
case START:
state=RUNNING;
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==RUNNING) {
state=PAUSE;
}
}
//鼠标移入事件;
public void mouseEntered(MouseEvent e) {
if(state==PAUSE) {
state=RUNNING;
}
}
};
//处理鼠标操作事件;
this.addMouseListener(l);
//处理鼠标滑动事件;
this.addMouseMotionListener(l);
//创建定时器对象
Timer timer=new Timer();
//设置时间间隔,单位是毫秒(ms);
int intervel=10;
//TimerTask为抽象类,里面的抽象方法是run(),用匿名内部类的形式创建;
timer.schedule(new TimerTask() {
//定时要做的那些事情;
@Override
public void run() {
/*******************************重点******************************/
if(state==RUNNING) {
// 敌人(敌机,密封)入场
enterAction();
//子弹入场
shootAction();
//飞行物移动(英雄机,子弹,蜜蜂敌)机移动;
stepAction();
//删除越界的飞行物;
outOfBoundsAction();
//子弹和敌人碰撞;
bangAction();
//敌人和英雄机碰撞;
hitAction();
//结束游戏
checkGameOverAction();
}
//重画(调用paint()方法)
repaint();
}
},intervel,intervel);
}
//重写paint方法,g是画笔;
public void paint(Graphics 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++) {
FlyingObject f=flyings[i];
g.drawImage(f.image,f.x,f.y,null);
}
}
//画子弹对象;
public void paintBullets(Graphics g) {
for(int i =0; i<bullets.length;i++) {
Bullet b=bullets[i];
g.drawImage(b.image,b.x,b.y,null);
}
}
//画分和命;
public void paintScoreAndLife(Graphics g) {
//设置颜色
g.setColor(new Color(0x00FF00));
//设置字体样式(字体,样式,字号)
g.setFont(new Font(Font.MONOSPACED,Font.BOLD,24));
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("飞机大战");
//创建面板对象;
ShootGame game=new ShootGame();
//将面板添加到窗口;
frame.add(game);
//设置窗口大小
frame.setSize(WIDTH, HEIGHT);
//设置窗口总是在最上方
frame.setAlwaysOnTop(true);
//设置窗口默认关闭操作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口居中显示
frame.setLocationRelativeTo(null);
//设置窗口可见,尽快调用paint()方法;
frame.setVisible(true);
//启动程序执行;
game.action();
}
}
![d10713b66738835888a0fedf525b6452.png](https://i-blog.csdnimg.cn/blog_migrate/05d6c94c8bdee875e7c4668a8644ff91.jpeg)