欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java实现飞机大战游戏思路

程序员文章站 2022-04-07 15:48:30
...

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

简单思路

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

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

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

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

成品如下图

Java实现飞机大战游戏思路

下面是代码的应用说明:
这是一个窗体类
定义游戏窗体的一些细节,以及在主方法中运行其他类的方法。
/*
	 * 游戏窗体类
	 */
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();
		}
	}
}

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

这就是我这两天做的一个小东西了,跟着做了一遍发现复习了好多的基础知识,还是很不错的。
大家有啥疑问也可以留言~
相关标签: 小游戏