27.Java 飛機遊戲小項目
開篇
- 遊戲項目基本功能開發
- 飛機類設計
- 炮彈類設計
- 碰撞檢測設計
- 爆炸效果的實現
- 其他功能
- 計時功能
遊戲項目基本功能開發
這裡將會一步步實現遊戲項目的基本功能。
使用 AWT 技術畫出遊戲主窗口
AWT 和 Swing 是 Java 中常見的 GUI(圖形用戶介面)技術。
本次使用的是 AWT 技術。
首先是建立一個 Java 項目,並創建類 MyGameFrame。
程式碼示例:MyGameFrame 類:畫出遊戲介面
package come.jungle.plane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
setSize(500,500); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
圖形和文本繪製
如果要在窗口中畫圖或者顯示什麼內容,我們需要重寫 paint(Graphics g) 方法。
這個方法的作用是:畫出整個窗口以及內部內容。
它會被系統自動調用,我們無需去調用這個方法。
paint 使用方法介紹
// 重寫 paint 方法
@Override
public void paint(Graphics g){
// paint 方法作用是:畫出整個窗口及內部內容。被系統自動調用。
}
Graphics 畫筆對象_畫圖形
程式碼示例:
package come.jungle.plane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 由於 g 是屬於對外借過來使用的,所以在使用以後需要將其還原,否則會導致下次其他地方使用時出現錯誤。
Color c = g.getColor();
// 在這個方法里添加我們要繪製的內容
// 可以自定義畫筆顏色,兩種方式
g.setColor(red);
// g.setColor(new Color(255,220,3));
// 畫直線:使用 drawLine() 畫一條線,確定起點和終點的坐標
g.drawLine(100,100,400,400);
// 畫矩形:起點為(100,100),寬高分別為300,200的矩形
g.drawRect(100,100,300,200);
// 畫橢圓:起點為(100,100),寬高分別為300,200的矩形
g.drawOval(100,100,300,200); // 橢圓的寬高來源是外切矩形的寬高,在程式里任何物體都是矩形。
// 畫字元串
g.drawString("阿jun",300,300);
// 返還畫筆最初傳進方法來時的顏色
g.setColor(c);
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
setSize(500,500); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
ImageIO實現圖片載入技術
遊戲開發中,圖片載入是最常見的技術。
在此處使用 ImageIO 類實現圖片載入,並且為了程式碼的復用,將圖片載入的方法封裝到 GameUtil 工具類中,便於我們以後直接調用。
需要先將項目所需圖片拷貝到項目的 src 下面,去建立新的文件夾 images 包用於存放所有圖片。即在 src 下創建一個名為 images 的 package 包。
目前有兩個文件:MyGameFrame.java 和 GameUtil.java
程式碼示例:GameUtil工具類:載入圖片程式碼
可以將一些輔助性的工具方法放到 GameUtil 中,便於重複調用。
MyGameFrame.java:
package come.jungle.plane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image plane = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,500,500,null);
// 載入 plane 作為飛機圖片
g.drawImage(plane,100,100,22,33,null);
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
setSize(500,500); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
GameUtil.java:
package come.jungle.plane;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
/**
* 遊戲的工具類
* 工具類中一般都是放置一些 static 方法
*/
public class GameUtil {
// 構造器私有,防止別人創建本類對象(本項目可有可無)
private GameUtil(){
}
public static Image getImage(String path){ // 將對應字元串的路徑傳入,比如 /images/plane.png
// 根據傳入的字元串路徑來載入對應的圖片,載入以後形成一個圖片對象返回
BufferedImage img = null; // 定義一個外部空對象
// 先獲得當前類的一個 class 對象,然後獲得一個類載入器,通過類載入器獲得一個 resource 資源
URL u = GameUtil.class.getClassLoader().getResource(path);
// 通過 ImageIO 調用 read() 方法,然後去讀文件,會產生異常,需要去處理
try {
img = ImageIO.read(u);
} catch (IOException e) {
e.printStackTrace();
}
return img;
}
public static void main(String[] args) {
// 測試程式是否異常,通過列印對象的方式,如果列印報異常或為空,則說明有異常
Image img = GameUtil.getImage("images/plane.png"); // 列印飛機圖片對象,路徑前方不加斜杠
System.out.println(img);
}
}
多執行緒和內部類實現動畫效果:增加繪製窗口的執行緒類。雙緩衝技術解決窗口閃爍問題。
目前窗口仍然是靜態的。本次結合多執行緒實現動畫效果。
在 MyGameFrame 類中定義 「重畫窗口執行緒 PaintThread 類」,將 PaintThread 定義成內部類,是為了方便使用 MyFameFrame 類的屬性和方法。
同時利用雙緩衝技術解決窗口閃爍問題。
雙緩衝技術介紹:
雙緩衝即在記憶體中創建一個與螢幕繪圖區域一致的對象,先將圖形繪製到記憶體中的這個對象上,再一次性將這個對象上的圖形拷貝到螢幕上,這樣能大大加快繪圖的速度。
程式碼示例:
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image plane = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
static int count = 0;
int planeX = 100; // 將飛機的X坐標設置成一個變數。
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
System.out.println("繪製窗口次數:" + count);
count++;
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 載入 plane 作為飛機圖片
g.drawImage(plane,planeX,100,22,33,null);
planeX += 1;
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 雙緩衝技術解決窗口閃爍問題
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
Constant.java
package come.jungle.plane;
/**
* 存放相關常量
* */
public class Constant {
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
// 遊戲窗口的寬度
public static final int GAME_WIDTH = 500;
// 遊戲窗口的寬度
public static final int GAME_HEIGHT = 500;
}
遊戲物體根類 GameObject 的實現(0.5版)
窗口中所有的對象(飛機、炮彈等等)都有很多共性:「圖片對象、坐標位置、運行速度、寬度和高度」。
為了方便程式開發,我們需要設計一個GameObject類,它可以作為所有遊戲物體的父類,方便我們編程。
程式碼示例:MyGameFrame.java的修改和GameObject物體根類實現
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
static int count = 0;
// 創建一個 GameObject 類的飛機對象,定義對應屬性進行傳入
// 要傳入的屬性值,GameObject(Image img, double x, double y, int speed, int width, int height)
GameObject plane2 = new GameObject(planeImg,100,100,3,22,33);
// 定義第二個GameObject類的飛機對象
// GameObject plane3 = new GameObject(planeImg,200,200,3,22,33);
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
System.out.println("繪製窗口次數:" + count);
count++;
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
plane2.drawMyself(g);
// 畫第二個飛機物體,直接創建GameObject類的第二個飛機對象並調用 drawMyself(g) 方法即可。
// plane3.drawMyself(g);
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 雙緩衝技術解決窗口閃爍問題
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
GameObject.java
package come.jungle.plane;
import java.awt.*;
/**
* 遊戲物體的根類
* */
public class GameObject {
// 將這些屬性定義好以後,之前定義的跟飛機相關的程式碼就可以去掉了
Image img; // 圖片
double x,y; // 物體的坐標
int speed; // 物體移動的速度
int width,height; // 物體的寬度和高度
// 屬性值可以通過初始化去獲取,使用構造器去生成相關內容。
// 通過外部傳入,對屬性進行賦值。
public GameObject(Image img, double x, double y, int speed, int width, int height) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
this.width = width;
this.height = height;
}
// 定義一個方法來畫自身
public void drawMyself(Graphics g){
// 傳入double,參數值要求是int,所以需要int強制轉型一下
g.drawImage(img,(int)x,(int)y,width,height,null);
}
/**
* 所有的物體都是矩形,當你獲得對應的矩形時,就可以做一些相關的判斷操作
* 返回物體對應的矩形區域,便於後續在碰撞檢測中使用
* @retrun
*/
public Rectangle getRect(){
// 返回飛機位置的坐標位置,寬度高度
return new Rectangle((int)x,(int)y,width,height);
}
}
設計飛機類 Plane(0.5版本):創建多個飛機
有了 GameObject 這個父類,設計飛機類只需要簡單繼承即可。
重新設計了飛機類,通過繼承 GameObject 父類以後,重寫構造方法,實現飛機的移動。
程式碼示例:增加了一個Plane飛機類,修改了 GameObject.java 和 MyGameFrame.java
Plane.java
package come.jungle.plane;
import java.awt.*;
public class Plane extends GameObject{
// 重寫父類方法
@Override
public void drawMyself(Graphics g) {
super.drawMyself(g);
// 飛機飛行的演算法,可以自行設定
x += speed;
}
// 定義構造方法
public Plane(Image img, double x, double y, int speed) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
// 不傳寬度高度時,就自動獲取傳入圖片的寬度高度
this.width = img.getWidth(null);
this.height = img.getHeight(null);
}
}
GameObject.java
多了一個無參構造方法
package come.jungle.plane;
import java.awt.*;
/**
* 遊戲物體的根類
* */
public class GameObject {
// 將這些屬性定義好以後,之前定義的跟飛機相關的程式碼就可以去掉了
Image img; // 圖片
double x,y; // 物體的坐標
int speed; // 物體移動的速度
int width,height; // 物體的寬度和高度
// 屬性值可以通過初始化去獲取,使用構造器去生成相關內容。
// 通過外部傳入,對屬性進行賦值。
public GameObject(Image img, double x, double y, int speed, int width, int height) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
this.width = width;
this.height = height;
}
// 定義一個方法來畫自身
public void drawMyself(Graphics g){
// 傳入double,參數值要求是int,所以需要int強制轉型一下
g.drawImage(img,(int)x,(int)y,width,height,null);
}
// Plane 飛機子類繼承 GameObject父類時,子類的無參構造器會調用父類的無參構造器,所以需定義一個GameObject父類的無參構造器
public GameObject(){}
/**
* 所有的物體都是矩形,當你獲得對應的矩形時,就可以做一些相關的判斷操作
* 返回物體對應的矩形區域,便於後續在碰撞檢測中使用
* @retrun
*/
public Rectangle getRect(){
// 返回飛機位置的坐標位置,寬度高度
return new Rectangle((int)x,(int)y,width,height);
}
}
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
static int count = 0;
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,200,200,2);
Plane p2 = new Plane(planeImg,200,200,3);
Plane p3 = new Plane(planeImg,200,200,4);
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
System.out.println("繪製窗口次數:" + count);
count++;
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
p2.drawMyself(g);
p3.drawMyself(g);
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
飛機類設計(0.6版)
鍵盤控制原理
重點使用鍵盤進行交互。
使用鍵盤操控遊戲物體,通過滑鼠或其他,我們只需要通過相關API的幫助即可輕鬆實現。
使用面向對象的思想,實現鍵盤控制。
鍵盤控制原理:
鍵盤和程式交互時,每次按下鍵、鬆開鍵都會觸發相應的鍵盤事件,事件的資訊都封裝到了KeyEvent對象中。
為了識別按下的鍵是哪個鍵,系統對鍵盤所有按鍵做了編號,每個按鍵都對應相應的數字。
比如∶回車鍵對應數字10,空格鍵對應數字32等。這些編號,我們都可以通過KeyEvent對象來查詢,KeyEvent.VK_ENTER實際就是存儲了數字10。
程式碼示例:Plane.java 和 MyGameFrame.java
將飛機的鍵盤控制程式碼,放入了 Plane.java 飛機類中,在 MyGameFrame.java 中傳入參數,調用對應方法即可。
Plane.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyEvent;
public class Plane extends GameObject{
// 飛機方向的控制
boolean left,right,up,down;
// 重寫父類方法
@Override
public void drawMyself(Graphics g) {
super.drawMyself(g);
// 飛機飛行的演算法,可以自行設定
// x += speed;
if (left){
x -= speed;
}
if (right){
x += speed;
}
if (up){
y -= speed;
}
if (down){
y += speed;
}
}
// 定義飛機方向的方法
// 實現鍵盤按下對應的監聽方法
public void addDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
left=true;
break;
case KeyEvent.VK_RIGHT:
right=true;
break;
case KeyEvent.VK_UP:
up=true;
break;
case KeyEvent.VK_DOWN:
down=true;
break;
}
}
// 實現鍵盤鬆開的監聽方法
public void minusDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
left=false;
break;
case KeyEvent.VK_RIGHT:
right=false;
break;
case KeyEvent.VK_UP:
up=false;
break;
case KeyEvent.VK_DOWN:
down=false;
break;
}
}
// 定義構造方法
public Plane(Image img, double x, double y, int speed) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
// 不傳寬度高度時,就自動獲取傳入圖片的寬度高度
this.width = img.getWidth(null);
this.height = img.getHeight(null);
}
}
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,200,200,7);
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
// 測試方向鍵的效果:右上角為 (0,0),通過更改坐標來實現移動
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
// 在程式初始化窗口時,啟動鍵盤監聽
this.addKeyListener(new KeyMonditor()); // 啟動鍵盤監聽
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 增加一個內部類,實現鍵盤的監聽處理
* */
class KeyMonditor extends KeyAdapter{
// 重寫方法,實現對按鍵狀態(按下或抬起)的監聽
// 重寫鍵盤按下對應的監聽方法
@Override
public void keyPressed(KeyEvent e) {
// 獲取按下的按鍵是哪一個,每個按鍵都有一個特定的 code 值
System.out.println("按下:" + e.getKeyCode());
p1.addDirection(e);
}
// 重寫鍵盤抬起(鬆開)對應的監聽方法
@Override
public void keyReleased(KeyEvent e) {
// 獲取抬起(鬆開)的按鍵是哪一個,每個按鍵都有一個特定的 code 值
System.out.println("抬起:" + e.getKeyCode());
p1.minusDirection(e);
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
炮彈類設計(0.7版)
可更深入了解構造器的用法以及容器的用法。
炮彈類基本設計
炮彈類我們用實心的黃色橢圓實現,不再載入新的圖片。
我們的邏輯是在窗口固定位置(200,200)處生成炮彈,炮彈方向是隨機的,並且遇到邊界會反彈。
需要考慮的是炮彈的長寬對於碰撞邊界值計算的影響。
使用容器或數組產生多發炮彈,本次用的是容器。
程式碼示例:新增了一個炮彈類 Shell.java
Shell.java
package come.jungle.plane;
import java.awt.*;
/**
* 炮彈類
* */
public class Shell extends GameObject{
// 繼承了遊戲物體根類的各個屬性,比如坐標、速度、長寬。
double degree; // 定義炮彈的角度屬性,炮彈沿著指定的角度飛行。
// 定義炮彈的初始構造器方法
public Shell(){
// 定義初始坐標
x = 200;
y = 200;
degree = Math.random()*Math.PI*2; // 生成一個任意角度數值
// System.out.println(degree);
width = 5;
height = 5;
speed = 5;
}
// 通過重寫 drawMyself() 方法來畫炮彈
@Override
public void drawMyself(Graphics g) {
// 定義炮彈的參數,畫一個紅色實心圓
Color c = g.getColor();
g.setColor(Color.yellow);
g.fillOval((int)x,(int)y,width,height);
g.setColor(c); // 用完以後,將畫筆原本顏色還原。
// 根據自己的演算法指定移動路徑,speed是速度,利用角度進行x,y的距離計算
x += speed*Math.cos(degree);
y += speed*Math.sin(degree);
// 碰到邊界改變方向,右上角為零點(0,0)
// 由於炮彈有寬度,所以需要在計算碰撞邊界時,去掉長寬值的影響
if (x < 0 || x > Constant.GAME_WIDTH - this.width){
degree = Math.PI-degree;
}
if (y < 40 || y > Constant.GAME_HEIGHT - this.height){
degree = -degree;
}
}
}
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,200,200,10);
// 定義炮彈對象
// Shell s1 = new Shell();
// 使用數組定義50個炮彈數組對象
Shell[] shells = new Shell[50];
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
// 畫炮彈
for (int i=0;i <shells.length;i++){
shells[i].drawMyself(g);
}
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
// 在程式初始化窗口時,啟動鍵盤監聽
this.addKeyListener(new KeyMonditor()); // 啟動鍵盤監聽
// 利用for循環初始化創建 50 個炮彈對象
for (int i=0;i<50;i++){
shells[i] = new Shell();
}
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 增加一個內部類,實現鍵盤的監聽處理
* */
class KeyMonditor extends KeyAdapter{
// 重寫方法,實現對按鍵狀態(按下或抬起)的監聽
// 重寫鍵盤按下對應的監聽方法
@Override
public void keyPressed(KeyEvent e) {
// 獲取按下的按鍵是哪一個,每個按鍵都有一個特定的 code 值
System.out.println("按下:" + e.getKeyCode());
p1.addDirection(e);
}
// 重寫鍵盤抬起(鬆開)對應的監聽方法
@Override
public void keyReleased(KeyEvent e) {
// 獲取抬起(鬆開)的按鍵是哪一個,每個按鍵都有一個特定的 code 值
System.out.println("抬起:" + e.getKeyCode());
p1.minusDirection(e);
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
碰撞檢測技術(0.8版)
遊戲中,碰撞是遇到最頻繁的技術。
很多遊戲引擎內部已經做了碰撞檢測處理,我們只需調用即可。
矩形碰撞原理
遊戲中,多個元素是否碰到一起,實際上,通常是用「矩形檢測」原理實現的。
遊戲中所有的物體都可以抽象成「矩形」,我們只需判斷兩個矩形是否相交即可。對於一些複雜的多邊形、不規則物體,實際上是將他分解成多個矩形,繼續進行矩形檢測。|
Java的 API中,為我們提供了Rectangle類來表示矩形相關資訊,並且提供了intersects)方法,直接判斷矩形是否相交。
程式碼示例:更改了兩個文件,MyGameFrame.java 和 Plane.java
實現了飛機碰到炮彈以後消失(即不再重畫)的效果
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,300,300,10);
// 定義炮彈對象
// Shell s1 = new Shell();
// 使用數組定義50個炮彈數組對象
Shell[] shells = new Shell[3];
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
// 畫炮彈
for (int i=0;i <shells.length;i++){
shells[i].drawMyself(g);
// 碰撞檢測,將所有的炮彈和飛機進行矩形碰撞檢測,p1.getRect()表示為獲取飛機對應的矩形
boolean peng = shells[i].getRect().intersects(p1.getRect());
if (peng){
// System.out.println("飛機被擊中了!!");
p1.live = false;
// System.out.println(p1);
}
}
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
// 在程式初始化窗口時,啟動鍵盤監聽
this.addKeyListener(new KeyMonditor()); // 啟動鍵盤監聽
// 利用for循環初始化創建 50 個炮彈對象
for (int i=0;i<shells.length;i++){
shells[i] = new Shell();
}
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 增加一個內部類,實現鍵盤的監聽處理
* */
class KeyMonditor extends KeyAdapter{
// 重寫方法,實現對按鍵狀態(按下或抬起)的監聽
// 重寫鍵盤按下對應的監聽方法
@Override
public void keyPressed(KeyEvent e) {
// 獲取按下的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("按下:" + e.getKeyCode());
p1.addDirection(e);
}
// 重寫鍵盤抬起(鬆開)對應的監聽方法
@Override
public void keyReleased(KeyEvent e) {
// 獲取抬起(鬆開)的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("抬起:" + e.getKeyCode());
p1.minusDirection(e);
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
Plane.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyEvent;
public class Plane extends GameObject{
// 飛機方向的控制
boolean left,right,up,down;
public boolean live = true; // 定義飛機是否存活的變數,默認為 true。
// 重寫父類方法
@Override
public void drawMyself(Graphics g) {
// 如果飛機沒有存活,live=false,就不畫了
if (live){
super.drawMyself(g);
// 飛機飛行的演算法,可以自行設定
// x += speed;
if (left){
x -= speed;
}
if (right){
x += speed;
}
if (up){
y -= speed;
}
if (down){
y += speed;
}
}
}
// 定義飛機方向的方法
// 實現鍵盤按下對應的監聽方法
public void addDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
left=true;
break;
case KeyEvent.VK_RIGHT:
right=true;
break;
case KeyEvent.VK_UP:
up=true;
break;
case KeyEvent.VK_DOWN:
down=true;
break;
}
}
// 實現鍵盤鬆開的監聽方法
public void minusDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
left=false;
break;
case KeyEvent.VK_RIGHT:
right=false;
break;
case KeyEvent.VK_UP:
up=false;
break;
case KeyEvent.VK_DOWN:
down=false;
break;
}
}
// 定義構造方法
public Plane(Image img, double x, double y, int speed) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
// 不傳寬度高度時,就自動獲取傳入圖片的寬度高度
this.width = img.getWidth(null);
this.height = img.getHeight(null);
}
}
爆炸效果的實現(0.9版本)
在 src 目錄的 images 文件夾下存放了爆炸相關係列圖片。
圖片輪播處理,實現爆炸效果
程式碼示例
新增了一個爆炸類文件 Explode.java,修改了 MyGameFrame.java 文件。
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,300,300,10);
// 使用數組定義炮彈數組對象
Shell[] shells = new Shell[50];
// 爆炸
Explode explode;
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
// 畫炮彈
for (int i=0;i <shells.length;i++){
shells[i].drawMyself(g);
// 碰撞檢測,將所有的炮彈和飛機進行矩形碰撞檢測,p1.getRect()表示為獲取飛機對應的矩形
boolean peng = shells[i].getRect().intersects(p1.getRect());
if (peng){
p1.live = false;
// 處理爆炸效果,默認炸了一次以後,炮彈再碰到就不炸了
if (explode==null){
// 當飛機被碰撞時,創建爆炸類的對象,並傳入飛機當時的(x,y)坐標值
explode = new Explode(p1.x,p1.y);
}
// 畫出爆炸效果系列圖片
explode.drawMyself(g);
}
}
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
// 在程式初始化窗口時,啟動鍵盤監聽
this.addKeyListener(new KeyMonditor()); // 啟動鍵盤監聽
// 利用for循環初始化創建 50 個炮彈對象
for (int i=0;i<shells.length;i++){
shells[i] = new Shell();
}
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 增加一個內部類,實現鍵盤的監聽處理
* */
class KeyMonditor extends KeyAdapter{
// 重寫方法,實現對按鍵狀態(按下或抬起)的監聽
// 重寫鍵盤按下對應的監聽方法
@Override
public void keyPressed(KeyEvent e) {
// 獲取按下的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("按下:" + e.getKeyCode());
p1.addDirection(e);
}
// 重寫鍵盤抬起(鬆開)對應的監聽方法
@Override
public void keyReleased(KeyEvent e) {
// 獲取抬起(鬆開)的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("抬起:" + e.getKeyCode());
p1.minusDirection(e);
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
Explode.java
package come.jungle.plane;
import java.awt.*;
/**
* 爆炸類
**/
public class Explode {
// 定義爆炸位置
double x,y;
// 創建一個數組存放爆炸相關圖片
static Image[] imgs = new Image[16];
// 計數器:計算現在畫了多少次了(確定圖片次序)
int count;
// 靜態初始化塊初始化靜態屬性
static {
// 通過 for 循環,將16個圖片全部初始化完成。
for(int i=0;i<16;i++){
imgs[i] = GameUtil.getImage("images/Explode/e"+(i+1)+".png");
// 需要用數組對象去調用真正的方法,圖片才會真正載入進來
imgs[i].getWidth(null); // 解決程式懶載入問題
}
}
// 定義方法來花自己
public void drawMyself(Graphics g){
if (count < 16){
g.drawImage(imgs[count],(int)x,(int)y,null );
count++;
}
}
// 定義無參構造器
public Explode(){
}
// 定義形參構造器
public Explode(double x, double y) {
this.x = x;
this.y = y;
}
}
其它功能設計(1.0版)
基本功能實現以後,增加一些錦上添花的功能。
比如:遊戲計時功能、全網排名等。
計時功能
我們希望在玩遊戲時,增加計時功能,可以清晰的看到自己玩了多長時間,增加刺激性。
這個功能的核心有兩點:
1.時間計算:當前時刻-遊戲結束的時刻。
2.顯示時間到窗口。
程式碼示例:只修改了 MyGameFrame.java
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,300,300,10);
// 使用數組定義炮彈數組對象
Shell[] shells = new Shell[50];
// 爆炸
Explode explode;
Date start = new Date(); // 遊戲開始時間(以當前遊戲運行時間作為一個開始)
Date end; // 遊戲結束時間(飛機碰撞炮彈時結束)
long period = 0; // 遊戲時長多少秒
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 調用計算遊戲時長相關方法,計算遊戲時間
drawtime(g);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
// 畫炮彈
for (int i=0;i <shells.length;i++){
shells[i].drawMyself(g);
// 碰撞檢測,將所有的炮彈和飛機進行矩形碰撞檢測,p1.getRect()表示為獲取飛機對應的矩形
boolean peng = shells[i].getRect().intersects(p1.getRect());
if (peng){
p1.live = false;
// 處理爆炸效果,默認炸了一次以後,炮彈再碰到就不炸了
if (explode==null){
// 當飛機被碰撞時,創建爆炸類的對象,並傳入飛機當時的(x,y)坐標值
explode = new Explode(p1.x,p1.y);
}
// 畫出爆炸效果系列圖片
explode.drawMyself(g);
}
}
}
// 對計算遊戲時長的程式碼進行封裝
public void drawtime(Graphics g){
// 獲取畫筆傳進來時的屬性
Color c = g.getColor();
Font f = g.getFont();
g.setColor(Color.green);
if (p1.live){
// 將計時功能文字畫在遊戲窗口中
period = (System.currentTimeMillis()-start.getTime())/1000; // 計算遊戲時長,單位是毫秒,所以除以1000變成秒
g.drawString("堅持:"+period+"秒",30,50);
}else{
if (end==null){ // 只有第一次的時候才為null,後面再畫的時候就不為 null 了
end = new Date();
period = (end.getTime() - start.getTime())/1000; // 計算遊戲結束時的總時長
}
// 設置字體樣式
g.setColor(Color.red);
g.setFont((new Font("微軟雅黑",Font.BOLD,30)));
g.drawString("最終遊戲時長:"+period+"秒",200,200);
}
// 還原畫筆傳進來時的屬性
g.setColor(c);
g.setFont(f);
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
// 在程式初始化窗口時,啟動鍵盤監聽
this.addKeyListener(new KeyMonditor()); // 啟動鍵盤監聽
// 利用for循環初始化創建 50 個炮彈對象
for (int i=0;i<shells.length;i++){
shells[i] = new Shell();
}
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 增加一個內部類,實現鍵盤的監聽處理
* */
class KeyMonditor extends KeyAdapter{
// 重寫方法,實現對按鍵狀態(按下或抬起)的監聽
// 重寫鍵盤按下對應的監聽方法
@Override
public void keyPressed(KeyEvent e) {
// 獲取按下的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("按下:" + e.getKeyCode());
p1.addDirection(e);
}
// 重寫鍵盤抬起(鬆開)對應的監聽方法
@Override
public void keyReleased(KeyEvent e) {
// 獲取抬起(鬆開)的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("抬起:" + e.getKeyCode());
p1.minusDirection(e);
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
最終版本
共有 7 個 java 文件,同時還有許多圖片(可自行去截圖尋找,比如尚學堂B站影片:鏈接地址)
7個Java文件分別為:MyGameFrame.java、Shell.java、Plane.java、GameObject.java、GameUtil.java、Explode.java、Constant.java
總程式碼
MyGameFrame.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;
import static java.awt.Color.red;
/**
* 遊戲主窗口
* 使用 AWT 技術畫出遊戲主窗口
*/
// 讓 MyGameFrame 繼承 Frame,這樣子可以在方法里使用 Frame 的所有方法
public class MyGameFrame extends Frame {
// 在窗口中載入 png 圖片
// 創建圖片類對象,利用 getImage 方法載入圖片
Image planeImg = GameUtil.getImage("images/plane.png");
Image bg = GameUtil.getImage("images/bg.png");
// 創建一個 Plane 類的飛機對象,定義對應屬性進行傳入
Plane p1 = new Plane(planeImg,300,300,10);
// 使用數組定義炮彈數組對象
Shell[] shells = new Shell[50];
// 爆炸
Explode explode;
Date start = new Date(); // 遊戲開始時間(以當前遊戲運行時間作為一個開始)
Date end; // 遊戲結束時間(飛機碰撞炮彈時結束)
long period = 0; // 遊戲時長多少秒
// 重寫 paint 方法
@Override
public void paint(Graphics g) { // 把 g 當成是一支畫筆來幫助實現繪製
// 利用 drawImage() 方法在窗口中畫圖
// 載入 bg 作為背景圖片,從(0,0) 開始,佔用寬高為 500,500
g.drawImage(bg,0,0,Constant.GAME_WIDTH,Constant.GAME_HEIGHT,null);
// 調用計算遊戲時長相關方法,計算遊戲時間
drawtime(g);
// 飛機對象調用方法中的畫筆屬性 g,自己畫自己
p1.drawMyself(g);
// 畫炮彈
for (int i=0;i <shells.length;i++){
shells[i].drawMyself(g);
// 碰撞檢測,將所有的炮彈和飛機進行矩形碰撞檢測,p1.getRect()表示為獲取飛機對應的矩形
boolean peng = shells[i].getRect().intersects(p1.getRect());
if (peng){
p1.live = false;
// 處理爆炸效果,默認炸了一次以後,炮彈再碰到就不炸了
if (explode==null){
// 當飛機被碰撞時,創建爆炸類的對象,並傳入飛機當時的(x,y)坐標值
explode = new Explode(p1.x,p1.y);
}
// 畫出爆炸效果系列圖片
explode.drawMyself(g);
}
}
}
// 對計算遊戲時長的程式碼進行封裝
public void drawtime(Graphics g){
// 獲取畫筆傳進來時的屬性
Color c = g.getColor();
Font f = g.getFont();
g.setColor(Color.green);
if (p1.live){
// 將計時功能文字畫在遊戲窗口中
period = (System.currentTimeMillis()-start.getTime())/1000; // 計算遊戲時長,單位是毫秒,所以除以1000變成秒
g.drawString("堅持:"+period+"秒",30,50);
}else{
if (end==null){ // 只有第一次的時候才為null,後面再畫的時候就不為 null 了
end = new Date();
period = (end.getTime() - start.getTime())/1000; // 計算遊戲結束時的總時長
}
// 設置字體樣式
g.setColor(Color.red);
g.setFont((new Font("微軟雅黑",Font.BOLD,30)));
g.drawString("最終遊戲時長:"+period+"秒",200,200);
}
// 還原畫筆傳進來時的屬性
g.setColor(c);
g.setFont(f);
}
// 定義一個初始化窗口
public void launchFrame(){
this.setTitle("The Plane War - 阿jun"); // 定義圖形化介面右上角的標題
setVisible(true); // 定義窗口是否可見,默認是不可見,所以需要設置為 true 可見
// Constant.GAME_WIDTH 用於調用 Constant 類的 GAME_WIDTH 常量來使用
setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); // 設置窗口大小的高寬均為 500
setLocation(100,100); // 定義打開窗口的位置,程式坐標系默認以右上角為零點
// 增加關閉窗口的動作,通過方法重寫和匿名內部類
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); // 讓程式正常退出
}
});
// 將重畫窗口的執行緒類放入初始化窗口方法中,在程式初始化窗口時,就會調用這個方法,順便調用執行緒類對象。
new PaintThread().start(); // 啟動重畫窗口的執行緒。
// 在程式初始化窗口時,啟動鍵盤監聽
this.addKeyListener(new KeyMonditor()); // 啟動鍵盤監聽
// 利用for循環初始化創建 50 個炮彈對象
for (int i=0;i<shells.length;i++){
shells[i] = new Shell();
}
}
/**
* 定義一個重畫窗口的執行緒類。
* 定義成內部類是為了方便直接使用窗口類的相關方法。
*/
class PaintThread extends Thread{
// 重寫 Thread類的 run() 方法
@Override
public void run() {
// 只要程式是活著的,就要求一直重畫窗口,所以是 while(true) 死循環
while(true){
// repaint() 是外部類 Frame 的成員方法
repaint();
// 給程式設定一個重畫時間間隔
try {
// 程式執行到此處時,進度停 50 s
Thread.sleep(50); // 1s=1000ms,即1s畫20次(20*50=1000)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 增加一個內部類,實現鍵盤的監聽處理
* */
class KeyMonditor extends KeyAdapter{
// 重寫方法,實現對按鍵狀態(按下或抬起)的監聽
// 重寫鍵盤按下對應的監聽方法
@Override
public void keyPressed(KeyEvent e) {
// 獲取按下的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("按下:" + e.getKeyCode());
p1.addDirection(e);
}
// 重寫鍵盤抬起(鬆開)對應的監聽方法
@Override
public void keyReleased(KeyEvent e) {
// 獲取抬起(鬆開)的按鍵是哪一個,每個按鍵都有一個特定的 code 值
// System.out.println("抬起:" + e.getKeyCode());
p1.minusDirection(e);
}
}
// 雙緩衝技術解決窗口閃爍問題
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
private Image offScreenImage = null;
public void update(Graphics g){
if(offScreenImage == null){
// 這裡是遊戲窗口的寬度和高度
offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);
}
Graphics gOff = offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args){
// 定義類對象,對象調用方法
MyGameFrame gameFrame = new MyGameFrame();
gameFrame.launchFrame();
}
}
Shell.java
package come.jungle.plane;
import java.awt.*;
/**
* 炮彈類
* */
public class Shell extends GameObject{
// 繼承了遊戲物體根類的各個屬性,比如坐標、速度、長寬。
double degree; // 定義炮彈的角度屬性,炮彈沿著指定的角度飛行。
// 定義炮彈的初始構造器方法
public Shell(){
// 定義初始坐標
x = 200;
y = 200;
degree = Math.random()*Math.PI*2; // 生成一個任意角度數值
// System.out.println(degree);
width = 5;
height = 5;
speed = 5;
}
// 通過重寫 drawMyself() 方法來畫炮彈
@Override
public void drawMyself(Graphics g) {
// 定義炮彈的參數,畫一個紅色實心圓
Color c = g.getColor();
g.setColor(Color.yellow);
g.fillOval((int)x,(int)y,width,height);
g.setColor(c); // 用完以後,將畫筆原本顏色還原。
// 根據自己的演算法指定移動路徑,speed是速度,利用角度進行x,y的距離計算
x += speed*Math.cos(degree);
y += speed*Math.sin(degree);
// 碰到邊界改變方向,右上角為零點(0,0)
// 由於炮彈有寬度,所以需要在計算碰撞邊界時,去掉長寬值的影響
if (x < 0 || x > Constant.GAME_WIDTH - this.width){
degree = Math.PI-degree;
}
if (y < 40 || y > Constant.GAME_HEIGHT - this.height){
degree = -degree;
}
}
}
Plane.java
package come.jungle.plane;
import java.awt.*;
import java.awt.event.KeyEvent;
public class Plane extends GameObject{
// 飛機方向的控制
boolean left,right,up,down;
public boolean live = true; // 定義飛機是否存活的變數,默認為 true。
// 重寫父類方法
@Override
public void drawMyself(Graphics g) {
// 如果飛機沒有存活,live=false,就不畫了
if (live){
super.drawMyself(g);
// 飛機飛行的演算法,可以自行設定
// x += speed;
if (left){
x -= speed;
}
if (right){
x += speed;
}
if (up){
y -= speed;
}
if (down){
y += speed;
}
}
}
// 定義飛機方向的方法
// 實現鍵盤按下對應的監聽方法
public void addDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
left=true;
break;
case KeyEvent.VK_RIGHT:
right=true;
break;
case KeyEvent.VK_UP:
up=true;
break;
case KeyEvent.VK_DOWN:
down=true;
break;
}
}
// 實現鍵盤鬆開的監聽方法
public void minusDirection(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
left=false;
break;
case KeyEvent.VK_RIGHT:
right=false;
break;
case KeyEvent.VK_UP:
up=false;
break;
case KeyEvent.VK_DOWN:
down=false;
break;
}
}
// 定義構造方法
public Plane(Image img, double x, double y, int speed) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
// 不傳寬度高度時,就自動獲取傳入圖片的寬度高度
this.width = img.getWidth(null);
this.height = img.getHeight(null);
}
}
GameObject.java
package come.jungle.plane;
import java.awt.*;
/**
* 遊戲物體的根類
* */
public class GameObject {
// 將這些屬性定義好以後,之前定義的跟飛機相關的程式碼就可以去掉了
Image img; // 圖片
double x,y; // 物體的坐標
int speed; // 物體移動的速度
int width,height; // 物體的寬度和高度
// 屬性值可以通過初始化去獲取,使用構造器去生成相關內容。
// 通過外部傳入,對屬性進行賦值。
public GameObject(Image img, double x, double y, int speed, int width, int height) {
this.img = img;
this.x = x;
this.y = y;
this.speed = speed;
this.width = width;
this.height = height;
}
// 定義一個方法來畫自身
public void drawMyself(Graphics g){
// 傳入double,參數值要求是int,所以需要int強制轉型一下
g.drawImage(img,(int)x,(int)y,width,height,null);
}
// Plane 飛機子類繼承 GameObject父類時,子類的無參構造器會調用父類的無參構造器,所以需定義一個GameObject父類的無參構造器
public GameObject(){}
/**
* 所有的物體都是矩形,當你獲得對應的矩形時,就可以做一些相關的判斷操作
* 返回物體對應的矩形區域,便於後續在碰撞檢測中使用
* @retrun
*/
public Rectangle getRect(){
// 返回飛機位置的坐標位置,寬度高度
return new Rectangle((int)x,(int)y,width,height);
}
}
GameUtil.java
package come.jungle.plane;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
/**
* 遊戲的工具類
* 工具類中一般都是放置一些 static 方法
*/
public class GameUtil {
// 構造器私有,防止別人創建本類對象(本項目可有可無)
private GameUtil(){
}
// 重點功能就是載入圖片文件,並提供給外部引用
public static Image getImage(String path){ // 將對應字元串的路徑傳入,比如 /images/plane.png
// 根據傳入的字元串路徑來載入對應的圖片,載入以後形成一個圖片對象返回
BufferedImage img = null; // 定義一個外部空對象
// 先獲得當前類的一個 class 對象,然後獲得一個類載入器,通過類載入器獲得一個 resource 資源
URL u = GameUtil.class.getClassLoader().getResource(path);
// 通過 ImageIO 調用 read() 方法,然後去讀文件,會產生異常,需要去處理
try {
img = ImageIO.read(u);
} catch (IOException e) {
e.printStackTrace();
}
return img;
}
public static void main(String[] args) {
// 測試程式是否異常,通過列印對象的方式,如果列印報異常或為空,則說明有異常
Image img = GameUtil.getImage("images/plane.png"); // 列印飛機圖片對象,路徑前方不加斜杠
System.out.println(img);
}
}
Explode.java
package come.jungle.plane;
import java.awt.*;
/**
* 爆炸類
**/
public class Explode {
// 定義爆炸位置
double x,y;
// 創建一個數組存放爆炸相關圖片
static Image[] imgs = new Image[16];
// 計數器:計算現在畫了多少次了(確定圖片次序)
int count;
// 靜態初始化塊初始化靜態屬性
static {
// 通過 for 循環,將16個圖片全部初始化完成。
for(int i=0;i<16;i++){
imgs[i] = GameUtil.getImage("images/Explode/e"+(i+1)+".png");
// 需要用數組對象去調用真正的方法,圖片才會真正載入進來
imgs[i].getWidth(null); // 解決程式懶載入問題
}
}
// 定義方法來花自己
public void drawMyself(Graphics g){
if (count < 16){
g.drawImage(imgs[count],(int)x,(int)y,null );
count++;
}
}
// 定義無參構造器
public Explode(){
}
// 定義形參構造器
public Explode(double x, double y) {
this.x = x;
this.y = y;
}
}
Constant.java
package come.jungle.plane;
/**
* 存放相關常量
* */
public class Constant {
// 定義雙緩衝技術所需要的窗口長寬屬性,基於初始化遊戲窗口的長寬而定
// 遊戲窗口的寬度
public static final int GAME_WIDTH = 500;
// 遊戲窗口的寬度
public static final int GAME_HEIGHT = 500;
}
最終效果
