前段线程学习过程中 小组合作完成的一个项目 我主要负责算法 嘿嘿 写的比较啰嗦 见谅 感谢各大牛指正
先上个图吧
欢迎界面
游戏中
实际开发过程中,经过第一次讨论,我们就定下了主要需要完成的几个模块:
1 挡板运动-鼠标或键盘监听器的运行方法
2 球的运行-球运动线程
3 砖块的显现-数组填充
4 球的碰撞算法
5 界面美化,图片插入等
我主要负责小球的碰撞算法和挡板的运动部分。我首先想到的是五子棋游戏过程中棋盘的绘制。所以将砖块宽高设置为Config接口中的属性,然后用数组表示砖块,插入图片。用属性值表示砖块有无,若小球碰到砖块,则将属性值赋为空,用背景色填充原位置。
弹球碰撞砖块的方法就是在线程中每次都遍历数组,判断球与哪个砖块碰撞。挡板与球碰撞时会根据水平方向球与挡板运动的方向使球加速或减速。
然后我就开始敲代码,先做了一个打企鹅的游戏,想法很清楚但是在实现过程中遇到一系列的问题:
首先就是如何碰撞砖块问题,如何判断小球碰撞的是哪个砖块的那个面这个问题摆在了面前,现在想起来很自然而然,但是开始确实很纠结了两天,后来忽然想到其实碰撞砖块就是四种主要情况,砖块的上下左右四个面,可以写四个方法,然后通过循环遍历数组判断球撞到的是哪个砖块的哪个面,然后给数组中相应的元素赋为空,用背景色填充相应位置,从而实现对砖块的碰撞。
搞定这个问题后,马上第二个就来了:小球能碰撞了,也能消除了,但是有时候消不掉,想了一阵后发现这个是因为碰撞时同时碰到两个球,所以我就在原来的四个方法里每个多加了一种判断情况,特别处理这种两个同时碰到的情况,发现问题解决了。
之后小球能碰砖块,也能消了了,运行起来没问题,但是有很小的概率会有擦肩而过,没有消除的状况。这个我一直到把其他部分做好后才想清楚,由于小球每次运动的距离是设定的,而砖块的宽高也是设定的,碰撞的边界消除条件就是根据这两部分设定的,如果小球的每次运动距离过大,就有可能超过边界判断的条件,从而使碰撞消除的方法未调用或调用错误,后来想到解决办法就是,将球的每次运动距离调小,将线程休眠时间也相应调小一点,就OK了,至此,砖块与小球碰撞的算法算是搞定。
然后就是挡板与小球的碰撞问题。最简单的就是用键盘或鼠标监听器控制每次画一个挡板。然后当小球与挡板碰撞时就将小球的竖直速度反向。呵呵,很快就做出来了,但是,在同志们的一致要求下,我们的目标是实现小球能与挡板摩擦,从而加速或减速。然后我就被迫开始另外想了。首先,玩了一阵网上的弹球游戏后,我借鉴了下别人的想法,将挡板分区,当球撞到左边某一块区域,就将小球的速度设为相应的值,撞到右边,设为另外的值,改变了小球的速度。但是发现这样虽然实现了碰撞改变速度和方向,但是小球的运动不符合物理规律,在同组成员的批评下,我再次做出了改正。实现接近物理规律的碰撞,在这里,暂不考虑能量损失,所以就需要水平方向动量守恒或接近守恒,用键盘监听器设置每次按键后挡板运动速度为固定值,然后就是对球速的判断,但是每次挡板碰撞是不会变速的,不符合动量守恒条件,所以最终我妥协了,只是根据球的速度和运动方向进行判断,每次球与挡板同向则给小球加速,反向减速,都是加减固定值,调整后使效果看起来不是太差。最终挡板碰撞算法版本暂定为这样,我也还在其他地方寻求相应算法,后续改进进行中。
算法写了个类如下:
public class SuFa {
private BallListener bl;
private int ballX;
private int ballY;
private String[][] brick;
private int X0,Y0;
private Graphics g;
private boolean gameOver=false;
public SuFa(BallListener bl,int ballX,int ballY,String[][] brick,int X0,int Y0,Graphics g){
this.bl=bl;
this.ballX=ballX;
this.ballY=ballY;
this.brick=brick;
this.X0=X0;
this.Y0=Y0;
this.g=g;
}
public int getX(){
return X0;
}
public int getY(){
return Y0;
}
public boolean isOver(){
return gameOver;
}
public void judge(){
//撞墙
if(ballX<=2|ballX>=558){
X0 = -X0;
}
if(ballY<=0){
Y0 = -Y0;
}
//游戏结束
if(ballY>=472){
javax.swing.JOptionPane.showMessageDialog(null, "游戏结束");
gameOver=true;
}
//球撞挡板的方法
if(ballX>=bl.getX()-30&&ballX<=bl.getX()+150&&ballY>=448){
Y0=-Y0;
if(X0>=0&&bl.getT().equals("right")){
X0=X0+5;
}else if(X0>=0&&bl.getT().equals("left")){
X0=X0-5;
}else if(X0<0&&bl.getT().equals("right")){
X0=X0+5;
}else if(X0<0&&bl.getT().equals("left")){
X0=X0-5;
}
}
// 球撞砖块的方法
for (int i = 0; i < brick.length; i++) {
for (int j = 0; j < brick[i].length; j++) {
// 撞砖块下表面
if (ballY <= (i + 1) * Config.Height&&ballY>(i + 1) * Config.Height-10
&& ballX >= j * Config.Width - 15
&& ballX <=(j + 1) * Config.Width-15
&& (brick[i][j].equals("") == false)&&Y0<0) {
if((ballX==j*Config.Width - 15)&&(brick[i][j-1].equals("") == false)){
brick[i][j] = "";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i][j-1]="";
g.setColor(Color.white);
g.fillRect((j-1)*Config.Width, i*Config.Height, Config.Width, Config.Height);
Y0 = -Y0;
}
if((ballX==(j+1)*Config.Width - 15)&&(brick[i][j+1].equals("") == false)){
brick[i][j] = "";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i][j+1]="";
g.setColor(Color.white);
g.fillRect((j+1)*Config.Width, i*Config.Height, Config.Width, Config.Height);
Y0 = -Y0;
}else{
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
Y0=-Y0;
}
}
// 撞砖块右表面
if (ballX <= (j + 1) * Config.Width && ballX>(j+1)*Config.Width-30&&ballY>= i * Config.Height-15
&& ballY <= (i + 1) * Config.Height-15
&& (brick[i][j].equals("") == false)&&X0<0) {
if((ballY==i * Config.Height-15)&&(brick[i-1][j].equals("")==false)){
brick[i][j] = "";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i-1][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, (i-1)*Config.Height, Config.Width, Config.Height);
X0 = -X0;
System.out.println("小球撞向右表面");
}
if((ballY==(i+1)*Config.Height-15)&&(brick[i+1][j].equals("")==false)){
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i+1][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, (i+1)*Config.Height, Config.Width, Config.Height);
X0=-X0;
System.out.println("小球撞向右表面");
}
else{
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
X0=-X0;
System.out.println("小球撞向右表面");
}
}
//撞砖块左表面
if(ballX>=j*Config.Width-30&&ballX<j* Config.Width&&ballY> i * Config.Height-15
&& ballY<= (i + 1) * Config.Height-15
&&(brick[i][j].equals("")==false)&&X0>0){
if((ballY==i * Config.Height-15)&&(brick[i-1][j].equals("")==false)){
brick[i][j] = "";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i-1][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, (i-1)*Config.Height, Config.Width, Config.Height);
X0 = -X0;
System.out.println("小球撞向左表面");
}
if((ballY==(i+1)*Config.Height-15)&&(brick[i+1][j].equals("")==false)){
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i+1][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width,(i+1)*Config.Height, Config.Width, Config.Height);
X0=-X0;
System.out.println("小球撞向左表面");
}
else{
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
X0=-X0;
System.out.println("小球撞向左表面");
}
}
//撞砖块上平面
if(ballY>=i*Config.Height-30&&ballY<i*Config.Height&&ballX>= j*Config.Width - 15
&& ballX<=(j + 1) * Config.Width-15
&&(brick[i][j].equals("")==false)&&Y0>0){
if(ballX==j*Config.Width-15&&brick[i][j-1].equals("")==false){
brick[i][j-1]="";
g.setColor(Color.white);
g.fillRect((j-1)*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i][j]="";
Y0=-Y0;
System.out.println("小球撞向上表面");
}
if(ballX==(j + 1) * Config.Width-15&&brick[i][j+1].equals("")==false){
brick[i][j+1]="";
g.setColor(Color.white);
g.fillRect((j+1)*Config.Width, i*Config.Height, Config.Width, Config.Height);
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
Y0=-Y0;
System.out.println("小球撞向上表面");
}
else{
brick[i][j]="";
g.setColor(Color.white);
g.fillRect(j*Config.Width, i*Config.Height, Config.Width, Config.Height);
Y0=-Y0;
System.out.println("小球撞向上表面");
}
}
}
}
}
}
总共我的项目设计部分完成过程就是这样。在此过程中,我最大的感触就是如何把握好独立与合作的关系。开始分工,我主负责算法,想法主线很清晰,但是实现过程各种错误不断,很多意想不到的问题出现,我就乱了,想找其他人沟通,但是种种原因吧,最终还是自己纠结又纠结把它给搞出来了。最终我意识到其实项目的真正团队开发就是分工与合作,每个人负责不同的部分,自己负责的部分问题出现都是难免的,调试再调试这才是学习和进步的最终方式。先将框架,主题功能实现,然后再步步优化。最终合作的结果是下一个人不需要了解前一个人的工作,只需要在相应的功能块上直接添加自己的部分,组合,拼装,OK了。如果每个自己的问题都需要团队其他人放下工作,一起来想,那么整个项目计划就会打乱。
第二个收获就是代码优化,思路实现。其实很久有句话就徘徊在我脑子里,姑且就当我说的吧:对代码的掌控力。必须时刻知道你这一块代码是干什么的,会有什么样的功能,也可能会有什么样的问题。我感觉自己每次敲,开始思路很清晰,但是确实也没法具体想到每个细节,然后出现问题,分析,分析,要不就马上搞定,要不就是一个很简单的问题在那绕圈,放一段时间再想忽然发现竟然如此简单,痛悔不已。也许这确实就是能力的问题,我个人的思路有时很清晰,有时有很凌乱,这个我发现问题越来越厉害了。主线思路可以很快有想法,但是到具体实现,一有问题出现就容易乱。回到掌控力,我觉得这个项目给我的启发就是下一次先把框架都先定好。例如第一遍写完了这些方法,看到那个Config类,我发现其实像这种砖块宽高,球的速度等涉及到参数的都可以写入一个这样个类,以后如果需要调整会很方便,所以我把所有涉及球的算法判断都写入了一个Sufa类,然后后期修改调整简洁了不少。
废话说了很多,不过该说的都说到了,这个算是给自己的一个总结吧,Java之路继续加油吧!!!
最新版游戏