目录
飞机大战
抽象类FlyingObject
小飞机Airplane类
大飞机BigAirplane类
奖励机Bee类
奖励类型Award接口
获得分数Score接口
天空Sky类
英雄级Hero类
子弹Bullet类
游戏开始World类
package cn.tedu.shoot;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
//飞机大战的父类飞行物
public abstract class FlyingObject {
// 定义三种状态常量:活着、死了、消失
public static final int LIFE = 0;
public static final int DEAD = 1;
public static final int REMOVE = 2;
// 定义当前对象的状态属性
protected int state = LIFE;// 初始默认活着
//所有子类共有的属性和方法、
protected int width;
protected int height;
protected int x;
protected int y;
// 小敌机,大敌机,奖励机使用的构造
public FlyingObject(int width, int height) {
this.width = width;
this.height = height;
// 所有敌级的y轴都是负高度,因为出现在屏幕上方
y = -height;
Random ran = new Random();
// x轴是从左侧开始到屏幕的宽度减去低级的宽度的随机数
x = ran.nextInt(400 - this.width);
}
// 子弹,天空,英雄机的构造
public FlyingObject(int width, int height, int x, int y) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
public void show() {
System.out.println("宽:" + width + ",高" + height);
System.out.println("x:" + x + ",y:" + y);
}
// 读取图片,将项目中保存的图片,转换成java内存的图片
public static BufferedImage readImage(String fileName) {
try {
// 根据文件名将图片获取并赋值给img
BufferedImage img = ImageIO.read(FlyingObject.class.getResource(fileName));
return img;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
// 定义一个单位移动的抽象方法
public abstract void step();
// 判断状态的方法
// 判断是否活着
public boolean isLife() {
return state == LIFE;
}
// 判断是否死了
public boolean isDead() {
return state == DEAD;
}
// 判断是否消失
public boolean isRemove() {
return state == REMOVE;
}
// 获得图片的抽象方法
public abstract BufferedImage getImage();
// 绘制图片到窗体
public void paintObject(Graphics g) {
g.drawImage(getImage(), x, y, null);
}
// 判断出界的方法
public boolean isOutOfBounds() {
return y > World.HEIGHT;
}
// 判断碰撞的方法
// 判断的是this(当前对象)是否和参数中的飞行物碰撞了
public boolean hit(FlyingObject f) {
// this.想象为子弹,f想象为一架敌机
int x1 = f.x - this.width;// 左侧点
int x2 = f.x + f.width;// 右侧点
int y1 = f.y - this.height;// 上方点
int y2 = f.y + f.height;// 下方点
return this.x > x1 && this.x < x2 && this.y > y1 && this.y < y2;
}
// 改变飞行物状态为DEAD的方法
public void goDead() {
state = DEAD;
}
}
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
public class Airplane extends FlyingObject implements Score{
// 保存小敌机相关图片的数组
private static BufferedImage[] images;
static {
// 初始化图片数组
images = new BufferedImage[5];
// 第一张图是小敌机图
images[0] = readImage("airplane0.png");
// 循环加载4张爆炸图
for (int i = 1; i < images.length; i++) {
images[i] = readImage("bom" + i + ".png");
}
}
// 速度
private int step;
public Airplane() {
super(48, 50);
step = 5;
}
public void show() {
super.show();
System.out.println("速度:" + step);
}
public void step() {
// 小敌机向下移动
y += step;
}
int index = 1;
public BufferedImage getImage() {
BufferedImage img = null;
// 如果活着返回第一张图
if (isLife()) {
img = images[0];
} else if (isDead()) {
// 如果死了获得爆炸图片
img = images[index];
index++;
//如果爆炸完毕,将小飞机设置为消失
if(index==images.length) {
state=REMOVE;
}
}
return img;
}
public int getScore() {
//击中小敌机得1分
return 1;
}
}
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
public class BigAirplane extends FlyingObject implements Score{
private static BufferedImage[] images;
static {
images = new BufferedImage[5];
images[0] = readImage("bigairplane0.png");
for (int i = 1; i < images.length; i++) {
images[i] = readImage("bom" + i + ".png");
}
}
// 速度
private int step;
public BigAirplane() {
super(66, 89);
step = 5;
}
public void show() {
super.show();
System.out.println("速度:" + step);
}
public void step() {
y += step;
}
int index = 1;
public BufferedImage getImage() {
BufferedImage img = null;
if (isLife()) {
img = images[0];
} else if (isDead()) {
img = images[index];
index++;
if (index == images.length) {
state = REMOVE;
}
}
return img;
}
public int getScore() {
//击中大敌机获得3分
return 3;
}
}
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
import java.util.Random;
public class Bee extends FlyingObject implements Award{
private static BufferedImage[] images;
static {
images = new BufferedImage[5];
images[0] = readImage("bee0.png");
for (int i = 1; i < images.length; i++) {
images[i] = readImage("bom" + i + ".png");
}
}
// 移动分方向
private int xStep;// 左右移动
private int yStep;// 上下移动
// 奖励类型
private int awardType;
public Bee() {
super(60, 51);
xStep = 2;
yStep = 2;
Random ran = new Random();
// 随机生成0和1存入奖励类型,用于击中敌机时获得不同奖励
awardType = ran.nextInt(2);
}
public void show() {
super.show();
System.out.println("x速度:" + xStep + "\ny速度:" + yStep);
System.out.println("奖励类型:" + awardType);
}
public void step() {
x += xStep;
y += yStep;
// 如果奖励机碰撞了左右两侧的边界
if (x <= 0 || x >= World.WIDTH - this.width) {
// 修改它的移动方向
xStep *= -1;
}
}
int index = 1;
public BufferedImage getImage() {
BufferedImage img = null;
if (isLife()) {
img = images[0];
} else if (isDead()) {
img = images[index];
index++;
if (index == images.length) {
state = REMOVE;
}
}
return img;
}
public int awardType() {
//返回当前奖励机的奖励类型值
return this.awardType;
}
}
package cn.tedu.shoot;
//击中获得奖励的接口
public interface Award {
//定义奖励常量
int LIFE=0;
int DOUBLE_FIRE=1;
//定义获得的奖励
public int awardType();
}
package cn.tedu.shoot;
//击中敌机后获得分数的接口
public interface Score {
//返回一个分数
public int getScore();
}
package cn.tedu.shoot;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class Sky extends FlyingObject {
private static BufferedImage image;
static {
image = readImage("background.png");
}
private int step;
// 第二张背景图y轴
private int y1;
public Sky() {
super(400, 700, 0, 0);
y1 = -700;
step = 1;
}
public void show() {
super.show();
System.out.println("y1:" + y1 + "\nstep:" + step);
}
public void step() {
// 两张图都要移动
y += step;
y1 += step;
// 当任何一张图片移动出窗体时,都要重置到窗体上方
if (y >= World.HEIGHT) {
y = -World.HEIGHT;
}
if (y1 >= World.HEIGHT) {
y1 = -World.HEIGHT;
}
}
public BufferedImage getImage() {
return image;
}
public void paintObject(Graphics g) {
g.drawImage(getImage(),x,y,null);
g.drawImage(getImage(),x,y1,null);
}
}
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
public class Hero extends FlyingObject {
private static BufferedImage[] images;
static {
images = new BufferedImage[2];
images[0] = readImage("hero0.png");
images[1] = readImage("hero1.png");
}
// 英雄机的生命值
private int life;
// 火力值
private int doubleFire;
public Hero() {
super(97, 139, 152, 410);
life = 3;
doubleFire = 0;
}
public void show() {
super.show();
System.out.println("生命值:" + life + "\n火力值:" + doubleFire);
}
// 空实现
public void step() {
}
int index = 0;
public BufferedImage getImage() {
BufferedImage img = null;
// 任何数值取余2之后只可能得0或1
img = images[index % 2];
// index++后下次获得另一张图
index++;
return img;
}
// 英雄级开炮方法
public Bullet[] shoot() {
Bullet[] bs = null;
// 为了方便我们使用英雄机宽度的1/4定义一个变量
int len = this.width / 4;
// 英雄机开炮分单排和双排
if (doubleFire > 0) {
// 双排
bs = new Bullet[2];
bs[0] = new Bullet(this.x + len, this.y - 20);
bs[1] = new Bullet(this.x + 3 * len, this.y - 20);
doubleFire--;
} else {
// 单排
bs = new Bullet[1];
bs[0] = new Bullet(this.x + 2 * len, this.y - 20);
}
return bs;
}
// 英雄机移动方法
public void moveTo(int x, int y) {
// 英雄机中心x轴到鼠标x轴
this.x = x - this.width / 2;
// 英雄机中心y轴到鼠标y轴
this.y = y - this.height / 2;
}
// 获得英雄机的生命值
public int getLife() {
return this.life;
}
// 增加英雄机的生命值
public void addLife() {
this.life++;
}
// 增加英雄机的火力值
public void addDoubleFire() {
this.doubleFire += 20;
}
// 英雄机减命
public void subLife() {
this.life--;
}
// 清空火力值
public void clearDoubleFire() {
this.doubleFire = 0;
}
}
package cn.tedu.shoot;
import java.awt.image.BufferedImage;
public class Bullet extends FlyingObject {
private static BufferedImage image;
static {
image = readImage("bullet.png");
}
private int step;
// 子弹初始位置根据英雄机决定,编写代码时不知道英雄机的位置,
// 所以只能使用参数代替
public Bullet(int x, int y) {
super(8, 20, x, y);
step = 20;
}
public void show() {
super.show();
System.out.println("速度:" + step);
}
public void step() {
// 子弹向上移动
y -= step;
}
public BufferedImage getImage() {
BufferedImage img = null;
if (isLife()) {
img = image;
} else if (isDead()) {
// 子弹不会爆炸,死了直接变为消失状态
state = REMOVE;
}
return img;
}
// 子弹向上出界的判断,重写父类的方法
public boolean isOutOfBounds() {
return y < -this.height;
}
}
package cn.tedu.shoot;
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.swing.JFrame;
import javax.swing.JPanel;
//继承JPanel表示World类是一个窗口
public class World extends JPanel {
// 声明两个常量,窗口宽和高
public static final int WIDTH = 400;
public static final int HEIGHT = 700;
// 定义游戏的分数
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;// 结束
// 设置当前游戏状态
private int state = START;// 默认开始状态
// 声明三个状态对应得三个图片
private static BufferedImage startImg;
private static BufferedImage pauseImg;
private static BufferedImage gameOverImg;
static {
startImg = FlyingObject.readImage("start.png");
pauseImg = FlyingObject.readImage("pause.png");
gameOverImg = FlyingObject.readImage("gameover.png");
}
// 声明飞机大战中出现的对象
// 将组建声明为成员变量
// 所有方法均能访问
Hero hero = new Hero();
Sky sky = new Sky();
FlyingObject[] enemy = {};
Bullet[] bullets = {};
public void start() {
// 编写鼠标的监听器
MouseAdapter l = new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
if (state == RUNNING)
hero.moveTo(e.getX(), e.getY());
}
// 鼠标点击时的状态切换
public void mouseClicked(MouseEvent e) {
switch (state) {
case START:// 如果是开始状态
state = RUNNING;// 点击进入运行状态
break;
case GAME_OVER:// 如果是结束状态
state = START;// 点击进入开始状态
// 重置游戏
score = 0;
hero = new Hero();
sky = new Sky();
bullets = new Bullet[0];
enemy = new FlyingObject[0];
break;
}
}
// 鼠标移入时状态切换
public void mouseEntered(MouseEvent e) {
if (state == PAUSE)// 如果是暂停
state = RUNNING;// 鼠标移入切换为运行
}
// 鼠标移出
public void mouseExited(MouseEvent e) {
if (state == RUNNING)// 如果是运行
state = PAUSE;// 鼠标移出切换为暂停
}
};
// 绑定鼠标的移动和滑动事件
this.addMouseListener(l);
this.addMouseMotionListener(l);
// 定义计时器时间间隔
int interval = 20;
// 声明计时器
Timer timer = new Timer();
// 声明计时器任务
TimerTask task = new TimerTask() {
// 匿名内部类
public void run() {
if (state == RUNNING) {
// 周期运行的内容
// 调用飞行物移动方法
moveAction();
// 调用敌机进场方法
enterAction();
shootAction();
outOfBoundsAction();
bulletHitAction();
heroHitAction();
gameOverAction();
// System.out.println(enemy.length);
// System.out.println(bullets.length);
// 重绘所有对象
}
repaint();
}
};
// 启动计时器周期运行
timer.schedule(task, interval, interval);
}
// 判断英雄机和敌机碰撞的方法
public void heroHitAction() {
// 遍历所有敌机
for (int i = 0; i < enemy.length; i++) {
FlyingObject f = enemy[i];
// 判断是否和当前敌机相撞
if (f.isLife() && hero.hit(f)) {
// 撞死敌机
f.goDead();
// 英雄机减命,清活力
hero.subLife();
hero.clearDoubleFire();
}
}
}
// 判断游戏结束的方法
public void gameOverAction() {
// 如果生命值小于等于0,游戏结束
if (hero.getLife() <= 0) {
state = GAME_OVER;
}
}
// 判断子弹和敌机碰撞的方法
public void bulletHitAction() {
// 遍历所有子弹
for (int i = 0; i < bullets.length; i++) {
Bullet b = bullets[i];// 提取子弹
// 遍历所有敌机
for (int j = 0; j < enemy.length; j++) {
FlyingObject f = enemy[j];// 提取敌机
// 判断子弹是否集中敌机
if (b.isLife() && f.isLife() && b.hit(f)) {
b.goDead();// 子弹死
f.goDead();// 敌机死
// 判断敌机是不是得分的
if (f instanceof Score) {
Score s = (Score) f;
score += s.getScore();
}
// 判断敌机是不是有奖励
if (f instanceof Award) {
Award a = (Award) f;
// 获得这架奖励机的奖励数值
int num = a.awardType();
switch (num) {
case Award.LIFE:// 如果是0
hero.addLife();// 加命
break;
case Award.DOUBLE_FIRE:// 如果是1
hero.addDoubleFire();// 加火力值
break;
}
}
}
}
}
}
// 检测出界的方法
public void outOfBoundsAction() {
// 没有出界的元素下标,同时保存没出界元素的数量
int index = 0;
// 定义保存未出界元素的数组
FlyingObject[] fs = new FlyingObject[enemy.length];
// 遍历所有敌机,检测是否出界
for (int i = 0; i < enemy.length; i++) {
// 提取当前元素
FlyingObject f = enemy[i];
// 判断是否未出界并且没移除
if (!f.isOutOfBounds() && !f.isRemove()) {
fs[index] = f;// 放入新数组
index++;// 下标加1
}
}
// 数组缩容
enemy = Arrays.copyOf(fs, index);
index = 0;
Bullet[] bs = new Bullet[bullets.length];
for (int i = 0; i < bullets.length; i++) {
Bullet b = bullets[i];
if (!b.isOutOfBounds() && !b.isRemove()) {
bs[index] = b;
index++;
}
}
bullets = Arrays.copyOf(bs, index);
}
// 子弹进场方法
int shootIndex = 1;
public void shootAction() {
if (shootIndex % 5 == 0) {
// 调用英雄机的开炮方法
Bullet[] bs = hero.shoot();
// 对bullets数组进行扩容
bullets = Arrays.copyOf(bullets, bullets.length + bs.length);
// 将英雄机发射的新炮弹放到扩容后的数组最后位置
System.arraycopy(bs, 0, bullets, bullets.length - bs.length, bs.length);
}
shootIndex++;
}
// 敌机进场方法
int enterIndex = 1;
public void enterAction() {
if (enterIndex % 20 == 0) {
// 生成一架敌机
FlyingObject fo = nextEnemy();
// 扩容当前敌机数组
enemy = Arrays.copyOf(enemy, enemy.length + 1);
// 将生成的敌机放入扩容后数组的最后
enemy[enemy.length - 1] = fo;
}
enterIndex++;
}
// 飞行物移动的方法
public void moveAction() {
sky.step();// 天空移动
// 敌机移动
for (int i = 0; i < enemy.length; i++) {
enemy[i].step();
}
// 子弹移动
for (int i = 0; i < bullets.length; i++) {
bullets[i].step();
}
}
// 随机产生敌机的方法
public FlyingObject nextEnemy() {
Random ran = new Random();
FlyingObject fo = null;
int num = ran.nextInt(100);
if (num < 40) {// 40%几率小敌机
fo = new Airplane();
} else if (num < 80) {// 40%几率大敌机
fo = new BigAirplane();
} else {// 20%几率奖励机
fo = new Bee();
}
return fo;
}
// 重写了父类JPanel中的paint方法,方法名不能错
public void paint(Graphics g) {
// 先画背景,再画其他的
sky.paintObject(g);
hero.paintObject(g);
for (int i = 0; i < enemy.length; i++) {
enemy[i].paintObject(g);
}
for (int i = 0; i < bullets.length; i++) {
bullets[i].paintObject(g);
}
// 显示分数和生命值
g.drawString("SCORE:" + score, 20, 20);
g.drawString("LIFE:" + hero.getLife(), 20, 45);
// 根据游戏状态绘制状态图片
switch (state) {
case START:
g.drawImage(startImg, 0, 0, null);
break;
case PAUSE:
g.drawImage(pauseImg, 0, 0, null);
break;
case GAME_OVER:
g.drawImage(gameOverImg, 0, 0, null);
}
}
public static void main(String[] args) {
World w = new World();
// 实例化一个窗口
JFrame f = new JFrame("飞机大战");
// 将World类设置到窗口中
f.add(w);
// 首先设置窗口的宽和高
f.setSize(400, 700);
// 设置窗口关闭时程序结束
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置窗口居中
f.setLocationRelativeTo(null);
// 显示窗口,自动调用上面的paint
f.setVisible(true);
w.start();
}
}
需要图片告诉我!