Java坦克大戰(三)


關於這個坦克大戰的項目是在學習Java基礎的時候,拿來練習的最近看到這些代碼,感覺很親切,就把他們都復制下來,編輯成博客。回首看去,Java基礎的學習確實應該建立在找項目練習上,這樣才能將學到的基礎知識用到實際當中,不然你知道什么是面向對象編程,什么是線程,什么是死鎖,概念都了解了一大堆,等到實際應用的時候,還是力不從心。初學者千萬不要灰心,真心是敲着敲着就有感覺了。下面還是循序漸進的介紹這個項目的幾個版本,注釋我寫的很詳細,新功能添加后部分代碼有改動,如果感興趣,可以看前幾篇博客。

坦克大戰(1.4版本)

1.MyTankGame類:

/*
 * 刪掉很多之前的注釋
 * 功能:線程(坦克打子彈)進過分析:子彈是個類
 */
package com.fanghua3;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.Vector;

import javax.swing.*;

public class MyTankGame1_4 extends JFrame {

    Mypanel1_2 mp = null;

    public static void main(String[] args) {
        new MyTankGame1_4();
    }

    // 構造函數
    public MyTankGame1_4() {
        mp = new Mypanel1_2();

        // 啟動mp線程
        Thread t = new Thread(mp);
        t.start();

        this.add(mp);
        // 注冊監聽
        this.addKeyListener(mp);

        this.setSize(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);

    }
}

// 我的面板,拓寬思路:Panel本身就是一個刷新體
class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable {

    // 定義我的坦克
    Hero1_2 hero = null;
    // 定義敵人的坦克(不止一輛,線程安全,集合)
    Vector<EnemyTank> ets = new Vector<EnemyTank>();
    int enSize = 3;// 敵人坦克保持三個

    // 構造函數
    public Mypanel1_2() {
        hero = new Hero1_2(10, 10);
        for (int i = 0; i < enSize; i++) {

            // 創建一輛敵人的坦克
            EnemyTank et = new EnemyTank((i + 1) * 50, 0);
            et.setColor(0);
            // 坦克默認反向是0(向上),這里改一下
            et.setDirect(2);
            // 加入
            ets.add(et);

        }
    }

    // 重寫paint函數
    public void paint(Graphics g) {
        // 一定要調用
        super.paint(g);
        g.fillRect(0, 0, 400, 300);
        // 畫出自己的坦克(將方向填進去)
        this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1);

        // 畫出子彈(后添加 hero.s.isLive==true,節省資源)
        if (hero.s != null && hero.s.isLive == true) {
            g.draw3DRect(hero.s.x, hero.s.y, 1, 1, false);
        }

