前言:在一定的java基础上就可以进行飞机大战小游戏的编写了。整个小游戏主要涉及到的基础知识为:类与对象,鼠标事件监听,线程、重绘等。

整体思路框架

设计一个初始界面,在开始后出现自己的战机和敌机且自己的战机不断地发射子弹。当子弹碰到敌机后加分,若自己碰到敌机则游戏结束。

需要的类
1.入口类 2.自己的战机类 3.敌机类 4.子弹类 5.背景类 (6.抽象类 7.数据类)
在设计类的时候可以注意到,战机、敌机和子弹都属于飞行物体,它们都拥有几个相同的属性和方法。例如XY坐标,长度、宽度、速度,移动方法,绘制方法等。因此可以建立一个抽象的飞行类作为父类,战机类、敌机类、子弹类继承父类的属性和方法,并重写其中的抽象方法,为我们的代码编写提供方便。

一、编写入口类(初始化界面)

首先,建立一个入口类(MainFrame)继承JPanel.实例化一个窗体,并设置窗体的各种属性。

public static void main(String[] args) {// TODO Auto-generated method stubJFrame jf = new JFrame();MainFrame mf = new MainFrame();         //入口类名为MainFramejf.add(mf);jf.setTitle("飞机大战");jf.setDefaultCloseOperation(3);jf.setSize(700,1000);jf.setLocationRelativeTo(null);jf.setVisible(true);mf.iniUI();}

这时候,我们已经拥有了一个界面。其中,iniUI是一个初始化的方法,其中包括了监听和线程两个内容。当然,可以将监听和线程分开来写成监听类和线程类,但因为这个小游戏中用到的内容不多便写在了一起,避免传参出现不便和错误。

二、背景类(天空类)

根据思路,在初始化了界面之后我们需要一个背景类。即在界面中绘制一张背景图,通过图像坐标的改变,实现背景不断的移动,造成战机不断前进的状态景象。可以想象,只有一张图片很难在移动后实现循环的绘制,因此采用两张图片。当一张图片移出画面后另一张接上,同时改变前一张图片的坐标。如此形成循环往复。

//读取图片static {try {bgimg1 = ImageIO.read(Sky.class.getResourceAsStream("/picture/背景.jpg"));//相对位置bgimg2 = ImageIO.read(Sky.class.getResourceAsStream("/picture/背景.jpg"));}catch(IOException e){e.printStackTrace();}}//构造方法public Sky(){height = 1000;speed =1;y1 = 0;y2 = -height;        //从画面外开始画}//绘制背景图public void paint(Graphics g) {g.drawImage(bgimg1, 0, y1, null);g.drawImage(bgimg2, 0, y2, null);}//背景图的移动public void move() {y1+=speed;y2+=speed;if(y1>=height) {y1=-height+1;//此处的1为微小的调整,可根据实际情况修改}else if(y2>=height) {y2=-height+1;}}

在写完这个天空类之后,将其在入口类中实例化后调用它的move方法,就可以看到滚动的天空背景图片了。

三、抽象飞行体类

根据思路,总结出战机、敌机和子弹类都具有的几个共同的属性和方法为:

  • X和Y的坐标
  • 图片的宽度和高度
  • 绘画方法(根据需要重写)
  • 移动方法
  • 是否碰撞方法
  • 获取图片的方法(抽象方法需要重写)
  • 飞行体是否死亡和设置的方法
  • 飞行体是否出界的方法

注意抽象类的声明关键字为abstract

 public int x;public int y;//横纵坐标public int width;public int height;//宽度高度
 //绘画方法
public void paint(Graphics g) {g.drawImage(getImage(), x, y, null);
}//判断碰撞方法
public boolean isHit(Flyobject fly) {int x = obj.x-this.x;int y = obj.y-this.y;int r = 100;//判断半径return (x*x+y*y<r*r);//判断碰撞的方法可以自己定义 可以设置更为宽松或严格的标准
}//是否死亡
public boolean isdead() {return state == dead;
}//设置为死亡状态
public void setdead() {state = dead;
}//获取图片public abstract BufferedImage getImage();//因为各自的移动方法和判断出界有较大不同 写在各自的类里了 当然也可以在这里定义
四、战机类、敌机类、子弹类

战机类、敌机类和子弹类都是飞行体这一抽象类的子类。因此继承飞行体这一父类之后可以直接使用里面定义好的属性值,重写里面的方法即可。

战机类

因为子弹的初始位置在战机的机头位置,因此将创建子弹的方法写在战机类里。实例化战机后调用该方法创建子弹。
继承飞行体抽象类。

private static BufferedImage heroimage;static {try {heroimage = ImageIO.read(Hero.class.getResourceAsStream("/picture/LiPlane.png"));} catch (IOException e) {e.printStackTrace();}}//读取图片
 //构造方法public Hero() {x = 280;y = 800;width = heroimage.getWidth();height = heroimage.getHeight();}public void paint(Graphics g) {g.drawImage(getImage(), x, y, null);}public void move(int x,int y) {this.x = x - width/2;this.y = y - height/2;//此处是左右边界判断if(this.x >= 700-width) {this.x = 700-width;}else if(this.x <= 0){this.x = 0;}//此处是上下边界判断if(this.y >= 1000-height) {this.y = 1000-height;}else if(this.y<=0){this.y = 0;}}//创建子弹方法public Bullet creatBullet() {Bullet b = new Bullet(this.x+this.width/2-2,this.y-2);//调用Bullet构造方法 具体可见下文中bullet类return b;}@Overridepublic BufferedImage getImage() {return heroimage;}
敌机类

在创建敌机类之前应该想到,敌机可以用数组或者用ArrayList来存储,且敌机出现的位置应该是纵坐标一致,横坐标随机。
继承飞行体抽象类。

private static BufferedImage enemyimage;static {try {enemyimage = ImageIO.read(Enemy.class.getResourceAsStream("/picture/敌机1.png"));} catch (IOException e) {e.printStackTrace();}}//创建图片private int enemyspeed;      //飞行速度//构造方法public Enemy() {width = enemyimage.getWidth();height = enemyimage.getHeight();Random rand = new Random();x = (int)(rand.nextInt(700-width));        //随机产生横坐标y = -height;enemyspeed = 2;                          //给定飞行速度为2}public void move() {y+=enemyspeed;}//是否出界public boolean outofbound() {return y > 1000;}@Overridepublic BufferedImage getImage() {return enemyimage;}
子弹类

子弹类的创建与敌机类是大同小异的,同样需要数组或ArrayList来存储子弹对象。
继承飞行体抽象类。

private static BufferedImage bulletImage;private int bulletspeed;        //子弹速度//构造方法public Bullet(int x,int y) {x=x;y=y;height=bulletImage.getHeight();width=bulletImage.getWidth();bulletspeed = 3;}static {try {bulletImage = ImageIO.read(Bullet.class.getResourceAsStream("/picture/zd.png"));} catch (IOException e) {e.printStackTrace();}}public void paint(Graphics g) {g.drawImage(getImage(), x, y, null);}public void move() {this.y-=bulletspeed;}public boolean outofbound() {return this.y <height;}public BufferedImage getImage() {return bulletImage;}
五、回到入口类中

准备工作已经完成,现在可以回到入口类中完成整个代码了。梳理整个过程我们应该可以得知,我们还应该解决至少以下几个疑问:

  1. 如何创建子弹和敌机?
  2. 如何绘制战机、敌机和子弹?
  3. 怎样调用它们的移动方法?
  4. 在子弹碰撞敌机后,如何将它们俩从各自的ArrayList中去除?
  5. 如何让它们周而复始地自己完成运动?
  6. 得分机制如何?

因此我们需要以下方法来解决这些问题,并通过这些方法的调用,最终完成我们的飞机大战小游戏。

———————————————————————————————————
实例化sky 和 hero(战机)
创建子弹和敌机的动态数组
定义得分 和 子弹与敌机的计数器
定义战机的状态

private Hero hero = new Hero();
private Sky sky = new Sky();public ArrayList<Bullet> bullets = new ArrayList<Bullet>();
public ArrayList<Enemy> enemies = new ArrayList<Enemy>();//飞机的得分
private int score = 0;//子弹和敌机的计数器,初始化设定为0
private int bulletCount = 0;
private int enemiesCount = 0;//定义战机的状态 开始为0运行为1死亡为2 STATE为当前状态
public static int BEGIN = 0;
public static int RUNNING = 1;
public static int OVER = 2;
public static int STATE = 0;

在这里将移动的方法都写到一个方法里,通过sky对象调用sky类里的move方法。将动态数组中的每个子弹和敌机都取出来,分别调用它们各自的移动方法。

//移动方法public void moveAction() {//背景移动sky.move();//发射子弹for(int i=0;i<bullets.size();i++) {bullets.get(i).move();}//敌机飞行for(int i=0;i<enemies.size();i++) {enemies.get(i).move();}}

通过已经实例化的对象调用各种绘画方法

public void paint(Graphics g) {super.paint(g);sky.paint(g);//开始状态时绘制背景图(logoImage)if(STATE == 0) {g.drawImage(logoImage, 0, 0, null);}//绘制己方飞机hero.paint(g);//绘画得分if(STATE!=0) {paintScore(g);}for(int i= 0 ;i< bullets.size();i++) {bullets.get(i).paint(g);}for(int i= 0;i<enemies.size();i++) {enemies.get(i).paint(g);}//overImage为结束时候的画面 即如果战机状态为死亡则绘制结束画面if(STATE==2) {g.drawImage(overImage, 6, 400, null);}}//绘制分数的方法public void paintScore(Graphics g) {Font font = new Font(Font.SANS_SERIF,Font.BOLD,30);    //设置字体g.setColor(Color.RED);    //设置颜色g.setFont(font);g.drawString("SCORE:"+score, 15, 45);       //绘画文字}

创建和移除子弹的方法
因为创建和移除敌机的方法几乎完全相同,因此不再赘述

public void createBullet() {bulletCount++;//此处的50即为控制子弹生成的速率的参数if(bulletCount % 50==0) {Bullet b = hero.creatBullet();bullets.add(b);       //在动态数组中加入新的子弹bulletCount=0;}}public void removeBullet() {for(int i=0;i<bullets.size();i++) {if(bullets.get(i).outofbound()==true || bullets.get(i).isdead()==true) {bullets.remove(i);}}//如果子弹超过边界或者子弹已经判定为死亡状态则移除子弹}

处理碰撞情况的方法(自己和敌机碰撞、敌机和子弹碰撞)

//子弹和敌机碰撞
public void bulletHitEnemy() {for(int i=0;i<bullets.size();i++) {Bullet bullet = bullets.get(i);for(int j = 0;j<enemies.size();j++) {Flyobject enemy = enemies.get(j);if(bullet.isHit(enemy)) {//设置子弹和敌机的状态为死亡bullet.setdead();enemy.setdead();//加分score += 2;}}}}//自己和敌机碰撞public void herohitenemy() {for(int i=0;i<enemies.size();i++) {Flyobject enemy = enemies.get(i);if(hero.isHit(enemy)) {//设置状态为死亡  enemy.setdead();hero.setdead();STATE = OVER;}}}

最后的准备工作也都已经完成了,现在只需要增加界面的监听并在线程中调用它们即可,在文章最前提到的iniUI 方法将实现它们。

public void iniUI() {MouseAdapter adapter = new MouseAdapter() {public void mouseClicked(MouseEvent e) {if(STATE==BEGIN) {STATE = RUNNING;}else if(STATE==OVER) {STATE = BEGIN;score = 0;//重新初始化自己的飞机hero = new Hero();//重新初始化子弹数组bullets = new ArrayList<Bullet>();//重新初始化敌机数组enemies = new ArrayList<Enemy>();}}public void mouseMoved(MouseEvent e) {if(STATE==RUNNING) {int x1 = e.getX();int y1 = e.getY();hero.move(x1,y1);}}};this.addMouseListener(adapter);this.addMouseMotionListener(adapter);//定义游戏的定时器//所要执行的任务 延迟时间(毫秒) 执行各后续任务的时间间隔(毫秒)Timer timer = new Timer();timer.schedule(new TimerTask(){public void run() {if(STATE == RUNNING) {moveAction();createBullet();removeBullet();createEnemy();removeEnemy();//子弹和敌机碰撞的方法bulletHitEnemy();//自己和敌机碰撞的方法herohitenemy();}repaint();}},10,10);}

此处有两点需要特别提示的地方。一是没有新建监听类进行监听。adapter是适配器的意思,也就是使用了MouseAdapter类了以后只需要重写自己需要的监听方法即可,不需要将所有的方法都进行重写。二是没有新建线程类,而是使用了Timer类。其实,Timer类实现了Runnable的接口,使用时相当于一个线程。schedule的具体意义在代码段里的注释中已经注明。
此时,已经完成了所有的编写。当然还有很多值得改进的地方,可以将一些常用的方法和数据都写到各自的类里,使入口类的书写更为清晰简洁。

改进思路
  1. 增加敌机被击中后的爆炸动画效果
  2. 抽象类实例化不同的敌机类,得分也设置为不同
  3. 设置奖励系统
  4. 设置生命系统和等级闯关系统,达到一定分数后增加难度
  5. 提供界面使玩家自选难度

如有问题,希望各位大佬指正!

JAVA小游戏之飞机大战(超详细)相关推荐

  1. java小游戏之飞机大战

    飞机大战脚本组成 1.有一个所有物体的父物体GameObject,然后就是一堆物体继承于他,等到他的属性于函数. 2.窗口组件 3.工具脚本(负责做一些杂事和存放图片) 代码 GameObject p ...

  2. Java Swing 经典小游戏《飞机大战》———— (四)碰撞检测 游戏状态与得分 玩家升级

    前期回顾 Java Swing 经典小游戏<飞机大战>---- (一)获取素材,创建窗口,添加滚动背景,双缓冲 Java Swing 经典小游戏<飞机大战>---- (二)玩家 ...

  3. 安卓小游戏:飞机大战

    安卓小游戏:飞机大战 前言 前面写了十二篇自定义view的博客,说实话写的还是有点无聊了,最近调整了一下,觉得还是要对开发有热情,就写了点小游戏,现在抽时间把博客也写一写,希望读者喜欢. 需求 这里就 ...

  4. 微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)

    微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞 ...

  5. java小游戏:五子棋人机大战

    一.java小游戏:五子棋人机大战 1.绘制窗口 package wuziqi;import javax.swing.*; import java.awt.event.MouseAdapter; im ...

  6. python飞机大战加背景音乐_python实现飞机大战小游戏 python飞机大战中的音频文件怎么改成MP3...

    怎么样用Python写飞机大战游戏 python开发飞机大战外星人游戏怎么弄双人模式新的一年,哪怕仍是一个人,也要活得像一支队伍,为自己的头脑和心灵招兵买马,不气馁,有召唤,爱自由. 主函数 impo ...

  7. Python小游戏之 - 飞机大战 !

    用Python写的"飞机大战"小游戏 源代码如下: # coding=utf-8 import random import os import pygame# 用一个常量来存储屏幕 ...

  8. JAVA小游戏有源代码,非常详细的注释,以及自己做的答辩PPT

    JAVA小游戏--阴阳师版三国战纪 阴阳战纪 具体的实现方法 程序 效果 阴阳战纪 由于疫情原因,我们班的生产实习被告知要在线上举行,然后老师给我们上了三天的网课,教我们用JAVA来制作一个小游戏,因 ...

  9. 手把手教你使用Pygame制作飞机大战小游戏,4万字超详细讲解!

    点击上方"早起Python",关注并"星标" 每日接收原创Python干货! 大家好,偷学Python系列是由小甜同学从初学者的角度学习Python的笔记,其特 ...

最新文章

  1. halcon的算子清点: Chapter 2-3-4 控制、开发、文件操作
  2. 微信小程序开发学习记录01
  3. AWR报告中的DB Time 及 Elapsed
  4. Python开发【Part 7】:常用模块
  5. android 多平台发布,内容多平台发布
  6. 关于childNodes的删除
  7. Q窗口操作函数(窗口最大化,全屏,隐藏最大化最小化按钮)
  8. Java中流的使用和说明(二)
  9. android车载导航测试,大众全系车载DVD导航之路畅安卓4.1测试
  10. 预卷积HDR环境贴图
  11. .NET报表设计器ActiveReports入门:操作界面详解
  12. 标题:史丰收速算 史丰收速算法的革命性贡献是:从高位算起,预测进位。不需要九九表,彻底颠覆了传统手算! 速算的核心基础是:1位数乘以多位数的乘法。 其中,乘以7是最复杂
  13. 强智教务系统验证码识别 Tensorflow CNN
  14. 深度学习分析--TextCNN算法原理及分类实现
  15. 【4. 扫描节点】 分布式漏洞扫描系统设计与实现
  16. MySQL安装教程及如何解决安装出现“current root password”问题
  17. 艺术 NFT 的发展之路
  18. python 一等公民_Python中一等公民——函数
  19. 吉林大学2021级计算机系统结构期末复习
  20. # 英文从零开始写作---常见问题和技巧csdn

热门文章

  1. 什么是 IoC 容器?
  2. C. Sweets Eating
  3. 最全的JAVA面试题网站
  4. HTML背景图片动画设置,CSS 背景图加载完成后的过渡动画
  5. 行人重识别之重排序(re-ranking)
  6. 网络层中查找路由表的过程(图文详解)
  7. IOS十进制转十六进制
  8. 关于U盘在装系统之后之后容量变小且不能格式化的解决方法
  9. JAVA函数assert的用法_assert()函数用法总结
  10. 关于子网掩码怎么计算!!!!我终于搞懂了!!!!