Java开发实现飞机大战

网友投稿 248 2022-08-07


Java开发实现飞机大战

目录一、飞机大战1 封装所有飞行物公共属性和功能的父类2 封装英雄机属性和功能类3 封装敌机属性和功能的类4 封装大飞机属性和功能的类5 子弹类6 飞机大战射击的主方法二、测试结果

本文实例为大家分享了java实现飞机大战的具体代码,供大家参考,具体内容如下

一、飞机大战

1 封装所有飞行物公共属性和功能的父类

import java.awt.image.BufferedImag

/**

* 封装所有飞行物的公共属性和功能的父类

*/

public abstract class Flyer {

protected int x; //飞行物左上角x坐标

protected int y; //飞行物左上角y坐标

protected int height; //飞行物的高度

protected int width; //飞行物的宽度

protected BufferedImage image; //飞行物的图片

/**

* 要求所有飞行物必须都能移动

* 但移动的方式由子类自己实现

*/

public abstract void step();

/**

* 检查越界的方法

* @return 是否越界

*/

public abstract boolean outOfBounds();

/**

* 专门检测两个矩形飞行物是否碰撞的工具方法

* 和具体对象无关,所以定义为静态方法

* @param f1 飞行对象1

* @param f2 飞行对象2

* @return 是否碰撞

*/

public static boolean boom(Flyer f1,Flyer f2){

//step1: 求出两个矩形的中心点

int f1x = f1.x + f1.width/2;

int f1y = f1.y + f1.height/2;

int f2x = f2.x + f2.width/2;

int f2y = f2.y + f2.height/2;

//step2: 横向和纵向碰撞检测

boolean H = Math.abs(f1x - f2x) < (f1.width + f2.width)/2;

boolean V = Math.abs(f1y -f2y) < (f1.height + f2.height)/2;

//step3: 必须两个方向同时碰撞

return H&V;

}

}

2 封装英雄机属性和功能类

import java.util.Random;

/**

* 封装英雄机的属性和功能类

*/

public class Hero extends Flyer {

private int doubleFire; //双倍火力子弹数

private int life; //生命值

private int score; //得分

//对外提供读取生命值的方法

public int getLife(){

return life;

}

//对外提供的获取得分的方法

public int getScore(){

return score;

}

/**

* 英雄机对象的无参构造方法

*/

public Hero(){

image = ShootGame.hero0;

height = image.getHeight();

width = image.getWidth();

x = 127;

y = 388;

doubleFire = 0;

life = 3;

score = 0;

}

/**

* 实现英雄机的动画效果的方法

* 让英雄机的图片在hero0和hero1之斗切换

*/

@Override

public void step() {

Random r = new Random();

if(r.nextInt(2) == 0){

image = ShootGame.hero0;

}else{

image = ShootGame.hero1;

}

}

@Override

public boolean outOfBounds() {

// TODO Auto-generated method stub

return false;

}

/**

* 英雄机随鼠标移动的方法

* 要求传入鼠标当前的位置

* @param x 鼠标位置的x坐标

* @param y 鼠标位置的y坐标

*/

public void move(int x,int y){

//传入的x,y是鼠标的坐标

//move的作用是让英雄机的中心位置和鼠标位置一致

this.x = x - width / 2;

this.y = y - height / 2;

}

/**

* 英雄机获得分数或奖励的方法

* @param f 是一个飞行物父类方法,可以指向敌机或者大飞机

*/

public void getScore_Award(Flyer f){

//先判断敌人对象的类型

if(f instanceof Airplane){ //如果敌人是敌机

//获得敌机对象中的分数,加到当现分数上

score += ((Airplane)f).getScore();

}else{ //如果对象是大飞机

//继续判断大飞机对象中保存的奖励类型

if(((BigPlane)f).getAwardType() == BigPlane.DOUBLE_FIRE){

//如果保存的是双倍火力

doubleFire += 20;

}else{

//如果保存的是生命值奖励

life += 1;

}

}

}

/**

* 英雄机发射子弹的方法

* @return 新创建出来的子弹对名

* 可能是一发,也可能 是两发,用数组保存

*/

public Bullet[] shoot(){

Bullet[] bullets = null;

//何时开启双倍火力:

if(doubleFire != 0){ //创建双倍火力

bullets = new Bullet[2];

Bullet b1 = new Bullet(x + width/4 - ShootGame.bullet.getWidth()/2,y + ShootGame.bullet.getWidth());

Bullet b2 = new Bullet(x + width*3/4 - ShootGame.bullet.getWidth()/2,y + ShootGame.bullet.getWidth());

bullets[0] = b1;

bullets[1] = b2;

//每创建一个双倍火力,doubleFire-1

doubleFire -= 1;

}else{

//单倍火力:

//子弹x坐标:x+英雄机宽度/2-子弹宽度/2

//子弹y坐标:y-子弹高度

bullets = new Bullet[1];

bullets[0] = new Bullet(x + width/2 - ShootGame.bullet.getWidth()/2,y - ShootGame.bullet.getHeight());

}

return bullets;

}

/**

* 英雄机自带和敌人碰撞检测方法

* @param f 可能发生碰撞的敌人

* 可能是敌机也可能是大飞机

* @return 是否碰撞

*/

public boolean hit(Flyer f){

//调用碰撞检测方法,检测是否碰撞

boolean r = Flyer.boom(this, f);

if(r){ //如果碰撞

life--;

doubleFire = 0;

}

return r;

}

}

