JAVA第二个小项目——线程游戏的制作

JAVA第二个项目——线程游戏的制作

线程的游戏的制作,其实在做这个小项目的时候还是很无语的,做什么游戏呢??这个困扰了我很久,做飞机大战?做的人太多了,做坦克大战?没新意。后来就迁就了一下,做一个大球吃小球吧(其实觉得逼格还不如前两个),哈哈。

那首先介绍一下这个游戏吧,就是在一个界面,初始有几个小球,大小随机,自己控制一个小球,用点击鼠标的方式控制这个小球的移动轨迹,当碰到比自己小的球时就会吃掉它,并且自己的半径变成自己原来的半径加上吃掉的小球的半径。当然如果遇到比自己大的球时,那就悲剧了,被吃掉了,GAME OVER!

其实这个游戏不难是不是,恩,听着确实很简单,但是还是有一点小问题值得注意的,要不我也不会费力气写这篇博客对不对。

首先老规矩——添加一个界面,贴出代码:

<pre name="code" class="java">package Thread;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Thread_JFrame 
{
	private Ball_Init ballinit;
	public static void main(String[] args) 
	{
		Thread_JFrame jframe = new Thread_JFrame();
		jframe.Init();
		
	}

	public void Init()
	{
		JFrame frame = new JFrame();
		frame.setSize(1000,700);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(3);
		frame.setTitle("thread");
		frame.setResizable(false);
		//区别,setBackGround
		frame.getContentPane().setBackground(Color.black);
		frame.setLayout(new BorderLayout());
		JPanel panel = new JPanel();
		panel.setPreferredSize(new Dimension(65,0));
		panel.setBackground(Color.gray);
		//初始化每个小球的坐标
		ballinit = new Ball_Init();
		ballinit.init();
		//设置暂停按钮
		JButton but1 = new JButton("暂停");
		JButton but2 = new JButton("开始");
		//添加监听器
		DrawListener listener = new DrawListener(frame);
		but1.addActionListener(listener);
		but2.addActionListener(listener);
		panel.add(but1);
		panel.add(but2);
		frame.add(panel,BorderLayout.EAST);
		frame.setVisible(true);
		
		frame.addMouseListener(listener);
	}
}

 
这里还是注意一下我用的布局——BorderLayout,这个布局在之前的文章里讲过,好处就是可以明显的分块,这样有助于区分,比如这里,分成了两块,一块就是我们游戏小球移动的界面,再腾出一块地方用于储存按钮等组件。代码很容易懂。 

似乎还是有点说反了,第一步应该考虑的是不变的因素,那我们就再补上吧。什么东西不变呢?比如我们要控制界面上球的数量,储存每个球的半径,因为球要运动,所以还要设置球的运动方向的方向向量等等,直接贴出代码:

<pre name="code" class="java">package Thread;

import java.awt.Color;
import java.awt.Point;

public interface point {
	//设置数组下标的初始值
	public static final int num = 0;
	//设置储存点坐标的值
	public static final int Length = 25;
	public static final Point[] points = new Point[Length];
	//设置圆的大小
	public static final int[] radius = new int[Length];
//	public static final int Size = 36;
	//设置圆的颜色
	public static final Color[] colors = new Color[Length];
	//设置方向向量
	public static final double[] X = new double[Length];
	public static final double[] Y = new double[Length];
	
}

 
好了,现在说一下要注意的东西,因为现有的代码都是经过修改的,当时也忘了储存有问题的代码了,现在只能口述了,不过我尽量说的清楚点。其实很容易理解的,接触过编程的同学都知道,程序运行的时候是顺序运行的,就是说,按照你写的程序的顺序来运行的,这就出现了一个问题——一开始我不是直接写这个游戏的,一开始我是先写的是要实现很多球在屏幕上运动的效果,因为这是写这个游戏必须具备的技能。话说到这里大家可以思考一下怎么才能实现这个效果?用平时简单的for循环是不是很难做到这一点?也就是说用平常我们顺序写的程序的方法是不能实现这个效果的。这时候就要用到一个东西——线程。什么是线程呢?举个例子,比如我要点击屏幕一下就能画出一个运动的圆,再点击一下还会再画出一个运动的圆,以此类推,所有的圆都能同时运动。那么如果不用线程这是不可能用平常的程序实现的,在这里我们可以把每个运动的圆当做一个独立的线程,每个线程都会控制这个圆移动,他们之间“不受影响”。一个进程可以包含很多线程。如果对这几个概念不是太清楚的就自行百度吧哈哈。 

贴出Listener的代码:

package Thread;

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;

public class DrawListener extends MouseAdapter implements point,ActionListener{
	private JFrame frame;
	private DrawThread thread;
	private int Num;
	private Suspend suspend; 
	
