天天看點

坦克大戰(上)

項目建設

坦克大戰(上)

在IDEA下建立一個項目TankGame檔案,檔案下建立TankGame類和MyPanel類

TankGame類主要用于遊戲入口和視窗的相關配置

MyPanel類主要用于繪畫坦克、子彈、移動、爆炸效果等

背景顯示

TankGame繼承JFrame并重寫構造器,給(main)啟動入口

import javax.swing.*;

/**
 * @author 謝陽
 * @version 1.8.0_131
 */
public class TankGame extends JFrame {
    MyPanel mp = null;

    public TankGame() {//重寫構造器
        mp = new MyPanel();//初始化屬性
        this.setSize(1100, 800);//設定界面大小
        this.add(mp);//添加mp
        this.setVisible(true);//設定可見
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定關閉視窗結束程式
    }

    public static void main(String[] args) {
        TankGame tankGame = new TankGame();//啟動遊戲入口
    }
}
           

MyPanel繼承JPanel并重寫paint()方法

import javax.swing.*;
import java.awt.*;

/**
 * @author 謝陽
 * @version 1.8.0_131
 */
public class MyPanel extends JPanel {
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fill3DRect(0, 0, 1000, 750, false);//設定背景大小
    }
}
           

測試結果

坦克大戰(上)

畫出坦克模型

在MyPanel添加drawTank( ) 方法

/**
 * @param x      坦克的橫坐标
 * @param y      坦克的縱坐标
 * @param g      畫筆
 * @param direct 坦克的方向
 * @param type   坦克的顔色
 */
public void drawTank(int x, int y, int direct, int type, Graphics g) {//設定坦克模闆

    switch (type) {//坦克顔色區分敵我坦克
        case 0:
            g.setColor(Color.CYAN);
            break;
        case 1:
            g.setColor(Color.YELLOW);
            break;
    }

    switch (direct) {//坦克方向
        case 0://上
            g.fill3DRect(x, y, 10, 40, false);//坦克左輪
            g.fill3DRect(x + 30, y, 10, 40, false);//坦克右輪
            g.fill3DRect(x + 10, y + 5, 20, 30, false);//坦克身子
            g.fillOval(x + 10, y + 10, 20, 20);//坦克蓋子
            g.fill3DRect(x + 18, y - 2, 4, 4, false);//
            g.drawLine(x + 20, y + 20, x + 20, y);//坦克炮膛
            for (int i = 2; i < 40; i += 4) {
                g.drawLine(x + 1, y + i, x + 9, y + i);
                g.drawLine(x + 31, y + i, x + 39, y + i);
            }
            break;
        case 1://右
            g.fill3DRect(x, y, 40, 10, false);
            g.fill3DRect(x, y + 30, 40, 10, false);
            g.fill3DRect(x + 5, y + 10, 30, 20, false);
            g.fillOval(x + 10, y + 10, 20, 20);
            g.fill3DRect(x + 38, y + 18, 4, 4, false);
            g.drawLine(x + 20, y + 20, x + 40, y + 20);
            for (int i = 2; i < 40; i += 4) {
                g.drawLine(x + i, y + 1, x + i, y + 9);
                g.drawLine(x + i, y + 31, x + i, y + 39);
            }
            break;
        case 2://下
            g.fill3DRect(x, y, 10, 40, false);
            g.fill3DRect(x + 30, y, 10, 40, false);
            g.fill3DRect(x + 10, y + 5, 20, 30, false);
            g.fillOval(x + 10, y + 10, 20, 20);
            g.fill3DRect(x + 18, y + 38, 4, 4, false);
            g.drawLine(x + 20, y + 20, x + 20, y + 40);
            for (int i = 2; i < 40; i += 4) {
                g.drawLine(x + 1, y + i, x + 9, y + i);
                g.drawLine(x + 31, y + i, x + 39, y + i);
            }
            break;
        case 3://左
            g.fill3DRect(x, y, 40, 10, false);
            g.fill3DRect(x, y + 30, 40, 10, false);
            g.fill3DRect(x + 5, y + 10, 30, 20, false);
            g.fillOval(x + 10, y + 10, 20, 20);//坦克蓋子
            g.fill3DRect(x - 2, y + 18, 4, 4, false);
            g.drawLine(x + 20, y + 20, x, y + 20);
            for (int i = 2; i < 40; i += 4) {
                g.drawLine(x + i, y + 1, x + i, y + 9);
                g.drawLine(x + i, y + 31, x + i, y + 39);

            }
            break;
    }
}
           

