ch16_tank_battle1

小兵学习笔记發表於2024-09-13
  • 第16章 坦克大戰1
    • java 繪圖座標體系
      • 座標體系-介紹
      • 座標體系-畫素
      • 介紹-快速入門
      • 繪圖原理
      • Graphics 類
      • 繪出坦克
    • java 事件處理機制
      • 基本說明
      • 示意圖
      • 事件處理機制深入理解
      • 坦克動起來
      • 本章作業

第16章 坦克大戰1

java 繪圖座標體系

座標體系-介紹

下圖說明了Java座標系。座標原點位於左上角,以畫素為單位。在Java座標系中,第一個是x座標,表示當前位置為水平方向,距離座標原點x個畫素;第二個是y座標,表示當前位置為垂直方向,距離座標原點y個畫素。

座標體系-畫素

繪圖還必須要搞清一個非常重要的概念-畫素一個畫素等於多少釐米?

計算機在螢幕上顯示的內容都是由螢幕上的每一個畫素組成的。例如,計算機顯示器的解析度是800×600,表示計算機螢幕上的每一行由800個點組成,共有600行,整個計算機螢幕共有480 000個畫素。畫素是一個密度單位,而釐米是長度單位,兩者無法比較

介紹-快速入門

繪圖原理

在皮膚上畫一個小圓。

Component類提供了兩個和繪圖相關最重要的方法:1. paint(Graphics g)繪製元件的外觀

repaint()重新整理元件的外觀。

當元件第一次在螢幕顯示的時候,程式會自動的呼叫paint()方法來繪製元件。在以下情況paint(將會被呼叫:

1.視窗最小化.再最大化

2.視窗的大小發生變化

3.repaint方法被呼叫

Graphics 類

Graphics類你可以理解就是畫筆,為我們提供了各種繪製圖形的方法:[參考jdk幫助文件]

1.畫直線 drawLine(int x1,int y1,int x2,int y2)

2.畫矩形邊框 drawRect(int x, int y, int width, int height)

3.畫橢圓邊框 drawOval(int x, int y, int width, int height)

4.填充矩形 fillRect(int x, int y, int width, int height)

5.填充橢圓 fillOval(int x, int y, int width, int height)

6.畫圖片 drawlmage(Image img, int x, int y. ..)

7.畫字串 drawString(String str, int x, int y) 這裡是以左下角為參考系的。

8.設定畫筆的字型 setFont(Font font)

9.設定畫筆的顏色 setColor(Color c)

package com.hspedu.draw;

import javax.swing.*;
import java.awt.*;
@SuppressWarnings({"all"})
public class DrawCircle extends JFrame { //JFrame對應視窗,可以理解成是一個畫框

    //定義一個皮膚
    private MyPanel mp = null;

    public static void main(String[] args) {
        new DrawCircle();
        System.out.println("退出程式~");
    }

    public DrawCircle() {//構造器
        //初始化皮膚
        mp = new MyPanel();
        //把皮膚放入到視窗(畫框)
        this.add(mp);
        //設定視窗的大小
        this.setSize(400, 300);
        //當點選視窗的小×,程式完全退出.
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);//可以顯示
    }
}

//1.先定義一個MyPanel, 繼承JPanel類, 畫圖形,就在皮膚上畫
class MyPanel extends JPanel {


    //說明:
    //1. MyPanel 物件就是一個畫板
    //2. Graphics g 把 g 理解成一支畫筆
    //3. Graphics 提供了很多繪圖的方法
    //Graphics g
    @Override
    public void paint(Graphics g) {//繪圖方法
        super.paint(g); //呼叫父類的方法完成初始化.
        System.out.println("paint 方法被呼叫了~");
        //畫出一個圓形.
        //g.drawOval(10, 10, 100, 100);


        //演示繪製不同的圖形..
        //畫直線 drawLine(int x1,int y1,int x2,int y2)
        //g.drawLine(10, 10, 100, 100);
        //畫矩形邊框 drawRect(int x, int y, int width, int height)
        //g.drawRect(10, 10, 100, 100);
        //畫橢圓邊框 drawOval(int x, int y, int width, int height)
        //填充矩形 fillRect(int x, int y, int width, int height)
        //設定畫筆的顏色
//        g.setColor(Color.blue);
//        g.fillRect(10, 10, 100, 100);

        //填充橢圓 fillOval(int x, int y, int width, int height)
//        g.setColor(Color.red);
//        g.fillOval(10, 10, 100, 100);

        //畫圖片 drawImage(Image img, int x, int y, ..)
        //1. 獲取圖片資源, /bg.png 表示在該專案的根目錄去獲取 bg.png 圖片資源
//        Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bg.png"));
//        g.drawImage(image, 10, 10, 175, 221, this);
        //畫字串 drawString(String str, int x, int y)//寫字
        //給畫筆設定顏色和字型
        g.setColor(Color.red);
        g.setFont(new Font("隸書", Font.BOLD, 50));
        //這裡設定的 100, 100, 是 "北京你好"左下角
        g.drawString("北京你好", 100, 100);
        //設定畫筆的字型 setFont(Font font)
        //設定畫筆的顏色 setColor(Color c)
    }
}

