Java版本飞机大战

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43219615/article/details/97138976

1.关于这个飞机大战

学Java有一段时间了,然后通过Java开发了一些简易安卓APP。最近很想实现以前自己很想写的飞机大战,也练练手。代码和界面参考(抄袭 )了许多,毕竟天下码农一家亲。
游戏大概长下面这样,鼠标点击即开始游戏,然后用鼠标操作飞机移动(鼠标到哪飞机移动到哪,超出游戏界面就暂停)。
游戏界面

2.代码实现

  1. 首先写几个bean。GameStateBean里面存放了一些游戏状态的常量,ImgBean里面存放了游戏所用到的图片资源,这些图片和代码放在同一个包下。
package xyz.xinghai.game.bean;

public class GameStateBean {
	//游戏启动
	public static final int START = 0;
	//游戏运行
	public static final int RUNNING = 1;
	//游戏暂停
	public static final int PAUSE = 2;
	//游戏结束
	public static final int GAMEOVER = 3;
}
package xyz.xinghai.game.bean;

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ImgBean {
	public static BufferedImage backgroundImg;
	//游戏启动图
	public static BufferedImage startImg;
	//游戏暂停图
	public static BufferedImage pauseImg;
	//游戏结束图
	public static BufferedImage gameOverImg;
	//敌机图
	public static BufferedImage enemyImg;
	public static BufferedImage beeImg;
	//子弹图
	public static BufferedImage bulletImg;
	public static BufferedImage hero0Img;
	public static BufferedImage hero1Img;
	public static BufferedImage bigBeeImg;
	
	static {
		try {
			backgroundImg = ImageIO.read(ImgBean.class.getResource("background.png"));
			startImg = ImageIO.read(ImgBean.class.getResource("start.png"));
			pauseImg = ImageIO.read(ImgBean.class.getResource("pause.png"));
			gameOverImg = ImageIO.read(ImgBean.class.getResource("gameover.png"));
			enemyImg = ImageIO.read(ImgBean.class.getResource("airplane.png"));
			beeImg = ImageIO.read(ImgBean.class.getResource("bee.png"));
			bulletImg = ImageIO.read(ImgBean.class.getResource("bullet.png"));
			hero0Img = ImageIO.read(ImgBean.class.getResource("hero0.png"));
			hero1Img = ImageIO.read(ImgBean.class.getResource("hero1.png"));
			bigBeeImg = ImageIO.read(ImgBean.class.getResource("bigBee.png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
  1. 游戏中的飞行物体都有显示图片、高度、宽度、在游戏界面中的坐标(x、y)属性和移动(move)、是否飞出游戏界面(outOfBounds)方法。基于这些共同点编写飞行物体的抽象类FlyingObject,所有飞行的物体都继承这个类。
package xyz.xinghai.game;

import java.awt.image.BufferedImage;

/**
 * 飞行物体的抽象类
 */
public abstract class FlyingObject {
	//飞行物的图片
	protected BufferedImage image; 
	//飞行物的宽度
	protected int width;
	//飞行物的高度
	protected int height;
	//飞行物的x坐标
	protected int x;
	//飞行物的y坐标
	protected int y;
	
	//飞行物的移动
	public abstract void move();
	
	//是否飞出游戏界面
	public abstract boolean outOfBounds();
	
	//判断是否被击中 这个不是所有飞行物都有 所以不是抽象方法
	public boolean shootBy(Bullet bullet) {
		int x = bullet.x;
		int y = bullet.y;
		
		return (x > this.x && x < (this.x + this.width)) && (y > this.y && y < (this.y + this.height));
	}
}
  1. 编写游戏中的子弹类和主飞机类。继承自FlyingObject,获得了它的Object属性然后重新abstract方法。因为子弹由主飞机从下面打到上面的,所有子弹移动的时候就是y的坐标根据速度减,判断是否超出了游戏界面也就是直接判断它的y坐标是否小于它的-height。编写主飞机类时,加了doubleFire(值越大飞机打子弹越多)、life(飞机生命)等属性。因为使用鼠标控制主飞机,所以它在移动的时候只是改变了了显示图片,鼠标移到哪里直接调用moveTo方法移动到鼠标的位置即可。
package xyz.xinghai.game;

import xyz.xinghai.game.bean.ImgBean;

/**
 * 子弹类
 *
 */
public class Bullet extends FlyingObject {
	//子弹速度
	private int speed = 3;
	
	/**
	 * @param x 游戏界面上的x坐标
	 * @param y 游戏界面上的y坐标
	 */
	public Bullet(int x, int y) {
		this.image = ImgBean.bulletImg;
		this.width = this.image.getWidth();
		this.height = this.image.getHeight();
		this.x = x;
		this.y = y;
	}

	@Override
	public void move() {
		this.y -= speed;
	}

	@Override
	public boolean outOfBounds() {
		return this.y <= -this.height;
	}
}
package xyz.xinghai.game;

import java.awt.image.BufferedImage;

import xyz.xinghai.game.bean.ImgBean;

public class Hero extends FlyingObject {
	private int life;
	private int doubleFire;
	private BufferedImage[] images;
	private int index;
	
	public Hero() {
		this.image = ImgBean.hero0Img;
		this.width = this.image.getWidth();
		this.height = this.image.getHeight();
		x = 150;
		y = 400;
		life = 3;
		doubleFire = 0;
		images = new BufferedImage[]{ImgBean.hero0Img,ImgBean.hero1Img};
		index = 0;
	}
	
	@Override
	public void move() {
		image = images[index++/10%images.length];
	}
	
	@Override
	public boolean outOfBounds() {
		return false;
	}
	
	/**
	 * 根据doubleFire的值打不同数目的子弹
	 * @return 子弹数组
	 */
	public Bullet[] shoot() {
		int xSpeed = this.width / 4;
		int ySpeed = 20;
		
		if (doubleFire >= 100) {
			//三粒子弹 子弹的初始位置不同
			Bullet[] bs=new Bullet[3];
			bs[0] = new Bullet(this.x + 1 * xSpeed, this.y - ySpeed);
			bs[1] = new Bullet(this.x + 2 * xSpeed, this.y - ySpeed);
			bs[2] = new Bullet(this.x + 3 * xSpeed, this.y - ySpeed);
			doubleFire -= 2;
			return bs;
		} else if (doubleFire < 100 && doubleFire > 0) {
			Bullet[] bs=new Bullet[2];
			//两粒子弹
			bs[0]=new Bullet(this.x + 1* xSpeed, this.y - ySpeed);
			bs[1]=new Bullet(this.x + 3* xSpeed, this.y - ySpeed);
			doubleFire -= 2;
			return bs;
		} else{
			//一粒子弹
			Bullet[] bs = new Bullet[1];
			bs[0] = new Bullet(this.x + 2 * xSpeed, this.y - ySpeed);
			return bs;
		}
	}
	
	/**
	 * 飞机移到指定坐标去
	 * @param x x坐标
	 * @param y y坐标
	 */
	public void moveTo(int x, int y) {
		this.x=x-this.width/2;
		this.y=y-this.height/2;	
	}
	
	public void addLife(){
		life+=2;
	}
	
	public int getLife(){
		return life;
	}
	
	/**
	 * 加火力
	 */
	public void addDoubleFire(){
		doubleFire += 20;
	}
	
	public int getDoubleFire(){
		return doubleFire;
	}
	
	/**
	 * 生命减少
	 */
	public void subtractLife(){
		life--;
	}
	
	/**
	 * 回到最初的发子弹速度
	 */
	public void clealDoubleFire(){
		doubleFire = 0;
	}
	
	/**
	 * 是否发生碰撞
	 * @param other 其他继承自FlyingObject的飞行物
	 * @return
	 */
	public boolean hit(FlyingObject other) {
		int x1 = other.x - this.width/2;
		int x2 = other.x + this.width/2 + other.width;
		int y1 = other.y - this.height/2;
		int y2 = other.y + this.height/2 + other.height;
		int x = this.x + this.width/2;
		int y = this.y + this.height;
		return (x>x1&&x<x2) && (y>y1&&y<y2);
	}
}
  1. 编写敌人(打败敌机获得分数)和游戏中奖励(打败带有奖励的敌人获得奖励)的接口。
package xyz.xinghai.game;

/**
 * 敌人接口
 *
 */
public interface Enemy {
	public int getScore();
}
package xyz.xinghai.game;

/**
 1. 奖励接口
 2.  */
public interface Award {
	int DOUBLE_FIRE = 0;
	int LIFE = 1;//生命
	public int getType();
}
  1. 编写敌机类、游戏中击败有奖励敌机(击败后子弹打得更多)和游戏中较强的敌机(击败后得分更多)。敌机添加了速度和击败得分属性,由于敌机是从游戏界面上面移动到下面的,所以在移动的时候y坐标要随着速度增大。
package xyz.xinghai.game;

import java.util.Random;

import xyz.xinghai.game.bean.ImgBean;

/**
 * 敌机类
 *
 */
public class EnemyPlane extends FlyingObject implements Enemy {
	//速度
	private int speed = 2;
	//得分
	private int score = 5;
	
	public EnemyPlane() {
		this.image = ImgBean.enemyImg;
		this.width = this.image.getWidth();
		this.height = this.image.getHeight();
		
		//随机初始化敌机位置
		Random random = new Random();
		x = random.nextInt(AircraftBattle.WIDTH - this.width);
		y = -this.height;
	}
	
	public int getScore() {
		return score;
	}

	@Override
	public void move() {
		y += speed;
	}

	@Override
	public boolean outOfBounds() {
		return this.y >= AircraftBattle.HEIGHT;
	}

	public void setScore(int score) {
		this.score = score;
	}
}
package xyz.xinghai.game;

import java.util.Random;

import xyz.xinghai.game.bean.ImgBean;

/**
 * 打败后奖励积分更多
 *
 */
public class BigBee extends FlyingObject implements Enemy {
	private int speed = 1;
	
	public BigBee() {
		this.image = ImgBean.bigBeeImg;
		this.width = this.image.getWidth();
		this.height = this.image.getHeight();
		
		Random random = new Random();
		x = random.nextInt(AircraftBattle.WIDTH - this.width);
		y = -this.height;
	}

	@Override
	public void move() {
		y += speed;
	}

	@Override
	public boolean outOfBounds() {
		return this.y >= AircraftBattle.HEIGHT;
	}

	@Override
	public int getScore() {
		return 40;
	}
}
package xyz.xinghai.game;

import java.util.Random;

import xyz.xinghai.game.bean.ImgBean;

/**
 1. 打败后获得子弹加成
 2. @author 15321
 3.  */
public class Bee extends FlyingObject implements Award {
	private int xSpeed = 1;
	private int ySpeed = 2;
	private int awardType;
	
	public int getType() {
		return awardType;
	}
	
	public Bee() {
		this.image = ImgBean.beeImg;
		this.width = this.image.getWidth();
		this.height = this.image.getHeight();
		
		Random rand=new Random();
		this.x = rand.nextInt(AircraftBattle.WIDTH-this.width);
		this.y = -this.height;
		awardType=rand.nextInt(2);
	}

	@Override
	public void move() {
		y += ySpeed;
		x += xSpeed;
		if(x >= AircraftBattle.WIDTH-this.width){
			xSpeed =-1;
		}
		if(x <= 0){
			xSpeed =1;
		}
	}

	@Override
	public boolean outOfBounds() {
		return this.height >= AircraftBattle.HEIGHT;
	}	
}
  1. 编写游戏主窗口。主窗口和背景图片一样大,继承自JPanel主要是想重写paint方法。由于其他类已经写好,剩下的就是在游戏窗口中的逻辑处理了。用数组存储显示在界面上的各种物体,然后开启一个定时器每10ms扫描一次界面上的元素,当有事件发生时就调用对应方法。
package xyz.xinghai.game;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;

import xyz.xinghai.game.bean.GameStateBean;
import xyz.xinghai.game.bean.ImgBean;

/**
 1. 主程序类
 2. @author 15321
 3.  */
public class AircraftBattle extends JPanel {
	private static final long serialVersionUID = 1L;
	//游戏界面宽度
	public static final int WIDTH = 400;
	//游戏界面高度
	public static final int HEIGHT = 645;
	
	private int state = GameStateBean.START;
	//主飞机
	private Hero hero = new Hero();
	//敌机数组
	private FlyingObject[] flyings = {};
	//子弹数组
	private Bullet[] bullets = {};
	//得分
	private int score = 0;
	
	@Override
	public void paint(Graphics g) {
		g.drawImage(ImgBean.backgroundImg, 0, 0, null);
		paintHero(g);
		paintFlyingObjects(g);
		paintBullets(g);
		paintScoreAndLife(g);
		patintState(g);
	}
	
	/**
	 * 画得分和生命
	 * @param g 画笔
	 */
	private void paintScoreAndLife(Graphics g) {
		g.setColor(new Color(0xFF0000));
		g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,30));
		g.drawString("SCORE:" + score, 10, 25);
		g.drawString("LIFE:"+ hero.getLife(), 10, 50);
		g.drawString("DoubleFire:" + hero.getDoubleFire(), 10, 75);
	}
	
	/**
	 * 画主飞机
	 * @param g 画笔
	 */
	private void paintHero(Graphics g) {
		g.drawImage(hero.image, hero.x, hero.y, null);
	}
	
	/**
	 * 画屏幕中的敌机等飞行物体
	 * @param g 画笔
	 */
	private void paintFlyingObjects(Graphics g){
		for(FlyingObject f : flyings)
			g.drawImage(f.image, f.x, f.y, null);
	}
	
	/**
	 * 画子弹
	 * @param g 画笔
	 */
	private void paintBullets(Graphics g){
		for(Bullet b : bullets)
			g.drawImage(b.image, b.x, b.y, null);
	}
	
	/**
	 * 绘制游戏状态 
	 * @param g 画笔
	 */
	private void patintState(Graphics g) {
    	switch(state){
    		case GameStateBean.START:
    			g.drawImage(ImgBean.startImg, 0, 0, null);
    			break;
    		case GameStateBean.PAUSE:
    			g.drawImage(ImgBean.pauseImg, 0, 0, null);
    			break;
    		case GameStateBean.GAMEOVER:
    			g.drawImage(ImgBean.gameOverImg, 0, 0, null);
    	}
    }
	
	/**
	 * 生成敌机
	 * @return 敌机
	 */
	private FlyingObject nextOne() {
		Random rand = new Random();
		int type = rand.nextInt(20);
		if(type < 4) {
			return new BigBee();
		} else if (type == 5) {
			return new Bee();
		} else {
			return new EnemyPlane();
		}
	}
	
	//敌机入场的index
	int flyEnteredIndex = 0;
	/**
	 * 敌机入场的action
	 */
	private void enterAction() {
		flyEnteredIndex++;
		if(flyEnteredIndex % 40 ==0){
			FlyingObject obj = nextOne();
			//更新敌机数组	
			flyings = Arrays.copyOf(flyings, flyings.length+1);
			flyings[flyings.length-1] = obj;
		}
	}
	
	//子弹入场的index
	int shootIndex = 0;
	/**
	 * 主飞机发射子弹的Action 主飞机返回子弹数组 把返回的数组加到游戏子弹数组即可
	 */
	private 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);
		} 
	}
	
	/**
	 *飞行物移动的action 调用各自的移动方法即可
	 */
	private void moveAction(){
		hero.move();
		for(FlyingObject f : flyings) 
			f.move();
		for(Bullet b : bullets)
			b.move();
	}
	
	/**
	 * 飞行物飞出界面的action 就是把飞出界面的飞行物从数组中去掉
	 */
	private void outOfBoundsAction() {
		int index = 0;
		FlyingObject[] flyingLives = new FlyingObject[flyings.length];
		for(FlyingObject f : flyings) 
			if(!f.outOfBounds()) 
				flyingLives[index++] = f;
		flyings=Arrays.copyOf(flyingLives, index);
		index = 0;
		Bullet[] bulletLives = new Bullet[bullets.length];
		for(Bullet b : bullets) 
			if(!b.outOfBounds()) 
				bulletLives[index++] = b;
		bullets=Arrays.copyOf(bulletLives, index);
	}
	
	int j = 0;
	/**
	 * 碰撞的action
	 */
	private void collisionAction() {
		for(int i=0;i < bullets.length;i++){
			collision(bullets[i],i);
		}
	}
	
	/**
	 * 碰撞
	 * @param b 子弹
	 * @param bu 碰撞的子弹在子弹数组中的index
	 */
	private void collision(Bullet b, int bu) {
		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 EnemyPlane){
				Enemy e = (Enemy)one;
				score += e.getScore();
				FlyingObject t = flyings[index];
				flyings[index] = flyings[flyings.length-1];
				flyings[flyings.length-1] = t;
				flyings=Arrays.copyOf(flyings, flyings.length-1);//去掉被子弹撞上的敌人
			}
			if(one instanceof BigBee){//打五下才会挂
				j++;			
				if(j==5){
					Enemy e = (Enemy)one;
					score += e.getScore();
					j = 0;
					FlyingObject t = flyings[index];
					flyings[index] = flyings[flyings.length-1];
					flyings[flyings.length-1] = t;
					flyings = Arrays.copyOf(flyings, flyings.length-1);//去掉被子弹撞上的敌人
				}
			}
			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);//去掉被子弹撞上的敌人
			}
			
			Bullet tBullet = bullets[bu];
			bullets[bu] = bullets[bullets.length-1];
			bullets[bullets.length-1] = tBullet;
			bullets=Arrays.copyOf(bullets, bullets.length-1);//去掉子弹
		}
	}
	
	/**
	 * 检查游戏是否结束
	 */
	public void checkGameOverAction(){
		if(GameOver()){
			state = GameStateBean.GAMEOVER;
		}
	}
	
	private boolean GameOver() {
		for(int i=0; i<flyings.length; i++){
			FlyingObject f = flyings[i];
			if(hero.hit(f)){
				hero.subtractLife();
				hero.clealDoubleFire();
				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结束
	}
	
	/**
	 * 启动程序的action
	 */
	public void action(){
		MouseAdapter l = new MouseAdapter() {
			//重写鼠标移动事件
			public void mouseMoved(MouseEvent e){
				if(state == GameStateBean.RUNNING){
					hero.moveTo(e.getX(),e.getY());
				}
			}
			//鼠标点击事件
			public void mouseClicked(MouseEvent e){
				switch(state){
					case GameStateBean.START:
						state= GameStateBean.RUNNING;
						break;
					case GameStateBean.GAMEOVER:
						state = GameStateBean.START;
						//清理现场
						score = 0;
						hero = new Hero();
						flyings = new FlyingObject[0];
						bullets = new Bullet[0];
						break;
				}
			}
			//鼠标移除
			public void mouseExited(MouseEvent e){
				if(state == GameStateBean.RUNNING){
					state = GameStateBean.PAUSE;
				}
			}
			//鼠标移入
			public void mouseEntered(MouseEvent e){
				if(state == GameStateBean.PAUSE){
					state = GameStateBean.RUNNING;
				}
			}
		};
		this.addMouseListener(l);//处理鼠标操作事件
		this.addMouseMotionListener(l);//鼠标的滑动
		Timer timer = new Timer();//定时器
		int intervel = 10;//定时间隔(以毫秒为单位)
		timer.schedule(new TimerTask() {
			public void run(){
				if(state == GameStateBean.RUNNING){
					enterAction();//敌人入场
					moveAction();//敌人移动
					shootAction();//子弹入场
					collisionAction();//子弹与敌人相撞
					outOfBoundsAction();//删除越界的敌人和子弹
					checkGameOverAction();//敌人和英雄机相撞					
				}
				repaint();//重新绘制界面
			}
		}, intervel, intervel);
	}
	
	public static void main(String[] args) {
		JFrame gameFrame = new JFrame();
		gameFrame.setTitle("AircraftBattle");
		AircraftBattle game = new AircraftBattle();
		gameFrame.getContentPane().add(game);
		gameFrame.setSize(new Dimension(WIDTH, HEIGHT));
		gameFrame.setAlwaysOnTop(true);
		gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		gameFrame.setLocationRelativeTo(null);
		gameFrame.setVisible(true);
		
		game.action();
	}
}

3.总结

  1. 代码还是要靠自己动手写才能提升(ಥ﹏ಥ)。
  2. 完整的工程在我的GitHub。戳这里

猜你喜欢

转载自blog.csdn.net/weixin_43219615/article/details/97138976