Java学习:用面向对象简单实现飞机大战

后端学习1 19.3K1字数 11804阅读39分20秒阅读模式

利用面想对象知识,实现一个简单的“飞机大战”小游戏。代码粗糙,有兴趣可以自己优化下!

Java学习:用面向对象简单实现飞机大战文章源自亦枫博客-https://yflad.cn/2024.html

 文章源自亦枫博客-https://yflad.cn/2024.html

案例具体要求:

要求实现飞机大战功能文章源自亦枫博客-https://yflad.cn/2024.html

  1. 有敌机,加分
  2. 有奖励机,加生命值,或者火力值,飞行有一定规则
  3. 有英雄机,碰到敌机会减少生命值,且火力值回复初始,直至游戏结束
  4. 有子弹,与敌机、奖励机碰撞,敌机、奖励机消失
  5. 有计分,生命值(声明变量,画出分数,生命值。子弹和飞行物有碰撞时,判断类型)
  6. 敌机,奖励机不可以越界(逆向思维,将在窗口中显示的对象添加到相关的数组中。定义一个abstract,判断y的值,有没有超过飞行物的值。遍历所有的飞行物,把没有越界的放到下面,判断是否越界)
  7. 有开始界面,鼠标控制英雄机,鼠标移出暂停游戏,鼠标进入继续游戏,生命归零,结束游戏(定义一个初始状态,添加鼠标监听器,调用英雄机类,重写相关方法)

Java学习:用面向对象简单实现飞机大战文章源自亦枫博客-https://yflad.cn/2024.html

 文章源自亦枫博客-https://yflad.cn/2024.html

实现代码:

 文章源自亦枫博客-https://yflad.cn/2024.html

飞行物的父类

package cn.yflad.game;

import java.awt.image.BufferedImage;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-09
 * @role: 飞行物的父类
 */
public abstract class FlyingObject {
	public BufferedImage image;// 有图片
	public int x;// x坐标
	public int y;// y坐标
	public int height;// 图片的高度
	public int width;// 图片的宽度

	public abstract void step(); // 飞机移动

	public boolean checkBang(Bullet b) {// 检查子弹和飞行物碰撞的方法
		int x1 = this.x - b.width;
		int y1 = this.y - b.height;
		int x2 = this.x + width;
		int y2 = this.y + height;
		if (b.x >= x1 && b.x <= x2 && b.y >= y1 && b.y <= y2) {
			return true;
		}
		return false;
	}

	// 检查飞行物越界
	public abstract boolean outOfBounds();

}

 文章源自亦枫博客-https://yflad.cn/2024.html

普通的敌机类:飞行物的子类

Java学习:用面向对象简单实现飞机大战文章源自亦枫博客-https://yflad.cn/2024.html

package cn.yflad.game;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-10
 * @role: 普通的敌机类:飞行物的子类
 */
public class Airplane extends FlyingObject implements Enemy {

	int ySpeed = 3; // y轴上的速度

	// 通过构造函数对敌机进行初始化操作
	public Airplane() {
		image = TestGame.airplane;// 敌机的图片
		width = image.getWidth();// 获取图片宽度
		height = image.getHeight();// 获取图片的高度
		x = (int) (Math.random() * (TestGame.WIDTH - width));// x轴 原点到(窗口宽-敌机宽)之间的随机数
		y = -height;
	}

	// 敌机移动的方式
	@Override
	public void step() {
		y += ySpeed;// y轴+(向下)
	}

	// 子弹碰撞敌机,每碰撞一次加10分
	@Override
	public int getScore() {
		return 10;
	}

	// 敌机越界
	@Override
	public boolean outOfBounds() {
		return y > TestGame.HEIGHT;// 判断y的值,有没有超过本类的值
	}
}

 文章源自亦枫博客-https://yflad.cn/2024.html

小蜜蜂类:飞行物的子类

Java学习:用面向对象简单实现飞机大战文章源自亦枫博客-https://yflad.cn/2024.html

package cn.yflad.game;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-09
 * @role: 小蜜蜂类:飞行物的子类
 */
public class Bee extends FlyingObject implements Award {
	int xspeed = 3; // x坐标移动速度
	int yspeed = 2; // y坐标移动速度

	// 初始化数据
	public Bee() {
		image = TestGame.bee;// 小蜜蜂的图片
		width = image.getWidth();
		height = image.getHeight();
		x = (int) (Math.random() * (TestGame.WIDTH - width));// x轴出现的位置在窗口高度范围内随机出现
		y = -height;
	}

