飞机大战部分算法,思维解析,附有源代码. - Go语言中文社区

飞机大战部分算法,思维解析,附有源代码.


##源代码:http://download.csdn.net/download/u012234452/9583953
##QQ:1151315936


##完成后的基本样式图:
这里写图片描述
##结构图:
这里写图片描述
###父类:FlyingObject,子类:Airplane(小飞机),BigPlane(大飞机),Bullet子弹,Bee(蜜蜂),Hero(英雄机),Sky(天空).世界:World.接口:Award(奖励),Enemy(敌人)
##根据逻辑分析把大飞机,小飞机,蜜蜂,子弹,英雄机的公共属性和方法泛化到父类(FlyingObject)中去.
这里写图片描述
##让World类继承JPanel,构建出窗口,重写paint方法
这里写图片描述
#FlyingObject.java
##1.默认构造器,用于初始化

	public FlyingObject() {
		life = 1;
		state = ACTIVE;
	}

##2.有参构造器

	public FlyingObject(double width,double heigth){
		this();//调用无参数的构造器,必须写在第一行.
		this.x = (int)(Math.random()*(480-width));
		this.y = -heigth;
		this.width = width;
		this.heigth = heigth;
		step = Math.random()*3+0.8;//初始化step为[0.8,3.8)之间的数
	}

##3.重写了toString()方法可以用来测试用

	public String toString() {
		return x+","+y+","+width+","+heigth+","+image;
	}

##4.父类中重写了paint(Graphics g)方法,方便了子类的使用,用于绘图

	public void paint(Graphics g) {
		g.drawImage(image, (int)x, (int)y, null);//绘制图片
	}

##5.父类中重构了move方法,实现了各种飞行物的移动和播放销毁动画功能

	public void move(){
		if(state == ACTIVE){
			y += step;
			return ;
		}
		if(state == DEAD){
			//从子类对象中获取下一张照片
			BufferedImage img = nextImage();
			if(img == null){
				state = REMOVE;//没有照片则回收
			}else{
				image = img;//否则把子类的图片传给image
			}
			//越界则销毁
			if(y>=825){
				state = REMOVE;
			}
		}
	}

##6.子类中必须有的方法,返回下一个要播放的照片引用,如果返回null表示没有可播放的照片了.(虽然只有一句,但是非常重要)

	protected abstract BufferedImage nextImage();

##7.飞行物被打了一下,生命-1,当生命等于0的时候为死亡状态
这里写图片描述

	public void hit(){
		if(life>0){
			life--;
		}
		if(life==0){
			state = DEAD;
		}
	}

##8.经典算法:碰撞检测的方法,用于检测物体的位置是否在碰撞的范围内
这里写图片描述

	public boolean duang(FlyingObject obj){
		//this(x,y,w,h)
		//obj(x,y,w,h)
		double x1 = this.x - obj.width;
		double x2 = this.x + this.width;
		double y1 = this.y - obj.width;
		double y2 = this.y + this.heigth;
		return x1<obj.x&&obj.x<x2&&y1<obj.y&&obj.y<y2;
	}

##9.状态检查方法,用于返回状态

	public boolean isDead(){
		return state == DEAD;
	}
	/** 检查飞行物是否活动的 */
	public boolean isActive(){
		return state == ACTIVE;
	}
	/** 检查飞行是否可以被删除*/
	public boolean canRemove(){
		return state == REMOVE;
	}
	/** 飞行物添加"去死"方法*/
	public void goDead(){
		if(isActive()){
			state = DEAD;
		}
	}

