我所知道坦克大战(单机版)之画出炮筒、让坦克发射多发炮弹、让炮弹消亡

28640

本章目的

  • 使用炮筒来确定坦克目前的方向
  • 让坦克发射多发炮弹问题

一、画出炮筒


一般坦克初始位置是不同的,但是我们目前的射击方向是与移动方向一致的

这就导致比较怪异,我们应该是初始时有一个射击方向,比如说:右边

class Tank{

    //省略其他关键性代码....
    
    //默认设计方向为右边
    private Direction ptdir = Direction.R;
}

而我们的射击方向有了,还需要与移动时方向一致同步并且画出来

这里我们只需要画出一条直线即可,可以使用drawLine方法

我们需要知道两点来确定一条直线,而在坐标中由横(X)、纵坐标(y)确定一个点。

而这drawLine方法的四参数实际就是确定两个点,要画的直线的起始点横纵坐标和终点的横纵坐标

X1,Y1是确定直线的起始点,即横坐标为x1,纵坐标为y1的点。

同理x2,y2确定直线的终点。

例:

A(x1,y1) B(x2,y2) 就可以画出直线AB了。
参数:

x1 - 第一个点的 x 坐标。

y1 - 第一个点的 y 坐标。

x2 - 第二个点的 x 坐标。

y2 - 第二个点的 y 坐标。

image.png

而我们的坦克中,想要画出对应方向的直线,则可以使用公式计算相应的坐标

image.png

class Tank{

    //省略其他关键性代码....
    
    //添加方法完成坦克的绘画
    public void draw(Graphics g) {
        //省略其他关键性代码....
        switch (ptdir) {
            case L:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x,y + Tank. HEIGHT/2);
                break;
            case LU:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x,y);
                break;
            case U:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x + Tank.WIDTH/2,y);
                break;
            case RU:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x + Tank.WIDTH,y);
                break;
            case R:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x + Tank.WIDTH,y + Tank. HEIGHT/2);
                break;
            case RD:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x + Tank.WIDTH,y + Tank. HEIGHT);
                break;
            case D:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x + Tank.WIDTH/2,y + Tank. HEIGHT);
                break;
            case LD:
                g.drawLine(x + Tank.WIDTH/2,y + Tank.HEIGHT/2,x,y + Tank. HEIGHT);
                break;
        }
    }
}

这时我们运行起来就发现,我们的坦克炮筒已经出来了

image.png

但是为什么它没有随着我们的移动方向而改变射击方向呢?

那么因为我们没有根据移动方向来进行调整,我们现在添加调整一下看看

class Tank{

    //省略其他关键性代码....
    
    void move() {
        //省略其他关键性代码....    
        if(this.dir !=Direction.STOP){
            this.ptdir = dir;
        }
    }
}

而之前我们根据坦克的方向进行发射炮弹,会有一个问题不知道大家发现没有

比如说当我们不动的时候,是无法发射炮弹出来的

那么现在我们根据射击方向进行发射,就可以解决这个问题

class Tank{

    //省略其他关键性代码....
    //坦克发射子弹.....
    public Missle fire(){
        //省略其他关键性代码....
        Missle missle = new Missle(x,y,ptdir);
        return missle;
    }
}

这时即使我们不动,也可以发射炮弹了

image.png

步骤总结

  • ✧Tank类增加新的属性ptDir
  • ✧每次move后根据Tank新的方向确定炮筒的方向
  • ✧将炮简用直线的形式表现出来

二、让坦克发射多发炮弹


目前我们的坦克按下Ctrl键时,就会发射一发炮弹

但是当我们再按下Ctrl键时,就会发现坦克发出的炮弹又从头开始了

这是因为我们目前只维护了一发炮弹的发射状态

那么怎么解决这个问题呢?

我们想想能不能将发出去的炮弹用一个容器将它装起来呢?

public class TankClient extends Frame {
    
    //使用集合容器管理炮弹
    List<Missle> missles = new ArrayList<Missle>();
    
    @Override
    public void paint(Graphics g) {
        //画出容器里的子弹
        for ( int i = 0; i < missles.size();i++){
            Missle m = missles.get(i);
            m.draw(g);
        }
        //画出坦克
        mytank.draw(g);
    }
    //省略其他关键性代码....
}

