設計模式之Tank大戰02

知識回顧:前一篇講解了窗口裏面繪製的正方形已經可以跟隨着鍵盤的上下左右鍵移動了

如果按鍵改變的不是每次+10或者減10,這時候又要改變源碼,所以優化代碼

思路:使用枚舉定義坦克的方向:

新建一個枚舉類(Dir.class):定義坦克的方向,然後在TankFrame中調用

1.定義坦克方向的enum,根據按鍵狀態確定坦克方向,根據坦克方向確定坦克位移

public enum Dir {
    //方向向左
    LEFT,
    //方向向上
    UP,
    //方向向右
    RIGHT,
    //方向向下
    DOWN
}

2.修改TankFrame的方法讓Tank運動起來:

public class TankFrame extends Frame {
    private static  int x = 200;
    private static  int y = 200;
    //默認方向向下
    Dir dir = Dir.DOWN;
    //默認坦克的速度爲10
    private static  int SPEED = 3;

    public TankFrame(){
        //設置窗口的尺寸
        this.setSize(800,600);
        //設置窗口位置
        this.setLocation(500,200);
        //參數設置爲true,當程序運行時,窗口顯示
        this.setVisible(true);
        //設置窗口標題
        this.setTitle("Tank war");
        //添加鍵盤監聽事件
        this.addKeyListener(new MyKeyListener());
        //點擊窗口X關閉窗口,添加監聽事件
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    /**
     * 重寫paint方法,然後在窗口中指定位置畫出一個矩形
     * @param g
     */
    @Override
    public void paint(Graphics g){
        g.fillRect(x,y,25,25);
        switch (dir){
            case LEFT:
                x-=SPEED;
                break;
            case UP:
                y-=SPEED;
            case RIGHT:
                x+=SPEED;
                break;
            case DOWN:
                y+=SPEED;
                break;
                default:
                    break;
        }
    }

    /**
     * 鍵盤監聽處理類
     */
    class MyKeyListener extends KeyAdapter{
        //定義幾個默認的方向
        boolean bL = false;
        boolean bU = false;
        boolean bR = false;
        boolean bD = false;
        //按下一個鍵的時候
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch (key){
                case KeyEvent.VK_LEFT:
                    bL = true;
                    x-=SPEED;
                    break;
                case KeyEvent.VK_UP:
                    bU = true;
                    y-=SPEED;
                    break;
                case KeyEvent.VK_RIGHT:
                    bR = true;
                    x+=SPEED;
                    break;
                case KeyEvent.VK_DOWN:
                    bD = true;
                    y+=SPEED;
                    break;
                default:
                    break;
            }
            setTankDir();
        }
        //按鍵擡起的時候
        @Override
        public void keyReleased(KeyEvent e) {
            int key = e.getKeyCode();
            switch (key){
                case KeyEvent.VK_LEFT:
                    bL = false;
                    break;
                case KeyEvent.VK_UP:
                    bU = false;
                    break;
                case KeyEvent.VK_RIGHT:
                    bR = false;
                    break;
                case KeyEvent.VK_DOWN:
                    bD = false;
                    break;
                        default:
                            break;
            }
            setTankDir();
        }
        private void setTankDir(){
            if (bL)  dir=Dir.LEFT;
            if (bU)  dir=Dir.UP;
            if (bR)  dir=Dir.RIGHT;
            if (bD)  dir=Dir.DOWN;
        }

    }


}

3.現在我們都在TankFrame裏面做的一系列操作,現在我們是否需要抽象一個Tank的類,封裝相應的屬性和方法

抽象出坦克類,封裝相應的屬性和方法,在tankFrame中直接調用

package com.tank;

import java.awt.*;

/**
 * @author litao
 * @date 2020-05-25
 * Tank封裝類
 */
public class Tank {
    //定義初始位置
    private int x,y;
    //定義初始速度
    private static final  int SPEED = 10;
    //默認方向
    private Dir dir = Dir.DOWN;
    public Dir getDir() {
        return dir;
    }

    public void setDir(Dir dir) {
        this.dir = dir;
    }
    //定義坦克的構造方法
    public  Tank(int x,int y ,Dir dir){
        this.x = x;
        this.y = y;
        this.dir = dir;
    }

    /**
     * tank裏面封裝畫坦克的方法,直接在TankFrame裏面調用
     * @param g
     */
    public void paint(Graphics g){
        g.fillRect(x,y,50,50);
        switch (dir){
            case LEFT:
                x-=SPEED;
                break;
            case UP:
                y-=SPEED;
            case RIGHT:
                x+=SPEED;
                break;
            case DOWN:
                y+=SPEED;
                break;
            default:
                break;
        }
    }

}

4.怎樣處理坦克靜止的狀態,因爲現在我們的坦克默認是運動狀態的:moving = false;

思路:在tank類裏面添加一個boolean moving標識,如果上下左右鍵有按下,moving爲true,反之爲false;(在tankFrame裏面設置是否爲true或者false)

//TankFrame類裏面的方法,具體源碼看gitHub 
private void setMainTankDir(){
            //上下左右鍵都沒有按下時,設置moving爲false
            if(!bL && !bU && !bR && !bD){
                myTank.setMoving(false);
            }else {
                myTank.setMoving(true);
                if (bL) {
                    myTank.setDir(Dir.LEFT);
                }
                if (bU) {
                    myTank.setDir(Dir.UP);
                }
                if (bR) {
                    myTank.setDir(Dir.RIGHT);
                }
                if (bD) {
                    myTank.setDir(Dir.DOWN);
                }
            }
        }

5.按下Ctrl鍵,主站坦克打出一個子彈,用面向對象的思想考慮

思路:5.1封裝一個子彈,新建一個Bullect類

package com.tank;

import java.awt.*;

/**
 * @author litao
 * @date 2020-05-25
 * 子彈封裝類
 */
public class Bullet {

    //定義子彈的寬度和高度
    private  static  final int BULLET_WIDTH = 30,BULLET_HEIGHT = 30;
    private static  final  int SPEED = 10;
    //定義初始位置
    private  int x,y;
    //定義方向
    private  Dir dir;

    //子彈的構造方法
    public Bullet(int x,int y,Dir dir){
        this.x = x;
        this.y = y;
        this.dir = dir;
    }

    public void paint(Graphics g){
        Color color =g.getColor();
        g.setColor(Color.RED);
        g.fillOval(x,y,BULLET_WIDTH,BULLET_HEIGHT);
        g.setColor(color);
        move();
    }

    private void move() {
        switch (dir){
            case LEFT:
                x-=SPEED;
                break;
            case UP:
                y-=SPEED;
            case RIGHT:
                x+=SPEED;
                break;
            case DOWN:
                y+=SPEED;
                break;
            default:
                break;
        }
    }
    }


5.2按下ctrl鍵,tank會fire出一顆子彈,必須持有tankframe的引用,把子彈傳遞給窗口,畫出來

在Tank類裏面寫一個fire的方法:

按下ctrl鍵,tank會fire出一顆子彈,必須持有tankframe的引用,把子彈傳遞給窗口,畫出來

//坦克發射子彈
    public void fire() {
        tf.bullet = new Bullet(this.x,this.y,this.dir);
    }

6.連續按Ctrl可以打出許多的子彈,講子彈裝在一個容器裏就可以了。

List<Bullet> bullets = new ArrayList<>();

7.當我們打出的子彈數量超出我們畫的邊框的時候,子彈的數量還是在無限增加,所以當子彈飛出窗口以後,就把子彈remove掉。

消除子彈列表的內存泄漏問題,小心處理迭代器中的刪除問題,1:使用普通方式迭代 2:在迭代過程中刪除(iterator.remove)

和判斷靜止還是移動是一個道理,當超出邊界範圍的時候,直接remove掉bullets裏面的子彈數量

if(x < 0 || y < 0 || x > TankFrame.GAME_WIDTH || y > TankFrame.GAME_HEIGHT) living = false;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章