飞机大战JAVA编程
最近在学习java语言,飞机大战是其中一个练习小项目。这里记录一下写这个程序的思路。
- 首先是需求分析,这里建立项目需求的思维导图,一目了然:
- 然后是思路分析
这是写程序前列举的一个基本的思路,以此为主线,写的时候遇到具体问题再进行修改,最后得出最终程序。 - 最后是写好的程序结构图:
这里就不多写文字了,所有内容都在图中,下面是运行结果和源代码。
运行结果:
源码:
//FlyingObject类,抽象类:
package com.centerm.shoot;
import java.awt.image.BufferedImage;
/**
* 将四个主要角色的共同属性提取出来组成一个父类
* @author 89447
*/
public abstract class FlyingObject {
//定义所有飞行器共有的属性:图片、坐标、尺寸
protected BufferedImage image;
protected int x;
protected int y;
protected int width;
protected int height;
//定义所有飞行器共有的方法:移动、越界
public abstract void moveFlying();
public abstract boolean outOfBound();
//下面定义的这个方法只有Airplane和Bee使用
public boolean shootBy(Bullet bullet){
int x1 = this.x;
int x2 = this.x + this.width;
int y1 = this.y;
int y2 = this.y + this.height;
int xb1 = bullet.x;
int xb2 = bullet.x + bullet.width;
int yb1 = bullet.y;
int yb2 = bullet.y + bullet.height;
return (xb1>x1 && xb1<x2) && (yb1>y1 && yb1<y2) ||
(xb2>x1 && xb2<x2) && (yb1>y1 && yb1<y2) ||
(xb1>x1 && xb1<x2) && (yb2>y1 && yb2<y2) ||
(xb2>x1 && xb2<x2) && (yb2>y1 && yb2<y2);
}
}
package com.centerm.shoot;
import java.util.Random;
/**
* 敌机
* @author 89447
*
*/
public class Airplane extends FlyingObject{
//前进速度
private int speed;
//构造方法
public Airplane(){
image = Stage.airplane;
x = new Random().nextInt(Stage.STAGE_WIDTH - image.getWidth());
y = -image.getHeight();
width = image.getWidth();
height = image.getHeight();
speed = 3;
}
//移动
public void moveFlying(){
this.y += this.speed;
}
//出界判断
public boolean outOfBound() {
return this.y > Stage.STAGE_HEIGHT;
}
}
package com.centerm.shoot;
import java.util.Random;
/**
* 蜜蜂类
* @author 89447
*
*/
public class Bee extends FlyingObject{
//奖励类型
public static final int AWARD_LIFE = 0;
public static final int AWARD_FIRE = 1;
//左右飞行,用于飞行方向的改变
private static final int FLY_RIGHT = 1;
private static final int FLY_LEFT = -1;
//飞行方向变量
private int flyingDirection = FLY_RIGHT;
//X、Y方向上前进的速度
private int speedX;
private int speedY;
//构造函数
public Bee(){
image = Stage.bee;
x = new Random().nextInt(Stage.STAGE_WIDTH - image.getWidth());
y = -image.getHeight();
width = image.getWidth();
height = image.getHeight();
speedX = 2;
speedY = 1;
}
//移动一步
public void moveFlying(){
/**
* 先确定小蜜蜂飞行的方向
*/
if(this.x >= Stage.STAGE_WIDTH - this.width){
flyingDirection = FLY_LEFT;
}
if(this.x <= 0){
flyingDirection = FLY_RIGHT;
}
/**
* 再根据方向进行移动
*/
if(flyingDirection == FLY_RIGHT){
this.x += this.speedX;
this.y += this.speedY;
}else{
this.x -= this.speedX;
this.y += this.speedY;
}
}
//获取奖励类型
public int getAwardType(){
return new Random().nextInt(2)==AWARD_LIFE ? AWARD_LIFE : AWARD_FIRE;
}
//判断出界
public boolean outOfBound() {
return this.y > Stage.STAGE_HEIGHT;
}
}
package com.centerm.shoot;
/**
* 子弹类
* @author 89447
*
*/
public class Bullet extends FlyingObject{
//子弹运行速率
private int speed;
//构造函数
public Bullet(int x, int y){
this.x = x;
this.y = y;
image = Stage.bullet;
width = image.getWidth();
height = image.getHeight();
speed = 4;
}
//子弹移动一步
public void moveFlying(){
this.y -= this.speed;
}
//判断出界
public boolean outOfBound() {
return this.y < 0;
}
}
package com.centerm.shoot;
/**
* 英雄机的类
* @author 89447
*
*/
public class Hero extends FlyingObject{
//定义默认生命值
private static final int DEFAULT_LIFE = 3;
//定义移动方向,用于键盘控制
public static final int MOVE_UP = 1;
public static final int MOVE_DOWN = -1;
public static final int MOVE_LEFT = 2;
public static final int MOVE_RIGHT = -2;
//定义一次移动的距离,用于键盘控制
public static final int STEP_DISTANCE = 10;
//定义火力值、生命值
private int firePower;
private int life;
//构造函数
public Hero(int x, int y){
this.x = x;
this.y = y;
image = Stage.hero0;
width = image.getWidth();
height = image.getHeight();
firePower = 0;
life = DEFAULT_LIFE;
}
//获取生命值
public int getLife(){
return life;
}
//生命值减一
public void substactLife() {
this.life --;
}
//生命值加一
public void addLife() {
this.life ++;
}
//获取双倍火力值
public int getFirePower(){
return firePower;
}
//清空火力值
public void clrFirePower(){
firePower = 0;
}
//火力值增加固定的数值
public void addFirePower(){
firePower += 40;
}
//英雄机按照某个方向移动一步,用于键盘控制
public void moveDirection(int direction){
switch (direction) {
case MOVE_DOWN:
if(this.y >= Stage.STAGE_HEIGHT - this.height){
break;
}else if(this.y >= Stage.STAGE_HEIGHT - this.height- STEP_DISTANCE &&
this.y <Stage.STAGE_HEIGHT - this.height){
this.y = Stage.STAGE_HEIGHT - this.height;
}else{
this.y += STEP_DISTANCE;
}
break;
case MOVE_UP:
if(this.y == 0){
break;
}else if(this.y > 0 && this.y <= STEP_DISTANCE){
this.y = 0;
}else {
this.y -= STEP_DISTANCE;
}
break;
case MOVE_LEFT:
if(this.x == 0){
break;
}else if(this.x > 0 && this.x <= STEP_DISTANCE){
this.x = 0;
}else {
this.x -= STEP_DISTANCE;
}
break;
case MOVE_RIGHT:
if(this.x == Stage.STAGE_WIDTH - this.width){
break;
}else if(this.x >= Stage.STAGE_WIDTH - this.width - STEP_DISTANCE &&
this.x < Stage.STAGE_WIDTH - this.width){
this.x = Stage.STAGE_WIDTH - this.width;
}else {
this.x += STEP_DISTANCE;
}
break;
default:
break;
}
}
//英雄机移动,用于图片切换
private int indexOfImage = 0;
public void moveFlying(){
indexOfImage = 1 - indexOfImage;
if(indexOfImage == 0){
image = Stage.hero0;
}else{
image = Stage.hero1;
}
}
//直接将英雄机移动到某个确定的位置,用于鼠标控制
public void setLocation(int x, int y){
this.x = x - this.width/2;
this.y = y - this.height/2;
}
//创建子弹,可能是一个,也可能是两个,因此这里返回数组
public Bullet[] createBullets(){
if(firePower > 0){
firePower -= 2;
return new Bullet[]{new Bullet(this.x + this.width/4 - 10, this.y),
new Bullet(this.x + this.width*3/4 - 10, this.y)};
}else {
return new Bullet[]{new Bullet(this.x + this.width/2 - 10, this.y)};
}
}
//出界判断,由于英雄机无法出界,因此这里返回null
public boolean outOfBound() {
return false;
}
//英雄机撞击敌机判断
public boolean hitFlying(FlyingObject flying){
int x1 = this.x;
int x2 = this.x + this.width;
int y1 = this.y;
int y2 = this.y + this.height;
int xb1 = flying.x;
int xb2 = flying.x + flying.width;
int yb1 = flying.y;
int yb2 = flying.y + flying.height;
return (xb1>x1 && xb1<x2) && (yb1>y1 && yb1<y2) ||
(xb2>x1 && xb2<x2) && (yb1>y1 && yb1<y2) ||
(xb1>x1 && xb1<x2) && (yb2>y1 && yb2<y2) ||
(xb2>x1 && xb2<x2) && (yb2>y1 && yb2<y2);
}
}
//舞台类,关键
package com.centerm.shoot;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
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;
/**
* 舞台类
* @author 89447
*
*/
@SuppressWarnings("serial")
public class Stage extends JPanel{
public static final int STAGE_WIDTH = 400;
public static final int STAGE_HEIGHT = 650;
public static final String PATH = "." + File.separator + "src" + File.separator + "com" + File.separator + "centerm" + File.separator + "shoot" + File.separator;
/**
* 下面统一管理图片,也可以放在各个具体的类中,但是会显得比较混乱,因此这里放在一起
*/
public static BufferedImage hero0;
public static BufferedImage hero1;
public static BufferedImage bee;
public static BufferedImage bullet;
public static BufferedImage airplane;
public static BufferedImage background;
public static BufferedImage pause;
public static BufferedImage start;
public static BufferedImage gameover;
//定义几个状态
public static final int START = 0;
public static final int RUNNING = 1;
public static final int PAUSE = 2;
public static final int GAMEOVER = 3;
//状态变量
private int state = START;
//主要的成员变量
private Hero hero = new Hero(200,300);;
private Bullet[] bullets = {};
private FlyingObject[] flyings = {};
//分数
private int score = 0;
//初始化图片的静态块
static{
try {
hero0 = ImageIO.read(new File(PATH + "hero0.bmp"));
hero1 = ImageIO.read(new File(PATH + "hero1.bmp"));
bee = ImageIO.read(new File(PATH + "bee.bmp"));
bullet = ImageIO.read(new File(PATH + "bullet.bmp"));
airplane = ImageIO.read(new File(PATH + "airplane.bmp"));
background = ImageIO.read(new File(PATH + "background.bmp"));
pause = ImageIO.read(new File(PATH + "pause.bmp"));
start = ImageIO.read(new File(PATH + "start.bmp"));
gameover = ImageIO.read(new File(PATH + "gameover.bmp"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//绘制所有元素
public void paint(Graphics g) {
// TODO Auto-generated method stub
g.drawImage(background, 0, 0, null); //绘制背景图片
paintHero(g); //绘制英雄机
paintFlyings(g);//绘制敌机:包括小蜜蜂和飞行器
paintBullet(g); //绘制子弹:
paintText(g);
paintState(g);
}
//绘制英雄机,为paint()服务
public void paintHero(Graphics g){
g.drawImage(hero.image, hero.x, hero.y, null);
}
//绘制敌机,为paint()服务
public void paintFlyings(Graphics g){
for(FlyingObject flying : flyings){
g.drawImage(flying.image, flying.x, flying.y, null);
}
}
//绘制子弹,为paint()服务
public void paintBullet(Graphics g){
for(Bullet bullet : bullets){
g.drawImage(bullet.image, bullet.x, bullet.y, null);
}
}
//绘制文字信息,为paint()服务
public void paintText(Graphics g){
g.setColor(new Color(0xff0000));
g.setFont(new Font(Font.DIALOG, 20, 20));
g.drawString("生命数:"+Integer.toString(hero.getLife()), 0, 100);
g.drawString("分数:"+Integer.toString(score), 0, 50);
g.drawString("火力剩余:"+Integer.toString(hero.getFirePower()), 0, 75);
}
//绘制各个状态下的图片,为paint()服务
public void paintState(Graphics g){
switch(state){
case START:
g.drawImage(start, 0, 0, null);
break;
case RUNNING:
break;
case PAUSE:
g.drawImage(pause, 0, 0, null);
break;
case GAMEOVER:
g.drawImage(gameover, 0, 0, null);
break;
default:
break;
}
}
//生成新的敌机,为FlyingEnter()方法服务
public FlyingObject createFlying(){
Random random = new Random();
if(random.nextInt(20) < 2){
return new Bee();
}else{
return new Airplane();
}
}
//敌机入场
int flyingEnterDelay = 0;
public void FlyingEnter(){
if(flyingEnterDelay ++ >= 40){
flyingEnterDelay = 0;
flyings = Arrays.copyOf(flyings, flyings.length + 1);
flyings[flyings.length - 1] = createFlying();
}
}
//子弹入场
int BulletEnterDelay = 0;
public void BulletEnter(){
if(BulletEnterDelay ++ >= 20){
BulletEnterDelay = 0;
Bullet[] bullets_temp = hero.createBullets();
bullets = Arrays.copyOf(bullets, bullets.length + bullets_temp.length);
for(int i = 0; i<bullets_temp.length; i++){
bullets[bullets.length - i - 1] = bullets_temp[i];
}
}
}
//移动一帧
public void moveStepAction(){
//移动英雄机:切换图片
hero.moveFlying();
//移动敌机
for(FlyingObject flying: flyings){
flying.moveFlying();
}
//移动子弹
for(Bullet bullet: bullets){
bullet.moveFlying();
}
}
//出界处理方法
public void outOfBoundAction(){
FlyingObject[] flying_temp = new FlyingObject[flyings.length];
int flying_index = 0;
for(int i = 0; i<flyings.length; i++){
FlyingObject flying = flyings[i];
if(!flying.outOfBound()){
flying_temp[flying_index ++] = flying;
}
}
flyings = Arrays.copyOf(flying_temp, flying_index);
Bullet[] bullets_temp = new Bullet[bullets.length];
int bullet_index = 0;
for(int i = 0; i<bullets.length; i++){
Bullet bullet = bullets[i];
if(!bullet.outOfBound()){
bullets_temp[bullet_index ++] = bullet;
}
}
bullets = Arrays.copyOf(bullets_temp, bullet_index);
}
//一颗子弹射击敌机方法,为shootAction()服务
public boolean bulletShootFlyings(Bullet bullet){
int index = -1;
FlyingObject flying;
for(int i = 0; i<flyings.length; i++){
flying = flyings[i];
if(flying.shootBy(bullet)){
index = i; //如果有撞击到,将被撞击的元素下标取出,在外部处理这个元素,避免嵌套过深
break;
}
}
if(index != -1){
flying = flyings[index];
if(flying instanceof Bee){
int type = ((Bee) flying).getAwardType();
if(type == Bee.AWARD_FIRE){
hero.addFirePower();
}
if(type == Bee.AWARD_LIFE){
hero.addLife();
}
}
if(flying instanceof Airplane){
score += 5;
}
//省去了使用循环操作的麻烦
flyings[index] = flyings[flyings.length - 1];
flyings = Arrays.copyOf(flyings, flyings.length - 1);
return true;
}
return false;
}
//子弹击落敌机方法
public void shootAction(){
Bullet[] bullets_temp = new Bullet[bullets.length];
int bullet_index = 0;
for(int i = 0; i<bullets.length; i++){
Bullet bullet = bullets[i];
if(!bulletShootFlyings(bullet)){
bullets_temp[bullet_index ++] = bullet;
}
}
bullets = Arrays.copyOf(bullets_temp, bullet_index);
}
//英雄机撞击敌人
public void hitAction(){
for(int i = 0; i<flyings.length; i++){
FlyingObject flying = flyings[i];
if(hero.hitFlying(flying)){
hero.substactLife();
hero.clrFirePower();
flyings[i] = flyings[flyings.length - 1];
flyings = Arrays.copyOf(flyings, flyings.length - 1);
}
}
}
public void gameOverAction(){
if(hero.getLife() <= 0){
state = GAMEOVER;
}
}
public void action(){
Timer timer = new Timer();
/**
* 用于移动的定时任务
*/
timer.schedule(new TimerTask() {
@Override
public void run() {
if(state == RUNNING){
FlyingEnter();
BulletEnter();
moveStepAction();
outOfBoundAction();
shootAction();
hitAction();
gameOverAction();
}
repaint();
}
}, 0, 10);
/**
* 键盘控制英雄机的行动
*/
this.requestFocus();
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if(state == RUNNING){ //程序没有运行时英雄机是不能动的
switch(e.getKeyCode()){
case KeyEvent.VK_UP:
hero.moveDirection(Hero.MOVE_UP);
repaint();
break;
case KeyEvent.VK_DOWN:
hero.moveDirection(Hero.MOVE_DOWN);
repaint();
break;
case KeyEvent.VK_LEFT:
hero.moveDirection(Hero.MOVE_LEFT);
repaint();
break;
case KeyEvent.VK_RIGHT:
hero.moveDirection(Hero.MOVE_RIGHT);
repaint();
break;
default:
break;
}
}
}
});
/**
* 鼠标控制英雄机的行动
*/
MouseAdapter adapter = new MouseAdapter() {
public void mouseMoved(MouseEvent arg0) {
if(state == RUNNING){ //状态不是运行时候不能移动英雄机
hero.setLocation(arg0.getX(), arg0.getY());
}
}
public void mouseClicked(MouseEvent arg0) {
if(state == START){
state = RUNNING;
}
if(state == GAMEOVER){
score = 0;
hero = new Hero(200, 300);
flyings = new FlyingObject[]{};
bullets = new Bullet[]{};
state = START;
}
}
public void mouseExited(MouseEvent arg0) {
if(state == RUNNING){
state = PAUSE;
}
}
public void mouseEntered(MouseEvent arg0) {
if(state == PAUSE){
state = RUNNING;
}
}
};
this.addMouseListener(adapter);
this.addMouseMotionListener(adapter);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
Stage panel = new Stage();
frame.add(panel);
frame.setSize(STAGE_WIDTH+10, STAGE_HEIGHT+10);
frame.setAlwaysOnTop(true); //将串口一直放在所有窗口之上
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置默认关闭操作为串口关闭时退出程序
frame.setLocationRelativeTo(null); //设置窗口位置居中
frame.setVisible(true); //显示窗口,尽快调用paint()方法
panel.action();
}
}