1. 游戏主体页面设计

  1. 创建JAVA工程(raiden2020)
  2. 创建主方法类(RaidenGameMain.class)使其继承JFrame类
    代码:
import javax.swing.*;
import java.awt.*;public class RaidenGameMain extends JFrame {private static final long serialVersionUID = -2335184685672713223L;//构造方法public RaidenGameMain(){RaidenGamePanel raidenGamePanel = new RaidenGamePanel();//界面大小this.setSize(550,770);//界面位置this.setLocation(400,0);//标题this.setTitle("雷霆战机-2020");//关闭和退出this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//不许调整窗体大小this.setResizable(false);//窗体上鼠标样式:十字光标this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));//设置内容面板,将面板类RaidenGamePanel设置到我们的游戏主界面上this.setContentPane(raidenGamePanel);//可见性:truethis.setVisible(true);}public static void main(String[] args) {new RaidenGameMain();}
}
  1. 测试结果

2. 插入背景图片,实现滚动效果

  1. 将背景图标放到src目录下

  1. 开发游戏面板类JPanel类,我们游戏的内容都放到这个面板上
    面板类代码:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JPanel;
/*** 游戏面板类,我们游戏中要画出来的内容,都是在这个面板中实现的* 然后,我们将这个面板放到JFrame中* @author Administrator**/
public class RaidenGamePanel extends JPanel {/*** */private static final long serialVersionUID = 1L;// 最开始必须先画出游戏背景图片// 加载图片到我们的类中// Image类,就是图片类static Image bjImg ;// Toolkit工具包类,可以创建图片static Toolkit tk = Toolkit.getDefaultToolkit();// 在静态块中,创建图片static{bjImg = tk.createImage(RaidenGamePanel.class.getClassLoader().getResource("bj005.jpg"));}// 定义一个y变量,用来设置背景图片的滚动int y = -1460;public RaidenGamePanel(){MyGameThread myThread = new MyGameThread();myThread.start();}// 在面板上画出游戏背景图片@Overridepublic void paint(Graphics g) {// 画游戏背景(你要画的图片,坐标x,坐标y,画出图片的宽度,想画多高,通知对象)g.drawImage(bjImg, 0, y, 550, 730*3, this);}// 通过线程来实现背景图片的滚动功能,定义一个内部类,实现线程class MyGameThread extends Thread{@Overridepublic void run() {while(true){y+=3; // y = y+5;RaidenGamePanel.this.repaint();if(y >= 0){y = -1460;}try {sleep(30);} catch (InterruptedException e) {e.printStackTrace();}}}   }
}
  1. 在主方法类中加载Jpanel类
public RaidenGameMain(){RaidenGamePanel raidenGamePanel = new RaidenGamePanel();//设置内容面板,将面板类RaidenGamePanel设置到我们的游戏主界面上this.setContentPane(raidenGamePanel);
}
  1. 测试结果

4. 给游戏添加背景音乐

  1. 复制背景音乐的文件到项目的src目录中

  1. 定义背景音乐播放器
// 音乐的播放器
static AudioClip ac;
  1. 在静态代码块加载背景音乐
// 创建音乐播放器ac = Applet.newAudioClip(RaidenGamePanel.class.getClassLoader().getResource("Every Breath You Take.mid"));
  1. 在线程类启动时,播放背景音乐

     //循环播放音乐ac.loop();
    
  2. 测试结果
    有声音!!

5. 开发战机:Hero类

  1. 在src目录下添加战机图片

  1. 新建一个类:Hero
/*** 我们的英雄战机* @author Administrator*/
public class Hero {// 通过x,y定义英雄的位置int x,y;// 用常量来定义一下战机的大小(宽度、高度),使其大小不会发生改变public static final int HERO_WIDTH = 100,HERO_HEIGHT = 100;;// 战机的图片static Image heroImg ;static Toolkit tk = Toolkit.getDefaultToolkit();static{// 加载战机的图片heroImg = tk.createImage(RaidenGamePanel.class.getClassLoader().getResource("hero4.gif"));}// 写战机类的构造方法public Hero(int x,int y){this.x = x;this.y = y;}// 定义战机画自己的方法public void paint(Graphics g) {// 画战机的方法(要画的图片,x,y,宽度,高度,通知对象)g.drawImage(heroImg, x, y, Hero.HERO_WIDTH, Hero.HERO_HEIGHT, null);}
}
  1. 在Jpanel类中调用Hero类的paint方法在窗口画出战机
//创建一个战机,new一台战机的实例对象
static Hero myHero = new Hero(220,450);public void paint(Graphics g) {//画出游戏背景(图片,x,y,宽度,高度,通知对象)g.drawImage(bjImg,0,y,550,770*3,this);//画出战机myHero.paint(g);
}
  1. 测试结果

6. 让英雄移动

  1. 在Hero类中添加鼠标移动方法mouseMoved()
 /***** 鼠标移动方法* @param e*/
public void mouseMoved(MouseEvent e){//输出鼠标所在位置x = e.getX()-53;y = e.getY()-80;
}
  1. 在主方法中添加鼠标移动事件监听器,创建匿名类,调用Hero类的mouseMoved方法实现战机的移动。
//添加鼠标移动事件监听器,创建匿名类
this.addMouseMotionListener(new MouseMotionListener() {//鼠标移动的处理方法@Overridepublic void mouseDragged(MouseEvent e) {//当鼠标移动时,让战机自己处理相关操作raidenGamePanel.myHero.mouseMoved(e);}//鼠标拖拽的处理方法@Overridepublic void mouseMoved(MouseEvent e) {raidenGamePanel.myHero.mouseMoved(e);}
});
  1. 测试结果

7. 战机的切换功能

  1. 将英雄的图片修改为数组,并且加载多个战机图片,定义一个index,用来标识出数组下标
//战机的图片(数组)
static Image[] heroImg = new Image[3];static int index = 0;static{// 加载战机的图片heroImg[0] = tk.createImage(RaidenGamePanel.class.getClassLoader().getResource("hero2.gif"));heroImg[1] = tk.createImage(RaidenGamePanel.class.getClassLoader().getResource("hero3.gif"));heroImg[2] = tk.createImage(RaidenGamePanel.class.getClassLoader().getResource("hero4.gif"));
}
public void paint(Graphics g) {// 画战机的方法(要画的图片,x,y,宽度,高度,通知对象)g.drawImage(heroImg[index], x, y, Hero.HERO_WIDTH, Hero.HERO_HEIGHT, null);
}
  1. 在游戏的主界面中添加键盘处理事件监听器
//添加键盘处理事件监听器,使用匿名类方式,使用适配器模式
this.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_1){Hero.index=0;}if (e.getKeyCode() == KeyEvent.VK_2){Hero.index=1;}if (e.getKeyCode() == KeyEvent.VK_3){Hero.index=2;}}
});
  1. 测试结果

7. 让战机开火

  1. 在src目录下添加子弹图片
  2. 创建子弹类
import java.awt.*;public class HeroMissile {//子弹坐标int x,y;//宽度高度public static final int HERO_MISSILE_WIDTH = 50,HERO_MISSILE_HEIGHT = 50;//子弹图片static Image missileImg;static Toolkit tk = Toolkit.getDefaultToolkit();static {missileImg = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet3.gif"));}/***** 有参构造方法* @param x 子弹x坐标* @param y 子弹y坐标*/public HeroMissile(int x, int y) {this.x = x;this.y = y;}/***** 画子弹的方法* @param g*/public void paint(Graphics g) {//图片,x,y,宽度,高度,通知对象g.drawImage(missileImg,x,y,HeroMissile.HERO_MISSILE_WIDTH,HeroMissile.HERO_MISSILE_HEIGHT,null);move();}/***** 子弹飞的方法*/public void move(){y-=5;}
}
  1. 在RaidenGamePenal类中创建存储子弹的数组,遍历数组实现子弹的连续产生
//    定义一个子弹的集合,用来盛子弹的容器static List<HeroMissile> heroMissileList = new ArrayList<>();public void paint(Graphics g) {//画出游戏背景(图片,x,y,宽度,高度,通知对象)g.drawImage(bjImg,0,y,550,770*3,this);//画出战机myHero.paint(g);//遍历子弹集合,逐个取出子弹for (Iterator<HeroMissile> it = heroMissileList.iterator(); it.hasNext();){HeroMissile missile = it.next();missile.paint(g);}}
  1. 在Hero类创建战机开火的方法fire
/***** 战机开火的方法*/
public HeroMissile fire(){//打出一个子弹,就是幺new一个子弹,然后把这个子弹放到子弹的集合中,就可以了HeroMissile missile = new HeroMissile(x,y);RaidenGamePanel.heroMissileList.add(missile);return missile;
}
  1. 在主方法类创建鼠标点击监听器,实现鼠标点击发射子弹
//点击鼠标监听器
this.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {raidenGamePanel.myHero.fire();}
});
  1. 测试结果

8. 开火声音

  1. 将开火的声音放到项目的src目录下
  2. 在Hero类中加载声音
//开火的声音
static AudioClip ac;static{//加载开火的声音ac = Applet.newAudioClip((Hero.class.getClassLoader().getResource("fire3.mp3"));
}
  1. 在战机开火的时候,播放一下声音
public HeroMissile fire(){//播放开火声音ac.play();
}
  1. 测试结果
    有声音!

9. 修改点击开火为按鼠标左键一直开火

  1. 在英雄类中定义一个变量,是标识变量,标识是否开火
//定义一个变量,标识是否开火
public static boolean fireLag = false;
  1. 在鼠标按下和抬起的事件中修改这个变量的值
//点击鼠标监听器
this.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {Hero.fireLag = true;}@Overridepublic void mouseReleased(MouseEvent e) {Hero.fireLag = false;}
});
  1. 在Hero类的paint方法中,决定是否开火
public void paint(Graphics g) {//判断是否开火if (fireLag){fire();}
}
  1. 测试结果

子弹太过密集!

10. 解决子弹过于密集

  1. 实现的思路,让子弹飞出一段距离后,再打出下一个子弹

  1. 怎么判断子弹已经飞出一段距离了?
    (a)首先在Hero类中定义一个变量oldMissile,用来结束上一个子弹
//定义一个子弹类的对象,用来保存前一个子弹
HeroMissile oldMissile;

(b)修改Hero类的fire方法,开火时候判断,上一个子弹是否已经飞出了一段距离,如果飞出一定的距离,那么我们打出下一个子弹

public HeroMissile fire(){//判断是否第一次开火if(oldMissile == null) {//打出一个子弹,就是new一个子弹,然后把这个子弹放到子弹的集合中,就可以了HeroMissile missile = new HeroMissile(x,y);//播放开火声音ac.play();RaidenGamePanel.heroMissileList.add(missile);oldMissile = missile;}else if (Math.abs(oldMissile.y - y) > 50){//打出一个子弹,就是new一个子弹,然后把这个子弹放到子弹的集合中,就可以了HeroMissile missile = new HeroMissile(x,y);//播放开火声音ac.play();RaidenGamePanel.heroMissileList.add(missile);oldMissile = missile;}return oldMissile;
}
  1. 测试结果

11. 移除飞出游戏边界的子弹

  1. 给HeroMissile类添加一个标识生死的变量
//标记子弹存亡的变量
boolean live = true;
  1. 当子弹飞出上边界,标识生死的变量就为false