	public DrawListener(JFrame frame)
	{
		this.frame = frame;
		this.Num = num;
		suspend = new Suspend();
		//声明进程
		thread = new DrawThread(this.frame,this.suspend);
		thread.start();
	}
	public void mouseReleased(MouseEvent e) 
	{
		//取点
		int x = e.getX();
		int y = e.getY();
		//改变小球的方向
		x = x-points[0].x;
		y = y-points[0].y;
		X[0] = (double)(x)/Math.sqrt(x*x+y*y);
		Y[0] = (double)(y)/Math.sqrt(x*x+y*y);
	}
	public void actionPerformed(ActionEvent e)
	{
		//每次判断是否暂停
		suspend.update();
	}
}

这个还是很简单的,因为我们现在要做的就是通过点击鼠标来改变球的方向,其实就是改变它的方向向量。至于为什么在构造函数中就要声明进程是因为我们的进程要在程序运行时就开始直到游戏结束。

现在来看看怎么实现一个线程类的,贴出代码:

package Thread;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class DrawThread extends Thread implements point{
    private Graphics g;
    private JFrame frame;
	private int x,y;
	private Judge judge;
	private Suspend suspend;
	private Hang hg;
	private int pp;
	
	public DrawThread(JFrame frame,Suspend suspend)
	{
		this.frame = frame;
		frame.setVisible(true);
		hg = new Hang(this.frame);
		judge = new Judge(suspend);
		this.g = this.frame.getGraphics();
		this.suspend = suspend;
		pp = 0;
	}
	public void run()
	{
		while(true)//一直循环
		{
			if(suspend.j())
			{
				try{
					Thread.sleep(5);
		    		}catch(Exception ef){}
				if(pp == 0) hg.hang();
				pp = 1;
				continue;
			}
			judge.judge();
			try{
				Thread.sleep(30);
	    		}catch(Exception ef){}
			Image image = new ImageIcon(this.getClass().getResource("11.jpg")).getImage();
			BufferedImage buff = new BufferedImage(935, 800, BufferedImage.TYPE_INT_ARGB);
			Graphics gg = buff.getGraphics();
			
			//画一个图像使能够动起来
			//待完成
			gg.drawImage(image,0,0,935,700,0,0,image.getWidth(null),image.getHeight(null),null);
		
			for(int i = 0;i < points.length; i++)
			{
				if(points[i] != null)
				{	
					if(points[i].x+radius[i]/2 <= 935)
					{
						x = points[i].x;
						y = points[i].y;
						gg.setColor(colors[i]);
						gg.fillOval(x,y,radius[i],radius[i]);
						//通过方向向量改变这个小球的路径
						points[i].x = (int)(points[i].x + 1.5*X[i]);
						points[i].y = (int)(points[i].y + 1.5*Y[i]);
					}
				}else 	break;
			}
			//将缓冲器里的东西画到原来的画板上
			g.drawImage(buff,0,0,null);
		}
	}
}

有个关键字是Thread,这个是一个线程的类,我们用这个线程所要干的东西就要写在它自带的函数run()函数里面。因为我们需要这个线程一直进行,那么就要用一个while(true)的循环来实现。在这里说一下对于圆是怎么处理的,为什么把所有的圆储存在数组里呢?因为我们每次画圆是通过遍历整个数组来画的,但是这出现了一个问题就是随着圆数量的增多,你会发现圆会“闪”?这是个什么问题呢?很有趣大家自己可以查查线程的本质。为了解决整个问题,我就用了一个buffer,每次把图画在buffer里面,再通过buffer画在界面上,这样问题就解决了。

那么剩下的就非常容易了,核心的东西都讲完了,其他的贴出代码大家自己看着玩玩吧。

球的初始化:

package Thread;

import java.awt.Color;
import java.awt.Point;
import java.util.Random;


//初始化小球的位置和速度矢量(状态)以及半径
public class Ball_Init implements point{
	private int num;
	
	public Ball_Init()
	{
		num = 0;
	}
	public void init()
	{
		double x,y;
		double dir_x,dir_y;
		int r;
		Random ran = new Random();
		while(num<25)
		{
			boolean p = true;
			//产生小球的坐标
			x = 18+ran.nextInt(900);
			y = 18+ran.nextInt(764);
			//产生方向矢量
			dir_x = 1+ran.nextInt(50);
			dir_y = 1+ran.nextInt(50);
//			System.out.println(dir_x+" "+dir_y);
			//保证每次移动是单位长度
			if(num%2 == 0)
				dir_x = -dir_x/(Math.sqrt(dir_x*dir_x+dir_y*dir_y));
			else 
				dir_x = dir_x/(Math.sqrt(dir_x*dir_x+dir_y*dir_y));
			dir_y = dir_y/(Math.sqrt(dir_x*dir_x+dir_y*dir_y));
			//产生小球的半径
			r = 1+ran.nextInt(34);
			radius[num] = r;
			double x1 = x-radius[num]/2;
			double y1 = y-radius[num]/2;
			//如果不相交就可以添加这个点
			for(int i = 0; i < points.length; i++)
			{
				if(points[i] != null)
				{
					if(Math.sqrt((x1-points[i].x)*(x1-points[i].x)+(y1-points[i].y)*(y1-points[i].y)) < (radius[i]+radius[num]))
					{
						p = false;
						break;
					}
				}else break;
			}
			if(p)
			{
				Random rann=new Random();
				int rr = rann.nextInt(225);
				int bb = rann.nextInt(225);
				int gg = rann.nextInt(30);
				colors[num] = new Color(rr,bb,gg); 
				//在数组中添加这个点
				if(num == 0)
				{
					x1 = 350; y1 = 350;
					points[num] = new Point((int)x1,(int)y1);
					X[num] = 1;
					Y[num] = 0;
					radius[num] = 20;
					colors[num] = Color.black;
				}else 
				{
					points[num] = new Point((int)x1,(int)y1);
					X[num] = dir_x;
					Y[num] = dir_y;
				}
				num++;
			}
		}
	}
}
判断输赢和暂停的原理:

package Thread;

import java.awt.Graphics;
import java.awt.Image;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

//画出初始时的状态
public class Hang implements point{
	private JFrame frame;
	private Graphics g;
	
	public Hang(JFrame frame)
	{
		this.frame = frame;
		g = this.frame.getGraphics();
	}
	
	public void hang()
	{
		int x,y;
		Image image1 = new ImageIcon(this.getClass().getResource("11.jpg")).getImage();
		g.drawImage(image1,0,0,935,700,0,0,image1.getWidth(null),image1.getHeight(null),null);
		for(int i = 0;i < points.length; i++)
		{
			if(points[i] != null)
			{	
				if(points[i].x+radius[i]/2 <= 935)
				{
					x = points[i].x;
					y = points[i].y;
					g.setColor(colors[i]);
					g.fillOval(x,y,radius[i],radius[i]);
				}
			}else 	break;
		}
	}
}
package Thread;

import java.awt.Dimension;
import java.awt.Font;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class Judge implements point{
	private Suspend suspend;
	private JFrame frame;
	public Judge(Suspend suspend)
	{
		frame = new JFrame();
		frame.setSize(400,300);
		frame.setDefaultCloseOperation(3);
		frame.setLocationRelativeTo(null);
		JLabel label = new JLabel("Game Over!!!");
		label.setPreferredSize(new Dimension(380,280));
		label.setFont(new Font("楷体",1,50));
		frame.add(label);
		
		this.suspend = suspend;
	}
	public void judge()
	{
		for(int i = 0;i < points.length; i++)
		{
			//遍历所有小球,找出越出边界的小球并更新这些小球
			if(points[i] != null)
			{
				Random ran = new Random();
				double dir_x = 1+ran.nextInt(50);
				double dir_y = 1+ran.nextInt(50);
				dir_x = dir_x/(Math.sqrt(dir_x*dir_x+dir_y*dir_y));
				dir_y = dir_y/(Math.sqrt(dir_x*dir_x+dir_y*dir_y));
				if((int)(dir_y)%2==1)	dir_x = -dir_x;
					
				if((points[i].x+radius[i])<=0)
				{
					points[i].y = 0;
					points[i].x = 20+ran.nextInt(890);
					X[i] = dir_x;
					Y[i] = dir_y;
				}else if((points[i].x-radius[i])>=935)
				{
					points[i].y = 0;
					points[i].x = 20+ran.nextInt(890);
					X[i] = dir_x;
					Y[i] = dir_y;
				}else if((points[i].y-radius[i]) >= 1000)
				{
					points[i].y = 0;
					points[i].x = 20+ran.nextInt(890);
					X[i] = dir_x;
					Y[i] = dir_y;
				}
			}
		}
		//通过边界判断操控的小球是否越界,如果越界,结果为输
		if(points[0].x-radius[0] <= 0)
		{
			suspend.setcount(1);
			frame.setVisible(true);
		}
		if(points[0].x+radius[0] >= 935)
		{
			suspend.setcount(1);
			frame.setVisible(true);
		}
		if(points[0].y+radius[0]>= 700)
		{
			suspend.setcount(1);
			frame.setVisible(true);
		}
		//遍历每个小球,如果相撞,大的小球就合并小的小球
		for(int i = 0; i < points.length; i++)
		{
			if(points[i] != null)
			{
				for(int j = i+1; j < points.length; j++)
				{
					if(points[i] != null)
					{
						//如果两个小球相撞,那么就可开始合并连个小球
						if(Math.sqrt((points[j].x-points[i].x)*(points[j].x-points[i].x)+(points[j].y-points[i].y)*(points[j].y-points[i].y)) <= (int)(radius[i]+radius[j]))
						{
							if(radius[i] >= radius[j])
							{
								radius[i] += radius[j];
								Random ran = new Random();
								points[j].y = 0;
								points[j].x = 20+ran.nextInt(890);
								radius[j] = 1+ran.nextInt(34);
							}else 
							{
								if(i == 0)
								{
									suspend.setcount(1);
									frame.setVisible(true);
								}
								radius[j] += radius[i];
								Random ran = new Random();
								points[i].y = 0;
								points[i].x = 20+ran.nextInt(890);
								radius[i] = 1+ran.nextInt(34);
 							}
						}
					}
				}
			}
		}
	}	
}




  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值