`
pgl
  • 浏览: 3614 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

线程游戏实战之像素肥鸟

 
阅读更多
  基于被这款游戏的虐心经历后,决定把刚学的线程应用出来,模仿写出这一个游戏,基本游戏功能实现后打算写个2.0增加左右飞行方向,和别的物体互动,比如道具获得,怪物出现。 现已经基本实现原本游戏功能。
  主要是有六个类来实现,游戏界面类,管子类,管子管理线程类,鸟管理线程类,鸟飞行鼠标监听器类,还有一个动画实现管理类。

首先是游戏界面类,用来创建游戏窗口和启动动画实现管理:
 
public class mainUI {
	//程序入口
	public static void main(String[] args) {
		mainUI ui = new mainUI();
	}
	//构造函数,自动调用初始化方法和开始游戏方法
	public mainUI() {
		// TODO Auto-generated constructor stub
		initUI();
	}
	
	//初始化游戏窗口
	public void initUI(){
		//判断是否开始游戏的标记
		boolean startflag = false; 
		
		JFrame jf = new JFrame("Flappy Bird");
		jf.setSize(600, 500);
		jf.setLocationRelativeTo(null);
		jf.setResizable(false);
		jf.setDefaultCloseOperation(3);
		jf.setVisible(true);
		
		
		//创建游戏动画线程管理线程
		Administration ad = new Administration(jf);
		ad.start();
	}

}


  然后是动画实现管理类,在写这么一个类之前是纠结了好一段时间了,因为刚开始写的时候发现管子的线程,和鸟的线程,和背景的动画实现,没办法协调起来,分别独自画在窗体上会出现相互覆盖的情况,所以后来想出了这么个类,用来获取当前鸟的坐标,还有管子队列中各个管子坐标,然后和背景一起在这个类里面的BufferedImage()里面画出来,再一起显示,没有看过别人写的动画实现方法,就只能先想出这个办法了。

public class Administration extends Thread {

	private JFrame jf;
	private boolean die;
	
	public Administration(JFrame jf){
		this.jf = jf;
	}
	
	public void run(){
		
		//创建柱子队列管理线程,管理柱子的生成
		PipeManage pm = new PipeManage(jf);
		pm.start();
		
		//创建鸟管理线程,管理鸟的动态
		BirdManage bm = new BirdManage(jf);
		bm.start();
		
		//创建图片图标
		ImageIcon baitian = new ImageIcon("image/白天.png");
		ImageIcon dimian = new ImageIcon("image/地.png");
		ImageIcon zheng = new ImageIcon("image/正.png");
		ImageIcon fan = new ImageIcon("image/反.png");
		ImageIcon shang = new ImageIcon("image/上.png");
		ImageIcon zhong = new ImageIcon("image/中.png");
		ImageIcon xia = new ImageIcon("image/下.png");
		//飞行状态
		int fly = 3;
		ImageIcon gameover = new ImageIcon("image/gameover.png");
		//图片坐标
		int x = 0;
		//管子队列起画坐标
		int xx = 0;
		//创建图片缓冲区
		BufferedImage buffer = new BufferedImage(jf.getWidth(), jf.getHeight(), BufferedImage.TYPE_INT_RGB);
		//获取缓冲区画布
		Graphics g = buffer.getGraphics();
		//获取窗体画布
		Graphics gg = jf.getGraphics();
		while(true){
			
			//延迟
			delay(10);
			
			//起画坐标移动
			x--;
			xx--;
			if(x <= -baitian.getIconWidth())
				x = 0;
			
			//判断是否碰撞
			iscrash(bm,pm,xx,zheng.getIconWidth(),zhong.getIconWidth(),zhong.getIconHeight(),dimian.getIconHeight());
			if(die){
				gg.drawImage(gameover.getImage(),(jf.getWidth()-gameover.getIconWidth())/2,(jf.getHeight()-gameover.getIconHeight())/2,null);
				this.stop();
			}
			
			//清屏
			g.setColor(jf.getBackground());
			g.fillRect(0, 0, jf.getWidth(), jf.getHeight());
			
			//----------画背景----------------------
			g.drawImage(baitian.getImage(),x,0,null);
			g.drawImage(baitian.getImage(),x+baitian.getIconWidth(),0,null);
			g.drawImage(baitian.getImage(),x+2*baitian.getIconWidth(),0,null);
			
			
			//---------画管子-------------
			for(int i=1;i<=pm.getPipeNum();i++){
				g.drawImage(fan.getImage(),xx+pm.getPipe(i).position,pm.getPipe(i).gap-fan.getIconHeight(),null);
				g.drawImage(zheng.getImage(),xx+pm.getPipe(i).position,pm.getPipe(i).gap+130,null);
			}
			//---------画地面-------
			g.drawImage(dimian.getImage(),x,jf.getHeight()-dimian.getIconHeight(),null);
			g.drawImage(dimian.getImage(),x+dimian.getIconWidth(),jf.getHeight()-dimian.getIconHeight(),null);
			g.drawImage(dimian.getImage(),x+2*dimian.getIconWidth(),jf.getHeight()-dimian.getIconHeight(),null);
			
			//---------画鸟--------
			if(fly==3){
				g.drawImage(shang.getImage(),bm.getX(),bm.getY(),null);
				fly--;
			}else if(fly==2){
				g.drawImage(zhong.getImage(),bm.getX(),bm.getY(),null);
				fly--;
			}else{
				g.drawImage(xia.getImage(),bm.getX(),bm.getY(),null);
				fly = 3;
			}
			
			//累计得分
			g.setColor(Color.black);
			g.drawString(CountScore(bm,pm, xx,zheng.getIconWidth()), 50, jf.getHeight()-dimian.getIconHeight()+50);			
			
			//显示出图片缓冲区的内容
			gg.drawImage(buffer, 0, 0, null);

		}
	}
	
	//累计得分
	 public String CountScore(BirdManage bm,PipeManage pm,int xx,int PipeWidth){
		 int score=0;
		 for(int i=1;i<=pm.getPipeNum();i++){
			 if(bm.getX()>=pm.getPipe(i).position+xx+PipeWidth)
				 score++;
		 }
		 System.out.println(score);
		 return Integer.toString(score);
	 }
	//判断碰撞方法
	public void iscrash(BirdManage bm,PipeManage pm,int xx,int PipeWidth,int birdWidth,int birdHeight,int dimian){
		for(int i=1;i<=pm.getPipeNum();i++){
			//判断上柱子(右碰)
			if(bm.getX()+birdWidth >= pm.getPipe(i).position+xx && bm.getX()+birdWidth <= pm.getPipe(i).position+xx+PipeWidth && bm.getY() <= pm.getPipe(i).gap-5){
				die = true;
				return;
			}
			//判断上柱子(左碰)
			if(bm.getX() >= pm.getPipe(i).position+xx && bm.getX() <= pm.getPipe(i).position+xx+PipeWidth && bm.getY() <= pm.getPipe(i).gap-5){
				die = true;
				return;
			}
			//判断下柱子(右碰)
			if(bm.getX()+birdWidth >= pm.getPipe(i).position+xx && bm.getX()+birdWidth <= pm.getPipe(i).position+xx+PipeWidth && bm.getY()+birdHeight >= pm.getPipe(i).gap+145){
				die = true;
				return;
			}
			//判断下柱子(左碰)
			if(bm.getX() >= pm.getPipe(i).position+xx && bm.getX() <= pm.getPipe(i).position+xx+PipeWidth && bm.getY()+birdHeight >= pm.getPipe(i).gap+145){
				die = true;
				return;
			}
			if(bm.getY()+birdHeight >= jf.getHeight()-dimian+12){
				die = true;
				return;
			}
		}
	}

	//延迟方法
	public void delay(int t){
		try {
			sleep(t);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


然后是管子线程管理类,用来生成管子对象,并移动管子坐标,用来给动画实现管理类提供管子坐标,实现动画还有碰撞判断:
public class PipeManage extends Thread {

	private static nodeQuene nq = new nodeQuene();;
	private int position, Heigh;
	private int gap;
	private JFrame jf;

	public PipeManage(JFrame jf) {
		this.position = jf.getWidth();
		this.Heigh = jf.getHeight();
	}

	public void run() {
		Random rd = new Random();
		

		while (true) {
			// 延迟
			try {
				sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 移动管子坐标
				position++;
				
			// 管子队列
			if (nq.size() == 0) {
				// 产生管子随机缝隙纵坐标
				gap = 70 + rd.nextInt(Heigh - 300);
				pipe pp = new pipe(position, gap);
				nq.add(pp);
			} else {
				// 产生管子随机缝隙纵坐标
				gap = 70 + rd.nextInt(Heigh - 300);
				// 如果前一个管子移动得足够远,生成管子,加入队列
				if (nq.get(nq.size()).o.position <= position -180) {
					pipe pp = new pipe(position, gap);
					nq.add(pp);
				}
			}
		}

	}

	public pipe getPipe(int i) {
		return nq.get(i).o;
	}

	public int getPipeNum() {
		return nq.size();
	}
}


这个是管子类:
public class pipe {

	public int position;
	public int gap;
	
	public pipe(int position,int gap){
		this.gap = gap;
		this.position = position;
	}
}


然后就是鸟线程管理类,由于只有一直鸟,就没有再特地写一个鸟的类了,就直接放在这个类里面了:
public class BirdManage extends Thread{

	private int X=150,Y=250;
	static boolean flyingFlag;
	static int dist = 25;
	private JFrame jf;
	
	public BirdManage(JFrame jf){
		this.jf = jf;
	}
	public void run(){
		//鼠标监听器
		BirdListener bl = new BirdListener();
		jf.addMouseListener(bl);
		
		while(true){
			//延迟
			try {
				sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			//如果按下了起飞
			if(flyingFlag){
				
				if(dist<=0){
					dist=25;
					flyingFlag = false;
				}
				else{
					Fly();
					dist--;
				}
				continue;
			}
			
			//自动下落
			DownBird();
		}
	}
	public void Fly(){
		Y-=3;
	}
	public void DownBird(){
		Y+=2;
	}
	public int getX(){
		return X;
	}
	public int getY(){
		return Y;
	}
}


还有就是点击事件,让鸟改变方向移动一段距离:
public class BirdListener extends MouseAdapter {

	
	public void mousePressed(MouseEvent e) {
		BirdManage.flyingFlag = true;
		BirdManage.dist = 25;
	}
}


还有就是一个队列类nodeQueue(),就不拿出来啦。。

最后感觉做出来的游戏,动画上还是和原作差不多,但是鸟的移动就感觉有点生硬,在考虑重力加速度而不是单纯地匀速移动之后应该会有更好的效果。然后看到最近挺火的超难游戏I Wanna 之后,感觉添上一些未知陷阱会添加更多欢乐,接下来往这方面修改。
分享到:
评论
1 楼 ayaome 2014-03-20  
重力加速度还是比较容易实现的,考虑加上去,效果要好很多

相关推荐

Global site tag (gtag.js) - Google Analytics