public void move(){y-=10;//如果飞上边界,子弹消亡if(y<0){live = false;}
}
  1. 修改RaidenGamePanel类中的paint方法,当子弹死了,就从集合中移除子弹
    public void paint(Graphics g) {//遍历子弹集合,逐个取出子弹for (Iterator<HeroMissile> it = heroMissileList.iterator(); it.hasNext();){HeroMissile missile = it.next();
//            判断子弹是否为活if (missile.live){missile.paint(g);}else {//如果子弹不为活,将其从集合中移除it.remove();}}}
  1. 测试结果
    成功

12. 开发一只敌机

敌机也是一个类,通过分析确定敌人的属性和方法

  1. 创建敌机类Enemy
import java.awt.*;public class Enemy {int x,y;//宽度、高度public static final int ENEMY_WIDTH = 60,ENEMY_HEIGHT = 60;//用数组存储敌人图片,图片表示左中下三个方向的敌机static Image[] enemyImg = new Image[3];static Toolkit tk = Toolkit.getDefaultToolkit();static {enemyImg[0] = tk.createImage(Enemy.class.getClassLoader().getResource("enemy.gif"));enemyImg[1] = tk.createImage(Enemy.class.getClassLoader().getResource("enemy left.gif"));enemyImg[2] = tk.createImage(Enemy.class.getClassLoader().getResource("enemy right.gif"));}//有参构造public Enemy(int x, int y) {this.x = x;this.y = y;}/**** 画敌机方法*/public void paint(Graphics g){g.drawImage(enemyImg[1],x,y,ENEMY_WIDTH,ENEMY_HEIGHT,null);}
}
  1. 在RaidenGamPanel类创建敌人的实例(对象)
//创建一个敌机
Enemy enemy = new Enemy(240,200);
  1. 在RaidenGamPanel类的paint方法中画出敌机
//画敌机
enemy.paint(g);
  1. 测试结果

13. 让敌机随机改变方向

  1. 在Enemy类中定义敌机方向的变量
//定义一个可以表示方向的变量,变量的值是0,1,2;分别代表左中右;
int dir = 1;
  1. 根据方向画出不同方向的敌机,每次改变方向后走n步
//每次改变方向之后,走n多步
int n = new Random().nextInt(10)+10;
public void paint(Graphics g){n--;if(n <= 0){//每次画的时候,我们改变一下的值。也就改变了敌机的方向dir = new Random().nextInt(3);//0,1,2n = new Random().nextInt(10)+10;}g.drawImage(enemyImg[dir],x,y,ENEMY_WIDTH,ENEMY_HEIGHT,null);//调用敌机的移动方法move();
}//敌机移动的方法public  void move(){y+=5;}

13. 实现多个敌人加入我们的集合

  1. 注释掉之前的单体敌机,定义一个敌机的集合
//定义一个敌机集合,用来存储敌机
static  List<Enemy> enemyList = new ArrayList<>();
  1. 向集合中添加敌机,注释掉之前的单体敌机绘画代码行,在paint类中加入敌机并绘画
//画敌机
//enemy.paint(g);
//每次画的时候加入敌机
int n =new Random().nextInt(10);
for (int i = 0; i < n; i++) {int x = new Random().nextInt(500);int y = new Random().nextInt(200) - 300;Enemy enemy = new Enemy(x,y);enemyList.add(enemy);
}
//逐个画出集合中的敌机
for (Iterator<Enemy> it = enemyList.iterator();it.hasNext();){Enemy enemy = it.next();enemy.paint(g);
}
  1. 测试结果

14. 解决一下敌机太多的问题

  1. 首先,我们在Enemy类给敌机增加生死的变量,开始的时候敌机是活的,当敌机飞出下边界的时候,敌机就死了,死了,就移除了
//生死的变量
boolean live = true;
//敌机移动的方法
public  void move(){y+=5;//左边方向if (dir == 0) {x-=5;}//右边方向if (dir == 2) {x+=5;}//不让敌机超出左右边界if (x <= 0){x = 0;}if (x >= 550-Enemy.ENEMY_WIDTH){x = 550-Enemy.ENEMY_WIDTH;}//如果移动后,飞出边界,敌机就死了if(y > 730){//敌机死了live = false;}
}
  1. 修改RaidenGamePanel类中的paint方法,结局敌机过于密集的情况
//逐个画出集合中的敌机
for (Iterator<Enemy> it = enemyList.iterator();it.hasNext();){Enemy enemy = it.next();if (enemy.live){enemy.paint(g);}else{it.remove();}
//在每次添加敌机的时候,判断集合中敌机数量是否少于5个,是的话我们就添加N多战机
if(enemyList.size() < 5){//每次画的时候加入敌机int n =new Random().nextInt(10);for (int i = 0; i < n; i++) {int x = new Random().nextInt(500);int y = new Random().nextInt(200) - 300;Enemy enemy = new Enemy(x,y);enemyList.add(enemy);}
}
  1. 测试结果

15. 实现战机的子弹打敌机功能

  1. 原理

  1. 在HeroMissile类和Enemy类中创建获取子弹、敌机所在区域的方法
//获取子弹所在位置的方法
public Rectangle getRect(){return new Rectangle(x,y,HERO_MISSILE_WIDTH,HERO_MISSILE_HEIGHT);
}
//获取敌机所在区域的方法
public Rectangle getRect(){return new Rectangle(x,y,ENEMY_HEIGHT,ENEMY_WIDTH);
}
  1. 在HeroMissile类中开发子弹打敌机的方法,打一个敌人和打敌人的集合
//打一个敌机,返回值为true就是打中了
public boolean hitEnemy(Enemy enemy){Rectangle missileRect = this.getRect();Rectangle enemyRect = enemy.getRect();if(missileRect.intersects(enemyRect)){//打中了,子弹死了,敌机死了live = false;enemy.live = false;return true;}return false;
}//打敌机集合
public void hitEnemyList(List<Enemy> enemyList){for (Iterator<Enemy> it = enemyList.iterator();it.hasNext();){Enemy enemy = it.next();boolean b = hitEnemy(enemy);if (b) {return;}}
  1. 在RaidenGamePanel类的paint方法中调用打敌人集合的方法,来消灭敌机
        //遍历子弹集合,逐个取出子弹for (Iterator<HeroMissile> it = heroMissileList.iterator(); it.hasNext();){HeroMissile missile = it.next();
//            判断子弹是否为活if (missile.live){missile.paint(g);//打敌人的集合missile.hitEnemyList(enemyList);}else {//如果子弹不为活,将其从集合中移除it.remove();}}
  1. 测试结果
    成功!

16. 产生爆炸功能

  1. 创建爆炸类,定义属性和方法
import java.awt.*;/***** 爆炸类*/
public class Explode {int x,y,w=50,h=50,index;// 每张爆炸的图片我们画n次int n = 2;static Image[] img = new Image[9];static Toolkit tk = Toolkit.getDefaultToolkit();static {img[0] = tk.createImage(Explode.class.getClassLoader().getResource("b0.gif"));img[1] = tk.createImage(Explode.class.getClassLoader().getResource("b11.gif"));img[2] = tk.createImage(Explode.class.getClassLoader().getResource("b22.gif"));img[3] = tk.createImage(Explode.class.getClassLoader().getResource("b33.gif"));img[4] = tk.createImage(Explode.class.getClassLoader().getResource("b44.gif"));img[5] = tk.createImage(Explode.class.getClassLoader().getResource("b55.gif"));img[6] = tk.createImage(Explode.class.getClassLoader().getResource("b66.gif"));img[7] = tk.createImage(Explode.class.getClassLoader().getResource("b77.gif"));img[8] = tk.createImage(Explode.class.getClassLoader().getResource("b88.gif"));}//有参构造public Explode(int x, int y) {this.x = x;this.y = y;}/*****画爆炸方法*/public void paint(Graphics g){g.drawImage(img[0],x,y,w,h,null);index++;}
}
  1. 在面板类中定义爆炸
//定义一个爆炸
Explode explode = new Explode(270,300);
  1. 在panel类中画出爆炸
        //画爆炸//explode.paint(g);
  1. 修改Explode类完成爆炸效果
   static Image[] img = new Image[12];static {img[0] = tk.createImage(Explode.class.getClassLoader().getResource("b00.gif"));img[1] = tk.createImage(Explode.class.getClassLoader().getResource("b11.gif"));img[2] = tk.createImage(Explode.class.getClassLoader().getResource("b11.gif"));img[3] = tk.createImage(Explode.class.getClassLoader().getResource("b22.gif"));img[4] = tk.createImage(Explode.class.getClassLoader().getResource("b22.gif"));img[5] = tk.createImage(Explode.class.getClassLoader().getResource("b33.gif"));img[6] = tk.createImage(Explode.class.getClassLoader().getResource("b33.gif"));img[7] = tk.createImage(Explode.class.getClassLoader().getResource("b44.gif"));img[8] = tk.createImage(Explode.class.getClassLoader().getResource("b55.gif"));img[9] = tk.createImage(Explode.class.getClassLoader().getResource("b22.gif"));img[10] = tk.createImage(Explode.class.getClassLoader().getResource("b22.gif"));img[11] = tk.createImage(Explode.class.getClassLoader().getResource("b66.gif"));}/*****画爆炸方法*/public void paint(Graphics g){if (index == img.length) {return;}g.drawImage(img[index],x,y,w,h,null);if (n <= 0) {index++;n = 2;}n--;}
  1. 在panel类中定义爆炸的集合,放所有的爆炸
   //定义一个爆炸的集合static List<Explode> explodeList = new ArrayList<>();
  1. 在panel类的paint方法中遍历集合,逐个画出所有爆炸
//画出集合中的爆炸
for(Iterator<Explode> it = explodeList.iterator();it.hasNext();){Explode explode = it.next();explode.paint(g);
}
  1. 修改HeroMissle类的hitEnemy方法,产生爆炸
public boolean hitEnemy(Enemy enemy){Rectangle missileRect = this.getRect();Rectangle enemyRect = enemy.getRect();if(missileRect.intersects(enemyRect)){//打中了,子弹死了,敌机死了live = false;enemy.live = false;//产生爆炸,并且添加爆炸到集合中Explode exp = new Explode(x,y);RaidenGamePanel.explodeList.add(exp);return true;}return false;
}
  1. 测试结果

17. 为爆炸增加声音

  1. 在项目的src目录下添加爆炸的声音文件
  2. 在Explode类中定义爆炸的声音
//爆炸的声音
static AudioClip ac;
static {ac = Applet.newAudioClip(Explode.class.getClassLoader().getResource("bom.au"));
}
//有参构造
public Explode(int x, int y) {this.x = x;this.y = y;//创建一个爆炸,播放声音ac.play();
}
已经消亡的爆炸从集合中移除掉
给爆炸类添加一个生死的变量,修改paint方法//添加爆炸生死的变量boolean live = true;public void paint(Graphics g){if (index == img.length) {live = false;return;}}
  1. 修改RaidenGamePanel类中的paint方法
//画出集合中的爆炸
for(Iterator<Explode> it = explodeList.iterator();it.hasNext();){Explode explode = it.next();if (explode.live) {explode.paint(g);} else {it.remove();}
}
  1. 测试结果
    有声音!!

18. 开发敌机的子弹

  1. 创建敌机子弹类
import java.awt.*;public class EnemyMissile {//属性和方法分析:坐标、大小、图片、构造方法、画敌机、子弹移动、打战机int x,y,width = 30,height = 50;static Image img;static  Toolkit tk = Toolkit.getDefaultToolkit();static {img = tk.createImage(EnemyMissile.class.getClassLoader().getResource("enemy bullet.gif"));}//有参构造public EnemyMissile(int x, int y) {this.x = x;this.y = y;}//画敌机子弹的方法public void paint(Graphics g){g.drawImage(img,x,y,width,height,null);}
}
  1. 在RaidenGamePanel类中创建敌机子弹的实例
//创建敌机子弹实例
EnemyMissile enemyMissile = new EnemyMissile(280,0);
  1. 在RaidenGamePanel类的paint方法中画出子弹
//画敌机子弹
enemyMissile.paint(g);
  1. 子弹显示测试结果

  1. 让敌人的子弹运动起来,增加move方法,y坐标不断地加
//画敌机子弹的方法
public void paint(Graphics g){g.drawImage(img,x,y,width,height,null);//每次画出子弹,让子弹移动move();
}/*** 敌机子弹的移动方法*/
public  void move(){y += 10;
}
  1. 在RaidenGamePanel类开发一个敌人子弹的集合,用来放敌人的子弹,并且要遍历集合,逐个画出敌人子弹。
    (1)注释之前的单个子弹,改换集合
    //创建敌机子弹实例
//EnemyMissile enemyMissile = new EnemyMissile(280,0);//创建敌机子弹实例集合
static List<EnemyMissile> enemyMissilesList = new ArrayList<>();

(2)在RaidenGamePanel类的paint方法中注释掉之前的单体子弹绘画,绘画除集合的子弹

//画敌机子弹
//enemyMissile.paint(g);//画子弹
Iterator<EnemyMissile> it = enemyMissilesList.iterator();while(it.hasNext()){EnemyMissile enemyMissile = it.next();enemyMissile.paint(g);
}
  1. 在Enemy类中定义敌机开火的方法
//敌机开火方法
public  void fire(){EnemyMissile em = new EnemyMissile(x,y);RaidenGamePanel.enemyMissilesList.add(em);
}

在画敌机的方法中调用fire方法

 /**** 画敌机方法*/
public void paint(Graphics g){//每次画的时候,敌机开火fire();
}
  1. 测试结果

敌军火力太旺!

  1. 解决敌人子弹过于密集的问题
    修改之前的画敌机方法中fire调用方式,添加判断
int f = new Random().nextInt(1000);//0-999
//每次画的时候,敌机开火
if (f > 990){fire();
}
  1. 敌机子弹测试

19. 敌机子弹对我方造成伤害功能

  1. 在hero类中编写获取战机所在区域的方法
/***获取战机所在区域的方法*/
public Rectangle getRect(){return new Rectangle(x,y,Hero.HERO_WIDTH,Hero.HERO_HEIGHT);
}
  1. 在EnemyMissile类中编写获取敌机子弹所在区域的方法
/*** 获取敌机子弹所在区域的方法*/
public Rectangle getRect(){return  new Rectangle(x,y,width,height);
}
  1. 在EnemyMissile类中编写表示敌机子弹生死的变量,让子弹超出左右边框死亡
//增加一个表示敌机子弹生死的变量
boolean live = true;
/*** 敌机子弹的移动方法*/
public  void move(){y += 10;if (y>750){live = false;}
}
  1. 在Hero类中编写表示战机生死的变量,并修改paint方法,如果战机死亡就不用再画战机了。
//英雄的生死变量
boolean live = true;//判断战机是否为活,如果不是活的,就不用画战机if(!live){return;}
  1. 在EnemyMissile类中编写敌机子弹打战机的方法
   /*** 敌机子弹打战机的方法*/public void hitHero(Hero hero){Rectangle enemyMissileRect = this.getRect();Rectangle heroRect = hero.getRect();if (enemyMissileRect.intersects(heroRect)){//打中了this.live=false;hero.live=false;//产生爆炸,并且添加到集合中Explode exp = new Explode(x,y);RaidenGamePanel.explodeList.add(exp);}}
  1. 在RaidenGamePanel类的paint方法中调用hitHero方法打战机
        //画敌机子弹Iterator<EnemyMissile> it = enemyMissilesList.iterator();while(it.hasNext()){EnemyMissile enemyMissile = it.next();if (enemyMissile.live){enemyMissile.paint(g);//敌机子弹打战机enemyMissile.hitHero(myHero);}}
  1. 测试结果
    战机已阵亡!

20. 战机的生命

  1. 将hero类的live变量类型改为静态的int,赋值为5
//英雄的生死变量
static int live = 5;
  1. 修改paint方法,如果live为0,战机死亡
//判断战机是否为活,如果不是活的,就不用画战机
if(live == 0){return;
}
  1. 修改EnemyMissile类中的hitHero方法,当战机的生命力不为空时,减1
if(hero.live > 0) {hero.live--;
}
  1. 测试结果
    成功!!

21. 增加图形化血条

  1. 原理

  1. 在RaidenGamePenal类中编写显示血条的代码
//图形化战机血条
g.drawRect(500,60,20,200);
g.setColor(Color.cyan);
g.fillRect(501,61,18,200*Hero.live/5);
  1. 测试

22. 超级火力,按A键打出十发子弹

  1. 在Hero类中创建超级火力方法
/***超级火力*/
public HeroMissile superFire(){//判断是否第一次开火if(oldMissile == null) {//打出十个子弹for (int i = 1; i < 10; i++) {HeroMissile missile = new HeroMissile(50 * i, y);RaidenGamePanel.heroMissileList.add(missile);oldMissile = missile;//打出一个子弹,就是new一个子弹,然后把这个子弹放到子弹的集合中,就可以了missile = new HeroMissile(x, y);}//播放开火声音ac.play();}else if (Math.abs(oldMissile.y - y) > 50 || !oldMissile.live) {//打出十个子弹for (int i = 1; i < 10; i++) {HeroMissile missile = new HeroMissile(50 * i, y);RaidenGamePanel.heroMissileList.add(missile);oldMissile = missile;//打出一个子弹,就是new一个子弹,然后把这个子弹放到子弹的集合中,就可以了missile = new HeroMissile(x, y);}//播放开火声音ac.play();}return oldMissile;}
  1. 在主方法中的键盘监听器中添加A键监听事件
//按A键,超级火力
if(e.getKeyCode() == KeyEvent.VK_A){//只有战机还活着才能开火if (Hero.live != 0) {raidenGamePanel.myHero.superFire();}
}
  1. 测试

24. 实现子弹的切换

  1. 在HerMissile类中的子弹图片该为静态的集合变量,用静态打index来表示集合中的图片
//子弹图片
static Image[] missileImg = new Image[7];
static int index;
  1. 在src目录下添加子弹图片,在类静态代码块中加载子弹图片
static {missileImg[0] = tk.createImage(HeroMissile.class.getClassLoader().getResource("hero2.gif"));missileImg[1] = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet3.gif"));missileImg[2] = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet4.gif"));missileImg[3] = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet5.gif"));missileImg[4] = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet6.gif"));missileImg[5] = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet7.gif"));missileImg[6] = tk.createImage(HeroMissile.class.getClassLoader().getResource("bullet8.gif"));
}
  1. 修改画子弹的方法,用index来表示要发射的子弹
public void paint(Graphics g) {//图片,x,y,宽度,高度,通知对象g.drawImage(missileImg[index],x,y,HeroMissile.HERO_MISSILE_WIDTH,HeroMissile.HERO_MISSILE_HEIGHT,null);move();
}
  1. 在主类中的键盘监听器中编写切换子弹的方法
//切换子弹
if (e.getKeyCode() == KeyEvent.VK_Z){HeroMissile.index=0;
}
if (e.getKeyCode() == KeyEvent.VK_X){HeroMissile.index=1;
}
if (e.getKeyCode() == KeyEvent.VK_C){HeroMissile.index=2;
}
if (e.getKeyCode() == KeyEvent.VK_V){HeroMissile.index=3;
}
if (e.getKeyCode() == KeyEvent.VK_B){HeroMissile.index=4;
}
if (e.getKeyCode() == KeyEvent.VK_N){HeroMissile.index=5;
}
if (e.getKeyCode() == KeyEvent.VK_M){HeroMissile.index=6;
}
  1. 测试结果

25. 按F键,给战机加满血,并且复活

在键盘监听器添加事件,按F键,战机加满血

//无敌模式
if(e.getKeyCode() == KeyEvent.VK_F){raidenGamePanel.myHero.live = 5;
}

26. 子弹打子弹功能

  1. 在HeroMissile类中编写子弹打子弹方法和子弹打子弹集合方法
/*** 子弹打子弹方法*/
public boolean hitEnemyMissile(EnemyMissile enemyMissile){Rectangle heroMissileRect = this.getRect();Rectangle enemyMissileRect = enemyMissile.getRect();if (heroMissileRect.intersects(enemyMissile.getRect())){//子弹打中子弹this.live = false;enemyMissile.live = false;//产生爆炸,并且添加到集合中Explode exp = new Explode(x,y);RaidenGamePanel.explodeList.add(exp);//如果打中了,返回true;return true;}return false;
}
/***子弹打子弹集合*/
public void hitEnemyMissileList(List<EnemyMissile> enemyMissileList){for (Iterator<EnemyMissile> it = enemyMissileList.iterator();it.hasNext();){EnemyMissile enemyMissile = it.next();if (hitEnemyMissile(enemyMissile)){it.remove();return;}}
}
  1. 在RaidenGamePanel类中paint方法中调用子弹打子弹集合方法
//遍历子弹集合,逐个取出子弹
for (Iterator<HeroMissile> it = heroMissileList.iterator(); it.hasNext();){HeroMissile missile = it.next();//判断子弹是否为活if (missile.live){missile.paint(g);//打敌人的集合missile.hitEnemyList(enemyList);//打中敌机子弹集合missile.hitEnemyMissileList(enemyMissilesList);}else {//如果子弹不为活,将其从集合中移除it.remove();}
}
  1. 测试结果

27. 游戏点击鼠标开始运行功能

  1. 在面板类RaidenGamePanel中定义一个游戏是否开始的变量
//游戏是否启动变量,初始值为false
boolean gameStart = false;
  1. 重构一下游戏代码,将鼠标事件处理(两个:一个是鼠标移动、鼠标点击)放到面板类中,重构之后,运行游戏,测试和原来功能是一样的。
   //无参构造方法public RaidenGamePanel(){//添加鼠标移动事件监听器,创建匿名类this.addMouseMotionListener(new MouseMotionListener() {//鼠标移动的处理方法@Overridepublic void mouseDragged(MouseEvent e) {//当鼠标移动时,让战机自己处理相关操作RaidenGamePanel.myHero.mouseMoved(e);}//鼠标拖拽的处理方法@Overridepublic void mouseMoved(MouseEvent e) {RaidenGamePanel.myHero.mouseMoved(e);}});//点击鼠标监听器this.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {Hero.fireLag = true;}@Overridepublic void mouseReleased(MouseEvent e) {Hero.fireLag = false;}});}
  1. 修改一下开始功能,线程的启动位置
public void mousePressed(MouseEvent e) {Hero.fireLag = true;//鼠标点击,判断如果游戏没有启动,那么就启动游戏if (!gameStart){//没有启动游戏进程,那么我们就启动这个线程MyGameThread myThread = new MyGameThread();//启动线程myThread.start();//游戏启动后。给变量赋值为true,说明游戏已经启动gameStart = true;}
}
  1. 在面板类的paint方法中编写开始文字信息的显示
//开始界面显示信息
if (!gameStart) {g.drawString("您能过10000分吗?来挑战吧!",120,220);g.drawString("点击鼠标开始游戏!",170,280);
}
  1. 测试结果

28. 英雄积分显示功能

打一个敌人计10分,拦截一个子弹计5分

  1. 在面板类中定义积分变量
//积分变量
static int score;
  1. 在面板类的paint方法编写积分显示
   g.drawString("积分:" + score, 0, 50);
  1. 在HeroMissile类的子弹打子弹方法中编写拦截子弹积分+5功能
public boolean hitEnemyMissile(EnemyMissile enemyMissile){Rectangle heroMissileRect = this.getRect();Rectangle enemyMissileRect = enemyMissile.getRect();if (heroMissileRect.intersects(enemyMissile.getRect())){//子弹打中子弹this.live = false;enemyMissile.live = false;//产生爆炸,并且添加到集合中Explode exp = new Explode(x,y);RaidenGamePanel.explodeList.add(exp);//拦截一个子弹,积分+5;RaidenGamePanel.score += 5;//如果打中了,返回true;return true;}return false;
}
  1. 在HeroMissile类的打敌机方法中编写每打一架敌机积分+10功能
public boolean hitEnemy(Enemy enemy){Rectangle missileRect = this.getRect();Rectangle enemyRect = enemy.getRect();if(missileRect.intersects(enemyRect)){//打中了,子弹死了,敌机死了live = false;enemy.live = false;//产生爆炸,并且添加爆炸到集合中Explode exp = new Explode(x,y);RaidenGamePanel.explodeList.add(exp);//积分+10RaidenGamePanel.score += 10;return true;}return false;
}
  1. 测试结果

29. 给战机加血的功能

  1. 首先将血包图片添加到src目录下
  2. 开发加血血包类,定义其中的属性和方法
import java.awt.*;/*** 加血包的类*/
public class BloodBank {//血包坐标和大小int x,y,width = 100,height = 100;//生死变量boolean live = true;//加载图片static Image img;static  Toolkit tk = Toolkit.getDefaultToolkit();static {img = tk.createImage(BloodBank.class.getClassLoader().getResource("blood1.png"));}//有参构造public BloodBank(int x, int y) {this.x = x;this.y = y;}/*** 画补血包的方法* @param g*/public void pain (Graphics g) {g.drawImage(img,x,y,width,height,null);move();}/*** 补血包移动功能*/public void move(){y += 9;if (y >= 740){live =false;}}/*** 补血包的方位* @return*/public Rectangle getRect(){return new Rectangle(x,y,width,height);}
}
  1. 在Hero类添加补血包碰撞方法和补血包碰撞集合遍历方法
/*** 补血包碰撞方法* @param blood*/
public void collideWithBlood(BloodBank blood){Rectangle heroRect = this.getRect();Rectangle bloodRect = blood.getRect();if (heroRect.intersects(bloodRect)){//加血包,补血包就死了blood.live = false;this.live += 1;if (live > 5){live = 5;}}
}/*** 补血包碰撞集合遍历方法* * @param bloodList*/
public void collideWithBloodList(List<BloodBank> bloodList){for (Iterator<BloodBank> it = bloodList.iterator();it.hasNext();){BloodBank bloodBank = it.next();collideWithBlood(bloodBank);}
}
  1. 在RaidenGamePanel类创建补血包集合
//补血包的创建
static List<BloodBank> bloodList = new ArrayList<>();
  1. 在RaidenGamePanel类的paint方法画出补血包
//画出补血包
for (Iterator<BloodBank> it1 = bloodList.iterator();it1.hasNext();) {BloodBank blood = it1.next();if (blood.live){blood.pain(g);}else{it1.remove();}}
  1. 在RaidenGamePanel类的paint方法中调用补血包碰撞集合方法
//调用补血包碰撞集合方法
myHero.collideWithBloodList(bloodList);
  1. 测试结果

30. 敌机撞战机功能

  1. 在Hero类添加敌机撞战机方法和敌机集合遍历方法
/*** 敌机撞战机方法*/
public void collideWithHero(Enemy enemy){Rectangle heroRect = this.getRect();Rectangle enemyRect = enemy.getRect();if (heroRect.intersects(enemyRect)){//打中了enemy.live = false;this.live--;if (live < 0){live = 0;}//产生爆炸,并且添加到集合中Explode exp = new Explode(x,y);RaidenGamePanel.explodeList.add(exp);
}
}/*** 敌机撞战机集合遍历方法* @param enemyList*/
public void collideWithHeroList(List<Enemy> enemyList){for (Iterator<Enemy> it = enemyList.iterator();it.hasNext();){Enemy enemy = it.next();collideWithHero(enemy);}
}
  1. 在RaidenGamePanel类的paint方法中调用敌机撞战机集合遍历方法
//调用敌机撞战机集合遍历方法
myHero.collideWithHeroList(enemyList);

31. 游戏结束效果实现

  1. 在RaidenGamePanel类中添加游戏结束文字升起的初始位置变量和判断游戏结束变量
//定义一个游戏结束文字升起的起始位置,从游戏的下边加位置升起
int gameOverStrY = 730;
//游戏结束的变量
boolean gameStop = false;
  1. 在RaidenGamePanel类的paint方法中添加游戏结束升起文字的实现代码
if(Hero.live == 0){//如果战机牺牲了,我们浮起一段文字:GAME OVERg.setFont(new Font("宋体",Font.BOLD,35));g.drawString("GAME OVER",200,gameOverStrY);gameOverStrY -= 5;if(gameOverStrY <= 300){//真正结束游戏gameStop = true;}
}
  1. 修改RaidenGamePanel类中的线程启动方法,使游戏应结束时,跳出线程
class MyGameThread extends Thread{@Overridepublic void run() {//循环播放音乐ac.loop();//无限循环,完成背景图片的滚动while(true){//gameStop为true,游戏结束if(gameStop){ac.stop();return;}y+=3; // y = y+3;RaidenGamePanel.this.repaint();if(y >= 0){y = -1460;}try {sleep(30);} catch (InterruptedException e) {e.printStackTrace();}}}}
  1. 测试结果

31. 游戏结束音乐播放功能

  1. 在项目的src目录下添加要播放的音乐文件
  2. 在RaidenGamePanel类中创建静态音乐播放器变量ac1,用来存储音乐文件
static AudioClip ac1;
  1. 在静态方法块中绑定音乐文件
static {Applet.newAudioClip(RaidenGamePanel.class.getClassLoader().getResource("over.mp3"));
}
  1. 在内部类MyGameThread的run方法中调用ac的音乐播放方法
public void run() {//循环播放音乐ac.loop();//无限循环,完成背景图片的滚动while(true){if(gameStop){//背景音乐的停止ac.stop();//结束音乐的播放ac1.play();return;}y+=3; // y = y+3;RaidenGamePanel.this.repaint();if(y >= 0){y = -1460;}try {sleep(30);} catch (InterruptedException e) {e.printStackTrace();}}
}

32. 源码附录

链接:https://pan.baidu.com/s/1L58H-j2ozwDHT8134mEkLA 提取码:yzan

雷霆战机游戏项目(JAVA)相关推荐

  1. java 雷霆战机 教程,java swing实现简单的雷霆战机小游戏项目源码附带视频指导修改教程...

    <p> <span style="font-family:微软雅黑;font-size:19px;color:#666666;background-color:#FFFFF ...

  2. python及pygame雷霆战机游戏项目实战01 控制飞机

    入门 在这个系列中,将制作一个雷霆战机游戏. 首先,将游戏设置修改一下: WIDTH = 480 HEIGHT = 600 FPS = 60 玩家精灵 要添加的第一件事是代表玩家的精灵.最终,这将是一 ...

  3. python及pygame雷霆战机游戏项目实战03 碰撞检测

    在这个系列中,将制作一个雷霆战机游戏. 碰撞 碰撞是游戏开发的基本部分. 碰撞检测意味着您要检测游戏世界中的一个对象是否正在触碰另一个对象. 碰撞反应决定了碰撞发生时你想要做什么 - 马里奥拿起硬币, ...

  4. python及pygame雷霆战机游戏项目实战05 改进的碰撞

    项目详细介绍 项目详细介绍 在这个系列中,将制作一个雷霆战机游戏. 碰撞发生了什么? Pygame中的默认碰撞类型是使用collide_rect()函数,该函数使用两个精灵的rect属性来计算它们是否 ...

  5. java 雷霆战机游戏 飞机大战 全过程教学+免费素材(附全部源代码)

    这个游戏已是我第二次编写了,之前写过一个简易版的飞机大战类似demo.这次在上一次基础上添加了许多元素,增添了可玩性. 游戏效果图如下: ps :完整源码+视频教程+论文文档 :java雷霆战机完整资 ...

  6. 基于Java swing开发的雷霆战机

    游戏截图 飞机项目的所有类的截图 主窗体类 package com.tarena.shout; import java.awt.Graphics; import java.awt.Image; imp ...

  7. JavaSwing多线程小游戏雷霆战机

    在做完连连看以后,想到要做一个多线程游戏,本来是做的一个跳伞的小游戏的.但是做到一半的时候,觉得可玩性太低了.后面想来想去还是打算做一个以前玩过的雷霆战机小游戏,也就是飞机大战. 1.效果展示 2.绘 ...

  8. C++ 雷霆战机 附完整源码

    先来看看效果图吧~ 游戏是有音乐的,很动感哦   具体的实现如何和Java开发雷霆战机是一样的.可以参见主页Java 开发雷霆战机的原理,写的很详细.源码已经打包好了放群里了.有需要的效果可以进来下载 ...

  9. JFrame实现简易雷霆战机

    一.项目简介         通过JAVA语言中的JFrame类中的方法实现简易的雷霆战机小游戏,功能包括飞机移动,发射子弹,生成敌机以及摧毁敌机等. 二.项目分析         模块一:玩家飞机模 ...

  10. 雷霆战机源代码c语言,C++实现雷霆战机可视化小游戏

    用C++和easyx实现简单的雷霆战机小游戏 之前在网上看了许多关于c++或者是其他语言实现雷霆战机的帖子,大多不完整,或者是要付费才能阅读,现将源码展示如下,仅作学习交流之用. 基本原理 基本思路 ...

最新文章

  1. 人脸识别技术在支付场景的机遇与挑战
  2. postfix邮件系统经典退信
  3. 【Android 应用开发】Android 返回堆栈 与 任务
  4. 实验六 Linux进程编程,Linux系统编程实验六:进程间通信
  5. IIS应用池保持激活工具开发
  6. 声压级 matlab,语音信号处理教程(二)声音的声压级和响度
  7. Spring Cloud Alibaba迁移指南(四):零代码兼容 Api-Gateway 1
  8. input内容右对齐_向右打方向倒库过程中,如何判断后车距离?光线较暗车库倒库技巧...
  9. Autodesk BIM 360将基础设施建模也推向云端
  10. centos如何使用nomachine远程连接GNOME桌面(二)
  11. 存储在U盘中的文件被误删后怎么免费恢复
  12. Office基础操作:Word插入visio图片显示不全
  13. VScode如何在浏览器中打开html文件
  14. Ubuntu:安装yarn
  15. linux格式化磁盘fdisk,linux下使用fdisk工具为磁盘分区格式化
  16. js实现漂亮的雪花飘落效果
  17. python处理实验数据,Python在热敏电阻测量实验数据处理中的应用
  18. 训练好的深度学习模型是怎么部署的?
  19. ICC Floorplan遇到的坑以及解决方法
  20. SVN提交成功,但是版本库里面的内容没有更新--解决方案

热门文章

  1. mysql删除一行_MySql删除表中一行的实操方法
  2. pandas精华总结
  3. execute immediate 用法详解
  4. 尝试重新启动计算机和应用程序 错误38,win10系统运行coreldraw x7 38错误怎么办?win10 cdr 错误38解决方法...
  5. 利用Python画出《人民日报》各国疫情图——南丁格尔玫瑰图
  6. 德玛西亚皇子背景故事
  7. [跟进]_微软关闭MSN博客,腾讯第一时间抢占市场
  8. CorelDRAW教程大全集
  9. 【无标题】【光纤光缆小知识】多模光纤的分类及应用
  10. 论文阅读:Social Media and Fake News in the 2016 Election