项目解析
飞机大战 玩过的朋友应该对这个游戏不陌生
游戏由背景、子弹、敌机、英雄机、小蜜蜂等主要组成;
其中子弹、敌机、英雄机、小蜜蜂拥有共同的特性,如贴图,图片宽高,XY坐标,移动方法,飞出屏幕等。
故抽取一个抽象类
package com.tarena.shott;
import java.awt.image.BufferedImage;
/**
* 飞行物类
* @author yaerpi
*
*/
public abstract class FlyingObject {
protected BufferedImage image;
protected int width;
protected int height;
protected int x;
protected int y;
/**
* 飞行物移动
*/
public abstract void step();
/**
* 飞行物越界
* @return yaerpi
*/
public abstract boolean outOfBounds();
/**
* 子弹碰撞敌机
* @param b 子弹对象
* @return yaerpi
*/
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;
}
}
接下来是敌机类型
为了后期的扩展性,新定义一个接口,写上敌机特有的得分行为
package com.tarena.shott;
/**
* 敌人
* @author yaerpi
*
*/
public interface Enemy {
/**
* 得分
* @return yaerpi
*/
public int getScore();
}
新建一个Airplane.java,继承飞行物类实现敌机接口,并重写方法
package com.tarena.shott;
import java.util.Random;
/**
* 敌机类型
* @author xin
*
*/
public class Airplane extends FlyingObject implements Enemy{
private int speed = 2;
public Airplane(){
image = ShootGame.airplane;//图片
width = image.getWidth();
height = image.getHeight();
Random rand = new Random();
x = rand.nextInt(ShootGame.WIDTH-this.width);//敌机随机出现的坐标
y = -this.height;
}
/**
* 重写得分方法
*/
public int getScore() {
return 5;
}
/**
* 重写走步方法
*/
public void step() {
y+=speed;
}
@Override
public boolean outOfBounds() {
return this.y>=ShootGame.HEIGHT;
}
}
接下来是小蜜蜂(带奖励的敌机),敌机特有的行为是奖励分数,而小蜜蜂则是奖励火力值或生命值。
先定义一个接口,写小蜜蜂特有的奖励类型
package com.tarena.shott;
/** 奖励 **/
public interface Award {
/**
* 火力值
*/
public int DOUBLE_FIRE = 0 ;
/**
* 生命值
*/
public int LIFE = 1;
/**
* 获取奖励类型
* @return yaerpi
*/
public int getType();
}
新建一个Bee.java,用于继承飞行物实现奖励接口
package com.tarena.shott;
import java.util.Random;
/**
* 小蜜蜂
* @author yaerpi
*
*/
public class Bee extends FlyingObject implements Award{
private int xSpeed = 1;
private int ySpeed = 2;
private int awardType;
public Bee(){
image = ShootGame.bee;
width = image.getWidth();
height = image.getHeight();
Random rand = new Random();
x = rand.nextInt(ShootGame.WIDTH-this.width);
y = -this.height;
awardType = rand.nextInt(2);//取0-1随机数,随机奖励类型
}
/**
* 获取到随机的奖励类型
*/
public int getType() {
return awardType;
}
/**
* 小蜜蜂特有的走步方法,从右到左向下
*/
public void step() {
x+=xSpeed;
y+=ySpeed;
if(x>ShootGame.WIDTH-this.width){
xSpeed=-1;
}
if(x<=0){
xSpeed = 1;
}
}
/**
* 小蜜蜂越界
*/
public boolean outOfBounds() {
return this.y>=ShootGame.HEIGHT;
}
}
英雄机
package com.tarena.shott;
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();
Random rand = new Random();
x = 150;//初始化英雄机坐标
y = 400;
life = 3;//初始化生命值
doubleFire = 0;//初始化火力值
images = new BufferedImage[]{ShootGame.hero0,ShootGame.hero1 };
index = 0;
}
public void step() {
image = images[index++/10%images.length];
}
/**
* 英雄机子弹类型,火力值大于0则一次发射两发制子弹
* @return yaerpi
*/
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);
doubleFire-=2;
return bs;
}else{
Bullet[] bs = new Bullet[1];
bs[0] = new Bullet(this.x+2*xStep,this.y - yStep);
return bs;
}
}
/**
* 鼠标位置,于英雄机中心点
* @param x
* @param y
*/
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(){
life++;
}
/**
* 增加火力值
*/
public void addFoubleFire(){
doubleFire +=40;
}
/**
* 获取当前生命值
* @return
*/
public int getLife(){
return life;
}
/**
* 获取当前火力值
* @return
*/
public int getDoubleFire(){
return doubleFire;
}
/**
* 英雄机与飞行物碰撞
* @param f 飞行物
* @return
*/
public boolean hit(FlyingObject f){
int x1 = f.x - this.x/2 ;
int x2 = f.x + f.width+this.width;
int y1 = f.x - 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 void subtractLife(){
life--;
}
/**
* 火力值归0
*/
public void clearDoubleFire(){
doubleFire = 0;
}
}
子弹
package com.tarena.shott;
/**
* 子弹类型
* @author yaerpi
*
*/
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;
}
public boolean outOfBounds() {
return this.y<=-this.height;
}
}
新建一个ShootGame.java
package com.tarena.shott;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;//随机数生成
import java.util.Timer;//定时器
import java.util.TimerTask;
import java.util.Arrays;
import java.awt.Color;
import java.awt.Font;
/**
* 游戏实现
* @author yaerpi
*
*/
public class ShootGame extends JPanel{
public static final int WIDTH = 400;
public static final int HEIGHT = 654;
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;
public static BufferedImage hero1;
//游戏运行状态 ↓
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;
//初始化对象
private Hero hero = new Hero();
private FlyingObject[] flyings = {};
private Bullet[] bullets = {};
/* ShootGame(){
flyings = new FlyingObject[2];
flyings[0] = new Airplane();
flyings[1] = new Bee();
bullets = new Bullet[1];
bullets[0] = new Bullet(100,200);
}*/
/**
* 静态加载图片资源
*/
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();
}
}
/**
* 生成敌人对象
* @return
*/
public FlyingObject nextOne(){
Random rand = new Random();
int type = rand.nextInt(20);
if (type == 0){
return new Bee();
}else{
return new Airplane();
}
}
int flyEnteredIndex = 0;
/**
* flyEnteredIndex控制敌人出现的间隔时间
* 敌人数组扩容,增加新敌人,敌人类型随机(敌机,小蜜蜂)
*/
public void enterAction(){
flyEnteredIndex++;
if(flyEnteredIndex%40==0){
FlyingObject one = nextOne();
flyings = Arrays.copyOf(flyings, flyings.length+1);//敌人数组扩容
flyings[flyings.length-1] = one ;
}
}
/**
* 所有对象走步
*/
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;
/**
* shootIndex 控制发射子弹间隔时间
* 发射子弹
*/
public void shootAction(){
shootIndex++;
if(shootIndex%30==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[] bulle = new Bullet[bullets.length];//存储未越界的子弹数组
for(int i = 0; i < bullets.length;i++){//遍历子弹
Bullet b = bullets[i];
if(!b.outOfBounds()){//判断子弹是否越界
bulle[index] = bullets[i];
index++;
}
}
bullets = Arrays.copyOf(bulle, index);//数组缩容,只存储未越界的子弹
}
/**
* 遍历子弹
*/
public void bangAction(){
for(int i = 0; i < bullets.length;i++){
Bullet b = bullets[i];
bang(b);
}
}
private int score = 0;
/**
* 判断子弹是否撞击飞行物,是则判断飞行物类型,敌机则奖励分数,小蜜蜂则奖励生命值或火力值
* @param 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();
}
if(one instanceof Award){
Award a = (Award)one;
int type = a.getType();//获取随机奖励制类型
switch(type){
case Award.DOUBLE_FIRE:
hero.addFoubleFire();
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);//数组缩容,删除碰撞后的飞行物
}
}
/**
* 检查游戏是否结束,英雄机生命值小于等于0时游戏结束
*/
public void checkGameOverAction(){
if(isGameOver()){
state = GAME_OVER;
}
}
/**
* 检查英雄机是否碰撞敌人,是则减少生命值
* @return 英雄机存活状态
*/
public boolean isGameOver(){
int index = -1;
for(int i = 0 ; i<flyings.length;i++){
FlyingObject f = flyings[i];
if(hero.hit(f)){//判断是否撞击到飞行物
index = i;
break;
}
}
if(index != -1){//撞击到飞行物
FlyingObject t = flyings[index];
flyings[index] = flyings[flyings.length-1];
flyings[flyings.length-1] = t;
flyings = Arrays.copyOf(flyings, flyings.length-1);//删除飞行物
hero.subtractLife();//减少生命值
hero.clearDoubleFire();//重置火力值
}
return hero.getLife()<=0;
}
/**
* 启动任务执行 主程序体
*/
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();//定时发生器
int intervel = 10;//定时发生时间间隔,毫秒
timer.schedule( new TimerTask(){
/**
* 定时发生器定时运行事件
*/
public void run(){
if(state == RUNNING){
enterAction();//出现敌人
stepAction();//敌人移动
shootAction();//英雄机射击
outOfBoundsAction();//删除越界飞行物
bangAction();//判断子弹撞击飞行物
checkGameOverAction();//检查游戏是否结束
}
repaint();//重画 - 调用的paint方法;
}
},intervel,intervel);
}
/**
* 画窗口对象
*/
public void paint(Graphics g){
g.drawImage(background,0,0,null);
paintHero(g);
paintFlyingObjects(g);
paintBullets(g);
paintScoreAndLife(g);
paintState(g);
}
/**
* 画英雄机
* @param g
*/
public void paintHero(Graphics g){
g.drawImage(hero.image,hero.x,hero.y,null);
}
/**
* 画飞行物
* @param g
*/
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);
}
}
/**
* 画子弹
* @param g
*/
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);
}
}
/**
* 画当前游戏分数
* @param g
*/
public void paintScoreAndLife(Graphics g){
g.setColor(new Color(0xff0000));
g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,24));
g.drawString("Score: "+ score, 10, 25);
g.drawString("Life: "+ hero.getLife(), 10, 45);
g.drawString("DoubleFire: "+hero.getDoubleFire(), 10, 65);
}
/**
* 画游戏状态
* @param g
*/
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");
ShootGame game = new ShootGame();
frame.add(game);
frame.setSize(WIDTH, HEIGHT);
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.action();
}
}