相关博文
《Java小游戏实现》:坦克大战http://blog.csdn.net/u010412719/article/details/51712663
《Java小游戏实现》:坦克大战(续一):http://blog.csdn.net/u010412719/article/details/51723570
《Java小游戏实现》:坦克大战(续二):http://blog.csdn.net/u010412719/article/details/51729655
《Java小游戏实现》:坦克大战(续三):http://blog.csdn.net/u010412719/article/details/51735013
《Java小游戏实现》:坦克大战(续四):http://blog.csdn.net/u010412719/article/details/51741059
最后一点要完成的功能如下
1、加入超级技能,例如:连发,大炮弹等。
2、为我方坦克加入生命值、血条
3、加入血块
完成这个功能比较简单。
在Tank类 中的事件处理中,加入一个按键B的事件,superfire()函数就是在8个方向上个产生一个炮弹。
//键盘按键松下时,也要进行记录
public void keyReleased(KeyEvent e) {
int key=e.getKeyCode();
switch(key){
//其它的case省略
//大绝招
case KeyEvent.VK_B:
if(this.live&&this.good){
tc.getMissiles().addAll(superFire());
}
break;
}
superFire()实现代码如下
//根据当前方向发射子弹
public Missile fire(Direction dir){
//计算子弹的位置,并利用炮筒的方向来new一个子弹对象
int x = this.x +(this.WIDTH)/2 - (Missile.WIDTH)/2;
int y = this.y + (this.HEIGHT)/2 -(Missile.HEIGHT)/2;
//根据坦克的类型(good)来new与之对应的子弹类型
Missile ms = new Missile(x,y,dir,this.good,this.tc);
return ms;
}
/*
* 函数功能:8个方向各发射一颗子弹
* */
public List superFire(){
Direction[] dirs = Direction.values();
List missiles =new ArrayList();
/*
* 除了暂停,其他每个方向都发射一个子弹
* */
for(int i=0;iif(dirs[i]!=Direction.STOP){
missiles.add(fire(dirs[i]));
}
}
return missiles;
}
分以下几步来完成:
1、为坦克类添加一个life属性。
//坦克的生命值
private int life = 100;
public int getLife() {
return life;
}
public void setLife(int life) {
this.life = life;
}
2、子弹每打主坦克一下,则生命值减少20.
在Missile类中的hitTank方法中代码如下:
public boolean hitTank(Tank t){
//先判断该坦克是否还是存活,如果已经死了,子弹就不打他了
if(!t.isLive()){
return false;
}
if(this.live&&this.good!=t.isGood()&&this.getRect().intersects(t.getRect())){//判断是否有碰撞
//碰撞之后,子弹和该坦克就应该都死了
this.live = false;//子弹死了
/*
* 如果是子弹击中了正方坦克,则生命值减 20,如果是敌方坦克,直接挂掉
* */
if(t.isGood()){
int life = t.getLife() -POWER;
if(life<=0){
t.setLive(false);
}
t.setLife(life);
}
else{
t.setLive(false);//敌方坦克死了
}
Explode e = new Explode(x,y,tc);
tc.getExplodes().add(e);
return true;
}
else{
return false;
}
}
有了生命值之后,我们就可以给我们的主坦克设置类似游戏中的生命值条图显示在主坦克头上了。
画血条比较简单,血条有两层细长条构成,底层是一层固定长度且透明的的细长条,表层是一层根据生命值来决定长度的细长条。在Tank类中新建一个函数bloodBar。
具体如下:
private void bloodBar(Graphics g) {
Color c = g.getColor();
g.setColor(Color.GREEN);
g.drawRect(x, y-10, this.WIDTH, 10);
int w = this.WIDTH * this.life/100;//根据生命值来决定显示的长度
g.fillRect(x, y-10, w, 10);
g.setColor(c);
}
在Tank类的draw方法中进行调用即可
public void draw(Graphics g){
if(!live){ //判断坦克是否存活,如果死了,则不绘画出来,直接返回
return ;
}
//不同的坦克绘制不同的颜色
Color c = g.getColor();
if(good){
g.setColor(Color.RED);
//给主坦克画血条
bloodBar(g);
}
else{
g.setColor(Color.BLUE);
}
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
//画一个炮筒
drawGunBarrel(g);
move();//根据键盘按键的结果改变坦克所在的位置
step--;
//敌方子弹开火
if(!this.good&&r.nextInt(40)>38){
this.tc.getMissiles().add(fire());
}
}
运行效果如下:
这里我们加入一个血块类,根据我们面向对象的思想,这个血块类,应该有如下属性和方法
1、位置,大小
2、标识是否存活的属性live
3、由于血块会运动,因此这里涉及一个运动点的集合
4、构造方法
5、画图显示的draw方法
6、由于存在和坦克的碰撞检测,因此还有getRect()方法
具体Blood类代码如下:
public class Blood {
private int x;
private int y;
private int w;
private int h;
private TankClient tc;
private int[][] pos ={
{300,400},{330,390},{340,370},{320,350},{300,370},{290,390}
};
private boolean live = true;
public void setLive(boolean live) {
this.live = live;
}
public boolean isLive() {
return live;
}
public Blood(int x, int y, int w, int h, TankClient tc) {
super();
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.tc = tc;
}
//用来表示跳动到第几个点了
private int step = 0;
public void draw(Graphics g){
if(!live){
return;
}
Color c = g.getColor();
g.setColor(Color.RED);
if(step>=pos.length){
step = 0;
}
int x = pos[step][0];
int y = pos[step][1];
step++;
g.fillRect(x, y, w, h);
g.setColor(c);
}
public Rectangle getRect(){
return new Rectangle(x,y,w,h);
}
}
由于坦克要吃血块,因此,在Tank类中,需要添加如下的方法:
//坦克吃血块
public boolean eatBlood(Blood blood){
if(this.live&&this.good&&this.getRect().intersects(blood.getRect())){
blood.setLive(false);
//吃完坦克满血
this.life = 100;
return true;
}
else{
return false;
}
}
以上就实现了坦克吃血块补血的功能。
刚在测试的过程中,坦克和坦克之间是可以相互跨越运动的,这是不允许的。因此,这里就对其进行一个完善。
处理坦克与坦克之间的相互碰撞问题,在本项目中,处理方法为:相互碰撞的坦克回到之前的位置即可。在我们的实际游戏中,我们见到的处理方式稍微要复杂一点,例如,双方坦克碰撞则会产生爆炸,都死掉,而一方的坦克碰撞则没事。
在Tank类中加入如下代码即可
/*
* 函数的功能:坦克不能跨越坦克
* */
public boolean notAcrossWithTank(Tank tank){
if(this.live&&tank.isLive()){
if(this.getRect().intersects(tank.getRect())){
this.x = oldX;
this.y = oldY;
tank.setX(tank.getOldX());
tank.setY(tank.getOldY());
return true;
}
}
return false;
}
/*
* 函数的功能:坦克不能跨越一系列坦克
* */
public boolean notAcrossWithTanks(List tanks){
for(int i=0;iif(this!=tanks.get(i)&¬AcrossWithTank(tanks.get(i))){
return true;
}
}
return false;
}
在TankClient类中进行如下的调用即可完成此功能
@Override
public void paint(Graphics g) {
//......与此功能无关的代码省略
for(int i=0;iget(i);
//敌方坦克不能跨越敌方坦克
enemy.notAcrossWithTanks(enemyTanks);
if(!enemy.isLive()){
enemyTanks.remove(enemy);
}
else{
enemy.draw(g);
enemy.tankHitWall(w1);
enemy.tankHitWall(w2);
}
}
//主坦克不能跨越敌方坦克
tk.notAcrossWithTanks(enemyTanks);
}
以上就对这个问题进行了修正。
完整代码可以在这里获取:https://github.com/wojiushimogui/Tank_2.3 下的Tank2.7是最终版本。
到这里为止,坦克大战的小游戏的实现到这里就彻底结束了。收获颇多,不过还需要花时间来理一理。