同时当我们按下Ctrl键时,我们就将炮弹放入集合容器中

class Tank{
    //坦克发射子弹.....
    public Missle fire(){
        //省略其他关键性代码.......
        Missle missle =new Missle(x,y,ptdir);
        tc.missles.add(missle);
        return missle;
    }
}   

这时我们运行起来就可以发现,我们可以连续打出多发炮弹了

image.png

但是有一个小问题:假如一直按住Ctrl键发出的炮弹就会没有间隔

image.png

这样的情况我们有两种解决方式:

  • 第一种:每一枚炮弹设置间隔时间
  • 第二种:将发射炮弹改为按下抬起释放Ctrl键发射炮弹

我们采用Ctrl键按下抬起发射炮弹来解决这个问题

class Tank{

    //省略其他关键性代码....
    //坦克键盘按下抬起监听器
    public void keyReleased(KeyEvent e) {
        int key = e.getKeyCode();
        switch (key) {
            case KeyEvent.VK_CONTROL:
                fire();
                break;
        }
        //省略其他关键性代码.......
    }
}

步骤总结

  • ✧使用容器装炮弹
  • ✧每当抬起Ctr键就往容器中加入新的炮弹
  • ✧逐一画出每一发炮弹

三、让炮弹消亡


由于我们目前采用集合容器的方式来管理发射的炮弹

若我们对于这个游戏一直玩个几小时,那么就会造成很多很多的炮弹积累在里面

我们现在在游戏的窗口中将我们当前的炮弹数量显示出来

public class TankClient extends Frame {
    
    //省略其他关键性代码....
    
    @Override
    public void paint(Graphics g) {
    
        //省略其他关键性代码....
        //展示炮弹容器当前数量
        g.drawString("missiles count:" + missles.size()+"",10,50);
    }
}

这下我们当前容器里的子弹数量就能显示出来了

image.png

刚刚我们说到若我们对于这个游戏一直玩个几小时,那么就会造成很多很多的炮弹积累在里面

那么我们什么时候就要让子弹消亡呢?

  • 击中敌方坦克时
  • 超出游戏边界时

我们为Missle子弹类添加属性区别存活状态、并且当超出边界时设置为消亡状态

class Missle{
    
    //区分子弹的存活
    private boolean live = true;
    public boolean isLive() {return live;}
    void move() {
        //省略其他关键性代码....
        //当子弹的x、y坐标小于0 或者大于游戏窗口的宽度与高度则消亡状态
        if (x < 0 || y < 0 || x > TankClient.WIDTH || y > TankClient.HEIGHT) {
            live = false;

        }
    }
     //省略其他关键性代码....
}

而在我们的游戏界面中的绘画方法,对于消亡的子弹我们从容器中里去除

class Missle{

    //引入TankClient管理子弹容器
    private TankClient tc;
    public Missle(int x, int y, Tank.Direction dir, TankClient tc) {
        this.x = x;
        this.y = y;
        this.dir = dir;
        this.tc = tc;
    }
    
    void move() {
        //省略其他关键性代码....
        //当子弹的x、y坐标小于0 或者大于游戏窗口的宽度与高度则消亡状态
        if (x < 0 || y < 0 || x > TankClient.GAME_WINDTH || y > TankClient.GAME_HEIGHT) {
            this.live = false;
            tc.missles.remove(this);
        }
     //省略其他关键性代码....
}

同时当我们发出炮弹的时候,将tc传给炮弹,让炮弹在消亡的时候自己去除

class Tank{

    //省略其他关键性代码....
    //坦克发射子弹.....
    public Missle fire(){
        //省略其他关键性代码.......
        Missle missle =new Missle(x,y,ptdir,tc);
        tc.missles.add(missle);
        return missle;
    }
}

image.png

image.png

步骤总结

  • ✧加入控制炮弹生死的量Live ( Missle )
  • ✧当炮弹已经死去就不需要对其重画
  • ✧当炮弹飞出边界就死亡
  • ✧当炮弹死亡就从容器中去除

参考资料


尚学堂:坦克大战(马士兵老师)

阅读 1.2k

心有多大,舞台就有多大

110 声望
23 粉丝
0 条评论

心有多大,舞台就有多大

110 声望
23 粉丝
文章目录
宣传栏