	@Override
	public void step() {
		// 蜜蜂走z字型
		x += xspeed;// x+(向左或向右)
		y += yspeed;// y+(向下)
		if (x <= 0 || x >= TestGame.WIDTH - width) {
			xspeed = -xspeed;
		}
	}

	//奖励类型可能为加命或者加双倍火力
	@Override
	public int getType() {
		int type = (int) (Math.random() * 2);
		return type;
	}

	// 小蜜蜂越界
	@Override
	public boolean outOfBounds() {
		return y > TestGame.HEIGHT;
	}
}

 文章源自亦枫博客-https://yflad.cn/2024.html

英雄机类:飞行物的子类

Java学习:用面向对象简单实现飞机大战文章源自亦枫博客-https://yflad.cn/2024.html

package cn.yflad.game;

import java.awt.image.BufferedImage;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-09
 * @role: 英雄机类:飞行物的子类
 */
public class Hero extends FlyingObject {
	// 有什么
	BufferedImage[] images;
	int doubleFire;// 默认为单倍火力
	int life;// 默认生命值

	public Hero() {// 通过构造函数对敌机进行初始化操作
		image = TestGame.hero0;
		images = new BufferedImage[] { TestGame.hero0, TestGame.hero1 };
		width = image.getWidth();
		height = image.getHeight();
		x = TestGame.WIDTH / 2 - width / 2;
		y = TestGame.HEIGHT / 2 + height / 2;
		doubleFire = 0;// 默认单倍火力
		life = 3;
	}

	int index = 0;
	int indexStep = 0;

	// 敌机移动的方式
	@Override
	public void step() {
		if (index++ % 5 == 0) {// 相当于150毫秒切换一次图片
			image = images[indexStep++ % images.length];
		}
	}

	// 英雄机移动的方法。x:鼠标的x坐标 y:鼠标的y坐标
	public void moveTo(int x, int y) {
		this.x = x - width / 2;
		this.y = y - height / 2;
	}

	// 英雄机发射子弹的方法
	public Bullet[] shoot() {
		int x = width / 4;// 局部变量
		Bullet[] b;
		if (doubleFire > 0) {
			// 双倍火力
			b = new Bullet[2];
			b[0] = new Bullet(this.x + x, this.y - 10);
			b[1] = new Bullet(this.x + 3 * x, this.y - 10);
			//双倍火力不是源源不断,给定一定量双倍,发射完时 为单倍火力
			doubleFire -= 2;// doubleFire=doubleFire-2;
		} else {
			// 单倍火力
			b = new Bullet[1];
			b[0] = new Bullet(this.x + 2 * x, this.y - 10);
		}
		return b;
	}

	// 获取生命的方法
	public int getLife() {
		return life;
	}

	// 加命的方法
	public void addLife() {
		life++;
	}

	// 减命的方法
	public void deleteLife() {
		life--;
	}

	// 加双倍火力的方法
	public void addDoubleFire() {
		doubleFire = 40;
	}

	// 双倍火力清零的方法
	public void clearDoubleFire() {
		doubleFire = 0;
	}

	// 英雄机和飞行物撞
	public boolean hit(FlyingObject other) {// 传对象
		int x1 = this.x - other.width;
		int x2 = this.x + width;
		int y1 = this.y - other.height;
		int y2 = this.y + height;
		if (other.x >= x1 && other.x <= x2 && other.y >= y1 && other.y <= y2) {
			return true;
		}
		return false;
	}

	// 飞行机永不越界
	@Override
	public boolean outOfBounds() {
		return false;
	}
}

 文章源自亦枫博客-https://yflad.cn/2024.html

子弹类:飞行物的子类

Java学习:用面向对象简单实现飞机大战文章源自亦枫博客-https://yflad.cn/2024.html

package cn.yflad.game;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-09
 * @role: 子弹类:飞行物的子类
 */
public class Bullet extends FlyingObject {
	// 有什么
	int yspeed = -3;// y轴上的速度
	/*
	 * 因为子弹的坐标是由英雄机所决定的 所以这里可以用有参构造函数来初始化子弹
	 */
	public Bullet(int x, int y) {
		image = TestGame.bullet;
		height = image.getHeight();
		width = image.getWidth();
		this.x = x;
		this.y = y;
	}

	@Override
	public void step() {
		y += yspeed;
	}

	// 子弹越界
	@Override
	public boolean outOfBounds() {
		return y < -height;
	}
}

 文章源自亦枫博客-https://yflad.cn/2024.html

加命或加双倍火力

package cn.yflad.game;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-11
 * @role: 奖励类型: 加命或加双倍火力
 */
public interface Award {
	public static final int ADD_LIFE = 0;// 生命
	public static final int DOUBLE_FIRE = 1;// 火力