在MyPanel的paint( )内傳參測試

@Override
public void paint(Graphics g) {
    super.paint(g);
    g.fill3DRect(0, 0, 1000, 750, false);//設定背景大小
    drawTank(100, 100, 1, 0, g);//測試
}
           

測試結果

坦克大戰(上)

畫出敵我坦克

添加Tank類(父類)、Hero類(我方坦克繼承Tank)、Enemy類(敵方坦克繼承Tank)

Tank類:

/**
 * @author 謝陽
 * @version 1.8.0_131
 */
public class Tank {
    private int x;//x坐标
    private int y;//有坐标
    private int direct;//方向

    public Tank(int x, int y, int direct) {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        this.direct = direct;
    }
}
           

Hero類

/**
 * @author 謝陽
 * @version 1.8.0_131
 */
public class Hero extends Tank{
    public Hero(int x, int y, int direct) {
        super(x, y, direct);
    }
}
           

Enemy類

/**
 * @author 謝陽
 * @version 1.8.0_131
 */
public class Enemy extends Tank {
    public Enemy(int x, int y, int direct) {
        super(x, y, direct);
    }
}
           

在MyPanel中添加敵我坦克屬性,并添加構造器初始化敵我坦克

Hero hero = null;//建立hero屬性
int enemyNum = 10;//建立敵方坦克數量
Vector<Enemy> enemies = new Vector<>();//建立敵方坦克集合

public MyPanel() {//建立無參構造器
    hero = new Hero(500, 500, 0);//初始化我方坦克

    for (int i = 0; i < enemyNum; i++) {//初始化敵方坦克
        Enemy enemy = new Enemy(100*(i+0),0,2);
        enemies.add(enemy);
    }
}
           

在MyPanel的paint( ) 方法中畫出敵我坦克

public void paint(Graphics g) {
    super.paint(g);
    g.fill3DRect(0, 0, 1000, 750, false);//設定背景大小
    
    drawTank(hero.getX(), hero.getY(), hero.getDirect(), 0, g);//畫出我方坦克

    for (int i = 0; i < enemies.size(); i++) {//周遊畫出敵方坦克
        Enemy enemy = enemies.get(i);
        drawTank(enemy.getX(),enemy.getY(),enemy.getDirect(),1,g);
    }
}
           

測試結果

坦克大戰(上)

我方坦克改變方向

讓MyPanel類實作KeyListener并在其方法内監聽WDSA鍵,對應鍵可以改變對應方向

注: this.repaint();每改變一次方向可以重新整理一次,後面可以通過多線程自動重新整理

public class MyPanel extends JPanel implements KeyListener {

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {//上
            hero.setDirect(0);
        } else if (e.getKeyCode() == KeyEvent.VK_D){//右
            hero.setDirect(1);
        }else if (e.getKeyCode() == KeyEvent.VK_S){//下
            hero.setDirect(2);
        }else if (e.getKeyCode() == KeyEvent.VK_A){//左
            hero.setDirect(3);
        }
        this.repaint();//重新整理畫布
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
           

在TankGame的構造器内添加監聽鍵盤方法

public TankGame() {//重寫構造器
        mp = new MyPanel();//初始化屬性
        this.setSize(1100, 800);//設定界面大小
        this.add(mp);//添加mp
        this.setVisible(true);//設定可見
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定關閉視窗結束程式
        this.addKeyListener(mp);//監聽鍵盤輸入
 }
           

測試結果(我方坦克可以上下左右切換方向)

坦克大戰(上)

我方坦克可以移動

在Tank類中添加速度speed屬性 (給set和get方法) ,并添加上下左右移動方法

private int speed = 1;//速度

public void moveUp() {
    y -= speed;
}

public void moveDown() {
    y += speed;
}

public void moveRight() {
    x += speed;
}

public void moveLeft() {
    x -= speed;
}

public int getSpeed() {
    return speed;
}

public void setSpeed(int speed) {
    this.speed = speed;
}
           

在MyPanel的監聽方法中給其WDSA對應的坦克(hero)的移動方法,并且可以在構造器中設定移動速度

@Override
public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_W) {//上
        hero.moveUp();
        hero.setDirect(0);
    } else if (e.getKeyCode() == KeyEvent.VK_D){//右
        hero.setDirect(1);
        hero.moveRight();
    }else if (e.getKeyCode() == KeyEvent.VK_S){//下
        hero.setDirect(2);
        hero.moveDown();
    }else if (e.getKeyCode() == KeyEvent.VK_A){//左
        hero.setDirect(3);
        hero.moveLeft();
    }
    this.repaint();
}

public MyPanel() {//建立無參構造器
        hero = new Hero(500, 500, 0);//初始化我方坦克
        hero.setSpeed(5);//設定我方移動速度
        
        for (int i = 0; i < enemyNum; i++) {//初始化敵方坦克
            Enemy enemy = new Enemy(100*(i+0),0,2);
            enemies.add(enemy);
        }
    }
           

測試結果

坦克大戰(上)

敵方坦克向下移動

Enemy類實作Runnable接口,并重寫run方法

public class Enemy extends Tank implements Runnable {
    public Enemy(int x, int y, int direct) {
        super(x, y, direct);
    }

    @Override
    public void run() {//重寫run方法
        while (true) {
            moveDown();//可以向下移動
            try {//捕獲異常
                Thread.sleep(20);//休眠20ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}
           

在MyPanel的構造器中啟動enemy線程

public MyPanel() {//建立無參構造器
    hero = new Hero(500, 500, 0);//初始化我方坦克
    hero.setSpeed(5);//設定我方移動速度

    for (int i = 0; i < enemyNum; i++) {//初始化敵方坦克
        Enemy enemy = new Enemy(100*(i+0),0,2);
        enemy.setSpeed(2);//設定敵人移動速度
        enemies.add(enemy);//添加至敵人坦克集合
        new Thread(enemy).start();//啟動敵人坦克線程
    }
}
           

這時敵方坦克可以移動,但視窗不會自動重新整理畫闆,隻有當我方坦克移動時,才能看見敵方坦克移動

同理,在MyPanel類實作Runnable接口,重寫run()方法添加repaint使其自動重新整理畫闆

public void run() {
    while(true) {

        this.repaint();//重新整理畫闆
        try {
            Thread.sleep(15);//每15ms重新整理一次
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
           

在TankGame的構造器中啟動MyPanel的線程

public TankGame() {//重寫構造器
    mp = new MyPanel();//初始化屬性
    new Thread(mp).start();//啟動Mypanel的線程
    this.setSize(1100, 800);//設定界面大小
    this.add(mp);//添加mp
    this.setVisible(true);//設定可見
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定關閉視窗結束程式
    this.addKeyListener(mp);//監聽鍵盤輸入
}
           

敵方坦克随機移動

@Override
public void run() {//重寫run方法
    while (true) {
        int random = (int)(Math.random()*60);//擷取随機移動距離
        switch (getDirect()){//擷取方向
            case 0://上
                for (int i = 0; i < random; i++) {
                    moveUp();//向上移動random步
                    try {
                        Thread.sleep(50);//休眠50ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 1://右
                for (int i = 0; i < random; i++) {
                    moveRight();//向右移動random步
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 2://下
                for (int i = 0; i < random; i++) {
                    moveDown();//向下移動random步
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 3://左
                for (int i = 0; i < random; i++) {
                    moveLeft();//向左移動random步
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
        setDirect((int)(Math.random()*4));//設定随機方向
    }
}