        // 畫出敵人的坦克
        for (int i = 0; i < enSize; i++) {
            this.drawTank(ets.get(i).getX(), ets.get(i).getY(), g, ets.get(i)
                    .getDirect(), 0);

        }

    }

    // 畫出坦克的函數
    public void drawTank(int x, int y, Graphics g, int direct, int type) {
        // 坦克類型
        switch (type) {
        case 0:
            g.setColor(Color.green);
            break;
        case 1:
            g.setColor(Color.yellow);
            break;
        }
        // 方向設置
        switch (direct) {
        // 向上
        case 0:
            g.fill3DRect(x, y, 5, 30, false);
            g.fill3DRect(x + 15, y, 5, 30, false);
            g.fill3DRect(x + 5, y + 5, 10, 20, false);
            g.fillOval(x + 5, y + 10, 10, 10);
            g.drawLine(x + 10, y + 15, x + 10, y);
            break;
        // 向右
        case 1:
            g.fill3DRect(x, y, 30, 5, false);
            g.fill3DRect(x, y + 15, 30, 5, false);
            g.fill3DRect(x + 5, y + 5, 20, 10, false);
            g.fillOval(x + 10, y + 5, 10, 10);
            g.drawLine(x + 15, y + 10, x + 30, y + 10);
            break;
        // 向下
        case 2:
            g.fill3DRect(x, y, 5, 30, false);
            g.fill3DRect(x + 15, y, 5, 30, false);
            g.fill3DRect(x + 5, y + 5, 10, 20, false);
            g.fillOval(x + 5, y + 10, 10, 10);
            g.drawLine(x + 10, y + 15, x + 10, y + 30);
            break;
        // 向左
        case 3:
            g.fill3DRect(x, y, 30, 5, false);
            g.fill3DRect(x, y + 15, 30, 5, false);
            g.fill3DRect(x + 5, y + 5, 20, 10, false);
            g.fillOval(x + 10, y + 5, 10, 10);
            g.drawLine(x + 15, y + 10, x, y + 10);
            break;
        }

    }

    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent e) {
        // TODO Auto-generated method stub
        // 已更正為順時針
        if (e.getKeyCode() == KeyEvent.VK_UP 
                || e.getKeyCode() == KeyEvent.VK_W) {
            this.hero.moveUp();
            this.hero.setDirect(0);
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT
                || e.getKeyCode() == KeyEvent.VK_D) {
            this.hero.setDirect(1);
            this.hero.moveRight();
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN
                || e.getKeyCode() == KeyEvent.VK_S) {
            this.hero.moveDown();
            this.hero.setDirect(2);
        } else if (e.getKeyCode() == KeyEvent.VK_LEFT
                || e.getKeyCode() == KeyEvent.VK_A) {
            this.hero.moveLeft();
            this.hero.setDirect(3);
        } else if (e.getKeyCode() == KeyEvent.VK_J) {
            // 將J鍵設置為發出子彈
            this.hero.shotEnemy();
            this.repaint();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        // 每隔100毫秒去重繪
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            // 重繪
            this.repaint();
        }
    }
}

2.Menbers類:

package com.fanghua3;
//子彈類
class Shot implements Runnable{
    int x;
    int y;
    int direct;
    //設置子彈的消亡(默認活着的)
    boolean isLive=true;
    //speed要給個初始值1,之前給0,按J鍵,子彈沒有動
    int speed=1;
    public Shot(int x, int y,int direct) {
        super();
        this.x = x;
        this.y= y;
        this.direct=direct;
        
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        
        while(true){
            //設置子彈休息50毫秒
            
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            switch(direct){
            case 0:
                y-=speed;
                break;
            case 1:
                x+=speed;
                break;
            case 2:
                y+=speed;
                break;
            case 3:
                x-=speed;
                break;
            }
            System.out.println("子彈坐標x="+x+"y="+y);
            //子彈什么時候死亡
            //判斷該子彈是否碰到邊緣
            if(x<0||x>400||y<0||y>300){
                this.isLive=false;
                break;
            }
        }
    }
}

//坦克類
class Tank1_2 {
    
    int x = 0;
    int y = 0;

    // 坦克方向:0表示上,1表示右,2表示下,3表示左
    int direct = 0;
    int speed = 1;
    //坦克的顏色
    int color;

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getSpeed() {
        return speed;
    }

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

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        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 Tank1_2(int x, int y) {
        this.x = x;
        this.y = y;

    }

}
//敵人的坦克
class EnemyTank extends Tank1_2{
    
    public EnemyTank(int x, int y) {
        super(x, y);
        // TODO Auto-generated constructor stub
    }
}

//我的坦克
class Hero1_2 extends Tank1_2 {
    //子彈
    Shot s=null;
    public Hero1_2(int x, int y) {
        super(x, y);
    }
    //坦克開火
    public void shotEnemy(){
        
        switch(this.direct){
        case 0:
            s=new Shot(x+10,y,0);
            
            break;
        
        case 1:
            s=new Shot(x+30,y+10,1);
            break;
        
        case 2:
            s=new Shot(x+10,y+30,2);
            break;
        
        case 3:
            s=new Shot(x,y+10,3);
            break;
        }
        //啟動子彈線程
        Thread t=new Thread(s);
        t.start();
    }

