Java第二个项目--基于线程实现飞机大战

2018.8.8

前言:学习Java也有好长一段时间了,前几天搞了一下线程游戏,借此写篇博客留点东西供日后回来学习,也希望能帮到刚入门线程的小伙伴。

一、先看看游戏界面:有点不美观,23333。暂时还没有太多的时间去考虑细节,红色的为飞机的血量,没有血量的飞机是玩家的,hhh无敌有点作弊。

二、具体实现:

        其实这个就是根据那个多线程弹跳小球改过来了。我本来想用多线程来实现,但无奈空指针错误多得我改不过来。(期待大佬指正)所以我就用了简单的循环+队列数组和一个单线程。循环主要是产生随机的敌机,线程用来控制飞机的绘制和移动。为了更好的理解游戏的结构,我新建了四个类,分别是主界面、监听、产生飞机、产生子弹。每个类干好自己的事情就好了,需要共用的数据就传一下,emmm就是这么简单。在写的时候可能会遇到空指针的错误,这是类对象在传递的时候的错误,简单的说就是当年需要用这个对象的时候,但这个对象还没有被传递过来,如果使用它就会产生空指针错误,所以线程的启动时间需要特别注意!给各位朋友的建议就是在所有数据都到位后在启动绘制飞机的线程,这样百分百不会产生空指针错误。

三、详细代码分析:

为了更方便管理我们的代码,我们将代码分成四个部分:1、主界面类 ; 2、负责画飞机的类 ; 3、负责画子弹的类; 4、监听器类;详细说明已在代码加注释。

1.主界面类:

package com.Lzh_20180727;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import com.zn.ArrayList0720.*; //自定义的数组队列,用系统自带的会发生空指针异常

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;