	public abstract int getType();// 奖励类型
}

 文章源自亦枫博客-https://yflad.cn/2024.html

加分类

package cn.yflad.game;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-09
 * @role: 奖励类型:加分
 */
public interface Enemy {
	// 获取分数的方法
	public abstract int getScore();
}

 文章源自亦枫博客-https://yflad.cn/2024.html

运行窗口程序:

package cn.yflad.game;

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.Timer;
import java.util.TimerTask;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * @Author: yflad (yflad@qq.com)
 * @Blog: https://yflad.cn
 * @Date: 2018-12-09
 * @role: 实现
 */
public class TestGame extends JPanel {// 继承了JPanel类,相当于继承了一个画笔
	public static final int WIDTH = 400;// 面板宽
	public static final int HEIGHT = 654;// 面板高

	/*
	 * 加载图片
	 */
	public static BufferedImage bj;
	public static BufferedImage airplane;
	public static BufferedImage bee;
	public static BufferedImage bullet;
	public static BufferedImage gameover;
	public static BufferedImage hero0;
	public static BufferedImage hero1;
	public static BufferedImage pause;
	public static BufferedImage start;

	// 程序的状态
	public static final int START = 0;// 开始
	public static final int RUNNING = 1;// 运行状态
	public static final int PAUSE = 2;// 暂停状态
	public static final int GAMEOVER = 3;// 结束状态
	// 默认状态为开始状态
	int state = 0;

	FlyingObject[] flyings = {};// 创建飞行物类型数组
	Hero hero = new Hero();// 创建英雄机对象
	Bullet[] bullets = {};// 声明一个存放子弹的数组
	int score = 0;// 声明一个分数的变量

	/*
	 * 通过静态代码块来加载图片
	 */
	static {
		try {
			bj = ImageIO.read(TestGame.class.getResource("..\\image\\background.png"));
			airplane = ImageIO.read(TestGame.class.getResource("..\\image\\airplane.png"));
			bee = ImageIO.read(TestGame.class.getResource("..\\image\\bee.png"));
			bullet = ImageIO.read(TestGame.class.getResource("..\\image\\bullet.png"));
			gameover = ImageIO.read(TestGame.class.getResource("..\\image\\gameover.png"));
			hero0 = ImageIO.read(TestGame.class.getResource("..\\image\\hero0.png"));
			hero1 = ImageIO.read(TestGame.class.getResource("..\\image\\hero1.png"));
			pause = ImageIO.read(TestGame.class.getResource("..\\image\\pause.png"));
			start = ImageIO.read(TestGame.class.getResource("..\\image\\start.png"));
		} catch (Exception e) { // 删掉IO
			System.out.println("图片加载失败!");
		}
	}