    public void moveUp() {
        y -= speed;
    }
    public void moveRight() {
        x += speed;
    }
    public void moveDown() {
        y += speed;
    }
    public void moveLeft() {
        x -= speed;
    }
}

坦克大戰(1.5版本)

1.MyTankGame類:

/*
 * 刪掉很多之前的注釋
 * 功能:子彈連發,(線程)最多5顆子彈
 * 敵人坦克消失
 */
package com.fanghua4;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.Vector;

import javax.swing.*;

public class MyTankGame1_5 extends JFrame {

    Mypanel1_2 mp = null;

    public static void main(String[] args) {
        new MyTankGame1_5();
    }

    // 構造函數
    public MyTankGame1_5() {
        mp = new Mypanel1_2();

        // 啟動mp線程
        Thread t = new Thread(mp);
        t.start();

        this.add(mp);
        // 注冊監聽
        this.addKeyListener(mp);

        this.setSize(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);

    }
}

// 我的面板,拓寬思路:Panel本身就是一個刷新體
class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable {

    // 定義我的坦克
    Hero1_2 hero = null;
    // 定義敵人的坦克(不止一輛,線程安全,集合)
    Vector<EnemyTank> ets = new Vector<EnemyTank>();
    int enSize = 3;// 敵人坦克保持三個

    // 構造函數
    public Mypanel1_2() {
        hero = new Hero1_2(10, 10);
        for (int i = 0; i < enSize; i++) {

            // 創建一輛敵人的坦克
            EnemyTank et = new EnemyTank((i + 1) * 50, 0);
            et.setColor(0);
            // 坦克默認反向是0(向上),這里改一下
            et.setDirect(2);
            // 加入
            ets.add(et);

        }
    }

    // 重寫paint函數
    public void paint(Graphics g) {
        // 一定要調用
        super.paint(g);
        g.fillRect(0, 0, 400, 300);
        // 畫出自己的坦克(將方向填進去)
        this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1);

        // 從ss中取出每一顆子彈,並畫出
        for (int i = 0; i < hero.ss.size(); i++) {

            Shot myShot = hero.ss.get(i);
            if (myShot != null && myShot.isLive == true) {
                g.draw3DRect(myShot.x, myShot.y, 1, 1, false);

                /*
                 * 畫出一顆子彈(后添加 hero.s.isLive==true,節省資源) if (hero.s != null
                 * &&hero.s.isLive == true) { g.draw3DRect(hero.s.x, hero.s.y,
                 * 1, 1,false); }
                 */
            }
            if (myShot.isLive == false) {
                // 從ss(向量)中刪除該子彈
                // hero.ss.remove(i);會報異常。
                hero.ss.remove(myShot);
            }
        }

