打砖块.docx
《打砖块.docx》由会员分享,可在线阅读,更多相关《打砖块.docx(20页珍藏版)》请在冰豆网上搜索。
打砖块
资讯|安全|论坛|下载|程序开发|源码|站长学院|电子书|QQ|网络|系统|数据库|考试|手册
Web开发|软件开发|安全编程|移动开发|其他综合|
首页>程序开发>软件开发>Java>正文
窗体顶端
窗体底端
以打砖块游戏来理解多线程
2011-08-0910:
08:
10我来说两句
收藏我要投稿[字体:
小大]
前几天学习了多线程,现在总结自己做的一个打砖块的游戏,以此来加深对多线程的理解(如有不正确的地方欢迎指正!
)。
首先来看游戏的效果图:
首先要有一个界面,界面的实现在前面已经作过很多次了,具体代码如下:
/**
*初始化窗体
*/
publicvoidinitFrame(){
this.setTitle("喷怒的小球");//设置窗体的标题
this.setSize(500,750);//设置窗体的大小
//this.getContentPane().setBackground(Color.BLACK);
this.setLayout(newFlowLayout());//设置流式布局管理器
JButtonbt=newJButton("开始");
JButtonbt1=newJButton("停止");
JPanelpanel=newJPanel();
Dimensiond=newDimension(495,650);
panel.setBackground(Color.BLACK);
panel.setPreferredSize(d);
this.add(bt);
this.add(bt1);
this.add(panel);
this.setResizable(false);//设置窗体的大小不可变
this.setDefaultCloseOperation(3);//点击关闭时退出窗体
this.setVisible(true);//将窗体显示在屏幕上
//设置焦点
bt.setFocusable(false);
bt1.setFocusable(false);
panel.setFocusable(true);
finalGraphicsg=panel.getGraphics();//得到画布
}
得到窗体之后,需要一个挡板,所以定义一个挡板类,并且这个挡板能够在窗体底部水平移动,所以这里是定义的挡板类是实现MouseMotionListener的接口,使得画出的挡板能够随着鼠标的移动而移动,然后在这个类里面定义挡板的属性和实现画挡板的方法。
挡板的属性有左上角的坐标,长,宽以及颜色等,为了美观,这里是直接画挡板的一张图片,具体代码如下所示:
publicclassFenderimplementsMouseMotionListener{
publicstaticintx=0;
publicstaticintgetX(){
returnx;
}
publicinty=630;
publicintwidth=100;
privateintheight=20;
privateJPanelpanel;
privateGraphicsg;
publicFender(){}
publicFender(netjava.wxh0807pm1.BallFrame.mypanelpanel){
this.panel=panel;
g=panel.getGraphics();
}
//重写父类的方法
publicvoidmouseMoved(MouseEvente){
//清除图像
g.setColor(panel.getBackground());
g.fillRect(x,y,width,height);
x=e.getX();
//g.setColor(Color.RED);
if(x>=400){
x=400;
}
//画挡板
createFender(g,x,y,width,height);
}
//画挡板的方法
publicvoidcreateFender(Graphicsg,intx,inty,intwidth,intheight){
javax.swing.ImageIconicon=newjavax.swing.ImageIcon("src\\netjava\\wxh0807pm1\\image\\5.png");
g.drawImage(icon.getImage(),x,y,width,height,null);
}
publicvoidmouseDragged(MouseEvente){
}
}
然后需要画出自己设计的砖块,这里是直接以地图的形式画砖块的。
首先是准备几张砖块的图片,然后是把要画得区域看成一个二维数组,二维数组中的元素为零的地方表示该区域没有画砖块,以不同数字表示不同的砖块,然后在另外一个文件中设计二维数组的元素以画出自己想要画得地图。
二维数组设计好之后,首先要定义一个方法来把文件读取到内存中,这时就用到了输入输出流的知识,在前面已经总结过,这里不再罗嗦了。
但是读取到的是字符串,所以还需要定义一个方法将字符串转化为数组,然后还要定义一个得到图片的静态方法,最后要定义一个根据得到的数组和图片创建地图的方法。
将前面三个方法写成一个类,具体代码如下所示:
publicclassMapTest{
/**
*读取文件中的地图数据
*
*@parampath
*@return
*/
publicstaticint[][]readMap(Stringpath){
try{
//创建文件输入流
FileInputStreamfis=newFileInputStream(path);
BufferedInputStreambis=newBufferedInputStream(fis);
byte[]bs=newbyte[bis.available()];
//将数据从流中读取到数组中
bis.read(bs);
Stringstr=newString(bs);
//对字符串进行处理
//System.out.println(str);
int[][]arr=changeToArray(str);
returnarr;
}catch(Exceptionef){
ef.printStackTrace();
}
returnnull;
}
/**
*将字符串转化为数组
*
*@paramstr
*@return
*/
privatestaticint[][]changeToArray(Stringstr){
//根据回车换行符将字符串分割为字符串数组
String[]strs=str.split("\r\n");
int[][]array=newint[strs.length][strs[0].length()];
//遍历字符串数组
for(inti=0;iStrings=strs[i];
//对字符串进行解析
char[]cs=s.toCharArray();
for(intj=0;jcharc=cs[j];
//将字符串转成数字
intnum=Integer.parseInt(c+"");
array[i][j]=num;
}
}
returnarray;
}
//根据路径得到图片对象的方法
publicstaticImageIconcreateImageIcon(Stringpath){
.URLurl=MapTest.class.getResource(path);
ImageIconicon=newImageIcon(url);
returnicon;
}
}
最后一个方法的代码如下:
/**
*根据地图数组创建地图
*@paramarray
*/
publicstaticvoidcreateMap(int[][]array,Graphicsg){
for(inti=0;ifor(intj=0;jif(array[i][j]!
=0){
intnum=array[i][j];
Stringpath="image/"+num+".png";
//根据路径构造图片对象
ImageIconicon=MapTest.createImageIcon(path);
g.drawImage(icon.getImage(),35*j,15*i,null);
}
}
}
}
上面的方法都写成之后只要调用就可以实现砖块的绘制了。
现在还需要绘制一个小球,这个小球是一个线程,所以定义一个小球类,在该类里面定义小球的属性和画得方法,在小球的移动过程中还要判断小球与界面的左右以及上边的碰撞反弹以及小球与砖块的碰撞。
小球与砖块的碰撞主要分别从砖块的四条边考虑与小球的碰撞,因为根据上面的方法得到数组可以得到每个砖块的位置,然后在遍历数组,判断数组中的每一个砖块是否与小球相撞,然后在做相应的反弹,砖块碰到小球之后要把砖块消掉,所谓消掉就是把砖块画成与背景一样的颜色,把数组中对应的元素变为零。
然后在判断小球是否与挡板碰撞,如果碰撞,则弹回,如果挡板没有接住小球,则游戏结束。
然后在写一个方法判断是否赢了,同样是遍历上面得到的数组,如果数组的元素全为零,则说明砖块全被打完了,则赢了。
具体点的代码如下所示:
/**
*小球类
*@authorlenovo
*
*/
publicclassBallextendsThread{
java.util.Randomrd=newjava.util.Random();
publicstaticintx0=240;
publicstaticinty0=605;
privateintwidth=20;
privateintheight=20;
privateintx1;
privateinty1;
privateJPanelpanel;
privateGraphicsg;
privateFenderfd;
publicstaticbooleanisStop=false;
publicstaticbooleanisPause=false;
publicBall(){}
publicBall(JPanelpanel,Fenderfd){
this.fd=fd;
this.panel=panel;
g=panel.getGraphics();
//小球的增量
x1=8;
y1=-8;
}
publicvoidrun(){
draw();
}
publicvoiddraw(){
Fenderfd=newFender();
while(!
isStop){
while(!
isPause){
//javax.swing.ImageIconicon=newjavax.swing.ImageIcon("src\\netjava\\wxh0807pm1\\image\\6.png");
//清除图像
g.setColor(panel.getBackground());
g.fillRect(x0,y0,width,height);
//遍历数组,判断是否与砖块相撞
//int[][]array=MapTest.readMap("src\\netjava\\wxh0806\\image\\map");
for(inti=0;ifor(intj=0;jif(BallFrame.arr[i][j]!
=0){
if(x0>=35*j-1&&x0<=35*j+10&&y0<=15*i+15&&y0>=15*i){//砖块左边碰撞
g.setColor(panel.getBackground());
g.fillRect(35*j,15*i,36,15);
//System.out.println("11");
BallFrame.arr[i][j]=0;
x1=-x1;
}elseif(y0>=15*i-1&&y0<=15*i+15&&x0<=35*j+35&&x0>=35*j){//砖块上边判断
g.setColor(panel.getBackground());
g.fillRect(35*j,15*i,36,15);
//System.out.println("12");
BallFrame.arr[i][j]=0;
y1=-y1;
}elseif(y0<=15*i+15+1&&y0>=15*i&&x0<=35*j+35&&x0>=35*j){//砖块下边判断
g.setColor(panel.getBackground());
g.fillRect(35*j,15*i,36,15);
//System.out.println("13");
BallFrame.arr[i][j]=0;
y1=-y1;
}elseif(x0>=35*j+35+1&&x0<=35*j+35-10&&y0<=15*i+15&&y0>=15*i){//砖块右边判断
g.setColor(panel.getBackground());
g.fillRect(35*j,15*i,36,15);
//System.out.println("14");
BallFrame.arr[i][j]=0;
x1=-x1;
}
}
}
}
isWin(BallFrame.arr);
if(x1!
=0){
if(x0<=0||x0>=470){//左右两壁
x1=-x1;
//System.out.println("1");
}elseif(y0<=0){//上下两壁
y1=-y1;
//System.out.println("2");
}elseif((x0<=0&&y0<=0)||x0<=0||(x0>=470&&y0<=0)||x0>=470){//垂直碰撞四壁
x1=-x1;y1=-y1;
//System.out.println("3");
}
elseif(y0>=630-20&&y0<=630-20+10&&x0<=Fender.getX()+100-10&&x0>=Fender.getX()-10){
//System.out.println("------------");
y1=-y1;
//System.out.println("0");
}elseif(y0>640&&y0<650){
javax.swing.JOptionPane.showMessageDialog(null,"加油哦!
");
}
x0+=x1;
y0+=y1;
//画球
createBall(g,x0,y0);
//g.drawImage(icon.getImage(),x0+=x1,y0+=y1,null);
//g.setColor(Color.RED);
//g.fillOval(x0+=x1,y0+=y1,width,height);
}
try{
Thread.sleep(40);
}catch(Exceptionep){
ep.printStackTrace();
}
}
try{
Thread.sleep
(1);
}catch(Exceptionef){
ef.printStackTrace();
}
}
}
//画球的方法
publicvoidcreateBall(Graphicsg,intx,inty){
javax.swing.ImageIconicon=newjavax.swing.ImageIcon("src\\netjava\\wxh0807pm1\\image\\6.png");
g.drawImage(icon.getImage(),x,y,null);
}
/**
*判断输赢的方法
*@paramchars
*/
publicvoidisWin(int[][]array){
intcount=0;
for(intm=0;mfor(intn=0;nif(array[m][n]!
=0){
count++;
}
}
}
System.out.println(count);
if(count==0){
JOptionPane.showMessageDialog(null,"YOUWIN!
!
!
");
stopThread();
}
}
}
然后再在小球类里面定义暂停、继续等的方法来控制小球的线程,具体代码如下所示:
//暂停的方法
publicstaticvoidpauseThread(){
isPause=true;
}
//继续的方法
publicstaticvoidresumeThread(){
isPause=false;
}
//停止的方法
publicstaticvoidstopThread(){
isPause=true;
isStop=true;
}
//初始的方法
publicstaticvoidinitThread(){
isPause=false;
isStop=false;
}
然后再在初始化窗体的方法里面定义一个内部匿名类,来启动线程,但是在这个类里面用到的不在此类里的变量都要定义成final,具体代码如下:
//匿名内部类
ActionListeneralt=newActionListener(){
publicvoidactionPerformed(ActionEvente){
Stringcommand=e.getActionCommand();
if(command.equals("开始")){
//读取文件
int[][]array=MapTest.readMap("src\\netjava\\wxh0807pm1\\image\\map");
arr=array;
//画图片
createMap(array,g);
Ballb=newBall(panel,fd);
b.start();
bt.setText("暂停");
}
if(command.equals("暂停")){
Ball.pauseThread();
bt.setText("继续");
}
if(command.equals("继续")){
Ball.resumeThread();
bt.setText("暂停");
}
if(command.equals("停止")){
Ball.stopThread();
bt.setText("开始");
}
}
};
//添加监听器
bt.addActionListener(alt);
bt1.addActionListener(alt);
Fenderfd=newFender(panel);
panel.addMouseMotionListener(fd);
然后再重绘挡板、小球以及砖块就可以了,重绘代码如下所示:
//重绘
classmypanelextendsJPanel{
publicvoidpaint(Graphicsg){
//重写父类的方法
super.paint(g);
//遍历砖块数组,实现重绘
for(inti=0;ifor(intj=0;jif(arr[i][j]!
=0){
intnum=arr[i][j];
Stringpath="image/"+num+".png";
ImageIconicon=MapTest.createImageIcon(path);
g.drawImage(icon.getImage(),35*j,15*i,35,15,null);
}
}
}
//重绘挡板
Fenderf=newFender(this);
f.createFender(g,Fender.x,630,100,20);
//重绘小球
Ballball=newBall();
ball.createBall(g,Ball.x0,Ball.y0);
}
}
到这里基本的游戏已成型了,但是发现砖块消掉不完全或则还没被碰到的砖块已经被擦掉了,所以要重新启动一个线程来不停的对画图区域进行刷新,具体代码如下所示:
//刷新画布监听线程
classPaintThreadextendsThread{
publicvoidrun(){
while(!
Ball.isStop){
while(!
Ball.isPause){
//重绘
repaint();
try{
Thread.sleep
(1);
}catch(Exceptionef){
ef.printStackTrace();
}
}
try{
Thread.sleep
(1);
}catch(Exceptionef){
ef.printStackTrace();
}
}
}
}
这样弄之后把上面的问题解决了,但是又发现挡板、小球在不停的闪动,这时就需要根据双缓冲原理在swing中实现消除闪烁,所以上面重绘的代码改动如下:
//重绘
classmypanelextendsJPanel{
publicvoidpaint(Graphicsg){
//重写双缓冲机制
offSreenImage=this.createImage(495,650);
//获得截取图片的画布
GraphicsgImage=offSreenImage.getGraphics();
//获取画布的底色并且使用这种颜色填充画布,如果没有填充效果的话,则会出现拖动的效果
gImage.setColor(gImage.getColor());
//有清楚上一步图像的功能,相当于gImage.clearRect(0,0,WIDTH,HEIGHT)
gImage.fillRect