#Airplane.java
##1.获取的小飞机的资源

	private static BufferedImage[] imgs;// 定义一个唯一的图片素材
	// 静态代码块用于获取图片资源
	static {
		imgs = new BufferedImage[5];
		try {
			for (int i = 0; i < imgs.length; i++) {
				String png = "cn/feike/shoot/airplane" + i + ".png";//获取图片路径
				imgs[i] = ImageIO.read(Airplane.class.getClassLoader().getResourceAsStream(png));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

##2.初始化小飞机,同时初始化照片

	public Airplane() {
		super(49, 36);// 初始化小飞机
		this.image = imgs[0];
	}

##3.继承父类的抽象方法,播放下一张图片,用于销毁

	protected BufferedImage nextImage() {
		index++;
		if(index >= imgs.length){
			return null;//如果下标大于等于数组长度,无图可播,返回null.
		}
		return imgs[index];
	}

#BigPlane.java
##1.初始化大飞机,让生命等于3(即子弹打三下才打死)

	public BigPlane() {
		super(69,99);//初始化大飞机
		this.image = imgs[0];
		life = 3;
	}

#BigPlaneAward.java
##1.继承了大飞机,重写了paint方法,在飞机外面加上一个框框

	public void paint(Graphics g){
		super.paint(g);//重载父类的paint方法
		//在大飞机外面加上一个框框
		g.drawRect((int)x, (int)y, (int)width, (int)heigth);
	}

##2.固定奖励,打掉返回双倍奖励

	public int getAward() {
		return DOUBLE_FIRE;//获取双倍奖励
	}

#Hero.java
##1.初始化英雄飞机,让其在地图的下方出现

	public Hero() {
		this.width = 97;
		this.heigth = 124;
		this.x = (480-width)/2;
		this.y =500;
		this.image = imgs[0];
	}

##2.重写了move()的无参方法,用于显示动态效果和英雄飞机的销毁效果
这里写图片描述

	public void move() {
		if(isActive()){
			n++;
			int i = n%2;//用这个i的值来控制切换的图片(i=n%2)的切换速度快于(i=n/2%2)
			this.image = imgs[i];//来回切换图片,显示出动画的效果.
		}
		if(isDead()){
			//从子类获取下一张照片
			BufferedImage img = nextImage();
			if(img==null){
				state = REMOVE;
			}else{
				image = img;
			}
		}
	}

##3.再次重写了move()有参方法,用于英雄飞机随着鼠标移动

	public void move(int x,int y){
		this.x = x-width/2;
		this.y = y-heigth/2;
	}

##4.射击方法,实现是单枪射击还是双枪射击
这里写图片描述

	public Bullet[] shoot(int type){
		int x = (int)(this.x+width/2-8/2);//子弹的出场的x位置
		int y = (int)(this.y-30);//子弹的出场的y位置
		if(type == 1){//单枪发射
			return new Bullet[]{
					new Bullet(x,y)//创建一颗子弹
			};
		}
		if(type == 2){
			return new Bullet[]{
					new Bullet(x-30, y),//第一个子弹原出场的位子向左移动30
					new Bullet(x+30,y)//第二个子弹原出场的位子向右移动30
			};
		}
		return new Bullet[0];
	}

#Bullet.java
##1.初始化子弹,因为子弹跟着英雄飞机一起走,所以需要传入两个参数.

	public Bullet(int x,int y) {
		this.x = x;
		this.y = y;
		width = 8;
		heigth = 14;
		this.image = img;
	}

##2.重写子弹move方法,子弹是从下往上移动

	public void move() {
		if(state == ACTIVE){
			y -= 8;
			if(y<=-heigth){
				state = REMOVE;
			}
		}
	}

##3.重写了nextImage()和hit()方法,子弹撞击后直接销毁

	protected BufferedImage nextImage() {
		return null;
	}
	public void hit(){
		state = REMOVE;//子弹撞击后销毁
	}

#Bee.java
##1.初始化蜜蜂,蜜蜂的出场方向是随机的

	public Bee() {
		super(60, 50);
		this.image = imgs[0];
		direction = Math.random() > 0.5 ? -2 : 2;// 初始化蜜蜂的移动方向,各占50%的概率.
	}

##2.重写了move()方法,蜜蜂的移动方向实现斜着的
这里写图片描述

	public void move() {
		super.move();// y += step;即把y坐标交个父类来处理
		if (state == ACTIVE) {
			x += direction;
			// 如果蜜蜂移动到右边边界,则反方向移动
			if (x >= 480 - width) {
				direction = -2;
			}
			// 如果蜜蜂移动到左边边界,则反方向移动
			if (x < 0) {
				direction = 2;
			}
		}
		// 蜜蜂的动漫效果
		n++;
		int i = n % 2;// 用来控制蜜蜂的扇翅膀的速度
		this.image = imgs[i];// 来回切换图片,显示出动画的效果.
	}

#Sky.java
##1.初始化天空,改变了天空的移动速度,和第二种图片的高度

	public Sky() {
		this.x = 0;
		this.y = 0;
		this.width=480;
		this.heigth=825;
		this.image = img;
		this.step = 1;//初始化图片移动的速度
		y1 = -heigth;//初始化第二张图片的位置
	}

##2.重写了move()方法,两张图片同时移动,一张图片移除下边界就重新定义高度再移动.
这里写图片描述

	public void move() {
		y++;
		y1++;
		if(y>=heigth){
			y = -heigth;//如果第一张图移动出下边界位置,则返回顶部
		}
		if(y1>=heigth){
			y1 = -heigth;//如果第二张图移动出下边界位置,则返回顶部
		}
	}

##3.重写了paint,用于绘制两张背景图片的位置.

	public void paint(Graphics g) {
		g.drawImage(image, (int)x, (int)y, null);
		g.drawImage(image, (int)x, (int)y1, null);
	}

##4.天空没有销毁图片,所以返回null

	protected BufferedImage nextImage() {
		return null;
	}

#Award.java
##1.奖励接口,用于获取奖励

public interface Award {
	int LIFE = 0;
	int FIRE = 1;
	int DOUBLE_FIRE = 2;
	
	int getAward();
}

#Enemy.java
##1.敌人接口,用于获取得分

public interface Enemy {
	int getScore();
}

#World.java
##1.初始化三张开始,暂停,结束照片

	private static BufferedImage pause;//暂停
	private static BufferedImage ready;//开始
	private static BufferedImage gameOver;//结束
	
	static{
		try {
			String png = "cn/feike/shoot/start.png";
			ready = ImageIO.read(World.class.getClassLoader().getResourceAsStream(png));
			png = "cn/feike/shoot/pause.png";
			pause = ImageIO.read(World.class.getClassLoader().getResourceAsStream(png));
			png = "cn/feike/shoot/gameover.png";
			gameOver = ImageIO.read(World.class.getClassLoader().getResourceAsStream(png));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

##2.初始化世界中的物体

	public World() {
		flyingObjects = new FlyingObject[]{};//初始化飞行物数组
		bullets = new Bullet[]{};//初始化子弹数组
		hero = new Hero();//初始化英雄飞机
		sky = new Sky();//初始化一片天空
		nextTime = System.currentTimeMillis()+1000;//初始化下一秒的系统时间
	}

##3.做每隔一秒出场一个飞行物
这里写图片描述

	//nextOne方法被定时(1/24秒)调用一次
	public void nextOne(){
		//方法调用一次,获取一下当前时间.
		long now = System.currentTimeMillis();//获取当前系统时间的毫秒数
		if(now>=nextTime){
			nextTime = now + 1000;//控制飞行物的出场时间间隔,即每隔一秒出场一个飞行物
			FlyingObject obj = randomOne();//调用randomOne方法,随机生成一个飞行物
			flyingObjects = Arrays.copyOf(flyingObjects, flyingObjects.length+1);//数组扩容
			flyingObjects[flyingObjects.length-1]=obj;//随机生成的飞行物放到数组中去
		}
	}

##4.随机出场一个飞行物,出场的概率不同

	private static FlyingObject randomOne(){
		int n = (int)(Math.random()*10);//[0,10)
		//这个上面这个随机数的大小也可以调整不同飞行物的出场概率
		switch(n){
		case 0 : return new Bee();
		case 1 : 
		case 2 : return new BigPlane();
		case 3 : 
		case 4 : return new BigPlaneAward();
		default : return new Airplane();//概率最高
		}
	}

##5.重写了paint(Graphics g)方法,绘制出天空的物体
这里写图片描述
这里写图片描述

	public void paint(Graphics g) {
		sky.paint(g);//画出天空,即背景图片
		hero.paint(g);//画出英雄飞机
		for(FlyingObject fly : flyingObjects){
			fly.paint(g);//绘制每个飞行物
		}
		for(Bullet bullet : bullets){
			bullet.paint(g);//绘制每个子弹
		}
		//添加分数,生命,子弹类型的显示
		g.drawString("SCORE:"+score, 20, 30);
		g.drawString("LIFE:"+life, 20, 50);
		g.drawString("FIRE:"+fireType, 20, 70);
		
		switch(state){
		case PAUSE : g.drawImage(pause, 0, 0, null);
		case READY : g.drawImage(ready, 0, 0, null);
		case GAME_OVER : g.drawImage(gameOver, 0, 0, null);
		}
	}

##6.添加一个定时器和启动定时器的方法.每1/24秒一个动作
这里写图片描述

	public void action(){
		timer = new Timer();//初始化一个定时器
		timer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				if(state == RUNNING){
					nextOne();//每(1/24秒)生成一个飞行物
					move();//各种物体的移动
					shoot();//射击封装到shoot方法
					duangDuang();//碰撞方法
					removeObjects();//回收方法
					heroLifeCircle();//英雄的声明周期
				}
				repaint();//重新绘制JPanel
			}
		}, 0,1000/24);//从0开始,间隔为(1/24)秒
		//创建鼠标的监听即开启鼠标的监听
		MouseAdapter l = new MouseAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {
				if(state == RUNNING){
					int x = e.getX();
					int y = e.getY();
					hero.move(x,y);//传递移动的x,y坐标.
				}
			}
			@Override
			public void mouseExited(MouseEvent e) {
				if(state == RUNNING){
					state = PAUSE;
				}
			}
			@Override
			public void mouseEntered(MouseEvent e) {
				if(state == PAUSE){
					state = RUNNING;
				}
			}
			@Override
			public void mouseClicked(MouseEvent e) {
				if(state == READY){
					state = RUNNING;
				}
				if(state == GAME_OVER){//游戏结束所有参数重置。
					score = 0;
					life = 3;
					fireType = 1;
					hero = new Hero();
					bullets = new Bullet[0];
					flyingObjects = new FlyingObject[0];
					state = READY;
				}
			}
		};
		//将监听器加入到当前的面板中
		addMouseListener(l);
		addMouseMotionListener(l);
	}

##7.英雄飞机的生命周期

	public void heroLifeCircle(){
		//检查飞行物和英雄的碰撞
		if(hero.isActive()){
			for(FlyingObject plane : flyingObjects){
				if(plane.isActive()&&plane.duang(hero)){
					plane.goDead();
					hero.goDead();
				}
			}
		}
		if(hero.canRemove()){
			if(life>0){
				life--;
				hero = new Hero();//新生一个英雄
				//清场,死了的瞬间,再碰撞英雄不死,碰撞物死.
				for(FlyingObject plane : flyingObjects){
					if(plane.isActive()&&plane.duang(hero)){
						plane.goDead();
					}
				}
			}else{
				//游戏结束
				state = GAME_OVER;
			}
		}
	}

##8.删除掉没有用的子弹和飞机
这里写图片描述

	public void removeObjects(){
		//删除掉废弃的子弹
		Bullet[] ary = {};//初始化一个子弹数组
		for(Bullet b : bullets){
			if(b.canRemove()){
				continue;//忽略掉要删除的子弹
			}
			ary = Arrays.copyOf(ary, ary.length+1);//数组扩容
			ary[ary.length-1] = b;//把没有删掉的子弹重新添加到数组中去
		}
		bullets = ary;//bullets变量引用到ary数组上,抛弃原来的引用
		//删除掉废弃的飞机
		FlyingObject[] arr = {};//初始化一个飞行物数组
		for(FlyingObject obj : flyingObjects){
			if(obj.canRemove()){
				continue;//忽略掉废弃的飞行物
			}
			arr = Arrays.copyOf(arr, arr.length+1);//数组扩容
			arr[arr.length-1] = obj;////把没有删掉的飞行物添加到数组中去
		}
		flyingObjects = arr;
	}

##9.在world里面添加碰撞检测方法
这里写图片描述

	public void duangDuang(){
		for(FlyingObject plane : flyingObjects){
			if(plane.isActive()){
				if(shootByBullet(plane)){
					plane.hit();
				if(plane.isDead()){
					//计分,获取奖励
					if(plane instanceof Enemy){
						Enemy enemy = (Enemy)plane;
						int s = enemy.getScore();
						score += s;
					}
					if(plane instanceof Award){
						Award award =(Award)plane;
						int awd = award.getAward();
						if(awd == Award.LIFE){
							life++;
						}else if(awd == Award.FIRE){
							fireType = 1;
						}else if(awd == Award.DOUBLE_FIRE){
							fireType = 2;
						}
					}
				}
				}
			}
			
		}
	}

##10.检查每个子弹是否和飞行物碰撞

	public boolean shootByBullet(FlyingObject plane){
		for(Bullet bullet : bullets){
			if(bullet.duang(plane)){
				bullet.hit();
				return true;
			}
		}
		return false;
	}

##11.射击控制方法被定时器定时调用,控制英雄飞机射击子弹的速度

	public void shoot(){
		//方法调用一次,获取一下当前时间.
		long now = System.currentTimeMillis();//获取当前系统时间的毫秒数
		if(now>nextShootTime){
			nextShootTime=now + 500;//半秒发射一发
			Bullet[] arr = hero.shoot(fireType);//子弹射击类型
			bullets = Arrays.copyOf(bullets, bullets.length+arr.length);
			System.arraycopy(arr, 0, bullets, bullets.length-arr.length, arr.length);
		}
	}

##12.各种物体的移动

	private void move() {
		//每个飞行物移动一下,重新绘制JPanel方法
		for(FlyingObject fly : flyingObjects){
			fly.move();
		}
		//每个子弹的移动
		for(Bullet bullet : bullets){
			bullet.move();
		}
		sky.move();//天空移动
		hero.move();//英雄飞机移动
	}

##13.main()方法,设置窗口大小并显示

	public static void main(String[] args) {
		World world = new World();//创建一个world面板
		JFrame frame = new JFrame();//创建一个窗口
		frame.setSize(400, 680);//窗口大小
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口即关闭程序
		frame.setLocationRelativeTo(null);//让窗口居中
		frame.add(world);//把world面板加到JFrame窗口里去
		frame.setVisible(true);//窗口可见
		
		world.action();//启动定时器
	}
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u012234452/article/details/52002887
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-24 22:14:03
  • 阅读 ( 1109 )
  • 分类:算法

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