繪出坦克

java 事件處理機制

基本說明

java事件處理是採取"委派事件模型"。當事件發生時,產生事件的物件,會把此"資訊”傳遞給"事件的監聽者"處理,這裡所說的“資訊"實際上就是java.awt.event事件類庫裡某個類所建立的物件,把它稱為"事件的物件"。

示意圖

事件處理機制深入理解

  1. 前面我們提到幾個重要的概念事件源,事件,事件監聽器我們下面來全面的介紹它們.

  2. 事件源:事件源是一個產生事件的物件,比如按鈕,視窗等。

  3. 事件:事件就是承載事件源狀態改變時的物件,比如當鍵盤事件、滑鼠事件、視窗事件等等,會生成一個事件物件,該物件儲存著當前事件很多資訊,比如KeyEvent物件有含有被按下鍵的Code值。 java.awt.event包和javax.swing.event包中定義了各種事件型別

  4. 事件型別:查閱jdk文件

  5. 事件監聽器介面:
    (1)當事件源產生一個事件,可以傳送給事件監聽者處理
    (2)事件監聽者實際上就是一個類,該類實現了某個事件監聽器介面比如前面我們案例中的MyPanle就是一個類,它實現了KeyListener介面,它就可以作為一個事件監聽者,對接受到的事件進行處理。
    (3)事件監聽器介面有多種,不同的事件監聽器介面可以監聽不同的事件,一個類可以實現多個監聽介面
    (4)這些介面在java.awt.event包和javax.swing.event包中定義。列出常用的事件監聽器介面,檢視jdk文件。

坦克動起來

現在我們學習java事件處理機制和java繪圖技術,請試試看如何讓你的坦克可以透過按鍵控制上右下左(wdsa表示)的移動。

package com.hspedu.tankgame2;

/**
 * 敵人的坦克
 */
public class EnemyTank extends Tank {
    public EnemyTank(int x, int y) {
        super(x, y);
    }
}
package com.hspedu.tankgame2;

public class Hero extends Tank {
    public Hero(int x, int y) {
        super(x, y);
    }
}
package com.hspedu.tankgame2;

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

public class HspTankGame02 extends JFrame {

    //定義MyPanel
    MyPanel mp = null;
    public static void main(String[] args) {

        HspTankGame02 hspTankGame01 = new HspTankGame02();
    }

    public HspTankGame02() {
        mp = new MyPanel();
        this.add(mp);//把皮膚(就是遊戲的繪圖區域)
        this.setSize(1000, 750);
        // 一個介面的引用可以指向實現該介面的物件(mp實現了該介面) this.addKeyListener(KeyListener I);
        this.addKeyListener(mp); //讓JFrame 監聽mp的鍵盤事件
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}
package com.hspedu.tankgame2;

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

/**
 * 坦克大戰的繪圖區域
 */

//為了監聽 鍵盤事件, 實現KeyListener
public class MyPanel extends JPanel implements KeyListener {
    //定義我的坦克
    Hero hero = null;
    //定義敵人坦克,放入到Vector
    Vector<EnemyTank> enemyTanks = new Vector<>();
    int enemyTankSize = 3;

    public MyPanel() {
        hero = new Hero(100, 100);//初始化自己坦克
        //初始化敵人坦克
        for (int i = 0; i < enemyTankSize; i++) {
            //建立一個敵人的坦克
            EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);
            //設定方向
            enemyTank.setDirect(2);
            //加入
            enemyTanks.add(enemyTank);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, 1000, 750);//填充矩形,預設黑色

        //畫出自己坦克-封裝方法
        drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);

