下面来完成桌面弹球游戏中最关键的一个类BallService(业务处理类)。
编写draw方法,分别将挡板、弹球、砖块、道具在画板中绘制出来,当游戏胜出,绘制胜出画面,当玩家失败,绘制失败画面。
挡板和弹球不难绘制,下面主要看下砖块和道具的绘制。
砖块类与控制台五子棋中的棋盘类很相似,可以用二维数组来表示所有的砖块,砖块的位置是不发生变化的,也就说不会涉及元素的移动,所以使用二维数组来表示是合理的。
在绘制砖块的时候,涉及两个问题,一个是创建和初始化砖块数组,另一个是绘制砖块。
在创建砖块的过程中,要掌握初始化二维数组和二重循环的知识。砖块的创建是有技巧的,首先砖块不是摆满的,假设这里设成11列6行,并且空缺的砖块是随机产生的,画板的宽度已设定为307,因此11列表示每块砖的大小为28,所以根据每块砖的大小、列号、行号就可以计算砖块的坐标:
x = i*imageSize;
y = j*imageSize;
那么如何决定哪些坐标有砖块,哪些坐标没有砖块呢?
通过随机函数Math.random()来决定
isDisable = Math.random()>0.8?true:false;
每个砖块还有类型,0表示普通砖块,没有魔法,1表示变长魔法,2表示变短魔法
那么如何决定哪些砖块有魔法,哪些没有呢?
当然初始化时没有显示出来的砖块一定是没有魔法,其余的砖块还是使用随机函数来决定是否有魔法。
if(isDisable){ //如果该砖块不存在,其魔法也不存在 random = 0; } random = (int)(Math.random()*3);
还需要注意的是在设置砖块的坐标的时候,如果该砖块有道具,还要设置道具的坐标。同样,在绘制砖块时,还要注意为有道具的砖块绘制相应的道具。
绘制砖块和道具的时候,分别使用了两个画图方法:
drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
绘制指定图像中已缩放到适合指定矩形内部的图像。
drawImage(Image img, int x, int y, ImageObserver observer)
绘制指定图像中当前可用的图像。
判断玩家是否胜出的方法,其实很简单,就是所有的砖块的状态都变为了不可用时,也就是所有砖块都被消除时,玩家就胜出了。
碰撞检测问题
1.弹球是否与砖块有碰撞
当砖块的中心与弹球的中心两点之间的距离小于n+r 时,就认为砖块与弹球碰撞上了。
2.道具或者弹球是否与挡板有碰撞:
判断道具与挡板是否碰撞上和判断弹球与挡板是否碰撞上的方法是一样的,碰撞检测的方法是这样设计的:
public boolean isHitStick(BallComponent bc)
不管是弹球实例还是道具实例都可以使用该方法。
小球最左面的点的横坐标最大不能超过档板的最右面点的横坐标(蓝线标记)
并且
小球最右面的点的横坐标最小不能小于档板的最左面点的横坐标(红线标记)
并且
小球最下面的点的纵坐标大于挡板的纵坐标
同时满足了以上三个条件,就认为弹球与挡板相撞了。
坐标变换
下面是时候让挡板、弹球、道具动起来了,挡板是靠玩家键盘操作控制的,而小球和道具则由程序来控制,下面分别实现挡板、小球、道具的坐标变换。
1.设置挡板图片的位置
判断键盘输入,来作出相应的处理。
2.设置道具的位置
3.实施魔法
调用magicDo(Stick stick)来实现。
4.设置弹球图片的位置
在移动弹球之前,先要保证小球的移动是合法的,也就是要判断弹球移动后,是否会碰到上、下、左、右边界。其中还有弹球碰到砖块、弹球碰到挡板这些特殊情况。
当弹球碰到左边界时,要将弹球的运动的X轴方向变为向右;
当弹球碰到右边界时,要将弹球的运动的X轴方向变为向左;
当弹球碰到上边界,要将弹球的运动Y轴方向改变为向下;
当弹球碰到挡板或者砖块时,都要改变弹球的Y轴方向为相反方向;
如果ball.getSpeedY()为正时,弹球向上运动,相反,如果ball.getSpeedY()为负时,向下
所以初始状态时,
private int speedX = 10;
private int speedY = 8;
弹球会向上走,这也就是玩家一移动挡板时,小球就向上走。
设定弹球的坐标:
// 设置x坐标 ball.setX(ball.getX() - (int) (Math.random() * 2)- ball.getSpeedX()); // 设置y坐标 ball.setY(ball.getY() - (int) (Math.random() * 2)- ball.getSpeedY());
调试
程序写完了,但是发现总是没有道具出现,初步判断创建砖块时,没有创建出类型为1或2的带魔法的砖块,发现在创建和设置砖块createBrickArr()中,打印出random,结果发现random这个值一直是0。
于是发现是这条语句发生了错误:
//创建一个新的砖块 random = (int)Math.random()*3;
应该是:
random = (int)(Math.random()*3);
马上检查了程序中其它使用Math.random()的代码,发现了并改正了:
//设置小球的坐标 ball.setX(ball.getX()-ball.getSpeedX()-(int)(Math.random()*2)); ball.setY(ball.getY()-ball.getSpeedY()-(int)(Math.random()*2));
怪不得在测试过程中总感觉小球的路径变化不大的缘故,因为如果不加括号的话,这个值(int)Math.random()*2也总是0。改正了上面存在的问题后,发现之前的问题是解决了,但是落下来的道具竟然是砖块!
找到LongMagic这个类,发现magicDo方法中:
double imageWidth = this.getImage().getWidth(null);
应该是:
double imageWidth = stick.getImage().getWidth(null); //当挡板没有变长过 if (stick.getPreWidth()<=imageWidth){ //加长变为原来的两倍 stick.setPreWidth((int)(stick.getPreWidth()*2)); // System.out.println("变长了"); }
打包
打包后运行,遇到Exception in thread "main" javax.imageio.IIOException: Can't read input file!错误。
在Eclipse中开发时,使用的是以当前工作路径为基准来寻找图片文件的:
stick = new Stick(width,height,"img/stick.jpg");
在创建Image图像时,使用的如下方法:
this.image = ImageIO.read(new File(path));
但是打包后,当前用户路径有可能发生变化,因此无法找到图片,解决办法是使用以当前类为基准的路径指定方式。
this.image = ImageIO.read(getClass().getResource(path));
这时就可以找到图片了。
为了确定指定的路径是否正确,可以打印出来:
System.out.println(getClass().getResource(path).toString());