Java实现飞机大战游戏思路

这两天跟着视频的老师做了一遍飞机大战游戏,下面分享一下自己的思路,当作让自己复习一遍了哈哈哈~
——————————————————————————————————————————————————————————

简单思路

1 先得把游戏界面的窗体实现
2 实现窗体里面背景的内容
3 选择游戏是采用鼠标监听还是键盘监听
4 把战机图片调用方法画在背景上面
5 让键盘或者鼠标监听战机的轨迹
6 将子弹始发点坐标定在战机坐标前面
7 将敌机图片也画在背景上面
8 定义方法规定敌机的飞行速度和轨迹

9 定义方法判断子弹是否击中敌机及战机是否与敌机相撞

10 定义一个敌机被子弹击中则消失的方法
11 定义一个相撞后战机的血量减少,敌机消失的方法
12 当战机血量为0时游戏结束,点击鼠标重新开始
13 定义不同型号敌机的属性
14 添加游戏音效

以上步骤最难的就是第九步,需要好好理解坐标轴中两个图片的对应关系
——————————————————————————————————————————————————————————

成品如下图

在这里插入图片描述

下面是代码的应用说明:
这是一个窗体类
定义游戏窗体的一些细节,以及在主方法中运行其他类的方法。
/*
	 * 游戏窗体类
	 */
public class GameFrame extends JFrame {

	public static void main(String[] args) {
		GameFrame frame = new GameFrame();
		frame.setVisible(true);// 显示窗体
	
		GamePanel panel =new GamePanel(frame);//方便调用键盘监听器
		panel.action();//调用启动游戏的方法
		frame.add(panel);
		frame.setVisible(true);
		//调用背景音乐类中的方法
		String filepath = "D:\\javagame\\飞机大战\\img\\game_music.wav";
		musicStuff musicObject = new musicStuff();
		musicObject.playMusic(filepath);
	}
	//构造方法设置窗体
	public GameFrame() {
		setTitle("全民飞机大战");// 设置标题
		setSize(512, 768);//设置窗体大小
		setLocationRelativeTo(null);// 相对于屏幕左上角居中
		setResizable(false);// 设置不允许玩家拖动界面
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭游戏时把进程也关掉
	}
}

——————————————————————————————————————————————————————————

这是一个画布背景类,我的绝大多数方法都在这个类中定义。
public class GamePanel extends JPanel {
	BufferedImage bg = null;

	// new战机对象
	hero hero = new hero();
	// 建立敌机集合 不使用数组做容器是因为敌机数量未知,建立数组先要定义数组长度
	List<Ep> eps = new ArrayList<Ep>();
	// 建立子弹的弹药库,与敌机集合想法一样
	List<Fire> fs = new ArrayList<Fire>();

	// 定义分数
	int socre;
	boolean gameover;// 设置开关
	int power = 1;// 火力值

	/*
	 * 开始游戏的方法 建立一个线程
	 */
	public void action() {
		new Thread() {
			public void run() {
				while (true) {
					// if语句判断是否gameover
					if (!gameover) {
						epEnter();// 敌机进场
						epMove();

						// 发射子弹
						shoot();

						// 子弹移动
						fireMove();
						shootEp();
						hit();
					}
					try {
						Thread.sleep(50);
					} catch (InterruptedException e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
					}
					repaint();// 刷新界面
				}
			}

			/*
			 * 检测子弹是否打到敌机
			 */
			public void shootEp() {
				// 遍历所有子弹
				for (int i = 0; i < fs.size(); i++) {
					Fire f = fs.get(i);
					bang(f);
				}

			}

			/*
			 * 判断子弹是否击中敌机
			 */
			private void bang(Fire f) {
				for (int i = 0; i < eps.size(); i++) {
					Ep e = eps.get(i);
					if (e.shootBy(f)) {
						e.hp--;
						// 当敌机没hp,死亡
						if (e.hp <= 0) {
							if (e.type == 12) {
								power++;
								if (power > 3) {
									hero.hp++;
									power = 3;
								}
							}
							eps.remove(e);// 移除敌机
							fs.remove(f);// 移除子弹
						}

						// 增加分数
						socre += 10;
						if(socre%1000==0) {
							Random rd = new Random();
							int index = rd.nextInt(5) + 1;
							bg = ImageUtil.getImage("/img/bg" + index + ".jpg");
							repaint();// 刷新界面
							hero.hp++;
						}
					}
				}

			}
		}.start();
	}

	/**
	 * 检测敌机是否撞到了英雄机
	 */

	protected void hit() {
		// 遍历敌机
		for (int i = 0; i < eps.size(); i++) {
			// 获取一个敌机
			Ep e = eps.get(i);
			// 如果敌机被英雄机撞上了
			if (e.hitBy(hero)) {
				// 1.删除敌机
				eps.remove(e);
				// 2.英雄机的血量减少
				hero.hp--;
				// 英雄机的火力恢复为1
				power = 1;
				// 3.增加分数
				socre += 10;
				
				// 4.当英雄机的血量减少到0时,游戏结束
				if (hero.hp <= 0) {
					// 游戏结束
					gameover = true;
				}
			}
		}
	}

	public void fireMove() {
		for (int i = 0; i < fs.size(); i++) {
			Fire f = fs.get(i);
			f.move();

		}

	}

	// 发射子弹
	int fireindax = 0;// 记录执行的次数

	public void shoot() {
		fireindax++;
		if (fireindax >= 10) {
			if (power == 1) {
				// 创建子弹
				Fire fire3 = new Fire(hero.x + 45, hero.y - 20, 1);
				// 将子弹加入到弹药库
				fs.add(fire3);
			} else if (power == 2) {
				// 创建子弹
				Fire fire1 = new Fire(hero.x + 15, hero.y, 1);
				// 将子弹加入到弹药库
				fs.add(fire1);
				// 创建子弹
				Fire fire2 = new Fire(hero.x + 75, hero.y, 1);
				// 将子弹加入到弹药库
				fs.add(fire2);
			} else if (power == 3) {
				// 创建子弹
				Fire fire1 = new Fire(hero.x + 15, hero.y, 0);
				// 将子弹加入到弹药库
				fs.add(fire1);
				// 创建子弹
				Fire fire2 = new Fire(hero.x + 75, hero.y, 2);
				// 将子弹加入到弹药库
				fs.add(fire2);
				// 创建子弹
				Fire fire3 = new Fire(hero.x + 45, hero.y - 20, 1);
				// 将子弹加入到弹药库
				fs.add(fire3);
			}
			fireindax = 0;// 计算器
		}
	}

	public void epMove() {
		for (int i = 0; i < eps.size(); i++) {
			Ep e = eps.get(i);
			e.move();
		}

	}

	/*
	 * 敌机入场方法 该方法在死循环中一直调用
	 */

	int indax = 0;

	public void epEnter() {
		indax++;
		if (indax >= 20) {
			Ep e = new Ep();
			eps.add(e);// 把敌机不断加入到集合里面
			indax = 0;
		}
	}

	public GamePanel(GameFrame frame) {
		setBackground(Color.BLACK);
		bg = ImageUtil.getImage("/img/bg2.jpg");

		// 使用鼠标监听器
		// 1创建鼠标监听器
		MouseAdapter md = new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent e) {
				// 点击鼠标时,会执行的代码
				// 如果游戏结束了,点击屏幕时重新开始游戏
				if (gameover) {
					// 重新开始游戏
					// 需要做些什么事情?
					// 重新创建一个英雄机
					hero = new hero();
					// 重置游戏开关
					gameover = false;
					// 分数清0
					socre = 0;
					eps.clear();// 清空敌机集合
					fs.clear();// 清空子弹集合
					// 随机背景图
					Random rd = new Random();
					int index = rd.nextInt(5) + 1;
					bg = ImageUtil.getImage("/img/bg" + index + ".jpg");
					repaint();
				}
			}

			// 2确定鼠标监听事件
			@Override
			public void mouseMoved(MouseEvent e) {
				int mX = e.getX();
				int mY = e.getY();
				// 判断游戏是否结束,结束则不能移动
				if (!gameover) {
					hero.MovetoMouse(mX, mY);
				}
				repaint();// 刷新界面
			}

		};
		// 将适配器加到监听器中
		addMouseListener(md);
		addMouseMotionListener(md);

		// ------------------------------------------
		// 使用键盘监听器