3 封装敌机属性和功能的类

import java.util.Random;

/**

* 封装敌机属性和功能的类

*/

public class Airplane extends Flyer {

private int speed = 2; //敌机每次下落2个单位长度

private int score = 5; //敌机包含的奖励分数

//对外提供的读取敌机奖励分数的方法

public int getScore(){

return score;

}

/**

* 敌机类的无参构造方法

*/

public Airplane(){

image = ShootGame.airplane;

width = image.getWidth();

height = image.getHeight();

y = -height;

Random r = new Random();

x = r.nextInt(ShootGame.WIDTH - width);

}

@Override

public void step() {

//敌机每次向下移动一个speed长度

y += speed;

}

@Override

public boolean outOfBounds() {

//敌机y坐标>游戏界面,越界

return y > ShootGame.HEIGHT;

}

}

4 封装大飞机属性和功能的类

import java.util.Random;

/**

* 封装大飞机属性和功能的类

*/

public class BigPlane extends Flyer {

/*定义奖励类型的备选项常量*/

public static final int DOUBLE_FIRE = 0; //奖励类型是0,说明奖励双倍火力

public static final int FILE = 1; //奖励类型是1,说明奖励一次生命

/*大飞机类私有成员*/

private int xspeed = 1; //水平移动的速度为1

private int yspeed = 2; //垂直移动的速度为2

private int awardType; //当前大飞机保存的奖励类型

//对外提供的读取大飞机奖励类型的方法

public int getAwardType(){

return awardType;

}

/**

* 大飞机的无参构造方法

*/

public BigPlane(){

//step1: 从主程序中获取大飞机图片的静态变量——bigplane

image = ShootGame.bigplane;

//step2: 使用图片宽高设置对象宽高

width = image.getWidth();

height= image.getHeight();

//step3: 设置大飞机开始下落的高度

y = -height;

//step4: 大飞机对象开始下落的x坐标在0~(界面宽度 - 大飞机图片宽度)之前随机

Random r = new Random();

x = r.nextInt(ShootGame.WIDTH - width);

//在0和1之间随机先择一种奖励类型

awardType = r.nextInt(2);

}

@Override

public void step() {

//每次x移动一个xspeed,y移动一个yspeed

x += xspeed;

y += yspeed;

//大飞机不能起出边界,一旦超出那么xspeed*(-1),相当于反向移动

if(x < 0 || x > ShootGame.WIDTH - width){

xspeed *= -1;

}

}

@Override

public boolean outOfBounds() {

//大飞机的y坐标>游戏界面,越界

return y > ShootGame.HEIGHT;

}

}

5 子弹类

public class Bullet extends Flyer{

private int speed = 5; //子弹上升的速度为3

/**

* 子弹类的带参构造方法

* 因为子弹对象创造的位置要根据英雄机的位置决定

* 所以子弹对名的x和y要从外界传入

* @param x 英雄机指定子弹创造位置的x坐标

* @param y 英雄机指定子弹创造位置的y坐标

*/

public Bullet(int x,int y){

image = ShootGame.bullet;

width = image.getWidth();

height = image.getHeight();

this.x = x;

this.y = y;

}

@Override

public void step() {

//子弹每次向上移动一个speed长度

y -= speed;

}

@Override

public boolean outOfBounds() {

//子弹的y坐标+子弹的高度<0,越界

return (y + height) < 0;

}

}