        //畫出敵人的坦克, 遍歷Vector
        for (int i = 0; i < enemyTanks.size(); i++) {
            //取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);
            drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
        }


    }

    //編寫方法,畫出坦克

    /**
     * @param x      坦克的左上角x座標
     * @param y      坦克的左上角y座標
     * @param g      畫筆
     * @param direct 坦克方向(上下左右)
     * @param type   坦克型別
     */
    public void drawTank(int x, int y, Graphics g, int direct, int type) {

        //根據不同型別坦克,設定不同顏色
        switch (type) {
            case 0: //敵人的坦克
                g.setColor(Color.cyan);
                break;
            case 1: //我的坦克
                g.setColor(Color.yellow);
                break;
        }

        //根據坦克方向,來繪製對應形狀坦克
        //direct 表示方向(0: 向上 1 向右 2 向下 3 向左 )
        //
        switch (direct) {
            case 0: //表示向上
                g.fill3DRect(x, y, 10, 60, false);//畫出坦克左邊輪子
                g.fill3DRect(x + 30, y, 10, 60, false);//畫出坦克右邊輪子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//畫出坦克蓋子
                g.fillOval(x + 10, y + 20, 20, 20);//畫出圓形蓋子
                g.drawLine(x + 20, y + 30, x + 20, y);//畫出炮筒
                break;
            case 1: //表示向右
                g.fill3DRect(x, y, 60, 10, false);//畫出坦克上邊輪子
                g.fill3DRect(x, y + 30, 60, 10, false);//畫出坦克下邊輪子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//畫出坦克蓋子
                g.fillOval(x + 20, y + 10, 20, 20);//畫出圓形蓋子
                g.drawLine(x + 30, y + 20, x + 60, y + 20);//畫出炮筒
                break;
            case 2: //表示向下
                g.fill3DRect(x, y, 10, 60, false);//畫出坦克左邊輪子
                g.fill3DRect(x + 30, y, 10, 60, false);//畫出坦克右邊輪子
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//畫出坦克蓋子
                g.fillOval(x + 10, y + 20, 20, 20);//畫出圓形蓋子
                g.drawLine(x + 20, y + 30, x + 20, y + 60);//畫出炮筒
                break;
            case 3: //表示向左
                g.fill3DRect(x, y, 60, 10, false);//畫出坦克上邊輪子
                g.fill3DRect(x, y + 30, 60, 10, false);//畫出坦克下邊輪子
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//畫出坦克蓋子
                g.fillOval(x + 20, y + 10, 20, 20);//畫出圓形蓋子
                g.drawLine(x + 30, y + 20, x, y + 20);//畫出炮筒
                break;
            default:
                System.out.println("暫時沒有處理");
        }

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //處理wdsa 鍵按下的情況
    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println(e.getKeyCode());
        if (e.getKeyCode() == KeyEvent.VK_W) {//按下W鍵
            //改變坦克的方向
            hero.setDirect(0);//
            //修改坦克的座標 y -= 1
            hero.moveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {//D鍵, 向右
            hero.setDirect(1);
            hero.moveRight();

        } else if (e.getKeyCode() == KeyEvent.VK_S) {//S鍵
            hero.setDirect(2);
            hero.moveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {//A鍵
            hero.setDirect(3);
            hero.moveLeft();
        }
        //讓皮膚重繪,如果不重繪的話,則不會有改變
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}
package com.hspedu.tankgame2;

public class Tank {
    private int x;//坦克的橫座標
    private int y;//坦克的縱座標
    private int direct = 0;//坦克方向 0 上1 右 2下 3左
    private int speed = 1;

    public int getSpeed() {
        return speed;
    }

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

    //上右下左移動方法
    public void moveUp() {
        y -= speed;
    }
    public void moveRight() {
        x += speed;
    }
    public void moveDown() {
        y += speed;
    }
    public void moveLeft() {
        x -= speed;
    }

    public int getDirect() {
        return direct;
    }

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

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

    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;
    }
}

本章作業

畫出三輛敵人的坦克,注意顏色。如圖所示分析:

  1. 因為敵人的坦克,是在MyPanel上所以我們的程式碼在MyPanel
  2. 因為敵人的坦克,後面有自己特殊的屬性和方法,可以單開一個EnemyTank
  3. 敵人坦克數量多,可以放入到集合Vector ,因為考慮多執行緒問題

程式碼同上。