        // 畫出敵人的坦克
        for (int i = 0; i < enSize; i++) {
            EnemyTank et = ets.get(i);
            if (et.isLive) {
                this.drawTank(et.getX(), et.getY(), g, et.getDirect(), 0);
            }
        }
    }

    // 寫一個函數 專門判斷是否擊中敵人坦克
    public void hitTank(Shot s, EnemyTank et) {
        // 判斷該坦克的方向
        switch (et.direct) {
        // 敵人坦克的方向是0或2,一致
        case 0:
        case 2:
            if (s.x > et.x && s.x < et.x + 20 && s.y > et.y && s.y < et.y + 30) {
                // 擊中(子彈死亡,敵人四萬)
                s.isLive = false;
                et.isLive = false;
            }
break;
case 1: case 3: if (s.x > et.x && s.x < et.x + 30 && s.y > et.y && s.y < et.y + 20) { // 擊中(子彈死亡,敵人四萬) s.isLive = false; et.isLive = false; }
break; } }
// 畫出坦克的函數 public void drawTank(int x, int y, Graphics g, int direct, int type) { // 坦克類型 switch (type) { case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } // 方向設置 switch (direct) { // 向上 case 0: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y); break; // 向右 case 1: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x + 30, y + 10); break; // 向下 case 2: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y + 30); break; // 向左 case 3: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x, y + 10); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // 已更正為順時針 if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { this.hero.moveUp(); this.hero.setDirect(0); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) { this.hero.setDirect(1); this.hero.moveRight(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) { this.hero.moveDown(); this.hero.setDirect(2); } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) { this.hero.moveLeft(); this.hero.setDirect(3); } else if (e.getKeyCode() == KeyEvent.VK_J) { // 將J鍵設置為發出子彈 // 子彈連發,J被按幾下,發幾顆:this.hero.shotEnemy(); // this.repaint();在run函數里,不應該設計在這里 if (this.hero.ss.size() <= 4) { this.hero.shotEnemy(); } } } @Override public void run() { // TODO Auto-generated method stub // 每隔100毫秒去重繪 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 判斷是否擊中(寫在這里,雖然在每次重繪的時候都要調用,但是沒辦法) // 每一顆子彈都要和每個坦克進行匹配 for (int i = 0; i < hero.ss.size(); i++) { // 取出子彈 Shot myShot = hero.ss.get(i); // 判斷子彈是否有效 if (myShot.isLive) { // 取出每個坦克,與它判斷 for (int j = 0; j < ets.size(); j++) { // 取出坦克 EnemyTank et = ets.get(j); if (et.isLive) { this.hitTank(myShot, et); } } } } // 重繪 this.repaint(); } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } }

2.Menbers類:

package com.fanghua4;

import java.util.Vector;

//子彈類
class Shot implements Runnable {
    int x;
    int y;
    int direct;
    // 設置子彈的消亡(默認活着的)
    boolean isLive = true;
    // speed要給個初始值1,之前給0,按J鍵,子彈沒有動
    int speed = 1;

    public Shot(int x, int y, int direct) {
        super();
        this.x = x;
        this.y = y;
        this.direct = direct;

    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        while (true) {
            // 設置子彈休息50毫秒

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            switch (direct) {
            case 0:
                y -= speed;
                break;
            case 1:
                x += speed;
                break;
            case 2:
                y += speed;
                break;
            case 3:
                x -= speed;
                break;
            }
            System.out.println("子彈坐標x=" + x + "y=" + y);
            // 子彈什么時候死亡
            // 判斷該子彈是否碰到邊緣
            if (x < 0 || x > 400 || y < 0 || y > 300) {
                this.isLive = false;
                break;
            }
        }
    }
}

// 坦克類
class Tank1_2 {

    int x = 0;
    int y = 0;

    // 坦克方向:0表示上,1表示右,2表示下,3表示左
    int direct = 0;
    int speed = 1;
    // 坦克的顏色
    int color;

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getSpeed() {
        return speed;
    }

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

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        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 Tank1_2(int x, int y) {
        this.x = x;
        this.y = y;

    }

}

// 敵人的坦克
class EnemyTank extends Tank1_2 {

    boolean isLive = true;


    public EnemyTank(int x, int y) {
        super(x, y);
        // TODO Auto-generated constructor stub
    }
}

// 我的坦克
class Hero1_2 extends Tank1_2 {
    // 多個子彈,用向量創建
    Vector<Shot> ss = new Vector<Shot>();
    // 子彈
    Shot s = null;

    public Hero1_2(int x, int y) {
        super(x, y);
    }

    // 坦克開火
    public void shotEnemy() {

        switch (this.direct) {
        case 0:
            s = new Shot(x + 10, y, 0);
            // 把子彈加入向量
            ss.add(s);
            break;

        case 1:
            s = new Shot(x + 30, y + 10, 1);
            // 把子彈加入向量
            ss.add(s);
            break;

        case 2:
            s = new Shot(x + 10, y + 30, 2);
            // 把子彈加入向量
            ss.add(s);
            break;

        case 3:
            s = new Shot(x, y + 10, 3);
            // 把子彈加入向量
            ss.add(s);
            break;
        }
        // 啟動子彈線程
        Thread t = new Thread(s);
        t.start();
    }

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

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

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

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

坦克大戰(1.6版本)

1.MyTankGame類

/*
 * 功能:
 * 1.實現爆炸效果
 * 2.敵人坦克可移動,可以連發子彈
 * 3.敵人擊中我的坦克,我爆炸
 * 4.擊中第一個坦克爆炸的效果不明顯
 */
package com.fanghua5;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.Vector;
import javax.swing.*;

public class MyTankGame1_6 extends JFrame {

    Mypanel1_2 mp = null;

    public static void main(String[] args) {
        new MyTankGame1_6();
    }

    // 構造函數
    public MyTankGame1_6() {
        mp = new Mypanel1_2();

        // 啟動mp線程
        Thread t = new Thread(mp);
        t.start();

        this.add(mp);
        // 注冊監聽
        this.addKeyListener(mp);

        this.setSize(400, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);

    }
}

// 我的面板,拓寬思路:Panel本身就是一個刷新體
class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable {

    // 定義我的坦克
    Hero1_2 hero = null;
    // 定義敵人的坦克(不止一輛,線程安全,集合)
    Vector<EnemyTank> ets = new Vector<EnemyTank>();

    // 定義炸彈集合
    Vector<Bomb> bombs = new Vector<Bomb>();

    int enSize = 4;// 敵人坦克保持四個

    // 定義三張圖片(三張圖片才能組成一顆炸彈)
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;

    // 構造函數
    public Mypanel1_2() {
        hero = new Hero1_2(10, 10);
        for (int i = 0; i < enSize; i++) {

            // 創建一輛敵人的坦克
            EnemyTank et = new EnemyTank((i + 1) * 50, 0);
            et.setColor(0);
            // 坦克默認反向是0(向上),這里改一下
            et.setDirect(2);
            // 加入
            ets.add(et);

            // 啟動敵人的坦克
            Thread t = new Thread(et);
            t.start();
            // 給敵人坦克添加一顆子彈
            Shot s = new Shot(et.x + 10, et.y + 30, 2);
            // 加入給敵人的坦克
            et.ss.add(s);

            Thread t2 = new Thread(s);
            t2.start();
            ets.add(et);

        }
        // 初始話圖片,這樣做擊中第一個坦克,爆炸的效果不明顯。下面優化
        image1 = Toolkit.getDefaultToolkit().getImage(
                Panel.class.getResource("/bomb_1.gif"));
        image2 = Toolkit.getDefaultToolkit().getImage(
                Panel.class.getResource("/bomb_2.gif"));
        image3 = Toolkit.getDefaultToolkit().getImage(
                Panel.class.getResource("/bomb_3.gif"));
    
//        引包:import javax.imageio.ImagesssIO;
//        try {
//            image1=ImageIO.read(new File("/bomsb_1.gif"));
//            image2=ImageIO.read(new File("/bomb_2.gif"));
//            image3=ImageIO.read(new File("/bomb_3.gif"));
//        } catch (IOException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
    }

    // 重寫paint函數
    public void paint(Graphics g) {
        // 一定要調用
        super.paint(g);
        g.fillRect(0, 0, 400, 350);
        // 畫出自己的坦克(將方向填進去)
        if (hero.isLive == true) {
            this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1);
        }
        // 從ss中取出每一顆子彈,並畫出
        for (int i = 0; i < hero.ss.size(); i++) {

            Shot myShot = hero.ss.get(i);
            if (myShot != null && myShot.isLive == true) {
                g.draw3DRect(myShot.x, myShot.y, 1, 1, false);

                /*
                 * 畫出一顆子彈(后添加 hero.s.isLive==true,節省資源) if (hero.s != null
                 * &&hero.s.isLive == true) { g.draw3DRect(hero.s.x, hero.s.y,
                 * 1, 1,false); }
                 */
            }
            if (myShot.isLive == false) {
                // 從ss(向量)中刪除該子彈
                // hero.ss.remove(i);會報異常。
                hero.ss.remove(myShot);
            }
        }

        // 畫出炸彈
        for (int i = 0; i < bombs.size(); i++) {
            // 取出炸彈
            Bomb b = bombs.get(i);
            if (b.life > 6) {
                g.drawImage(image1, b.x, b.y, 30, 30, this);
            } else if (b.life > 4) {
                g.drawImage(image2, b.x, b.y, 30, 30, this);
            } else {
                g.drawImage(image3, b.x, b.y, 30, 30, this);
            }
            // 讓b的生命值減小
            b.lifeDown();
            // 如果炸彈生命值為零,就把該炸彈從bombs向量中去掉
            if (b.life == 0) {
                bombs.remove(b);
            }

        }

        // 畫出敵人的坦克
        for (int i = 0; i < ets.size(); i++) {
            EnemyTank et = ets.get(i);
            if (et.isLive) {
                this.drawTank(et.getX(), et.getY(), g, et.getDirect(), 0);

                // 畫出敵人的子彈
                for (int j = 0; j < et.ss.size(); j++) {
                    // 取出子彈
                    Shot enemyShot = et.ss.get(j);
                    if (enemyShot != null && enemyShot.isLive == true) {
                        g.draw3DRect(enemyShot.x, enemyShot.y, 1, 1, false);
                    }
                    if (enemyShot.isLive == false) {
                        // 如果敵人的坦克死亡了,就從Vector中刪除
                        et.ss.remove(enemyShot);
                    }
                }
            }
        }
    }

    // 判斷敵人的子彈是否擊中我
    public void hitMe() {
        // 取出每一個敵人的坦克
        for (int i = 0; i < this.ets.size(); i++) {
            // 取出坦克
            EnemyTank et = ets.get(i);
            // 取出每一顆子彈
            for (int j = 0; j < et.ss.size(); j++) {
                // 取出子彈
                Shot enemyShot = et.ss.get(j);

                this.hitTank(enemyShot, hero);
            }

        }
    }

    // 判斷我是否擊中敵人的坦克
    public void hitEnemyTank() {
        // 判斷是否被擊中敵人的坦克
        for (int i = 0; i < hero.ss.size(); i++) {
            // 取出我的子彈與敵人坦克匹配
            Shot myShot = hero.ss.get(i);
            // 判斷子彈是否有效
            if (myShot.isLive) {
                // 取出每個坦克,與它判斷
                for (int j = 0; j < ets.size(); j++) {
                    // 取出坦克
                    EnemyTank et = ets.get(j);
                    if (et.isLive) {
                        this.hitTank(myShot, et);
                    }
                }
            }
        }
    }

    // 寫一個函數 專門判斷是否擊中敵人坦克(原來第二參數: EnemyTank et)
    public void hitTank(Shot s, Tank1_2 et) {
        // 判斷該坦克的方向
        switch (et.direct) {
        // 敵人坦克的方向是0或2,一致
        case 0:
        case 2:
            if (s.x >= et.x && s.x <= et.x + 20 && s.y >= et.y
                    && s.y <= et.y + 30) {
                // 擊中(子彈死亡,敵人死亡)
                s.isLive = false;
                et.isLive = false;

                // 創建一顆炸彈,放入Vector中
                Bomb b = new Bomb(et.x, et.y);
                // 放入
                bombs.add(b);
            }
break;
case 1: case 3: if (s.x >= et.x && s.x <= et.x + 30 && s.y >= et.y && s.y <= et.y + 20) { // 擊中(子彈死亡,敵人死亡) s.isLive = false; et.isLive = false; // 創建一顆炸彈,放入Vector中 Bomb b = new Bomb(et.x, et.y); // 放入 bombs.add(b); }
break; } }
// 畫出坦克的函數 public void drawTank(int x, int y, Graphics g, int direct, int type) { // 坦克類型 switch (type) { case 0: g.setColor(Color.green); break; case 1: g.setColor(Color.yellow); break; } // 方向設置 switch (direct) { // 向上 case 0: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y); break; // 向右 case 1: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x + 30, y + 10); break; // 向下 case 2: g.fill3DRect(x, y, 5, 30, false); g.fill3DRect(x + 15, y, 5, 30, false); g.fill3DRect(x + 5, y + 5, 10, 20, false); g.fillOval(x + 5, y + 10, 10, 10); g.drawLine(x + 10, y + 15, x + 10, y + 30); break; // 向左 case 3: g.fill3DRect(x, y, 30, 5, false); g.fill3DRect(x, y + 15, 30, 5, false); g.fill3DRect(x + 5, y + 5, 20, 10, false); g.fillOval(x + 10, y + 5, 10, 10); g.drawLine(x + 15, y + 10, x, y + 10); break; } } @Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub // 已更正為順時針 if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { this.hero.moveUp(); this.hero.setDirect(0); } else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) { this.hero.setDirect(1); this.hero.moveRight(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) { this.hero.moveDown(); this.hero.setDirect(2); } else if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) { this.hero.moveLeft(); this.hero.setDirect(3); } else if (e.getKeyCode() == KeyEvent.VK_J) { // 將J鍵設置為發出子彈 // 子彈連發,J被按幾下,發幾顆:this.hero.shotEnemy(); // this.repaint();在run函數里,不應該設計在這里 if (this.hero.ss.size() <= 4) { this.hero.shotEnemy(); } } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void run() { // TODO Auto-generated method stub // 每隔100毫秒去重繪 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 判斷是否擊中(寫在這里,雖然在每次重繪的時候都要調用,但是沒辦法) // 每一顆子彈都要和每個坦克進行匹配 for (int i = 0; i < hero.ss.size(); i++) { // 取出子彈 Shot myShot = hero.ss.get(i); // 判斷子彈是否有效 if (myShot.isLive) { // 取出每個坦克,與它判斷 for (int j = 0; j < ets.size(); j++) { // 取出坦克 EnemyTank et = ets.get(j); if (et.isLive) { this.hitTank(myShot, et); } } } } this.hitEnemyTank(); this.hitMe(); // 重繪 this.repaint(); } } }

2.Menbers類:

package com.fanghua5;

import java.util.Vector;

//炸彈類(沒必要定義為線程,因為它不會移動,沒有坐標改變)
class Bomb {
    // 定義炸彈的坐標
    int x, y;
    int life = 9;// 炸彈的生命(三張圖片)
    // 可以看出isLive很有用,它可以決定類(或者對象)要不要展現在面板上
    boolean isLive = true;

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

    // 炸彈減少生命值
    public void lifeDown() {
        if (life > 0) {
            life--;
        } else {
            this.isLive = false;
        }
    }

}

// 子彈類
class Shot implements Runnable {
    int x;
    int y;
    int direct;
    // 設置子彈的消亡(默認活着的)
    boolean isLive = true;
    // speed要給個初始值1,之前給0,按J鍵,子彈沒有動
    int speed = 1;

    public Shot(int x, int y, int direct) {
        super();
        this.x = x;
        this.y = y;
        this.direct = direct;

    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        while (true) {
            // 設置子彈休息50毫秒

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            switch (direct) {
            case 0:
                y -= speed;
                break;
            case 1:
                x += speed;
                break;
            case 2:
                y += speed;
                break;
            case 3:
                x -= speed;
                break;
            }
            // 測試用:System.out.println("子彈坐標x=" + x + "y=" + y);
            // 子彈什么時候死亡
            // 判斷該子彈是否碰到邊緣
            if (x < 0 || x > 400 || y < 0 || y > 300) {
                this.isLive = false;
                break;
            }
        }
    }
}

// 坦克類
class Tank1_2 {

    int x = 0;
    int y = 0;
    boolean isLive=true;

    // 坦克方向:0表示上,1表示右,2表示下,3表示左
    int direct = 0;
    int speed = 1;
    // 坦克的顏色
    int color;

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getSpeed() {
        return speed;
    }

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

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        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 Tank1_2(int x, int y) {
        this.x = x;
        this.y = y;

    }

}

// 敵人的坦克(做成線程,會移動)
class EnemyTank extends Tank1_2 implements Runnable {

    //從父類繼承了,去掉:boolean isLive = true;
    int times = 0;// 讓time累積
    // 定義向量,可以存放敵人的子彈
    Vector<Shot> ss = new Vector<Shot>();

    // 敵人添加子彈,應該剛剛創建坦克和敵人子彈死亡后

    public EnemyTank(int x, int y) {
        super(x, y);
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            try {
                // 設置坦克休息一會
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            switch (this.direct) {
            case 0:
                // 說明坦克正在向上運動(繼續往上走,符合實際)
                // y -= speed;設置坦克平滑移動的效果
                for (int i = 0; i < 30; i++) {
                    if (y > 0) {
                        y -= speed;
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                break;
            case 1:
                for (int i = 0; i < 30; i++) {
                    // 這里注意坐標起點問題不是(400x300)
                    if (x < 350) {
                        x += speed;
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                break;
            case 2:
                for (int i = 0; i < 30; i++) {
                    if (y < 250) {
                        y += speed;
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                break;
            case 3:
                for (int i = 0; i < 30; i++) {
                    if (x > 0) {
                        x -= speed;
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                break;
            }
            this.times++;
            // 設置3秒發一顆子彈
            if (times % 2 == 0) {
                if (isLive) {
                    if (ss.size() < 5) {
                        Shot s = null;
                        // 沒有子彈,添加
                        switch (direct) {
                        case 0:
                            s = new Shot(x + 10, y, 0);
                            ss.add(s);
                            break;
                        case 1:
                            s = new Shot(x + 30, y + 10, 1);
                            ss.add(s);
                            break;
                        case 2:
                            s = new Shot(x + 10, y + 30, 2);
                            ss.add(s);
                            break;
                        case 3:
                            s = new Shot(x, y + 10, 3);
                            ss.add(s);
                            break;
                        }
                        // 啟動子彈線程
                        Thread t = new Thread(s);
                        t.start();
                    }
                }
            }
            // 讓坦克隨機產生一個新的方向
            this.direct = (int) (Math.random() * 4);
            // 判斷敵人坦克是否死亡了(雙等號)
            if (this.isLive == false) {
                // 讓坦克死亡,后退出線程
                return;
            }

        }
    }
}

// 我的坦克
class Hero1_2 extends Tank1_2 {
    // 多個子彈,用向量創建
    Vector<Shot> ss = new Vector<Shot>();
    // 子彈
    Shot s = null;
    

    public Hero1_2(int x, int y) {
        super(x, y);
    }

    // 坦克開火
    public void shotEnemy() {

        switch (this.direct) {
        case 0:
            s = new Shot(x + 10, y, 0);
            // 把子彈加入向量
            ss.add(s);
            break;

        case 1:
            s = new Shot(x + 30, y + 10, 1);
            // 把子彈加入向量
            ss.add(s);
            break;

        case 2:
            s = new Shot(x + 10, y + 30, 2);
            // 把子彈加入向量
            ss.add(s);
            break;

        case 3:
            s = new Shot(x, y + 10, 3);
            // 把子彈加入向量
            ss.add(s);
            break;
        }
        // 啟動子彈線程(創建線程,趕緊傳參,我在這里吃了大虧!)
        Thread t = new Thread(s);
        t.start();
    }

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

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

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

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

1.6版本(圖:基本功能都已經實現)

之后,我還會繼續寫一篇博客,來記錄該坦克大戰的最終版本(2.0),這個坦克大戰的項目利用Java圖形界面來實現坦克的繪制,畫質比較粗糙,部分的坐標沒有那么精確。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM