飞机大战 订阅
飞机大战是一款由腾讯公司微信团队开发的射击操作类游戏,搭载于微信平台,操作简单,玩家众多。游戏整体环境主要是围绕太空为主,玩家在游戏中要做的就是驾驶着最新战机向敌人的总部发起冲击。 展开全文
飞机大战是一款由腾讯公司微信团队开发的射击操作类游戏,搭载于微信平台,操作简单,玩家众多。游戏整体环境主要是围绕太空为主,玩家在游戏中要做的就是驾驶着最新战机向敌人的总部发起冲击。
信息
原版名称
飞机大战
地    区
中国
开发商
腾讯
游戏类型
射击
发行商
腾讯
中文名
飞机大战
游戏平台
微信
飞机大战基本信息
游戏名称:飞机大战游戏类型:敏捷小游戏游戏大小:2680K开发语言:HTML5游戏开发:腾讯游戏平台:微信
收起全文
精华内容
下载资源
问答
  • 飞机大战

    万次阅读 多人点赞 2019-10-16 13:31:15
    实现一个简单的飞机大战程序,当消灭掉一个小飞机的时候的5分,当消灭掉一个小蜜蜂的时候有可能火力值增加24也有可能生命值增加1,如果打飞机也就是英雄机和敌人(蜜蜂+小飞机)碰撞之后,英雄机的火力值清零,生命...

    项目需求:

            实现一个简单的飞机大战程序,当消灭掉一个小飞机的时候的5分,当消灭掉一个小蜜蜂的时候有可能火力值增加24也有可能生命值增加1,如果打飞机也就是英雄机和敌人(蜜蜂+小飞机)碰撞之后,英雄机的火力值清零,生命值减去1。当英雄机的生命值为0的时候游戏结束。

    具体思路:

           1.首先进行类的设计(明确本项目中有哪些对象,对象的属性和行为),为了提高程序的可扩张性,本项目中总共设计了8个(类和接口)分别为:飞机类(Airplane),奖励的接口(Award),蜜蜂类(Bee),子弹类(Bullet),敌人的接口用来获取得分(Enemy),飞行物类(FlyingObject),英雄机类(Hero),主程序类(ShootGame)。

           2.明确不同对象属性和行为:其中蜜蜂,敌机,英雄机,子弹公共属性为:图片,图片的高度,图片的宽度,位置坐标x和y。

    除此之外英雄机还有得分,生命值,火力值等属性。

           3.画窗口,和对象,调动java中的paint(Graphics g)方法将对象画在窗口中。

           4.为了让对象动起来,需要设置一个定时器。

           5.子弹和飞行物的碰撞(蜜蜂+小飞机)。

           6.英雄机和飞行物的碰撞。

           7.画状态(状态分为:启动状态,运行状态,暂停状态,结束状态),得分,火力值,生命值。

    具体的代码实现:

    1.小飞机类:

    package com.feijidazhan.shaodong;
    //敌机类
    import java.util.Random;
    
    public class Airplane extends FlyingObject implements Ememy{
    	 private int score=5;
    	 private int xSteep=1;
    	 private int ySteep=2;
         public  Airplane(){
        	  image=ShootGame.airplane;//敌机的图片
        	  height=image.getHeight();//图片的高度
        	  width=image.getWidth();//图片的宽度
        	  Random rand=new Random();//定义一个随机数对象
        	  x=rand.nextInt(ShootGame.WIDTH-this.width);//敌机的横坐标x的范围
        	  y=-this.height;//敌机纵坐标y的范围
         }
         //敌机的走步
    	  public void step() {
    		  y+=ySteep;
    
           }
    	  //敌机快速走步
    	  public void ShiftStep() {
    		  y+=ySteep+1;
    		  //让英雄机碰到左边的时候弹回来
    //		  x-=xSteep;
    //		  if(x>ShootGame.WIDTH-this.width) {
    //			  xSteep=1;
    //		  }
    //		  if(x<0) {
    //			  xSteep=-1;
    //		  }
    	  }
    	  //打掉一个敌机获得的分数
    	  public int getScore() {
    		  return score;
    	  }
    	  //重写outOfBounds方法//true表示越界.false表示没有越界
    	  public   boolean outOfBounds() {
    		  return this.y>ShootGame.HEIGHT;//当敌机的高大于窗口的高的时候表示越界
    	  }
    }
    

    2.奖励的借口:

    package com.feijidazhan.shaodong;
    //获取奖励的类型,其中0表示增加火力,1表示增加生命
    public interface Award {
    	public   int DOUBLE_FIRE=0;//表示刚开始的火力值是0
    	public   int LIFE=1;
    	public int getType();//表示获取奖励的类型
    	
    
    }
    

    3.蜜蜂类:

    package com.feijidazhan.shaodong;
    //蜜蜂类
    import java.util.Random;
    
    public class Bee extends FlyingObject implements Award{
    	private int xSpeed=1;
    	private int ySpeed=2;
    	public int awardType;
    	public Bee() {
    		image=ShootGame.bee;//蜜蜂的图片
    		height=image.getHeight();//蜜蜂的高度
    		width=image.getWidth();//蜜蜂的宽度
    		Random rand=new Random();
    		x=rand.nextInt(ShootGame.WIDTH-image.getWidth());
    		y=-image.getHeight();
    		awardType=rand.nextInt(2);
    		
    	}
    	//蜜蜂的走步
    	public void step() {
    		x+=xSpeed;
    		y+=ySpeed;
    		if(x>=ShootGame.WIDTH-this.width) {
    			//当蜜蜂的坐标大于窗口的宽度减去蜜蜂的宽度的时候蜜蜂应该往左边移动
    			xSpeed=-1;
    		}
    		if(x<0) {
    			xSpeed=1;
    		}
    		
    	}
    	//蜜蜂的快速走步
    	public void ShiftStep() {
    		x+=xSpeed;
    		y+=ySpeed;
    		if(x>=ShootGame.WIDTH-this.width) {
    			//当蜜蜂的坐标大于窗口的宽度减去蜜蜂的宽度的时候蜜蜂应该往左边移动
    			xSpeed=-2;
    		}
    		if(x<0) {
    			xSpeed=1;
    		}
    	}
    	//蜜蜂的奖励
    	public int getType() {
    		return awardType;
    	}
    	//判断蜜蜂是否越界重写outOfBounds方法
    	public   boolean outOfBounds() {
    		return this.y>ShootGame.HEIGHT;//当蜜蜂的坐标大于窗口的高的时候表示越界
    	}
         
    }
    

    4.子弹类:

    package com.feijidazhan.shaodong;
    //子弹类
    public class Bullet extends FlyingObject {
    	private int ySpeed=2;
    	public Bullet(int x,int y) {
    		image=ShootGame.bullet;
    		width=image.getWidth();//获取子弹的图片的宽度
    		height=image.getHeight();//获取子弹图片的高度
    		this.x=x;//由于子弹的坐标随着英雄机的坐标的变化而变化
    		this.y=y;
    		
    	}
    	//子弹的走步
    	public void step() {
    		y-=ySpeed;
    	}
    	//重写outOfBounds方法
    	public   boolean outOfBounds() {
    		return this.y+image.getHeight()<0;
    	}
    	//
    	public void ShiftStep() {
    		
    	}
    
    }
    

    5.敌人的接口(主要获取得分):

    package com.feijidazhan.shaodong;
    //该接口为获取得分
    public interface Ememy {
    	public int  getScore();//表示击败一个敌机所得到的到分数
    
    }
    

    6.飞行物类(父类):

    package com.feijidazhan.shaodong;
    //表示飞行物类
    
    import java.awt.image.BufferedImage;
    public  abstract class FlyingObject {
    	protected int x;//表示x坐标
    	protected int y;//表示y坐标
    	protected int height;//表示相应图片的高度
    	protected int width;//表示相应图片的宽度
    	protected BufferedImage image;//表示相应的图片
    	public abstract void step();//飞行物的走步
    	public  abstract boolean outOfBounds();//飞行物的越界
    	//子弹和敌人的碰撞
    	public boolean shootBy(Bullet bullet) {
    			int x1=this.x;
    			int y1=this.y;
    			int x2=x+this.width;
    			int y2=y+this.height;
    			int x=bullet.x;
    			int y=bullet.y;
    			return x>x1&&x<x2
    					&&
    					y>y1&&y<y2;
    	}
    	public abstract  void ShiftStep();
          
    }
    

    7.英雄机类:

    package com.feijidazhan.shaodong;
    //英雄机
    import java.awt.image.BufferedImage;
    
    
    public class Hero extends FlyingObject{
    	private int Fire;//英雄机的火力值
    	private int Life;//英雄机的生命值
    	private BufferedImage []images;//
    	private int index;
    	
    	public Hero() {
    		image=ShootGame.hero0;
    		width=image.getWidth();//英雄机的宽度
    		height=image.getHeight();//英雄机的高度
    		x=150;
    		y=300;
    		Fire=0;
    		Life=3;
    		images=new BufferedImage[] {ShootGame.hero0,ShootGame.hero1};
    		index=0;
    	}
    	//英雄机的走步即使英雄机图片的切换
    	public  void step() {
    		index++;
    		int a=index/10;
    		int b=a%images.length;
    		image=images[b];
    	}
    	//重写outOfBounds,用来判断是否越界
    	public   boolean outOfBounds() {
    		return false;
    	}
    	//产生子弹对象
    	public Bullet[] shoot(){
    		int xStep=(this.width)/4;//子弹的x坐标
    		int yStep=20;//子弹的纵坐标
    		if(Fire>0) {
    			Bullet []bs=new Bullet[2];
    			bs[0]= new Bullet(this.x+1*xStep,this.y-yStep);
    			bs[1]=new Bullet(this.x+3*xStep,this.y-yStep);
    			Fire-=2;
    			return bs;
    
    		}else {
    			Bullet bs[]=new Bullet[1];
    			bs[0]=new Bullet(this.x+2*xStep,this.y-yStep);
    			return bs;
    		}
    	}
    	//英雄机的移动
    	public void moveTo(int x,int y) {
    		this.x=x-this.width/2;
    		this.y=y-this.height/2;
    
    	}
    	//增加英雄机的火力值
    	public void addFire() {
    		 Fire+=24;
    	}
    	//英雄机火力值清零
    	public void subFire() {
    		 Fire=0;//将英雄机的火力值置0
    	}
    	//增加英雄机的生命值
    	public void  addLife() {
    		 Life++;
    	}
    	//英雄机与飞行物的碰撞
    	public boolean shootFy(FlyingObject f) {
    		int x1=f.x-this.width/2;//边界坐标
    		int y1=f.y-this.height/2;//边界坐标
    		int x2=f.x+this.width/2+f.width;
    		int y2=f.y+f.height+this.height/2;
    		int x=this.x+this.width/2;//英雄机的中心x坐标
    		int y=this.y+this.height/2;//英雄机的中心y坐标		
    		return x>x1&&x<x2
    				&&
    				y>y1&&y<y2;
    	}
    	//英雄机减命
    	public void subLife() {
    		  Life--;
    	}
    	//返回生命值
    	public int Life() {
    		return Life;
    	}
    	//返回英雄机的火力值
    	public int Fire() {
    		return Fire;
    	}
    	public void ShiftStep() {
    		
    	}
    
    
    }
    

    8.主程序类:

    package com.feijidazhan.shaodong;
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    //测试类
    import java.awt.image.BufferedImage;
    import java.util.Arrays;
    import java.util.Random;
    import java.util.Timer;
    import java.util.TimerTask;
    
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class ShootGame extends JPanel{
    	public static final int WIDTH=400;
    	public static final int HEIGHT=654;
    	public static BufferedImage background;//定义背景图片
    	public static BufferedImage airplane;
    	public static BufferedImage bee;
    	public static BufferedImage bullet;
    	public static BufferedImage gameover;
    	public static BufferedImage hero0;
    	public static BufferedImage hero1;
    	public static BufferedImage pause;
    	public static BufferedImage start;
    	public Hero hero=new Hero();
    	public static final int START=0;//程序的启动
    	public static final int RUNNING=1;//程序的运行
    	public static final int PAUSE=2;
    	public static final int GAMEOVER=3;
    	private  int state=START;//表示当前的状态
    	private FlyingObject[]flyings= {};
    	private Bullet[] bullets= {};
    	static {
    		try {
    			background=ImageIO.read(ShootGame.class.getResource("background.png"));
    			airplane=ImageIO.read(ShootGame.class.getResource("airplane.png"));
    			bee=ImageIO.read(ShootGame.class.getResource("bee.png"));
    			bullet=ImageIO.read(ShootGame.class.getResource("bullet.png"));
    			gameover=ImageIO.read(ShootGame.class.getResource("gameover.png"));
    			hero0=ImageIO.read(ShootGame.class.getResource("hero0.png"));
    			hero1=ImageIO.read(ShootGame.class.getResource("hero1.png"));
    			pause=ImageIO.read(ShootGame.class.getResource("pause.png"));
    			start=ImageIO.read(ShootGame.class.getResource("start.png"));
    			
    		}catch(Exception e) {
    			e.printStackTrace();
    		}
    		
    	}
    	//飞行物入场(敌人+小蜜蜂)
    	public FlyingObject nextOne() {
    		int type;
    		Random rand=new Random();
    		type=rand.nextInt(20);
    		if(type>=5) {
    			return new Airplane();
    		}else {
    			return new Bee();
    		}
    	}
    	int flyEnterIndex=0;//敌人入场计数器为了避免产生的敌人太多需要对其数量进行限制
    	//敌人入场的方法
    	public void enterAction() {
    		flyEnterIndex++;
    		if(flyEnterIndex%20==0) {
    			FlyingObject obj=nextOne();//获取敌人对象
    			flyings=Arrays.copyOf(flyings, flyings.length+1);//数组的扩容
    			flyings[flyings.length-1]=obj;//将刚刚产生的敌人添加到数组的末端
    		}
    		
    	}
    	int bulletIndex=0;//子弹入场计数器
    	//子弹的入场
    	public void bulletAction() {
    		bulletIndex++;
    		if(bulletIndex%30==0) {
    			Bullet bs[]=hero.shoot();//获取子弹对象
    			bullets=Arrays.copyOf(bullets, bullets.length+bs.length);//子弹数组的扩容
    			//其中System.arraycopy中参数依次表示:原始数组,原始数组的下标,目标数组,目标数组的起始位置,需要赋值的数组的长度)
    			//其中表示的是将数组bs复制到数组bullets中
    			System.arraycopy(bs, 0, bullets, bullets.length-bs.length, bs.length);//数组的扩容
    		}
    	}
    	
    	//敌人,英雄机,子弹的走步
    	public void stepAction() {
    		hero.step();//英雄机的走步
    		for(int i=0;i<flyings.length;i++) {//敌人的走步
    			if(hero.Life()<=9) {
    			flyings[i].step();
    			}
    			if(hero.Life()>=10) {
    				flyings[i].ShiftStep();
    			}
    		}
    		for (int i = 0; i < bullets.length; i++) {//子弹的移动
    			bullets[i].step();//子弹的走步
    		}
    		
    	}
    	//删除越界的飞行物(敌人和子弹)
    	public void outOfBoundsAction() {
    		int index=0;//飞行物的下标数值
    		int bulletIndex=0;//子弹的下标数值
    		Bullet bulletlives[]=new Bullet[bullets.length];
    		FlyingObject flyingslives[]=new FlyingObject[flyings.length];
    		//删除越界的飞行物
    		for(int i=0;i<flyings.length;i++) {
    			FlyingObject f=flyings[i];
    			if(!f.outOfBounds()) {
    				flyingslives[index]=flyings[i];
    				index++;
    			}
    		}
    		//删除越界的子弹
    		for(int i=0;i<bullets.length;i++) {
    			Bullet bs=bullets[i];
    			if(!bs.outOfBounds()) {
    				bulletlives[bulletIndex]=bullets[i];
    				bulletIndex++;
    			}
    		}
    		flyings=Arrays.copyOf(flyingslives, index);
    		bullets=Arrays.copyOf(bulletlives, bulletIndex);
    		
    	}
    	
        //子弹和敌人的碰撞
    	public void shootByAction() {
    		for(int i=0;i<bullets.length;i++) {
    			Bullet b=bullets[i];//获取子弹
    			bullet(b);//
    		}
    		
    	}
    	//子弹碰撞结果
    	int score =0;//表示得分
    	public void bullet(Bullet b) {
    		//遍历飞行物数组
    	 	int index=-1;//记录碰撞敌人的下标
    		for(int i=0;i<flyings.length;i++) {
    			FlyingObject f=flyings[i];//获取敌人
    			if(f.shootBy(b)) {
    				index=i;
    				break;
    			}
    		}
    		if(index!=-1) {
    			FlyingObject one=flyings[index];
    			if(one instanceof Ememy) {
    				Ememy ememy=(Ememy)one;
    				score+=ememy.getScore();//分数加
    			}
    			if(one instanceof Award) {
    				Award award=(Award)one;
    				int type=award.getType();
    				switch(type) {
    				case Award.DOUBLE_FIRE:
    					hero.addFire();
    					break;
    				case Award.LIFE:
    					hero.addLife();
    					break;
    				}
    				
    			}
    			FlyingObject t=flyings[index];
    			flyings[index]=flyings[flyings.length-1];
    			flyings[flyings.length-1]=t;
    			flyings=Arrays.copyOf(flyings,flyings.length-1);
    			
    		}
    		
    }
    	//检查游戏是否结束 
    	public void CheckGameOverAction() {
    		 if(isGameOverAction()) {
    			 state=GAMEOVER;
    		 }
    	 }
    	//判断游戏是否结束
    	public boolean isGameOverAction() {
    		for (int i = 0; i < flyings.length; i++) {
    			FlyingObject f=flyings[i];
    			if(hero.shootFy(f)) {//撞上l
    				hero.subLife();
    				hero.subFire();
    				//利用数组的扩容,删除碰撞
    				FlyingObject t=flyings[i];
    				flyings[i]=flyings[flyings.length-1];
    				flyings[flyings.length-1]=t;
    				flyings=Arrays.copyOf(flyings,flyings.length-1);
    			}
    			
    		}
    		return hero.Life()<=0;
    		
    	}
    	//程序的启动的方法
    	public void action() {
    		//为了监视飞行物随着鼠标的移动创建一个监听器
    		MouseAdapter l=new MouseAdapter() {
    			public void mouseMoved(MouseEvent e){
    				int x=e.getX();//获取鼠标的x坐标
    				int y=e.getY();//获取鼠标的y坐标
    				hero.moveTo(x, y);//英雄机移动
    			}
    			//重写鼠标点击事件
    			public void mouseClicked(MouseEvent e) {
    				switch(state) {
    				case START:
    					state=RUNNING;
    					break;
    				case GAMEOVER:
    					score=0;
    					hero=new Hero();
    					flyings=new FlyingObject[0];
    					bullets=new Bullet[0];
    					state=START;
    					break;
    				}
    			}
    			//重写鼠标进入事件
    			 public void mouseEntered(MouseEvent e) {
    				 if(state==PAUSE) {
    					 state=RUNNING;
    				 }
    			 }
    			 //重写鼠标离开事件
    			 public void mouseExited(MouseEvent e) {
    				 if(state==RUNNING) {
    					 state=PAUSE;
    				 }
    			 }
    			 
    			 
    			
    			
    		};
    		this.addMouseListener(l);
    		this.addMouseMotionListener(l);//处理鼠标滑动事件
    		Timer time=new Timer();
    		time.schedule(new TimerTask() {
    			public void run() {
    				if(state==RUNNING) {
    				nextOne();//产生飞行物对象
    				enterAction();//飞行物的入场
    				stepAction();//走步
    				bulletAction();//子弹的入场
    				outOfBoundsAction();//删除越界的飞行物
    				shootByAction();//子弹和敌人的碰撞
    				CheckGameOverAction();//检查游戏是否结束
    				}
    				repaint();
    				
    			}
    		}, 10,10);
    		
    		
    	}
    	//绘制图
    	public void paint(Graphics g) {
    		g.drawImage(background, 0,0,null);
    		paintHero(g);//画英雄机对象
    		paintFlyingObject(g);//画敌机对象
    		paintFlyingObjectAndBulletLength(g);//绘制飞行物数组和子弹数组的长度
    		paintBullet(g);//画子弹
    		paintState(g);//画状态
    	}
    	//画英雄机对象
    	public void paintHero(Graphics g) {
    		g.drawImage(hero.image,hero.x,hero.y,null);
    		
    	}
    	//画敌人对象
    	public void paintFlyingObject(Graphics g) {
    		for(int i=0;i<flyings.length;i++) {
    			g.drawImage(flyings[i].image,flyings[i].x,flyings[i].y,null);
    		}
    	}
    	//画子弹对象
    	public void paintBullet(Graphics g) {
    		for(int i=0;i<bullets.length;i++) {
    			g.drawImage(bullets[i].image,bullets[i].x,bullets[i].y,null);
    		}
    		
    	}
    	//画敌人数组和子弹数组的长度
    	public void paintFlyingObjectAndBulletLength(Graphics g) {
    		g.setColor(Color.red);
    		g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,24));
    		g.drawString("score:"+score, 20, 20);
    		g.drawString("Fire:"+hero.Fire(), 20, 40);
    		g.drawString("Life:"+hero.Life(), 20, 60);
    	}
    	//画状态
    	public void paintState(Graphics g) {
    		switch(state) {
    		case START:
    			g.drawImage(start, 0,0,null);
    			break;
    		case PAUSE:
    			g.drawImage(pause,0,0,null);
    			break;
    		case GAMEOVER:
    			g.drawImage(gameover, 0, 0, null);
    			break;
    	
    		}
    		
    	}
    
    	public static void main(String[] args) {
    		JFrame frame=new JFrame("飞机大战");
    		ShootGame game=new ShootGame();
    		frame.add(game);
    		frame.setSize(ShootGame.WIDTH, ShootGame.HEIGHT);
    		frame.setAlwaysOnTop(true);//该窗口位于所有面板之上
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口的可关闭性
    		frame.setVisible(true);//设置窗口的可见性此方法的功能1.使得窗口可见。2.尽快的调用repaint()方法
    		game.action();//程序的启动方法
    
    	}
    
    }
    

    9.最终效果:

     

     

     

     

     

    展开全文
  • 老Java程序员花一天时间写了个飞机大战,很舒服!

    万次阅读 多人点赞 2021-05-25 21:42:07
    前两天我发现CSDN上有两篇飞机大战的文章异常火爆,各种指标都很高(阅读、点赞、评论、收藏等),但都是python写的,竟然不是我大Java,说实话作为老java选手,我心里是有那么一些失落的,难道我大java打飞机不行?...

    老Java程序员花1天时间做了个飞机大战

    引言:

    前两天我发现CSDN上有两篇飞机大战的文章异常火爆,各种指标都很高(阅读、点赞、评论、收藏等),但都是python写的,竟然不是我大Java,说实话作为老java选手,我心里是有那么一些失落的,难道我大java打飞机不行?就算大java打飞机不行,那我用单身30年的打飞机手速,我肯定行(反正我的代码我做主,就是玩!),于是我决定一展伸手,用java写了一个飞机大战。我就问你们我打飞机行不行,我媳妇都说行,你们呢?欢迎我亲爱的大Java选手们 点赞+评论+收藏!给我冲、冲、冲。。。

    代码实现

    创建窗口

    首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。

    /*
     * 游戏窗体类
     */
    public class GameFrame extends JFrame {
    	
    	public GameFrame() {
    		setTitle("飞机大战");//设置标题
    		setSize(526, 685);//设定尺寸
    		setLayout(new BorderLayout());
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
            setLocationRelativeTo(null);   //设置居中
        	setResizable(false); //不允许修改界面大小
    	}
    }
    

    创建面板容器GamePanel继承至JPanel

    package main;
    
    import java.awt.Graphics;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    /*
     * 画布类
     */
    public class GamePanel extends JPanel{
    	GamePanel gamePanel=this;
    	private JFrame mainFrame=null;
    	//构造里面初始化相关参数
    	public GamePanel(JFrame frame){
    		this.setLayout(null);
    		mainFrame = frame;
    		
    		mainFrame.setVisible(true);
    	}
    	
    	@Override
    	public void paint(Graphics g) {
    		
    	}
    }
    

    再创建一个Main类,来启动这个窗口,用来启动。

    package main;
    public class Main {
    	//主类
    	public static void main(String[] args) {
    		GameFrame frame = new GameFrame();
    		GamePanel panel = new GamePanel(frame);
    		frame.add(panel);
    		frame.setVisible(true);//设定显示
    	}
    }
    

    右键执行这个Main类,窗口建出来了
    ​​窗口

    创建菜单及菜单选项

    创建菜单

    private void  initMenu(){
    	// 创建菜单及菜单选项
    	jmb = new JMenuBar();
    	JMenu jm1 = new JMenu("游戏");
    	jm1.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
    	JMenu jm2 = new JMenu("帮助");
    	jm2.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
    	
    	JMenuItem jmi1 = new JMenuItem("开始新游戏");
    	JMenuItem jmi2 = new JMenuItem("退出");
    	jmi1.setFont(new Font("微软雅黑", Font.BOLD, 15));
    	jmi2.setFont(new Font("微软雅黑", Font.BOLD, 15));
    	
    	JMenuItem jmi3 = new JMenuItem("操作说明");
    	jmi3.setFont(new Font("微软雅黑", Font.BOLD, 15));
    	JMenuItem jmi4 = new JMenuItem("胜利条件");
    	jmi4.setFont(new Font("微软雅黑", Font.BOLD, 15));
    	
    	jm1.add(jmi1);
    	jm1.add(jmi2);
    	
    	jm2.add(jmi3);
    	jm2.add(jmi4);
    	
    	jmb.add(jm1);
    	jmb.add(jm2);
    	mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
    	jmi1.addActionListener(this);
    	jmi1.setActionCommand("Restart");
    	jmi2.addActionListener(this);
    	jmi2.setActionCommand("Exit");
    	
    	jmi3.addActionListener(this);
    	jmi3.setActionCommand("help");
    	jmi4.addActionListener(this);
    	jmi4.setActionCommand("win");
    }
    

    实现ActionListener并重写方法actionPerformed
    在这里插入图片描述
    actionPerformed方法的实现

    @Override
    public void actionPerformed(ActionEvent e) {
    
    	String command = e.getActionCommand();
    	UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
    	UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
    	if ("Exit".equals(command)) {
    		Object[] options = { "确定", "取消" };
    		int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "",
    				JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
    				options, options[0]);
    		if (response == 0) {
    			System.exit(0);
    		} 
    	}else if("Restart".equals(command)){
    		if(startFlag){
    			Object[] options = { "确定", "取消" };
    			int response = JOptionPane.showOptionDialog(this, "游戏中,您确认要重新开始吗", "",
    					JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
    					options, options[0]);
    			if (response == 0) {
    				//需要先结束游戏
    				realGameEnd(1);
    				restart();
    			} 
    		}else{
    			restart();
    		}
    	}else if("help".equals(command)){
    		JOptionPane.showMessageDialog(null, "游戏开始后,要先动鼠标到飞机处,触发移动效果,然后飞机就会跟随鼠标移动!",
    				"提示!", JOptionPane.INFORMATION_MESSAGE);
    	}else if("win".equals(command)){
    		JOptionPane.showMessageDialog(null, "得分1000,获得胜利!",
    				"提示!", JOptionPane.INFORMATION_MESSAGE);
    	}
    }
    

    在这里插入图片描述

    创建背景

    在GamePanel类中重写paint方法,绘制背景图即可

    //绘图方法
    @Override
    public void paint(Graphics g) {
    	gameHeight = this.getHeight();
    	gameWidth = this.getWidth();
    	//绘制背景
    	g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);
    }
    

    在这里插入图片描述

    开启主线程

    主线程,用来重绘页面,重绘全部交给主线程,主线程调用 repaint方法就行,要产生动画就要靠这个repaint。

    //刷新线程,用来重新绘制页面
    private class RefreshThread implements Runnable {
    	@Override
    	public void run() {
    		while (startFlag) {
    			repaint();
    			try {
    				Thread.sleep(50);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    在GamePanel的构造里面启动这个主线程
    在这里插入图片描述
    有了这个主线程刷新,待会我们更新飞机的位置,飞机就会移动,不需要另外的代码去调用repaint方法了(这是我的做法,仅供参考)。

    创建我方飞机

    创建MyPlane类,属性有坐标x、y,宽高、图片、是否存活、是否可以移动等;方法主要有绘制、移动、射击等。

    public class MyPlane {
    
    	private int x = 0;
    	private int y = 0;
    	private int width = 0;
    	private int height = 0;
    	private BufferedImage image = null;
    	private GamePanel panel=null;
    	private HashMap imageMap=null;
    	private boolean alive=true;
    	private boolean canMove=false;
    	private int key=1;
    	private HashMap boomImageMap=null;
    	private boolean hitFlag=false;//正在碰撞
    	
    	public MyPlane(int x,int y,int width,int height,GamePanel panel) {
    		this.x=x;
    		this.y=y;
    		this.width=width;
    		this.height=height;
    		this.panel=panel;
    		this.imageMap=panel.imageMap;
    		this.image=(BufferedImage)imageMap.get("myplane1");
    		this.boomImageMap=panel.mypalneBoomImageMap;
    		
    	}
    	//绘制
    	public void draw(Graphics g) {
    		g.drawImage(image, x, y, width,height, null);
    	}
    }
    

    创建(这里只是创建好了飞机对象,需要绘制)

    //创建自己飞机
    private void initMyPlane() {
    	myPlane = new MyPlane(200, 530, 132, 86, this);
    }
    

    在paint方法中绘制

    //绘图方法
    @Override
    public void paint(Graphics g) {
    	gameHeight = this.getHeight();
    	gameWidth = this.getWidth();
    	//绘制背景
    	g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);
    	
    	//绘制飞机
    	if(myPlane!=null){
    		myPlane.draw(g);
    	}
    }
    

    在这里插入图片描述

    鼠标事件监听

    加入监听是为了让飞机跟随鼠标移动,我这里定的规则是第一次鼠标必须移动到飞机上,然后飞机才会跟随。

    代码里面用一个属性canMove来控制,默认是false,只有鼠标第一次移入到飞机上时,这个属性设置为true,然后就可以跟随鼠标移动了。

    //鼠标事件的创建
    private void createMouseListener() {
    	MouseAdapter mouseAdapter = new MouseAdapter() {
    		@Override
    		public void mouseMoved(MouseEvent e) {
    			int x = e.getX();
    			int y = e.getY();
    			if(myPlane==null) return ;
    			//飞机第一次是不允许移动的,只有飞机的canMove为true才去跟随
    			if(myPlane.isCanMove()){
    				myPlane.move(x,y);
    				return;
    			}
    			//判断鼠标的移入,如果移动到飞机上则canMove设置为true
    			if(myPlane.isPoint(x,y)){
    				myPlane.setCanMove(true);
    			}
    		}
    	};
    	addMouseMotionListener(mouseAdapter);
    	addMouseListener(mouseAdapter);
    }
    

    来实现一下MyPlane的move方法,这里处理了边界,保证飞机不出界,同时保证鼠标在飞机的中间位置

    //飞机跟随鼠标移动
    public void move(int x,int y) {
    	//判断范围,当横向移动在窗口范围内
    	if(x-width/2>=0 && x<=panel.getWidth()-width/2){
    		this.x=x-width/2;
    	}
    	//判断范围,当纵向移动在窗口范围内
    	if(y-height/2>=0 && y<=panel.getHeight()-height/2){
    		this.y=y-height/2;
    	}
    }
    

    在这里插入图片描述

    创建子弹类

    属性也就是坐标、宽高这些,给子弹加入移动方法

    //移动
    void move(){
    	new Thread(new Runnable() {
    		@Override
    		public void run() {
    			while(alive){
    				y-=speed;
    				if(y<=0){
    					clear();
    				}
    				
    				try {
    					Thread.sleep(50);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}).start();
    }
    

    飞机类加入射击方法,200毫秒创建一发子弹

    //射击
    void shoot() {
    	new Thread(new Runnable() {
    		@Override
    		public void run() {
    			while(alive){
    				//创建子弹
    				createBullet();
    				try {
    					Thread.sleep(200);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    
    		private void createBullet() {
    			Bullet bullet = new Bullet(x+width/2-10, y, 20, 30, panel);
    			panel.bulletList.add(bullet);
    			new MusicPlayer("/music/shoot.wav").play();
    		}
    	}).start();
    }
    

    别忘记在paint方法里面绘制子弹出来

    //绘制子弹
    Bullet bullet=null;
    for (int i = 0; i < bulletList.size(); i++) {
    	bullet = (Bullet)bulletList.get(i);
    	bullet.draw(g);
    }
    

    在这里插入图片描述

    创建敌机

    创建抽象类Plane

    package main;
    
    import java.awt.Graphics;
    
    public abstract class Plane {
    	public abstract void move();
    	public abstract void draw(Graphics g);
    	public abstract void boom();
    	public abstract void clear();
    	
    	abstract int getX();
    	abstract int getY();
    	abstract int getWidth();
    	abstract int getHeight();
    }
    

    创建敌机子类

    有个特殊一点的地方: 因为有4种敌机,这里随机1、2、3、4这4个数字作为属性index,然后根据这个index来展现不同的飞机图片,当然也可以通过这个index来设置敌机不同的移动速度,但是我为了偷懒,就全部一样的移动速度,嘿嘿。
    移动就是开启线程让y坐标增加,没什么好讲的,这里加一个飞机碰撞,就是当敌机跟我方飞机如何判断碰撞的问题。

    撞机分析(敌机与我机的撞机)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    从上面几个图可看出什么?因为图片是方形的,他们的4个顶点一定至少有一个在对方的范围内。再看一下从左边撞击的图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    从上图看到也是这样,其他两个方向的也是一样的道理,为了稳点我还加了一种情况:

    1.判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了。
    2.如果1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是表示碰撞了。

    //判断飞机与子弹是否碰撞
    private boolean isPoint(MyPlane plane) {
    	/*
    	 * 
    	 * 两种情况
    	 * 1.需要判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了
    	 * 2.如果步骤1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是标志碰撞了
    	*/
    	
    	//方式1
    	
    	//左上角
    	int x1 = x;
    	int y1 = y;
    	//右上角
    	int x2 = x+width;
    	int y2 = y;
    	//右下角
    	int x3 = x+width;
    	int y3 = y+height;
    	//左下角
    	int x4 = x;
    	int y4 = y+height;
    	//只要有一个点在范围内,则判断为碰撞
    	if(comparePointMyPlane(x1,y1,plane)|| comparePointMyPlane(x2,y2,plane)||comparePointMyPlane(x3,y3,plane)||comparePointMyPlane(x4,y4,plane) ){
    		return true;
    	}
    	
    	//方式1没成立则用方式2判断
    	
    	//方式2
    	x1 = plane.getX();
    	y1 = plane.getY();
    	//右上角
    	x2 = plane.getX()+plane.getWidth();
    	y2 = plane.getY();
    	//右下角
    	x3 = plane.getX()+plane.getWidth();
    	y3 =plane.getY()+plane.getHeight();
    	//左下角
    	x4 = plane.getX();
    	y4 = plane.getY()+plane.getHeight();
    	if(comparePoint(x1,y1)|| comparePoint(x2,y2)||comparePoint(x3,y3)||comparePoint(x4,y4) ){
    		return true;
    	}
    	return false;
    }
    //用敌机的坐标来判断
    private boolean comparePointMyPlane(int x,int y,MyPlane plane){
    	//大于左上角,小于右下角的坐标则肯定在范围内
    	if(x>plane.getX() && y >plane.getY()
    		&& x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight()	){
    		return  true;
    	}
    	return false;
    }
    //用我机的坐标来判断
    private boolean comparePoint(int x,int y){
    	//大于左上角,小于右下角的坐标则肯定在范围内
    	if(x>this.x && y >this.y
    		&& x<this.x+this.width && y <this.y+this.height){
    		return  true;
    	}
    	return false;
    }
    

    测试一下效果
    在这里插入图片描述

    忘记说击中敌机的了(原理跟刚才差不多,代码直接放了)

    //判断击中敌机
    protected void hitEnemy() {
    	EnemyPlane enemyPlane=null;
    	List enemys = panel.enemyList;
    	for (int i = 0; i < enemys.size(); i++) {
    		try {
    			enemyPlane = (EnemyPlane)enemys.get(i);
    		} catch (Exception e) {
    		}
    		if(enemyPlane==null) continue;
    		if(this.isPoint(enemyPlane)){
    			
    			panel.curCount+=enemyPlane.getCount();
    			//删除当前子弹
    			clear();
    			
    			//飞机爆炸
    			enemyPlane.boom();
    			
    			if(panel.curCount>=panel.totalCount){
    				panel.myPlane.setCanMove(false);
    				panel.gameWin();
    			}
    		}
    	}
    }
    
    //判断飞机与子弹是否碰撞
    private boolean isPoint(EnemyPlane plane) {
    	//因为子弹比飞机小,所以只需要判断子弹的4个点是否在飞机范围内,如果有则表示碰撞了
    	//左上角
    	int x1 = x;
    	int y1 = y;
    	//右上角
    	int x2 = x+width;
    	int y2 = y;
    	//右下角
    	int x3 = x+width;
    	int y3 = y+height;
    	//左下角
    	int x4 = x;
    	int y4 = y+height;
    	//只要有一个点在范围内,则判断为碰撞
    	if(comparePoint(x1,y1,plane)|| comparePoint(x2,y2,plane)||comparePoint(x3,y3,plane)||comparePoint(x4,y4,plane) ){
    		return true;
    	}
    	return false;
    }
    
    private boolean comparePoint(int x,int y,EnemyPlane plane){
    	//大于左上角,小于右下角的坐标则肯定在范围内
    	if(x>plane.getX() && y >plane.getY()
    		&& x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight()	){
    		return  true;
    	}
    	return false;
    }
    

    最后加上计分的、胜利、失败等提示就完成了!

    看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。

    想要代码的 加微信私聊 我!

    为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,有兴趣的小伙伴可以了解一下。
    在这里插入图片描述

    ★ 更多精彩

    ♥ Java植物大战僵尸 ♥

    ♥ Java坦克大战,回忆童年!♥

    ♥ Java扫雷小游戏,以前上学经常玩 ♥

    ♥ Java学生宿舍管理系统 ♥

    展开全文
  • 500行代码,教你用python写个微信飞机大战

    万次阅读 多人点赞 2019-10-15 07:39:43
    这几天在重温微信小游戏的飞机大战,玩着玩着就在思考人生了,这飞机大战怎么就可以做的那么好,操作简单,简单上手。 帮助蹲厕族、YP族、饭圈女孩在无聊之余可以有一样东西让他们振作起来!让他们的左手 / 右手有...

    在这里插入图片描述

    这几天在重温微信小游戏的飞机大战,玩着玩着就在思考人生了,这飞机大战怎么就可以做的那么好,操作简单,简单上手。

    帮助蹲厕族、YP族、饭圈女孩在无聊之余可以有一样东西让他们振作起来!让他们的左手 / 右手有节奏有韵律的朝着同一个方向来回移动起来!

    这是史诗级的发明,是浓墨重彩的一笔,是……

    在一阵抽搐后,我结束了游戏,瞬时觉得一切都索然无味,正在我进入贤者模式时,突然想到,如果我可以让更多人已不同的方式体会到这种美轮美奂的感觉岂不美哉?

    所以我打开电脑,创建了一个 plan_game.py……

    先看效果图

    在这里插入图片描述

    操作环境

    • 操作系统:windows10
    • python版本:python 3.7
    • 代码编辑器:pycharm 2018.2
    • 使用模块:os,sys,random,pygame

    因为实现代码使用到了一个pygame的第三方模块,没有的先 pip install 一下,这里顺便提供一个比较好的pygame的教程.

    https://eyehere.net/2011/python-pygame-novice-professional-index/

    具体实现

    1. 首先我们先指定素材文件的文件目录.方便我们后面的使用。这些素材已经全部上传至公众号Python专栏,后台回复:飞机大战,即可获得。
    import os
    
    # 得到当前文件夹下面的material_images目录的路径
    source_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'material_images')
    
    1. 实现一个Game类,用来完成这个游戏的主要逻辑。
    import pygame
    
    
    class Game():
        def __init__(self, background_image_path, size=(480, 700), title='飞机大战', font_name='方正舒体', font_size=30, speed=2000):
            '''
            :param background_image_path: 背景图片的路径地址
            :param size: 游戏窗口的大小
            :param title: 游戏窗口的标题
            :param font_name: 指定字体
            :param font_size: 指定字体大小
            :param speed: 背景图滚动整个窗口一次所用时间,单位为ms
            '''
            self.size = size
            self.screen = pygame.display.set_mode(size)
            self.title = title
            self.background_image_path = background_image_path
            self.background = pygame.image.load(self.background_image_path).convert()
            # 设置字体对象,得到系统中自带的字体
            self.font = pygame.font.SysFont(font_name, font_size)
            # 得到Clock对象,我们可以使用它来获取距离上次绘制图像的时间
            self.clock = pygame.time.Clock()
            # 背景图初始位置
            self.height = 0
            # 使用窗口的高度处于滚动的时间,就能得到每ms滚动的距离
            self.every_ms_move_distance = self.size[1] / speed   # 2秒
            # 分数
            self.score = 0
            # 存放所有的敌机
            self.enemies = []
    
    
        def show_score(self):
            '''
            显示分数, 在窗口的的最上方距离上边距10px, 左右居中
            '''
            pass
    
    
        def set_time_passed(self):
            # 控制画 的帧, 越大越快
            # 得到上一次绘制图像到到现在的时间, ms
            self.time_passed = self.clock.tick()
    
    
        def draw_background(self):
            '''
            绘制背景图片,一直向下滚动,营造飞机一直往上面飞的感觉
            '''
            # 每次移动的距离 = 每ms移动的距离 * 上次到现在的时间(ms)
            pass
    
    
        def create_enemy(self, image_path=os.path.join(source_dir,'enemy1.png'), enemy_number=5):
            '''
            创建敌机
            :param image_path: 敌机的图片地址
            :param enemy_number: 最多有几个敌机在屏幕上
            '''
            pass
    
    
        def draw_enemies(self, time_passed, screen):
            '''
            绘制敌机到屏幕上,清理跑出窗口的敌机,
            :param time_passed: 上次绘制导向现在经过的时间
            :param screen: 绘制的窗口对象
            '''
            pass
    
    
        def bullet_and_enemy_crash_detection(self, bullets):
            '''
            检测子弹是否击中敌机
            :param bullets: 飞机的所有子弹
            '''
            pass
    
    
        def plan_and_enemy_crash_detection(self, plan, allow_crash_size=None):
            '''
            检测敌机与飞机是否相撞
            :param plan: 飞机对象
            :param allow_crash_size: 允许飞机碰撞的大小,只有左右有效
            '''
            pass
    
    
        def draw_plan(self, plan, time_passed):
            '''
            绘制飞机
            :param plan: 飞机对象
            :param time_passed: 距离上次绘制的时间
            :return:
            '''
            pass
    
    
        def game_over(self):
            '''
            游戏结束
            '''
            while True:
                # 绘制背景图
                pass
    
    
        def run(self):
            '''
            游戏入口函数,开始函数,主体函数
            :return:
            '''
    
            # 设置游戏窗口的大小
            pygame.display.set_caption(self.title)
            # 初始化一个飞机对象
            plan = Plan()
    
            while True:
                # 如果飞机自毁完成, 游戏结束, 调用game_over函数
                pass
    
                # 检测监听事件
                pass
    
                # 检测上下左右的移动案件.
                # w,a,s,d 和 上,下,左,右键都可以
                # 然后执行plan.update函数,改变飞机的位置
                pass
    
                # 子弹和敌机的碰撞检测
                self.bullet_and_enemy_crash_detection(plan.bullets)
                # 飞机与敌机的碰撞检测
                self.plan_and_enemy_crash_detection(plan)
                # 设置属性time_passed的值, 距离上次的时间,方便后面使用
                self.set_time_passed()
                # 绘制背景图片
                self.draw_background()
                # 显示分数
                self.show_score()
                # 生成敌机
                self.create_enemy()
                # 绘制敌机
                self.draw_enemies(time_passed=self.time_passed, screen=self.screen)
                # 绘制飞机
                self.draw_plan(plan=plan, time_passed=self.time_passed)
                # 绘制子弹
                plan.draw_bullets(time_passed=self.time_passed, screen=self.screen)
                # 显示我们的图像
                pygame.display.update()
    
    这里说以下怎样查看自己的系统中有哪些自带的字体.
    pygame.font.get_fonts(),这个函数就能够得到系统中所有的自带字体文件。不过,当我们游戏中有中文的时候,我们也得选择支持中文的字体,否则的话是显示不出中文的。
    

    在这里插入图片描述

    1. 实现DestroyAnimationMixin类,这个类主要是用来显示飞机或敌机的自毁动画
    # 显示飞机自毁动画的Mixin类, 可用于飞机和敌机的自毁动画显示
    class DestroyAnimationMixin():
    
        def show_destroy_animation(self, time_passed, destroy_time=200):
            '''
            显示自毁动画
            动画其实就是几张图片切换的比较快,我们的眼睛识别不出来,所以认为他是动态的,也就是动画
            :param time_passed: 距离上次绘制图像到现在的时间,单位ms
            :param destroy_time: 自毁动画总共显示时间,单位ms
            '''
    
            # 因为我们的自毁图片有四张,需要依次显示,首先动画的效果
            # self.destroy_image_position 表示第几章自毁图片,从零开始
            # 如果大于等于4了,说明自毁动画显示完成,设置self.destroyed变量为True, 方便别处调用
            if self.destroy_image_position >= 4:
                self.destroyed = True
                return
    
            # 依次加载自毁图片
            if self.time_passed >= destroy_time / 4:
                self.image = pygame.image.load(os.path.join(source_dir, self.destroy_images[self.destroy_image_position])).convert_alpha()
                self.destroy_image_position += 1
                self.time_passed = 0
            else:
                self.time_passed += time_passed
    

    在这里插入图片描述

    1. 实现飞机类,完成飞机的主要操作。飞机的操作包括:飞机位置、飞机子弹、发射子弹等。
    # 飞机类,继承DestroyAnimationMixin, 方便使用显示自毁动画的函数
    class Plan(DestroyAnimationMixin):
        def __init__(self, image_path=os.path.join(source_dir,'plan.png'), background_size=(480, 700)):
            '''
            :param image_path: 飞机图片地址
            :param background_size: 游戏窗口大小
            '''
            self.background_size = background_size
            self.image = pygame.image.load(image_path).convert_alpha()
            self.image_size = self.image.get_size()
            self.position = [(background_size[0]-self.image_size[0]) / 2, 500]
            # 飞机每次移动的距离
            self.every_time_move_distance = 0.5
            # 飞机的子弹
            self.bullets = []
            # destroy association attributes, 自毁相关属性
            # 开始自毁
            self.start_destroy = False
            # 自毁结束
            self.destroyed = False
            # 自毁图片
            self.destroy_images = ['me_destroy_1.png', 'me_destroy_2.png', 'me_destroy_3.png', 'me_destroy_4.png']
            # 自毁图片位置
            self.destroy_image_position = 0
            # 距离上次绘制图像到现在的时间
            self.time_passed = 0
    
        def update(self, direction):
            '''
            更新飞机位置
            :param direction: 飞机移动方向
            '''
            pass
    
        def shut(self, image_path=os.path.join(source_dir,'bullet.png')):
            '''
            飞机发射子弹
            :param image_path: 子弹图片
            '''
            pass
    
        def draw_bullets(self, time_passed, screen):
            '''
            绘制飞机的所有子弹
            :param time_passed: 距离上次绘制图像到现在的时间
            :param screen: 绘制到哪一个窗口中
            '''
            pass
    

    在这里插入图片描述

    1. 实现敌机类,完成敌机的主要操作。主要是用来更新位置。
    # 敌机类,继承DestroyAnimationMixin, 方便使用显示自毁动画的函数
    class Enemy(DestroyAnimationMixin):
        def __init__(self, image_path=os.path.join(source_dir, 'enemy1.png'), speed=2000, background_size=(480, 700)):
            '''
            :param image_path: 敌机图片地址
            :param speed: 敌机移动整个窗口需要的时间,单位ms,也就是速度
            :param background_size: 游戏窗口的尺寸
            '''
            self.image = pygame.image.load(image_path).convert_alpha()
            self.speed = background_size[1] / speed
            self.background_size = background_size
            self.position = [random.randint(0, background_size[0]-self.image.get_size()[0]), -self.image.get_size()[1]]
            # 开始自毁
            self.start_destroy = False
            # 自毁完成
            self.destroyed = False
            # 自毁图片路径
            self.destroy_images = ['enemy1_down1.png', 'enemy1_down2.png', 'enemy1_down3.png', 'enemy1_down3.png']
            # 距离上次绘制图像到现在的时间
            self.time_passed = 0
            # 自毁图片在self.destroy_images的位置
            self.destroy_image_position = 0
    
        def update(self, time_passed):
            '''
            更新敌机的位置
            :param time_passed: 距离上次绘制图像到现在的时间
            :return:
            '''
            pass
    
    1. 实现子弹类,完成子弹的主要操作
    # 飞机子弹类
    class Bullet():
        def __init__(self, image_path=os.path.join(source_dir,'bullet.png'), background_size=(480, 700), plan=None, speed=1000):
            '''
            :param image_path: 子弹的图片地址
            :param background_size: 游戏窗口大小
            :param plan: 飞机对象
            :param speed: 子弹飞行速度
            '''
            self.image = pygame.image.load(image_path).convert_alpha()
            self.background_size = background_size
            self.speed = background_size[1] / speed
            # 子弹是否击中敌机
            self.destroyed = False
            self.position = self._get_position(plan)
    
        def _get_position(self, plan):
            '''
            根据plan得到子弹发出位置
            :param plan: 飞机对象
            '''
            bullet_size = self.image.get_size()
            plan_width = plan.image_size[0]
            x = (plan_width-bullet_size[0]) / 2
            return [plan.position[0] + x, plan.position[1]]
    
        def update(self, time_passed):
            '''
            改变子弹位置
            :param time_passed: 距离上次绘制图像到现在的时间
            '''
            # 如果子弹超出屏幕或者击中敌机,就设置self.position[1]为-100,在plan.draw的时候就移除它
            if self.position[1] + self.image.get_size()[1] <= 0 or self.destroyed:
                self.position[1] = -100
                return
    
            # 改变的距离 = 时间 * 速率
            self.position[1] -= time_passed * self.speed
    

    在这里插入图片描述

    这样,我们就把所有的操作都实现完了,接下来只需要使用 Game().run(),就可以运行我们的游戏了。

    关注公众号:糙科技,后台回复:飞机大战,即可获得完整代码及素材包。

    展开全文
  • Python代码实现飞机大战

    万次阅读 多人点赞 2020-06-19 11:40:37
    文章目录经典飞机大战一.游戏设定二.我方飞机三.敌方飞机四.发射子弹五.发放补给包六.主模块 经典飞机大战 源代码以及素材资料(图片,音频)可从下面的github中下载: 飞机大战源代码以及素材资料github项目地址链接...

    参考资料

    • 《零基础入门学习python》

    源代码以及素材资料(图片,音频)可从下面的github中下载:

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

    经典飞机大战

    不知道大家有没有打过飞机,喜不喜欢打飞机。当我第一次接触这个东西的时候,我的内心是被震撼到的。第一次接触打飞机的时候作者本人是身心愉悦的,因为周边的朋友都在打飞机, 每次都会下意识彼此较量一下,看谁打得更好。打飞机也是需要有一定的技巧的,熟练的朋友一把能打上半个小时,生疏的则三五分钟就败下阵来。

    一.游戏设定

    游戏界面如下图所示
    游戏的基本设定:

    • 敌方共有大中小3款飞机,分为高中低三种速度;
    • 子弹的射程并非全屏,而大概是屏幕长度的80%;
    • 消灭小飞机需要1发子弹,中飞机需要8发,大飞机需要20发子弹;
    • 每消灭一架小飞机得1000分,中飞机6000分,大飞机10000分;
    • 每隔30秒有一个随机的道具补给,分为两种道具,全屏炸弹和双倍子弹;
    • 全屏炸弹最多只能存放3枚,双倍子弹可以维持18秒钟的效果;
    • 游戏将根据分数来逐步提高难度,难度的提高表现为飞机数量的增多以及速度的加快。

    另外还对游戏做了一些改进,比如为中飞机和大飞机增加了血槽的显示,这样玩家可以直观地知道敌机快被消灭了没有;我方有三次机会,每次被敌人消灭,新诞生的飞机会有3秒钟的安全期;游戏结束后会显示历史最高分数。

    这个游戏加上基本的注释代码量在800行左右,代码看上去比较多,多打代码少动脑。所以大家不要怕,越是多的代码,逻辑就越容易看得清楚,就越好学习。好,那让我们从无到有,从简单到复杂来一起打造这个游戏吧!

    二.我方飞机

    首先创建一个myplane. py模块来定义我方飞机:

    import pygame
    
    class MyPlane(pygame.sprite.Sprite):
        def __init__(self, bg_size):
            pygame.sprite.Sprite.__init__(self)
    
            self.image1 = pygame.image.load("images/me1.png").convert_alpha()
            self.image2 = pygame.image.load("images/me2.png").convert_alpha()
            self.destroy_images = []
            self.destroy_images.extend([\
                pygame.image.load("images/me_destroy_1.png").convert_alpha(), \
                pygame.image.load("images/me_destroy_2.png").convert_alpha(), \
                pygame.image.load("images/me_destroy_3.png").convert_alpha(), \
                pygame.image.load("images/me_destroy_4.png").convert_alpha() \
                ])
            self.rect = self.image1.get_rect()
            self.width, self.height = bg_size[0], bg_size[1]
            self.rect.left, self.rect.top = \
                            (self.width - self.rect.width) // 2, \
                            self.height - self.rect.height - 60
            self.speed = 10
            self.active = True
            self.invincible = False
            self.mask = pygame.mask.from_surface(self.image1)
    
        # 分别定义moveUp()、moveDown()、moveLeft()和moveRight()控制我方飞机上、下、左、右移动:
    
        def moveUp(self):
            if self.rect.top > 0:
                self.rect.top -= self.speed
            else:
                self.rect.top = 0
    
        def moveDown(self):
            if self.rect.bottom < self.height - 60:
                self.rect.top += self.speed
            else:
                self.rect.bottom = self.height - 60
    
        def moveLeft(self):
            if self.rect.left > 0:
                self.rect.left -= self.speed
            else:
                self.rect.left = 0
    
        def moveRight(self):
            if self.rect.right < self.width:
                self.rect.left += self.speed
            else:
                self.rect.right = self.width
    
        def reset(self):
            self.rect.left, self.rect.top = \
                            (self.width - self.rect.width) // 2, \
                            self.height - self.rect.height - 60
            self.active = True
            self.invincible = True
    
    

    三.敌方飞机

    既然英雄已经有了,那现在就是需要创造敌人的时候。敌机分为小、中、大三个尺寸,它们的速度依次是快、中、慢,在游戏界面的上方位置创造位置随机的敌机,可以让它们不在同一排出现。将敌机的定义写在enemy. py模块中:

    import pygame
    from random import *
    
    
    class SmallEnemy(pygame.sprite.Sprite):
        def __init__(self, bg_size):
            pygame.sprite.Sprite.__init__(self)
            
            self.image = pygame.image.load("images/enemy1.png").convert_alpha()
            self.destroy_images = []
            self.destroy_images.extend([ \
                pygame.image.load("images/enemy1_down1.png").convert_alpha(), \
                pygame.image.load("images/enemy1_down2.png").convert_alpha(), \
                pygame.image.load("images/enemy1_down3.png").convert_alpha(), \
                pygame.image.load("images/enemy1_down4.png").convert_alpha() \
                ])
            self.rect = self.image.get_rect()
            self.width, self.height = bg_size[0], bg_size[1]
            self.speed = 2
            self.active = True
            self.rect.left, self.rect.top = \
                randint(0, self.width - self.rect.width), \
                randint(-5 * self.height, 0)
            self.mask = pygame.mask.from_surface(self.image)
        
        def move(self):
            if self.rect.top < self.height:
                self.rect.top += self.speed
            else:
                self.reset()
        
        def reset(self):
            self.active = True
            self.rect.left, self.rect.top = \
                randint(0, self.width - self.rect.width), \
                randint(-5 * self.height, 0)
    
    
    class MidEnemy(pygame.sprite.Sprite):
        energy = 8
        
        def __init__(self, bg_size):
            pygame.sprite.Sprite.__init__(self)
            
            self.image = pygame.image.load("images/enemy2.png").convert_alpha()
            self.image_hit = pygame.image.load("images/enemy2_hit.png").convert_alpha()
            self.destroy_images = []
            self.destroy_images.extend([ \
                pygame.image.load("images/enemy2_down1.png").convert_alpha(), \
                pygame.image.load("images/enemy2_down2.png").convert_alpha(), \
                pygame.image.load("images/enemy2_down3.png").convert_alpha(), \
                pygame.image.load("images/enemy2_down4.png").convert_alpha() \
                ])
            self.rect = self.image.get_rect()
            self.width, self.height = bg_size[0], bg_size[1]
            self.speed = 1
            self.active = True
            self.rect.left, self.rect.top = \
                randint(0, self.width - self.rect.width), \
                randint(-10 * self.height, -self.height)
            self.mask = pygame.mask.from_surface(self.image)
            self.energy = MidEnemy.energy
            self.hit = False
        
        def move(self):
            if self.rect.top < self.height:
                self.rect.top += self.speed
            else:
                self.reset()
        
        def reset(self):
            self.active = True
            self.energy = MidEnemy.energy
            self.rect.left, self.rect.top = \
                randint(0, self.width - self.rect.width), \
                randint(-10 * self.height, -self.height)
    
    
    class BigEnemy(pygame.sprite.Sprite):
        energy = 20
        
        def __init__(self, bg_size):
            pygame.sprite.Sprite.__init__(self)
            
            self.image1 = pygame.image.load("images/enemy3_n1.png").convert_alpha()
            self.image2 = pygame.image.load("images/enemy3_n2.png").convert_alpha()
            self.image_hit = pygame.image.load("images/enemy3_hit.png").convert_alpha()
            self.destroy_images = []
            self.destroy_images.extend([ \
                pygame.image.load("images/enemy3_down1.png").convert_alpha(), \
                pygame.image.load("images/enemy3_down2.png").convert_alpha(), \
                pygame.image.load("images/enemy3_down3.png").convert_alpha(), \
                pygame.image.load("images/enemy3_down4.png").convert_alpha(), \
                pygame.image.load("images/enemy3_down5.png").convert_alpha(), \
                pygame.image.load("images/enemy3_down6.png").convert_alpha() \
                ])
            self.rect = self.image1.get_rect()
            self.width, self.height = bg_size[0], bg_size[1]
            self.speed = 1
            self.active = True
            self.rect.left, self.rect.top = \
                randint(0, self.width - self.rect.width), \
                randint(-15 * self.height, -5 * self.height)
            self.mask = pygame.mask.from_surface(self.image1)
            self.energy = BigEnemy.energy
            self.hit = False
        
        def move(self):
            if self.rect.top < self.height:
                self.rect.top += self.speed
            else:
                self.reset()
        
        def reset(self):
            self.active = True
            self.energy = BigEnemy.energy
            self.rect.left, self.rect.top = \
                randint(0, self.width - self.rect.width), \
                randint(-15 * self.height, -5 * self.height)
    
    

    四.发射子弹

    现在的情况是我方飞机处于落后挨打的状态,敌强我弱,所以应该拿起武器进行反击! 接下来定义子弹,子弹分为两种: 一种是普通子弹一次只发射一颗; 另一种是补给发放的超级子弹一次可以发射两颗。

    我们将子弹定义为独立的模块bullet.py:

    import pygame
    
    
    class Bullet1(pygame.sprite.Sprite):
        def __init__(self, position):
            pygame.sprite.Sprite.__init__(self)
            
            self.image = pygame.image.load("images/bullet1.png").convert_alpha()
            self.rect = self.image.get_rect()
            self.rect.left, self.rect.top = position
            self.speed = 12
            self.active = False
            self.mask = pygame.mask.from_surface(self.image)
        
        def move(self):
            self.rect.top -= self.speed
            
            if self.rect.top < 0:
                self.active = False
        
        def reset(self, position):
            self.rect.left, self.rect.top = position
            self.active = True
    
    
    class Bullet2(pygame.sprite.Sprite):
        def __init__(self, position):
            pygame.sprite.Sprite.__init__(self)
            
            self.image = pygame.image.load("images/bullet2.png").convert_alpha()
            self.rect = self.image.get_rect()
            self.rect.left, self.rect.top = position
            self.speed = 14
            self.active = False
            self.mask = pygame.mask.from_surface(self.image)
        
        def move(self):
            self.rect.top -= self.speed
            
            if self.rect.top < 0:
                self.active = False
        
        def reset(self, position):
            self.rect.left, self.rect.top = position
            self.active = True
    
    

    五.发放补给包

    游戏设计每30秒随机发放一个补给包,可 能是超级子弹,也可能是全屏炸弹。补给包有自己的图像和运动轨迹,不妨单独为其定义一个模块supply.py:

    import pygame
    from random import *
    
    
    class Bullet_Supply(pygame.sprite.Sprite):
        def __init__(self, bg_size):
            pygame.sprite.Sprite.__init__(self)
            
            self.image = pygame.image.load("images/bullet_supply.png").convert_alpha()
            self.rect = self.image.get_rect()
            self.width, self.height = bg_size[0], bg_size[1]
            self.rect.left, self.rect.bottom = \
                randint(0, self.width - self.rect.width), -100
            self.speed = 5
            self.active = False
            self.mask = pygame.mask.from_surface(self.image)
        
        def move(self):
            if self.rect.top < self.height:
                self.rect.top += self.speed
            else:
                self.active = False
        
        def reset(self):
            self.active = True
            self.rect.left, self.rect.bottom = \
                randint(0, self.width - self.rect.width), -100
    
    
    class Bomb_Supply(pygame.sprite.Sprite):
        def __init__(self, bg_size):
            pygame.sprite.Sprite.__init__(self)
            
            self.image = pygame.image.load("images/bomb_supply.png").convert_alpha()
            self.rect = self.image.get_rect()
            self.width, self.height = bg_size[0], bg_size[1]
            self.rect.left, self.rect.bottom = \
                randint(0, self.width - self.rect.width), -100
            self.speed = 5
            self.active = False
            self.mask = pygame.mask.from_surface(self.image)
        
        def move(self):
            if self.rect.top < self.height:
                self.rect.top += self.speed
            else:
                self.active = False
        
        def reset(self):
            self.active = True
            self.rect.left, self.rect.bottom = \
                randint(0, self.width - self.rect.width), -100
    

    六.主模块

    所有的模块都到齐了,接下来就该实现我们的主模块了

    
    # main.py
    import pygame
    import sys
    import traceback
    import myplane
    import enemy
    import bullet
    import supply
    
    from pygame.locals import *
    from random import *
    
    pygame.init()
    pygame.mixer.init()
    
    bg_size = width, height = 480, 700
    screen = pygame.display.set_mode(bg_size)
    pygame.display.set_caption("飞机大战 -- FishC Demo")
    
    background = pygame.image.load("images/background.png").convert()
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GREEN = (0, 255, 0)
    RED = (255, 0, 0)
    
    # 载入游戏音乐
    pygame.mixer.music.load("sound/game_music.ogg")
    pygame.mixer.music.set_volume(0.2)
    bullet_sound = pygame.mixer.Sound("sound/bullet.wav")
    bullet_sound.set_volume(0.2)
    bomb_sound = pygame.mixer.Sound("sound/use_bomb.wav")
    bomb_sound.set_volume(0.2)
    supply_sound = pygame.mixer.Sound("sound/supply.wav")
    supply_sound.set_volume(0.2)
    get_bomb_sound = pygame.mixer.Sound("sound/get_bomb.wav")
    get_bomb_sound.set_volume(0.2)
    get_bullet_sound = pygame.mixer.Sound("sound/get_bullet.wav")
    get_bullet_sound.set_volume(0.2)
    upgrade_sound = pygame.mixer.Sound("sound/upgrade.wav")
    upgrade_sound.set_volume(0.2)
    enemy3_fly_sound = pygame.mixer.Sound("sound/enemy3_flying.wav")
    enemy3_fly_sound.set_volume(0.2)
    enemy1_down_sound = pygame.mixer.Sound("sound/enemy1_down.wav")
    enemy1_down_sound.set_volume(0.2)
    enemy2_down_sound = pygame.mixer.Sound("sound/enemy2_down.wav")
    enemy2_down_sound.set_volume(0.2)
    enemy3_down_sound = pygame.mixer.Sound("sound/enemy3_down.wav")
    enemy3_down_sound.set_volume(0.5)
    me_down_sound = pygame.mixer.Sound("sound/me_down.wav")
    me_down_sound.set_volume(0.2)
    
    
    def add_small_enemies(group1, group2, num):
        for i in range(num):
            e1 = enemy.SmallEnemy(bg_size)
            group1.add(e1)
            group2.add(e1)
    
    
    def add_mid_enemies(group1, group2, num):
        for i in range(num):
            e2 = enemy.MidEnemy(bg_size)
            group1.add(e2)
            group2.add(e2)
    
    
    def add_big_enemies(group1, group2, num):
        for i in range(num):
            e3 = enemy.BigEnemy(bg_size)
            group1.add(e3)
            group2.add(e3)
    
    
    def inc_speed(target, inc):
        for each in target:
            each.speed += inc
    
    
    def main():
        pygame.mixer.music.play(-1)
        
        # 生成我方飞机
        me = myplane.MyPlane(bg_size)
        
        enemies = pygame.sprite.Group()
        
        # 生成敌方小型飞机
        small_enemies = pygame.sprite.Group()
        add_small_enemies(small_enemies, enemies, 15)
        
        # 生成敌方中型飞机
        mid_enemies = pygame.sprite.Group()
        add_mid_enemies(mid_enemies, enemies, 4)
        
        # 生成敌方大型飞机
        big_enemies = pygame.sprite.Group()
        add_big_enemies(big_enemies, enemies, 2)
        
        # 生成普通子弹
        bullet1 = []
        bullet1_index = 0
        BULLET1_NUM = 4
        for i in range(BULLET1_NUM):
            bullet1.append(bullet.Bullet1(me.rect.midtop))
        
        # 生成超级子弹
        bullet2 = []
        bullet2_index = 0
        BULLET2_NUM = 8
        for i in range(BULLET2_NUM // 2):
            bullet2.append(bullet.Bullet2((me.rect.centerx - 33, me.rect.centery)))
            bullet2.append(bullet.Bullet2((me.rect.centerx + 30, me.rect.centery)))
        
        clock = pygame.time.Clock()
        
        # 中弹图片索引
        e1_destroy_index = 0
        e2_destroy_index = 0
        e3_destroy_index = 0
        me_destroy_index = 0
        
        # 统计得分
        score = 0
        score_font = pygame.font.Font("font/font.ttf", 36)
        
        # 标志是否暂停游戏
        paused = False
        pause_nor_image = pygame.image.load("images/pause_nor.png").convert_alpha()
        pause_pressed_image = pygame.image.load("images/pause_pressed.png").convert_alpha()
        resume_nor_image = pygame.image.load("images/resume_nor.png").convert_alpha()
        resume_pressed_image = pygame.image.load("images/resume_pressed.png").convert_alpha()
        paused_rect = pause_nor_image.get_rect()
        paused_rect.left, paused_rect.top = width - paused_rect.width - 10, 10
        paused_image = pause_nor_image
        
        # 设置难度级别
        level = 1
        
        # 全屏炸弹
        bomb_image = pygame.image.load("images/bomb.png").convert_alpha()
        bomb_rect = bomb_image.get_rect()
        bomb_font = pygame.font.Font("font/font.ttf", 48)
        bomb_num = 3
        
        # 每30秒发放一个补给包
        bullet_supply = supply.Bullet_Supply(bg_size)
        bomb_supply = supply.Bomb_Supply(bg_size)
        SUPPLY_TIME = USEREVENT
        pygame.time.set_timer(SUPPLY_TIME, 30 * 1000)
        
        # 超级子弹定时器
        DOUBLE_BULLET_TIME = USEREVENT + 1
        
        # 标志是否使用超级子弹
        is_double_bullet = False
        
        # 解除我方无敌状态定时器
        INVINCIBLE_TIME = USEREVENT + 2
        
        # 生命数量
        life_image = pygame.image.load("images/life.png").convert_alpha()
        life_rect = life_image.get_rect()
        life_num = 3
        
        # 用于阻止重复打开记录文件
        recorded = False
        
        # 游戏结束画面
        gameover_font = pygame.font.Font("font/font.TTF", 48)
        again_image = pygame.image.load("images/again.png").convert_alpha()
        again_rect = again_image.get_rect()
        gameover_image = pygame.image.load("images/gameover.png").convert_alpha()
        gameover_rect = gameover_image.get_rect()
        
        # 用于切换图片
        switch_image = True
        
        # 用于延迟
        delay = 100
        
        running = True
        
        while running:
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()
                
                elif event.type == MOUSEBUTTONDOWN:
                    if event.button == 1 and paused_rect.collidepoint(event.pos):
                        paused = not paused
                        if paused:
                            pygame.time.set_timer(SUPPLY_TIME, 0)
                            pygame.mixer.music.pause()
                            pygame.mixer.pause()
                        else:
                            pygame.time.set_timer(SUPPLY_TIME, 30 * 1000)
                            pygame.mixer.music.unpause()
                            pygame.mixer.unpause()
                
                elif event.type == MOUSEMOTION:
                    if paused_rect.collidepoint(event.pos):
                        if paused:
                            paused_image = resume_pressed_image
                        else:
                            paused_image = pause_pressed_image
                    else:
                        if paused:
                            paused_image = resume_nor_image
                        else:
                            paused_image = pause_nor_image
                
                elif event.type == KEYDOWN:
                    if event.key == K_SPACE:
                        if bomb_num:
                            bomb_num -= 1
                            bomb_sound.play()
                            for each in enemies:
                                if each.rect.bottom > 0:
                                    each.active = False
                
                elif event.type == SUPPLY_TIME:
                    supply_sound.play()
                    if choice([True, False]):
                        bomb_supply.reset()
                    else:
                        bullet_supply.reset()
                
                elif event.type == DOUBLE_BULLET_TIME:
                    is_double_bullet = False
                    pygame.time.set_timer(DOUBLE_BULLET_TIME, 0)
                
                elif event.type == INVINCIBLE_TIME:
                    me.invincible = False
                    pygame.time.set_timer(INVINCIBLE_TIME, 0)
            
            # 根据用户的得分增加难度
            if level == 1 and score > 50000:
                level = 2
                upgrade_sound.play()
                # 增加3架小型敌机、2架中型敌机和1架大型敌机
                add_small_enemies(small_enemies, enemies, 3)
                add_mid_enemies(mid_enemies, enemies, 2)
                add_big_enemies(big_enemies, enemies, 1)
                # 提升小型敌机的速度
                inc_speed(small_enemies, 1)
            elif level == 2 and score > 300000:
                level = 3
                upgrade_sound.play()
                # 增加5架小型敌机、3架中型敌机和2架大型敌机
                add_small_enemies(small_enemies, enemies, 5)
                add_mid_enemies(mid_enemies, enemies, 3)
                add_big_enemies(big_enemies, enemies, 2)
                # 提升小型敌机的速度
                inc_speed(small_enemies, 1)
                inc_speed(mid_enemies, 1)
            elif level == 3 and score > 600000:
                level = 4
                upgrade_sound.play()
                # 增加5架小型敌机、3架中型敌机和2架大型敌机
                add_small_enemies(small_enemies, enemies, 5)
                add_mid_enemies(mid_enemies, enemies, 3)
                add_big_enemies(big_enemies, enemies, 2)
                # 提升小型敌机的速度
                inc_speed(small_enemies, 1)
                inc_speed(mid_enemies, 1)
            elif level == 4 and score > 1000000:
                level = 5
                upgrade_sound.play()
                # 增加5架小型敌机、3架中型敌机和2架大型敌机
                add_small_enemies(small_enemies, enemies, 5)
                add_mid_enemies(mid_enemies, enemies, 3)
                add_big_enemies(big_enemies, enemies, 2)
                # 提升小型敌机的速度
                inc_speed(small_enemies, 1)
                inc_speed(mid_enemies, 1)
            
            screen.blit(background, (0, 0))
            
            if life_num and not paused:
                # 检测用户的键盘操作
                key_pressed = pygame.key.get_pressed()
                
                if key_pressed[K_w] or key_pressed[K_UP]:
                    me.moveUp()
                if key_pressed[K_s] or key_pressed[K_DOWN]:
                    me.moveDown()
                if key_pressed[K_a] or key_pressed[K_LEFT]:
                    me.moveLeft()
                if key_pressed[K_d] or key_pressed[K_RIGHT]:
                    me.moveRight()
                
                # 绘制全屏炸弹补给并检测是否获得
                if bomb_supply.active:
                    bomb_supply.move()
                    screen.blit(bomb_supply.image, bomb_supply.rect)
                    if pygame.sprite.collide_mask(bomb_supply, me):
                        get_bomb_sound.play()
                        if bomb_num < 3:
                            bomb_num += 1
                        bomb_supply.active = False
                
                # 绘制超级子弹补给并检测是否获得
                if bullet_supply.active:
                    bullet_supply.move()
                    screen.blit(bullet_supply.image, bullet_supply.rect)
                    if pygame.sprite.collide_mask(bullet_supply, me):
                        get_bullet_sound.play()
                        is_double_bullet = True
                        pygame.time.set_timer(DOUBLE_BULLET_TIME, 18 * 1000)
                        bullet_supply.active = False
                
                # 发射子弹
                if not (delay % 10):
                    bullet_sound.play()
                    if is_double_bullet:
                        bullets = bullet2
                        bullets[bullet2_index].reset((me.rect.centerx - 33, me.rect.centery))
                        bullets[bullet2_index + 1].reset((me.rect.centerx + 30, me.rect.centery))
                        bullet2_index = (bullet2_index + 2) % BULLET2_NUM
                    else:
                        bullets = bullet1
                        bullets[bullet1_index].reset(me.rect.midtop)
                        bullet1_index = (bullet1_index + 1) % BULLET1_NUM
                
                # 检测子弹是否击中敌机
                for b in bullets:
                    if b.active:
                        b.move()
                        screen.blit(b.image, b.rect)
                        enemy_hit = pygame.sprite.spritecollide(b, enemies, False, pygame.sprite.collide_mask)
                        if enemy_hit:
                            b.active = False
                            for e in enemy_hit:
                                if e in mid_enemies or e in big_enemies:
                                    e.hit = True
                                    e.energy -= 1
                                    if e.energy == 0:
                                        e.active = False
                                else:
                                    e.active = False
                
                # 绘制大型敌机
                for each in big_enemies:
                    if each.active:
                        each.move()
                        if each.hit:
                            screen.blit(each.image_hit, each.rect)
                            each.hit = False
                        else:
                            if switch_image:
                                screen.blit(each.image1, each.rect)
                            else:
                                screen.blit(each.image2, each.rect)
                        
                        # 绘制血槽
                        pygame.draw.line(screen, BLACK, \
                                         (each.rect.left, each.rect.top - 5), \
                                         (each.rect.right, each.rect.top - 5), \
                                         2)
                        # 当生命大于20%显示绿色,否则显示红色
                        energy_remain = each.energy / enemy.BigEnemy.energy
                        if energy_remain > 0.2:
                            energy_color = GREEN
                        else:
                            energy_color = RED
                        pygame.draw.line(screen, energy_color, \
                                         (each.rect.left, each.rect.top - 5), \
                                         (each.rect.left + each.rect.width * energy_remain, \
                                          each.rect.top - 5), 2)
                        
                        # 即将出现在画面中,播放音效
                        if each.rect.bottom == -50:
                            enemy3_fly_sound.play(-1)
                    else:
                        # 毁灭
                        if not (delay % 3):
                            if e3_destroy_index == 0:
                                enemy3_down_sound.play()
                            screen.blit(each.destroy_images[e3_destroy_index], each.rect)
                            e3_destroy_index = (e3_destroy_index + 1) % 6
                            if e3_destroy_index == 0:
                                enemy3_fly_sound.stop()
                                score += 10000
                                each.reset()
                
                # 绘制中型敌机:
                for each in mid_enemies:
                    if each.active:
                        each.move()
                        
                        if each.hit:
                            screen.blit(each.image_hit, each.rect)
                            each.hit = False
                        else:
                            screen.blit(each.image, each.rect)
                        
                        # 绘制血槽
                        pygame.draw.line(screen, BLACK, \
                                         (each.rect.left, each.rect.top - 5), \
                                         (each.rect.right, each.rect.top - 5), \
                                         2)
                        # 当生命大于20%显示绿色,否则显示红色
                        energy_remain = each.energy / enemy.MidEnemy.energy
                        if energy_remain > 0.2:
                            energy_color = GREEN
                        else:
                            energy_color = RED
                        pygame.draw.line(screen, energy_color, \
                                         (each.rect.left, each.rect.top - 5), \
                                         (each.rect.left + each.rect.width * energy_remain, \
                                          each.rect.top - 5), 2)
                    else:
                        # 毁灭
                        if not (delay % 3):
                            if e2_destroy_index == 0:
                                enemy2_down_sound.play()
                            screen.blit(each.destroy_images[e2_destroy_index], each.rect)
                            e2_destroy_index = (e2_destroy_index + 1) % 4
                            if e2_destroy_index == 0:
                                score += 6000
                                each.reset()
                
                # 绘制小型敌机:
                for each in small_enemies:
                    if each.active:
                        each.move()
                        screen.blit(each.image, each.rect)
                    else:
                        # 毁灭
                        if not (delay % 3):
                            if e1_destroy_index == 0:
                                enemy1_down_sound.play()
                            screen.blit(each.destroy_images[e1_destroy_index], each.rect)
                            e1_destroy_index = (e1_destroy_index + 1) % 4
                            if e1_destroy_index == 0:
                                score += 1000
                                each.reset()
                
                # 检测我方飞机是否被撞
                enemies_down = pygame.sprite.spritecollide(me, enemies, False, pygame.sprite.collide_mask)
                if enemies_down and not me.invincible:
                    me.active = False
                    for e in enemies_down:
                        e.active = False
                
                # 绘制我方飞机
                if me.active:
                    if switch_image:
                        screen.blit(me.image1, me.rect)
                    else:
                        screen.blit(me.image2, me.rect)
                else:
                    # 毁灭
                    if not (delay % 3):
                        if me_destroy_index == 0:
                            me_down_sound.play()
                        screen.blit(me.destroy_images[me_destroy_index], me.rect)
                        me_destroy_index = (me_destroy_index + 1) % 4
                        if me_destroy_index == 0:
                            life_num -= 1
                            me.reset()
                            pygame.time.set_timer(INVINCIBLE_TIME, 3 * 1000)
                
                # 绘制全屏炸弹数量
                bomb_text = bomb_font.render("× %d" % bomb_num, True, WHITE)
                text_rect = bomb_text.get_rect()
                screen.blit(bomb_image, (10, height - 10 - bomb_rect.height))
                screen.blit(bomb_text, (20 + bomb_rect.width, height - 5 - text_rect.height))
                
                # 绘制剩余生命数量
                if life_num:
                    for i in range(life_num):
                        screen.blit(life_image, \
                                    (width - 10 - (i + 1) * life_rect.width, \
                                     height - 10 - life_rect.height))
                
                # 绘制得分
                score_text = score_font.render("Score : %s" % str(score), True, WHITE)
                screen.blit(score_text, (10, 5))
            
            # 绘制游戏结束画面
            elif life_num == 0:
                # 背景音乐停止
                pygame.mixer.music.stop()
                
                # 停止全部音效
                pygame.mixer.stop()
                
                # 停止发放补给
                pygame.time.set_timer(SUPPLY_TIME, 0)
                
                if not recorded:
                    recorded = True
                    # 读取历史最高得分
                    with open("record.txt", "r") as f:
                        record_score = int(f.read())
                    
                    # 如果玩家得分高于历史最高得分,则存档
                    if score > record_score:
                        with open("record.txt", "w") as f:
                            f.write(str(score))
                
                # 绘制结束画面
                record_score_text = score_font.render("Best : %d" % record_score, True, (255, 255, 255))
                screen.blit(record_score_text, (50, 50))
                
                gameover_text1 = gameover_font.render("Your Score", True, (255, 255, 255))
                gameover_text1_rect = gameover_text1.get_rect()
                gameover_text1_rect.left, gameover_text1_rect.top = \
                    (width - gameover_text1_rect.width) // 2, height // 3
                screen.blit(gameover_text1, gameover_text1_rect)
                
                gameover_text2 = gameover_font.render(str(score), True, (255, 255, 255))
                gameover_text2_rect = gameover_text2.get_rect()
                gameover_text2_rect.left, gameover_text2_rect.top = \
                    (width - gameover_text2_rect.width) // 2, \
                    gameover_text1_rect.bottom + 10
                screen.blit(gameover_text2, gameover_text2_rect)
                
                again_rect.left, again_rect.top = \
                    (width - again_rect.width) // 2, \
                    gameover_text2_rect.bottom + 50
                screen.blit(again_image, again_rect)
                
                gameover_rect.left, gameover_rect.top = \
                    (width - again_rect.width) // 2, \
                    again_rect.bottom + 10
                screen.blit(gameover_image, gameover_rect)
                
                # 检测用户的鼠标操作
                # 如果用户按下鼠标左键
                if pygame.mouse.get_pressed()[0]:
                    # 获取鼠标坐标
                    pos = pygame.mouse.get_pos()
                    # 如果用户点击“重新开始”
                    if again_rect.left < pos[0] < again_rect.right and \
                            again_rect.top < pos[1] < again_rect.bottom:
                        # 调用main函数,重新开始游戏
                        main()
                    # 如果用户点击“结束游戏”
                    elif gameover_rect.left < pos[0] < gameover_rect.right and \
                            gameover_rect.top < pos[1] < gameover_rect.bottom:
                        # 退出游戏
                        pygame.quit()
                        sys.exit()
                        
                        # 绘制暂停按钮
            screen.blit(paused_image, paused_rect)
            
            # 切换图片
            if not (delay % 5):
                switch_image = not switch_image
            
            delay -= 1
            if not delay:
                delay = 100
            
            pygame.display.flip()
            clock.tick(60)
    
    
    if __name__ == "__main__":
        try:
            main()
        except SystemExit:
            pass
        except:
            traceback.print_exc()
            pygame.quit()
            input()
    
    

    切记: 所有的模块应该放在同一个文件夹下

    最后运行我们的主模块就大功告成了!

    下面是运行结果:

    展开全文
  • 1小时1篇文学会python再做个飞机大战游戏

    万次阅读 多人点赞 2021-04-30 16:02:07
    小媛:我看他们都可以,直接做一个飞机大战,说是一下子就学会了。 小C:你是想先大概过一遍内容吗?还是具体的全面的学? 小媛:我想先有一点体验感,自己学的舒服玩一下也行。 小C:那就可以,1小时搞定。 小媛:...
  • Unity制作飞机大战。本篇文章完整的介绍了使用Unity引擎制作一个飞机大战的小游戏,附带游戏过程,截图和讲解,最后附带工程源码,请品尝!
  • h5飞机大战
  • 飞机大战一个简单的java项目
  • 第一次发博客,学了3天的android studio还有一点以前的java基础做了个飞机大战的游戏游戏比较简单大概就这几个功能1.会动的背景2.我的飞机3.发射子弹3.敌人飞机第一步新建一个项目我用的是Android4.4版本新建好项目...
  • demo:飞机大战游戏 python小项目

    万次阅读 多人点赞 2020-11-30 19:22:50
    记得刚学python那会,作过一个飞机大战小项目,这个项目非常经典,可以帮助初学者提高动手能力,今天把它分享出来一、项目介绍二、项目代码1.首先安装库2.主要python代码三、说明:代码功能都在注释里面,我就不做...
  • 飞机大战游戏

    2017-06-25 14:58:51
    飞机大战
  • 飞机大战.html

    2019-12-28 13:48:54
    飞机大战
  • 飞机大战planegame

    2018-05-17 17:26:33
    飞机大战planegame飞机大战planegame飞机大战planegame飞机大战planegame
  • 飞机大战坦克.zip
  • 飞机大战canvas.zip

    2021-06-07 14:06:36
    飞机大战
  • 飞机大战MFC

    2018-05-17 17:49:41
    vs下 飞机大战MFC 飞机大战MFC 飞机大战MFC 飞机大战MFC 飞机大战MFC
  • planegame飞机大战

    2018-05-17 17:52:45
    基于vs MFC实现飞机大战planegame飞机大战planegame飞机大战

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,535
精华内容 3,814
关键字:

飞机大战