import java.awt.Image;
;public class MainFrame extends JFrame implements Runnable{
	
	public static void main(String[] args) {
		MainFrame mf = new MainFrame();
		mf.Init();	
	}
	public ArrayList<Ball>list = new ArrayList();//存储小球的
	public ArrayList<DrawBullet>bulletlist = new ArrayList();//存储子弹的
	public volatile boolean StopFlag = false;//是否开始游戏的标志
	public volatile boolean PauseFlag = true;//是否处于游戏暂停状态
	public int Count = 0;
	public JLabel s ,Time;//游戏剩余时间
	
	//BallAI ballai = new BallAI(this,5);//将窗体和要生成的小球数量传过去
	
	public void Init()
	{
		this.setTitle("飞机大战2`.0");
		this.setSize(1400, 1000);
		this.setResizable(false);
		this.setLocationRelativeTo(null);
		this.getContentPane().setBackground(Color.black);
		this.setDefaultCloseOperation(3);
		String type[]={"启动","暂停","恢复","停止"};
		this.setLayout(null);
		AllListener AL = new AllListener(StopFlag,PauseFlag,this,bulletlist);//建立监听器对象
		
		ImageIcon IMG = new ImageIcon("src\\1285.gif");
		JLabel score = new JLabel();
		score.setIcon(IMG);
		score.setBounds(950, 20, 150, 50);
		score.setFont(new Font("宋体",Font.BOLD,30));
		this.add(score);
		
		ImageIcon MMG = new ImageIcon("src\\1313.gif");
		JLabel time = new JLabel();
		time.setIcon(MMG);
		time.setBounds(60, 20, 150, 50);
		time.setFont(new Font("宋体",Font.BOLD,30));
		this.add(time);
		
		 Time = new JLabel();
		Time.setBounds(250,20, 50, 50);
		Time.setFont(new Font("宋体",Font.BOLD,30));
		this.add(Time);
		s = new JLabel("0");
		
		s.setText(Count+" ");
		s.setBounds(1150, 20, 150, 50);
		s.setFont(new Font("宋体",Font.BOLD,30));
		this.add(s);
		for(int i=0;i<type.length;i++)
		{
			JButton button = new JButton(type[i]);
			button.setFont(new Font("宋体",Font.BOLD,30));
			
			button.setBounds(300+i*160, 20, 150, 50);
			this.add(button);
			button.addActionListener(AL);
		}
		
		
		this.setVisible(true);	
		Graphics g = this.getGraphics();
		ImageIcon MG = new ImageIcon("src\\飞机2.png");
		Image mg = MG.getImage();
		Ball ball = new Ball(30,50,10,5,6,10,Color.black,this,g,list,10,mg);
		ball.Craeat_Ball();
		AllListener LL =  new AllListener(StopFlag,PauseFlag,this,bulletlist);
		this.addMouseListener(LL);
		this.addMouseMotionListener(LL);//由于游戏是鼠标左键开火和拖拉操作飞机,故需要添加两种监听器
	
	}

	public void run() { //绘制飞机的线程
		
		//System.out.println("画球线程启动!StopFlag:"+StopFlag);
		
		while(StopFlag)
		{
			if(PauseFlag)
			{
				
				BufferedImage b = new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_INT_RGB);
				Graphics pan = b.getGraphics();
				ImageIcon image = new ImageIcon("src\\boom.gif"); 
				Image img = image.getImage();
				for(int t=120;t>=0;t--)//倒计时,时间结束后GameOver
				{
					Time.setText(t+"");
				}
				
		
			   pan.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), null);
				
				pan.fillRect(list.get(0).x-3*list.get(0).r, list.get(0).y-2*list.get(0).r, 
				this.list.get(0).blood*10, 10);//飞机当前的生命值
				this.list.get(0).drawBall(pan);
				this.list.get(0).moveBall();
				//this.list.get(0).Iscrash(0);
				for(int i=1;i<this.list.size();i++)
				{
					//list.get(i).clearBall();
					pan.setColor(Color.red);
					pan.fillRect(list.get(i).x-3*list.get(i).r, list.get(i).y-2*list.get(i).r, 
							list.get(i).blood*10, 10);
					list.get(i).drawBall(pan);
					list.get(i).moveBall();
					//list.get(i).Iscrash(i);
					if(list.get(i).blood<=0)//生命值为0的飞机要移除掉
					{
						list.remove(i);
					}
				}
				if(list.size()==1)
				{
					Graphics g = this.getGraphics();
					ImageIcon MG = new ImageIcon("src\\飞机2.png");
					Image mg = MG.getImage();
					Ball ball = new Ball(30,50,10,5,6,10,Color.black,this,g,list,10,mg);
					ball.Craeat_Ball();
				}
				for(int j=0;j<this.bulletlist.size();j++)//绘制子弹
				{
					if(this.bulletlist.get(j).y==0)//到达边界的子弹要移除,否则内存容易溢出
					{
						this.bulletlist.remove(j);
						break;
					}
					this.bulletlist.get(j).drawbullet(pan);//绘制子弹
					this.bulletlist.get(j).movebullet();//移动子弹
					this.bulletlist.get(j).Ishit();	//子弹是否击中飞机
					
				}
				Graphics g = this.getGraphics();
				
			
				g.drawImage(b, 0, 150, null);//次画布
				
				try { //异常抛出
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
	
	
}

2.负责保存飞机信息的类:

package com.Lzh_20180727;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import com.zn.ArrayList0720.*;
import java.util.Random;

import javax.swing.ImageIcon;

public class Ball {
	
	
	public int x=30,y=30,r=5,speedX=5,speedY=7,num=0;//初始化一些数据,防止空指针错误
	public int blood =0;//飞机的生命值
	private Color c = Color.black;//这个没用到
	private Image img ;//飞机的图片对象
	public MainFrame mf;//将主界面传过来
	private Graphics g;//绘制需要的画笔类
	private ArrayList<Ball>list;//存储飞机所有信息的队列数组
	private Random random = new Random();//随机数,主要用于产生敌机的位置和生命值
	public Ball(int x, int y, int r, int speedX, int speedY,int num, Color c, MainFrame mf,
			Graphics g, ArrayList<Ball> list,int blood,Image img) {//构造函数
		super();
		this.x = x;
		this.y = y;
		this.r = r;
		this.speedX = speedX;
		this.speedY = speedY;
		this.c = c;
		this.mf = mf;
		this.g = g;
		this.list = list;
		this.num = num;
		this.blood = blood;
		this.img = img ;
	}
//	public Ball(int x,int y,int r,int speedX,int speedY)
//	{
//		this.x = x;
//		this.y = y;
//		this.r = r;
//		this.speedX = speedX;
//		this.speedY = speedY;
//	}
	public void drawBall(Graphics gg)
	{
//		int i = random.nextInt(255);
//		int j = random.nextInt(255);
//		int k = random.nextInt(255);
//		g.setColor(new Color(i,j,k));//设不设置颜色都无所谓了,因为我们画的是图片
		gg.setColor(Color.blue);
	//	gg.fillOval(x-r, y-r, 2*r, 2*r);
		gg.drawImage(img, x-r, y-r, null);//绘制飞机
	}
	public void moveBall()//飞机移动的方法
	{
		if(x+r>=mf.getWidth())//到达右边界
		{
			speedX*=-1;
		}
		else if(x+r<0)
		{
			speedX*=-1;
		}
		if(y+r>=mf.getHeight())
		{
			speedY*=-1;
		}
		else if(y+r<0)
		{
			speedY*=-1;
		}
		x+=speedX;
		y+=speedY;
	}
	public void clearBall()//清除飞机的,现在不需要了,用remove就好了。
	{
		g.setColor(Color.black);
		g.fillOval(x-r-speedX, y-r-speedY, 2*r, 2*r);
	}
//	public void Iscrash(int  i)//此方法原来是判断飞机间是否碰撞的,但现在判断子弹和飞机,故注释
//	{
//		for(int j=0;j<mf.list.size();j++)
//		{
//			if(i==j) continue;
//			
//				int DistanceX=x-mf.list.get(j).x;
//				int DistanceY=y-mf.list.get(j).y;
//				int RadiiI=r/2;
//				int RadiiJ=mf.list.get(j).r/2;
//				//两个小球之间的距离小于两者的半径和
//				if((DistanceX*DistanceX+DistanceY*DistanceY)<=((RadiiI+RadiiJ)*(RadiiI+RadiiJ))) {
//					if(x>mf.list.get(j).x) { 
//						speedX*=1;
//						blood-=1;
//					}
//					else	
//					{
//						speedX*=-1;
//						blood-=1;
//					}
//					if(y>mf.list.get(j).y) { 
//						speedY*=1;
//						blood-=1;
//					}
//					else	
//					{
//						speedY*=-1;
//						blood-=1;
//					}
//					
//				}
//				
//			}
//		}
	public void Craeat_Ball()//生成绘制飞机所需要的信息,并存入队列数组
	{
		
		ImageIcon image = new ImageIcon("src\\飞机1.png"); 
		Image img = image.getImage();
		Ball myball = new Ball(400,400,30,0,0,num,Color.red,mf,g,list,0,img);
		list.add(myball);
		System.out.println("生成球的方法被调用!");
		for(int i=0;i<num;i++)
		{
			int x = random.nextInt(mf.getWidth());
			int y = random.nextInt(mf.getHeight());
			int r = 25;
			int b = random.nextInt(10)+5;
			int speedX = 0;
			int speedY = 2;
			int or = random.nextInt(10)+2;
			ImageIcon iimage = new ImageIcon("src\\飞机"+or+".png"); 
			Image iimg = iimage.getImage();
			Ball ball = new Ball(x,y,r,speedX,speedY,num,Color.BLUE,mf,g,list,b,iimg);
			list.add(ball);	//添加到队列数组里面去。
		}
		
	}
	
}

3.绘制子弹类:

package com.Lzh_20180727;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import com.zn.ArrayList0720.*;
import java.util.Random;

import javax.swing.JOptionPane;

public class DrawBullet {
	
	//public  ArrayList<DrawBullet>bulletlist;
	public int x,y,r,speedX,speedY; //定义绘制子弹需要的信息
	public Color c ;
	private Random random = new Random();
	public boolean IsFire = false;是否开火的标志
	public MainFrame mf;
	private Image img ;
	public DrawBullet(int x,int y,int r,int speedX,int speedY,Color c,MainFrame mf ,
			Image img  )//构造函数,传入需要的数据
	{
		this.mf = mf ;
		
		this.x = x;
		this.y = y;
		this.r = r;
		this.speedX = speedX;
		this.speedY = speedY;
		this.c = c;
		this.img = img;
	}
	
	public void drawbullet(Graphics gg)//根据拿到的信息绘制子弹
	{
		//gg.setColor(Color.red);
	//	gg.fillOval(x, y, 2*r, 2*r);
		gg.drawImage(img, x+10, y-50, 40,80,null);
		
	}
	public void movebullet()//子弹移动
	{
//		if(x+r>=mf.getWidth())
//		{
//			speedX*=-1;
//		}
//		else if(x+r<0)
//		{
//			speedX*=-1;
//		}
//		if(y+r>=mf.getHeight())
//		{
//			speedY*=-1;
//		}
//		else if(y+r<0)
//		{
//			speedY*=-1;
//		}
		y+=speedY;
	}
	public void Ishit()//子弹是否击中飞机,遍历所有的子弹和飞机,计算两者的距离,小于某个值则碰撞,飞机生命值减少。
	{
		for(int i=1;i<mf.list.size();i++)
		{
			for(int j=0;j<mf.bulletlist.size();j++)
			{
				int DistanceX = mf.list.get(i).x - mf.bulletlist.get(j).x;//算出两个点间的x
				int DistanceY = mf.list.get(i).y - mf.bulletlist.get(j).y;
				int DistanceR = mf.list.get(i).r + mf.bulletlist.get(j).r+6;
				if(DistanceX*DistanceX + DistanceY*DistanceY <= DistanceR*DistanceR )
				{
				//	System.out.println("碰撞!");
					mf.list.get(i).blood -=1;
					mf.Count++;
					mf.s.setText(mf.Count+"");
					if(mf.Count==100)
					{
						String msg = String.format("恭喜通过!");
						JOptionPane.showMessageDialog(mf, msg);
					}
					System.out.println("COUNG:"+mf.Count);
					mf.bulletlist.remove(j);

				}
				
				
						
			}
		}
	}
}

4.监听器类:

package com.Lzh_20180727;

import java.awt.Color;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import com.zn.ArrayList0720.*;

import javax.swing.ImageIcon;

public class AllListener extends MouseAdapter implements ActionListener {
	
	
	 public  volatile boolean StopFlag ,PauseFlag;
	 public MainFrame mf ;
	 public ArrayList<DrawBullet>bulletlist;
	 private boolean Isfire = false;
	 
	 public AllListener( boolean StopFlag, boolean PauseFlag,MainFrame mf,ArrayList<DrawBullet>bulletlist)
	 {
		 this.StopFlag = StopFlag;
		 this.PauseFlag = PauseFlag;
		 this.mf = mf;
		 this.bulletlist = bulletlist;
		 
	 }
	 
	 public void actionPerformed(ActionEvent e)
	 {
		 if(e.getActionCommand().equals("启动"))
		 {
			 mf.StopFlag = true;
			 System.out.println("启动!"+StopFlag);
			 Thread t = new Thread(mf);
			 t.start();
				
		 }
		 else if(e.getActionCommand().equals("暂停"))
		 {
			 mf.PauseFlag = false;
		 }
		 else if(e.getActionCommand().equals("恢复"))
		 {
			 mf.PauseFlag = true;
		 }
		 else if(e.getActionCommand().equals("停止"))
		 {
			 mf.StopFlag = false;
		 }
	 }
	 public void mousePressed(MouseEvent e) {
		// bulletlist.clear();
		 Isfire = true;
		 int x = mf.list.get(0).x+5;
		 int y = mf.list.get(0).y-30;
		 Isfire = true;
		 ImageIcon IMG = new ImageIcon("src\\bullet2.png");
		 Image img = IMG.getImage();
		 DrawBullet mybullet = new DrawBullet(x,y,5,0,-10,Color.red,mf,img); 
		 bulletlist.add(mybullet);
		
		 
		 
	 }
	 public void mouseReleased(MouseEvent e) {
		 Isfire = false; 
		  
	 }
	 public void mouseMoved(MouseEvent e){

		 int x = e.getX();
		 int y = e.getY()-150;
		 mf.list.get(0).x = x-mf.list.get(0).r;
		 mf.list.get(0).y = y-mf.list.get(0).r;
		 
	 }
	 
	 
	 public void mouseDragged(MouseEvent e){
		 
		
		 
	 }
}

我有罪啊。。8.8就写的博客现在才完成。。请鞭策我。。。下次绝对不懒了!!

猜你喜欢

转载自blog.csdn.net/weixin_42294984/article/details/81510471
今日推荐