	/*
	 * 用画笔画画,重写JPanel中的paint()方法
	 */
	@Override
	public void paint(Graphics g) {
		// 画背景
		g.drawImage(bj, 0, 0, null);
		// 画敌人
		for (int i = 0; i < flyings.length; i++) {// 遍历所有敌人(敌机+小蜜蜂)
			FlyingObject f = flyings[i];// 获取每一个飞行物对象
			g.drawImage(f.image, f.x, f.y, null);// 画敌人(敌机+小蜜蜂)对象
		}
		// 画英雄机
		g.drawImage(hero.image, hero.x, hero.y, null);
		// 画子弹
		for (int i = 0; i < bullets.length; i++) {// 遍历所有子弹
			Bullet b = bullets[i];
			g.drawImage(b.image, b.x, b.y, null);
		}

		// 设置字体的颜色和大小
		g.setColor(Color.BLUE);
		g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 24));
		// 画分数和生命值
		g.drawString("分数:" + score, 10, 25);
		g.drawString("生命:" + hero.life, 10, 45);
		// 画状态
		switch (state) {
		case START:
			g.drawImage(start, 0, 0, null);
			break;
		case PAUSE:
			g.drawImage(pause, 0, 0, null);
			break;
		case GAMEOVER:
			g.drawImage(gameover, 0, 0, null);
			break;
		}
	}

	/**
	 * 创建飞行物对象的方法
	 */
	public FlyingObject nextOne() {
		int type = (int) (Math.random() * 10);// 生成敌机和小蜜蜂的概率比大约为10:1
		if (type == 0) {
			return new Bee();// 蜜蜂对象
		}
		return new Airplane();// 敌机对象
	}

	/**
	 * 将创建的飞行物对象添加到飞行物数组中去
	 */
	int index = 0;

	public void addAction() {// 添加飞行物,并对数组进行扩容
		/*
		 * 默认情况下是30毫秒创建一个对象添加到数组中去,通过index遍历来控制添加的对象个数
		 */
		if (index++ % 20 == 0) {// 相当于30*20毫秒往数组中添加一个对象
			flyings = Arrays.copyOf(flyings, flyings.length + 1);// 将飞行物数组扩容
			flyings[flyings.length - 1] = nextOne();// 将创建的对象放到数组中去
		}
	}

	/**
	 * 将英雄机发射出来的子弹数组复制到bullets子弹数组中去
	 */
	int index2 = 0;

	public void shootAction() {
		if (index2++ % 20 == 0) {
			// 英雄机发射子弹
			Bullet[] bs = hero.shoot();
			// 对bullets子弹数据进行扩容
			bullets = Arrays.copyOf(bullets, bullets.length + bs.length);
			// 通过数组的复制将英雄机发射出来的子弹添加到bullets数组中去
			System.arraycopy(bs, 0, bullets, bullets.length - bs.length, bs.length);
		}
	}

	/**
	 * 所有飞行物类移动的方法
	 */
	public void stepAction() {
		for (int i = 0; i < flyings.length; i++) {// 敌机飞行一步
			FlyingObject f = flyings[i];// 向上造型。获取每一个敌机对象
			/*
			 * 判断对象属于哪个子类,因为子类的移动方式不一样 所以在这里得通过向下造型来操作
			 */
			if (f instanceof Airplane) {// 敌机移动方式
				Airplane a = (Airplane) f;
				a.step();
			}
			if (f instanceof Bee) {// 蜜蜂移动方式
				Bee b = (Bee) f;
				b.step();
			}
		}
		// 英雄机移动的方式
		hero.step();
		// 子弹的移动方式
		for (int i = 0; i < bullets.length; i++) {// 敌机飞行一步
			Bullet b = bullets[i];// 获取每一个敌机对象
			b.step();
		}
	}

	/**
	 * 一个子弹和一堆敌人撞
	 */
	public void bang(Bullet b) {
		int index = -1;// 记录被撞飞行物下标
		// 遍历所有的飞行物对象
		for (int i = 0; i < flyings.length; i++) {
			FlyingObject f = flyings[i];
			if (f.checkBang(b)) {
				// 进入到这说明子弹和飞行物发生了碰撞
				index = i;// 将被撞物的下标赋值给临时变量
				break;
			}
		}
		if (index != -1) {// 说明子弹和飞行物有碰撞
			if (flyings[index] instanceof Enemy) {// 判断属于什么奖励类型.是敌人,则加分
				score += ((Enemy) flyings[index]).getScore();
			}
			if (flyings[index] instanceof Award) {// 若为奖励,设置奖励
				Bee bee = (Bee) flyings[index];
				int type = bee.getType();// 获取奖励类型
				if (type == 0) {
					hero.addLife();// 加命
				}
				if (type == 1) {
					hero.addDoubleFire();// 加双倍火力
				}
			}
			// 对碰撞的飞行物进行缩容
			FlyingObject fo = flyings[index];
			flyings[index] = flyings[flyings.length - 1];
			flyings[flyings.length - 1] = fo;
			flyings = Arrays.copyOf(flyings, flyings.length - 1);
		}
	}

	/**
	 * 一堆子弹和一堆敌人碰撞
	 */
	public void bangAction() {
		// 遍历所有的子弹对象
		for (int i = 0; i < bullets.length; i++) {
			Bullet b = bullets[i];
			bang(b);
		}
	}

	/**
	 * 英雄机和敌机碰撞
	 */
	public void hitAction() {
		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 fo = flyings[index];
			flyings[index] = flyings[flyings.length - 1];
			flyings[flyings.length - 1] = fo;
			flyings = Arrays.copyOf(flyings, flyings.length - 1);// 缩容(去掉最后一个元素,即被撞的敌人对象)

			hero.deleteLife();// 碰撞后要减命
			hero.clearDoubleFire();// 碰撞后火力值清零
		}
	}

	/**
	 * 检查游戏是否结束的方法
	 */
	public void checkGameOver() {
		int life = hero.getLife();
		if (life == 0) {
			state = GAMEOVER;
		}
	}

	/**
	 * 检查飞行物越界方法 
	 * 思路:用逆向思维,将在窗口中显示的对象添加到相关的数组中
	 */
	public void outOfBoundsAction() {
		int index = 0;// 记录数组的下标,记录对象的个数
		// 声明一个在窗口中显示的飞行物对象数组
		FlyingObject[] aliveFlying = new FlyingObject[flyings.length];// 默认所有的对象都活着
		// 遍历所有的飞行物,把没有越界的放到下面,判断是否越界,越界的不添加到aliveFlying
		for (int i = 0; i < flyings.length; i++) {
			FlyingObject f = flyings[i];
			if (!f.outOfBounds()) {
				// 进入到这说明没有越界,添加到aliveFlying
				aliveFlying[index++] = f;
			}
		}
		// for循环遍历完之后,fAlive中存放的都是没有越界的对象
		// 将fAlive这个数组中对象把flying数组覆盖掉
		flyings = Arrays.copyOf(aliveFlying, index);
		
		// 子弹数组和飞行物数组一致
		index = 0;// 记录数组的下标,记录对象的个数
		Bullet[] aliveBullet = new Bullet[bullets.length];// 默认所有的对象都活着
		// 遍历所有的飞行物,把没有越界的放到下面,判断是否越界,越界的不添加到aliveBullet
		for (int i = 0; i < bullets.length; i++) {
			Bullet b = bullets[i];
			if (!b.outOfBounds()) {
				// 进入到这说明没有越界,添加到aliveBullet
				aliveBullet[index++] = b;
			}
		}
		// for循环遍历完之后,fAlive中存放的都是没有越界的对象
		// 将fAlive这个数组中对象把flying数组覆盖掉
		bullets = Arrays.copyOf(aliveBullet, index);

	}

	/**
	 * 通过定时器来执行程序和刷新页面
	 */
	Timer timer = new Timer();

	/*
	 * 执行代码
	 */
	public void action() {
		// 添加一个鼠标监听器
		MouseAdapter l = new MouseAdapter() {
			// 重写所需的方法
			@Override
			public void mouseMoved(MouseEvent e) {// 鼠标移动
				// 获取鼠标的x坐标,y坐标
				int x = e.getX();
				int y = e.getY();
				hero.moveTo(x, y);// 调用Hero类的方法
			}

			// 重写鼠标点击的方法
			@Override
			public void mouseClicked(MouseEvent e) {
				if (state == START) {
					state = RUNNING;
				}
				if (state == GAMEOVER) {
					state = START;
					score = 0;// 分数清零
					hero.life = 3;// 恢复到初始状态
					hero.clearDoubleFire();
					state=START;
				}
			}

			// 重写鼠标移入的方法
			@Override
			public void mouseExited(MouseEvent e) {
				if (state == RUNNING) {
					state = PAUSE;
				}
			}

			// 重写鼠标移出的方法
			@Override
			public void mouseEntered(MouseEvent e) {
				if (state == PAUSE) {
					state = RUNNING;
				}
			}
		};
		
		timer.schedule(new TimerTask() {
			@Override
			public void run() {
				if (state == RUNNING) {
					
					addAction();// 调用 将敌机对象放入敌机数组 的方法
					shootAction();// 英雄机发射出来的子弹
					stepAction();// 调用 敌机移动 方法
					bangAction();// 一堆子弹和一堆敌人碰撞
					hitAction();// 英雄机撞
					outOfBoundsAction();
					checkGameOver();
				}
				repaint();// 刷新页面
			}
		}, 0, 30);
		// 将监听器适配器添加到鼠标移动事件中去
		this.addMouseMotionListener(l);// 添加鼠标运动监听器
		this.addMouseListener(l);// 添加鼠标监听器
	}

	public static void main(String[] args) {
		JFrame frame = new JFrame("飞机大战1.0"); // 创建一个窗体程序,相当于画板
		TestGame game = new TestGame(); // 创建一个画板对象
		frame.add(game); // 将面板(画笔)添加到frame
		frame.setSize(WIDTH, HEIGHT);// 设置窗口的大小
		frame.setVisible(true);// 设置窗口的显示
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 设置窗口关闭按钮
		frame.setAlwaysOnTop(true);// 设置窗口置顶显示
		frame.setLocationRelativeTo(null);// 设置窗口居中
		game.action(); // 执行
	}
}

 文章源自亦枫博客-https://yflad.cn/2024.html

下载信息 飞机大战素材
最近更新2019-5-11
下载地址 查看演示
文章源自亦枫博客-https://yflad.cn/2024.html文章源自亦枫博客-https://yflad.cn/2024.html
继续阅读
扫扫关注公众号
weinxin
我的微信
扫扫体验小程序
weinxin
我的公众号
亦枫
  • 本文由 发表于 2018年12月11日 18:35:15
评论  1  访客  1
    • yy33344
      yy33344 4

      照这个写法,背景图是静止的吗,好像并不会动

    匿名

    发表评论

    匿名网友 填写信息

    :?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

    确定

    取消