//		KeyAdapter kd = new KeyAdapter() {
//			@Override
//			public void keyPressed(KeyEvent e) {
//				int KeyCode = e.getKeyCode();
//				if (KeyCode == KeyEvent.VK_UP || KeyCode == KeyEvent.VK_W) {
//					hero.MoveUp();
//					repaint();// 刷新界面
//				} else if (KeyCode == KeyEvent.VK_DOWN || KeyCode == KeyEvent.VK_S) {
//					hero.MoveDown();
//					repaint();// 刷新界面
//				} else if (KeyCode == KeyEvent.VK_LEFT || KeyCode == KeyEvent.VK_A) {
//					hero.MoveLeft();
//					repaint();// 刷新界面
//				} else if (KeyCode == KeyEvent.VK_RIGHT || KeyCode == KeyEvent.VK_D) {
//					hero.MoveRight();
//					repaint();// 刷新界面
//				}
//			}
//		};
//		frame.addKeyListener(kd);// 键盘适配器要加到窗体监听器

	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		// draw有先后顺序,先画会被后画覆盖
		g.drawImage(bg, 0, 0, null);
		g.drawImage(hero.img, hero.x, hero.y, hero.w, hero.h, null);

		// 遍历集合里面的所有敌机并显示出来
		for (int i = 0; i < eps.size(); i++) {
			Ep ep = eps.get(i);
			g.drawImage(ep.img, ep.x, ep.y, ep.w, ep.h, null);
		}

		// 遍历集合里面的所有子弹并显示出来
		for (int i = 0; i < fs.size(); i++) {
			Fire fire = fs.get(i);
			g.drawImage(fire.img, fire.x, fire.y, fire.w, fire.h, null);
		}

		// 画分数
		g.setColor(Color.white);
		g.setFont(new Font("楷体", Font.BOLD, 20));
		g.drawString("分数: " + socre, 10, 30);

		// 画英雄机的血量
		for (int i = 0; i < hero.hp; i++) {
			g.drawImage(hero.img, 470, 5+ i * 30, 30, 30, null);
		}

		// 当游戏结束时,显示Gameover
		if (gameover) {
			g.setColor(Color.red);
			g.setFont(new Font("楷体", Font.BOLD, 35));
			g.drawString("GAMEOVER!", 35, 300);
			g.setColor(Color.green);
			g.setFont(new Font("楷体", Font.BOLD, 20));
			g.drawString("提醒你点击屏幕重新开始游戏", 40, 350);
		}
	}
}

由于这个画布类我写得有点乱,我会单独拿写一篇解释这个类中的方法 链接

——————————————————————————————————————————————————————————

接下来时我定义的一个属性类,由于战机,敌机,子弹这三个飞行物有着共同的的属性,所以我把他们的属性抽了出来做了一个属性类供他们继承。
/*
 * 提高代码的复用性
 * 定义飞行物的属性
 */
public class FlyObject {
	BufferedImage img;
	// 定义飞行物的横纵坐标及宽高
	int x, y, w, h;
}

——————————————————————————————————————————————————————————

接着是一个工具类,工具类中是获取图片url的方法,当其他类需要实现获取图片url地址的时,直接调用本类方法即可。

需要注意的一个点,工具类用静态方法定义较为方便。
1:静态方法可以通过类名直接调用,而不需要实例化类的对象。
2:公用的,所有对象共用。

/*
 * 处理图片的工具类
 */
public class ImageUtil {
	public static BufferedImage getImage(String path) {
		try {
			BufferedImage img = ImageIO.read(ImageUtil.class.getResource(path));
			return img;
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return null;
	}
}

——————————————————————————————————————————————————————————

下面是一个战机类,定义战机的移动方法
public class hero extends FlyObject{  //继承属性类属性
	int hp;//英雄机的血量

	public hero() {
		img = ImageUtil.getImage("/img/hero.png");
		x = 200;
		y = 500;
		w = img.getWidth();
		h = img.getHeight();
		hp = 3;//初始血量
	}
	/**
	 * 英雄机移动到鼠标位置上的方法
	 * @param mX  鼠标的横坐标
	 * @param mY  鼠标的纵坐标
	 */
	public void MovetoMouse(int mX, int mY) {
		x = mX - w / 2;
		y = mY - h / 2;

	}
    //当用键盘监听时,判断战机是否移动出边界,规定让其不会飞出窗体外
//	public void MoveUp() {
//		if (y == 0) {
//			y = 0;
//		} else {
//			y-=10;
//		}
//	}
//
//	public void MoveDown() {
//		if (y == 650) {
//			y=650;
//		} else {
//			y += 10;
//
//		}
//	}
//
//	public void MoveLeft() {
//		if (x == -50) {
//			x=-50;
//		} else {
//			x -= 10;
//
//		}
//	}
//
//	public void MoveRight() {
//		if (x == 440) {
//			x=440;
//		} else {
//			x += 10;
//
//		}
//	}
	
	/**
	 * 向上移动的方法
	 */
	public void moveUp(){
		y -= 10;
	}
	
	/**
	 * 向下移动的方法
	 */
	public void moveDown(){
		y += 10;
	}
	
	/**
	 * 向左移动的方法
	 */
	public void moveLeft(){
		x -= 10;
	}
	
	/**
	 * 向右移动的方法
	 */
	public void moveRight(){
		x += 10;
	}
}

——————————————————————————————————————————————————————————

下面是一个子弹类,与战机类作用相似
public class Fire extends FlyObject {  //继承属性类属性
	int dir;//定义子弹的飞行轨迹   0左上角飞      1中间飞        2右边飞
	/**
	 * 构造方法:初始化子弹
	 * hx :英雄机的横坐标
	 * hy :英雄机的纵坐标
	 * dir:子弹移动的方向
	 */
	public Fire(int hx,int hy,int dir) {
		 img=ImageUtil.getImage("/img/fire.png");
		 //确定子弹大小
		 w=img.getWidth()/4;
		 h=img.getHeight()/4;
		 //确定子弹位置(在英雄机上面)
		 x=hx;
		 y=hy;
		 this.dir=dir;
	}
   /**
	 * 子弹移动的方法
	 */
	public void move() {
		if(dir==0) {
			x-=1;
			y-=10;
		}else if(dir==1) {
			y-=10;
		}else if(dir==2) {
			x+=1;
			y-=10;
		}
	}
}

——————————————————————————————————————————————————————————

下面是敌机类,我把判断敌机是否被击中的方法也写在了这个类中,大家画坐标轴好好理解一下如何判断是否碰撞。
public class Ep extends FlyObject {  //继承属性类属性
	int sp;// 定义速度
	int hp;// 敌机的血量
	int type;//定义敌机类型
	
	/**
	 * 构造器:给敌机定型
	 */
	public Ep() {
		Random rd = new Random();
		// 生成一个随机数,用来选取图片
		int index = rd.nextInt(15) + 1;
		type =index;
		// 三目运算符
		String path = "/img/ep" + (index < 10 ? "0" : "") + index + ".png";
		System.out.println(path);
		img = ImageUtil.getImage(path);
		w = img.getWidth();
		h = img.getHeight();
		x = rd.nextInt(512 - w);
		y = -h;
		sp = 17 - index;// 设置速度
		// 设置血量
		hp = index;
	}
	/**
	 * 敌机移动的方法
	 */
	public void move() {
		if(type==5) {  //类型5的敌机往左下飞
			y += sp;
			x -=5;
		}else if(type==6) { //类型6的敌机往右下飞
			y += sp;
			x+=5;
		}else if(type==15) {  //加快类型15敌机的飞行速度
			y += 14;
		}
		y += sp;

	}

	// 判断敌机是否被击中
	public boolean shootBy(Fire f) {
		boolean hit = x <= f.x + f.w && x >= f.x - f.w && y <= f.y + f.h && y >= f.y - h;
		return hit;
	}
	//判断敌机与战机是否相撞
	public boolean hitBy(hero f) {
		boolean hit = x <= f.x + f.w && x >= f.x - w && y <= f.y + f.h && y >= f.y - h;
		return hit;
	}
}

——————————————————————————————————————————————————————————

下面是最后一个音乐类,负责循环播放背景音乐
public class musicStuff {
	void playMusic(String musicLocation) {
		try {
			File musicPath = new File(musicLocation);

			if (musicPath.exists()) {
				AudioInputStream audioInput = AudioSystem.getAudioInputStream(musicPath);
				Clip clip = AudioSystem.getClip();
				clip.open(audioInput);
				clip.start();
				clip.loop(Clip.LOOP_CONTINUOUSLY);
			} else {

			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

——————————————————————————————————————————————————————————

这就是我这两天做的一个小东西了,跟着做了一遍发现复习了好多的基础知识,还是很不错的。
大家有啥疑问也可以留言~
发布了11 篇原创文章 · 获赞 0 · 访问量 175

猜你喜欢

转载自blog.csdn.net/weixin_44824500/article/details/104381658