6 飞机大战射击的主方法

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.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;

public class ShootGame extends JPanel {

private static final long serialVersionUID = 1L;

//背景图片的大小320*568

public static final int WIDTH = 320;

public static final int HEIGHT = 568;

//游戏界面固定大小336*607

public static final int FRAME_WIDTH = 336;

public static final int FRAME_HEIGHT = 607;

/*

* 游戏启动第一件事是从硬盘加载所有要用到的图片到内存当中

* 而且仅在启动时加载一次——静态块

* 缓存在程序中的所有图片,都会反复使用,仅保存一份——静态变量

* 下面,为每张图片加载一个静态变量,然后在静态块加加载每张图片

*/

public static BufferedImage background; //背景图片

public static BufferedImage start; //开始图片

public static BufferedImage airplane; //敌机图片

public static BufferedImage bigplane; //大飞机

public static BufferedImage hero0; //英雄机状态0

public static BufferedImage hero1; //英雄机状态1

public static BufferedImage bullet; //子弹

public static BufferedImage pause; //暂停图片

public static BufferedImage gameover; //游戏结束

//静态块,在类加载到方法区时执行一次,专门加载静态资源

static{

/*

* java从硬盘中加载图片到内存中:

* ImageIO.read方法:专门从硬盘中加载图片的静态方法

* 不用实例化,直接调用

* ShootGame.class:获得当前类的加载器所在路径

* ShootGame.class.getRerource("文件名"); 从当前类所在路径加载指定文件到程序中

*/

try {

background = ImageIO.read(ShootGame.class.getResource("background.png"));

airplane = ImageIO.read(ShootGame.class.getResource("airplane.png"));

bigplane = ImageIO.read(ShootGame.class.getResource("bigplane.png"));

bullet = ImageIO.read(ShootGame.class.getResource("bullet.png"));

start = ImageIO.read(ShootGame.class.getResource("start.png"));

pause = ImageIO.read(ShootGame.class.getResource("pause.png"));

hero0 = ImageIO.read(ShootGame.class.getResource("hero0.png"));

hero1 = ImageIO.read(ShootGame.class.getResource("hero1.png"));

gameover = ImageIO.read(ShootGame.class.getResource("gameover.png"));

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/*

* 为游戏中的角色定义数据结构,包括:

* 1个英雄机对象

* 1个存储所有敌人(敌机和大飞机)的对象数组

* 1个存储所有子弹的对象数组

*/

public Hero hero = new Hero();

public Flyer[] flyers = {}; //存储所有敌人对象的数组

public Bullet[] bullets = {}; //存储所有子弹对象的数组

//定义游戏状态:当前状态变量:默认为开始状态

private int state = START;

//定义游戏状态的备选项常量:

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 static void main(String[] args) {

/*

* java中绘制窗体:JFrame对象——窗框

* 要想在窗体中绘制内容,还需要嵌入背景面板——JPanel

*/

JFrame frame = new JFrame("ShootGame");

frame.setSize(FRAME_WIDTH,FRAME_HEIGHT);//(336, 607);

frame.setAlwaysOnTop(true); //设置窗体置顶

//设置窗体关闭同时,退出程序

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setLocationRelativeTo(null); //设置窗体的位置,null表示居中

/*在窗体中嵌入背景面板对象——JPanel*/

ShootGame game = new ShootGame(); //创建背景面板对象

frame.add(game); //将背景面板对象嵌入到窗体对象中

/*窗体默认不可见!必须调用setVisible方法才能显示窗体*/

frame.setVisible(true); //自动调用窗体的paint方法

game.action();

}

/**

* 游戏启动时要做的事

*/

public void action(){

/*游戏开始时,要定义鼠标事件的监听*/

//step1: 创建MouseAdapter匿名内部类——事件的响应程序

MouseAdapter l = new MouseAdapter(){

//step2: 重写希望的鼠标事件——鼠标移动

@Override

public void mouseMoved(MouseEvent e) {

//只有在RUNNING状态下英雄机才跟随鼠标移动

if(state == RUNNING){

//step3: 获得鼠标新位置

int x = e.getX();

int y = e.getY();

//step4: 将鼠标位置传给英雄机的move方法

hero.move(x, y);

}

}

@Override

public void mouseClicked(MouseEvent e) {

if(state == START || state == PAUSE){ //START或者PAUSE状态,单击才会改改为RUNNING状态

state = RUNNING;

}else if(state == RUNNING){ //游戏点击暂停

state = PAUSE;

}else if(state == GAME_OVER){ //游戏结束后单击,游戏初始化

state = START;

//从GAME_OVER到START,要重新初始化游戏数据

flyers = new Flyer[0];

bullets = new Bullet[0];

hero = new Hero();

}

}

@Override

public void mouseExited(MouseEvent e) {

if(state == RUNNING){

//仅在处于RUNNING状态下,鼠标移出才暂停

state = PAUSE;

}

}

@Override

public void mouseEntered(MouseEvent e) {

if(state == PAUSE){

state = RUNNING;

}

}

}; //匿名内部类要以分号结尾

/*step5: 要响应鼠标事件,必须将鼠标事件添加到程序的监听器中*/

this.addMouseMotionListener(l); //支持鼠标的移动事件,不支持鼠标单击事件

this.addMouseListener(l);; //支持鼠标单击事件

//step1: 创建定时器

Timer timer = new Timer();

//step2: 调用定时器对象的schedule方法,做计划

// 第一个参数:TimerTask类型的匿名内部类

// 必须重写run方法——核心——要做什么事

timer.schedule(new TimerTask(){

//首先定义一个计时器变量index,记录run方法运行的次数

private int runTimes = 0;

@Override

public void run() {

//除了repaint方法,其余功能只在RUNNING状态下执行

if(state == RUNNING){

//每执行一次run方法,runTimes就+1

runTimes++;

//每500亳秒生成一次敌人

if(runTimes % 50 == 0){

nextOne(); //自动随机创建敌人对象

}

//遍历每一个对象,调用对象的step方法,移动一次对象的位置

for(int i = 0;i < flyers.length;i++){

flyers[i].step();

}

//每300亳秒生成一次子弹

if(runTimes % 30 == 0){

shoot(); //创建一次子弹

}

//遍历子弹数组的每一个对象,移动位置

for(int i = 0;i < bullets.length;i++){

bullets[i].step();

}

//英雄机动画效果

hero.step();

//添加子弹和敌人的碰撞检测

boom();

//英雄机碰撞检测

hit();

//添加越界检测

outOfBounds();

}

/*强调:只要界面发生变化,必须调用repaint方法重新绘制界面*/

repaint();

}

}, 10,10); //界面每隔10亳秒变化一次

}

@Override

public void paint(Graphics g) {

//step1: 绘制背景图片

g.drawImage(background, 0, 0, null);

//step2: 绘制英雄机

paintHero(g);

//step3: 批量绘制敌人数组

paintFlyers(g);

//step4: 批量绘制子弹数组

paintBullets(g);

//绘制分数和生命值

paintScore_Life(g);

//根据游戏状态绘制不同图片

if(state == START){

g.drawImage(start, 0, 0, null);

}else if(state == PAUSE){

g.drawImage(pause, 0, 0, null);

}else if(state == GAME_OVER){

g.drawImage(gameover, 0, 0, null);

}

}

/**

* 绘制英雄机对象的方法

* @param g 画笔

*/

public void paintHero(Graphics g){

g.drawImage(hero.image, hero.x, hero.y, null);

}

/**

* 遍历敌人数组,批量绘制所有敌人的方法

* @param g

*/

public void paintFlyers(Graphics g){

for(int i = 0;i < flyers.length;i++){

g.drawImage(flyers[i].image, flyers[i].x, flyers[i].y, null);

}

}

/**

* 遍历子弹数组,批量绘制所有子弹的方法

* @param g

*/

public void paintBullets(Graphics g){

for(int i = 0;i < bullets.length;i++){

g.drawImage(bullets[i].image, bullets[i].x, bullets[i].y, null);

}

}

/**

* 随机生成1个敌人对象

* 每生成一个新敌人, flyers数组就要扩容1

* 然后将新敌人放入数组最后一个元素

*/

public void nextOne(){

Random r = new Random();

Flyer f = null;

if(r.nextInt(20) == 0){ //只有随机数取0时才创建大飞机

f = new BigPlane();

}else{ //其余全部生成敌机

f = new Airplane();

}

//对flyers数组扩容1

flyers = Arrays.copyOf(flyers, flyers.length + 1);

//将新敌人放入数组末尾

flyers[flyers.length - 1] = f;

}

/**

* 获得英雄机对象发射的子弹对象

* 将新的子弹对象保存到子弹数组中,统一管理

*/

public void shoot(){

Bullet[] newBullets = hero.shoot(); //获得英雄机返回的新子弹数组

//根据返回新子弹的数量,扩容子弹数组

bullets = Arrays.copyOf(bullets, bullets.length + newBullets.length);

//从newBullets数组中拷贝所有元素到bullets数组末尾

System.arraycopy(newBullets, 0, bullets, bullets.length - newBullets.length, newBullets.length);

}

/**

* 遍历子弹数组和敌人数组,进行碰撞检测

* 一旦发生碰撞,子弹和敌人都减少一个

*/

public void boom(){

for(int i = 0;i < bullets.length;i++){

for(int j = 0;j < flyers.length;j++){

if(Flyer.boom(bullets[i], flyers[j])){

//为英雄机获得分数和奖励

hero.getScore_Award(flyers[j]);

//从敌人数组中删除被击中的敌机

//step1: 使用敌人数组最后一个元素替换被击中的敌机

flyers[j] = flyers[flyers.length - 1];

//step2: 压缩数组

flyers = Arrays.copyOf(flyers, flyers.length - 1);

//从子弹数组中删除击中敌机的子弹

bullets[i] = bullets[bullets.length - 1];

bullets = Arrays.copyOf(bullets, bullets.length -1);

i--; //第发现一次碰撞,子弹就要退一个元素,重新检测当前位置

break; //只要发现碰撞就退出当前敌人数组的循环

}

}

}

}

/**

* 绘制分数和生命值的方法

* @param g

*/

public void paintScore_Life(Graphics g){

int x = 10; //文字在左上角的x坐标

int y = 15; //文字在左上角的y坐标

Font font = new Font(Font.SANS_SERIF,Font.BOLD,14);

g.setFont(font); //设置字体的画笔对象

//绘制第一行:分数

g.drawString("SCORE: " + hero.getScore(), x, y);

//绘制第二行:生命值,y坐标下移20个单位

y += 20;

g.drawString("LIFE: " + hero.getLife(), x, y);

}

/**

* 检查所有飞行物是否越界

*/

public void outOfBounds(){

//检查所有敌人是否越界

Flyer[] Flives = new Flyer[flyers.length];

//遍历敌人数组,将存活的敌人对象存到新数组中

//设置Flives数组的计数器index: 1.标示下一个存活对象的位置

// 2.统计Flives中一共有多少元素

int index = 0;

for(int i = 0;i < flyers.length;i++){

if(!flyers[i].outOfBounds()){ //没有越界的对象

Flives[index] = flyers[i];

index++;

} //遍历结束后:

//index是存活对象的个数

//Flives数组里是存活的对象,个数为index

//把Flives数组压缩为index大小

//压缩后的新数组 应替换回flyers数组

}

flyers = Arrays.copyOf(Flives, index);

//检测所有子弹是否越界

Bullet[] Blives = new Bullet[bullets.length];

index = 0;

for(int i = 0;i < bullets.length;i++){

if(!bullets[i].outOfBounds()){

Blives[index] = bullets[i];

index++;

}

}

bullets = Arrays.copyOf(Blives, index);

}

/**

* 遍历敌人数组,判断英雄机和每个敌人是否碰撞

*/

public void hit(){

Flyer[] lives = new Flyer[flyers.length];

//记录存活的敌人

int index = 0;

for(int i = 0;i < flyers.length;i++){

if(!hero.hit(flyers[i])){

lives[index] = flyers[i];

index++;

}

}

if(hero.getLife() <= 0){ //如果英雄机生命值小于等于0,游戏结束

state = GAME_OVER;

}

//压缩敌人数组,并替换数组

flyers = Arrays.copyOf(lives, index);

}

}

二、测试结果


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java超细致讲解数组的使用(java数组实例)
下一篇:Java全面细致讲解类与对象(《Java面向对象编程》)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~