Download presentation
Presentation is loading. Please wait.
1
Java System Concepts and Animation Programming
網路動畫遊戲入門程式 Java System Concepts and Animation Programming 賈蓉生 胡大源 林金池 編著
2
1-1 簡介 1-2 本書主要內容 1-3 本書編著特色 1-4 光碟使用
第一章 導讀(Introduction) 1-1 簡介 1-2 本書主要內容 1-3 本書編著特色 1-4 光碟使用
3
1-1 簡介 自1990年昇陽開始研發Java以來,至今Java儼然已成為最具網路潛力的程式語言。目前大多數之網路線上遊戲、網路銀行都是以Java撰寫而成。 筆者有感於Java之重要性,計劃有系統地撰寫一系列最新Java叢書,包括:Java基礎入門程式(已出版)、Java網路入門程式(已出版)、Java網路動畫遊戲入門程式(本書)、Java網站資料庫、Java系統程式、Java網路圖表應用等。讓一位從來不知Java為何物的讀者,從基礎入門學起,到網路完全應用,進而溶入程式設計之最新主流。
4
1-2 本書主要內容 第一篇 基礎圖文動畫(Basic Graphics / Animation) 第二篇 事件處理(Events)
本書是以Java網路動畫遊戲入門程式為編輯方向,沒有背景的讀者,也可輕易打開大門,只要依序研習各章節,習作範例、習題,即可完全剖析。 第一篇 基礎圖文動畫(Basic Graphics / Animation) 第二篇 事件處理(Events) 第三篇 網路線上遊戲(On Line Games) 第四篇 2D繪圖設計(2D Graphics) 第五篇 3D繪圖設計(3D Graphics) 第六篇 Java Applet 與網頁
5
1-3 本書編著特色 (1) 輕鬆入門:本書以Java繪圖初學入門觀點 切入網路線上遊戲設計,配合範例解說每一指令與原始類別之用法、輕鬆入門。 (2) 熟練實作:任何學習重點都搭配範例探討並實作,完全剖析。 (3) 問題導向:協助讀者提出問題,解決問題,本書編輯實作範例程式88則。 (4) 原文接軌:編輯中英文索引(如附錄E),讓讀者儲備未來閱讀原文書籍或期刊的能力。
6
1-4 光碟使用 本書隨書附光碟一片,內容有Java安裝程式(System)、範例應用程式(Program);另於出版書局附教學光碟一片,內容有教學投影(Ppt)、習題解答(Ex)、Java安裝程式(System)、範例應用程式(Program)。 1、Java安裝程式(System):置於光碟目錄System內,下載自昇陽(SUN) 最新6.0 Java安裝程式,讀者可依附錄A內容安裝Java。 2、範例應用程式(Program):置於光碟目錄Program內,配合各章節範例執行賓作。 3、教學投影片(Ppt):置於教學光碟目錄Ppt內,以Power Point 依本書各章節內容製作,用於堂課投影教學。 4、習題解答(Ex):置於教學光碟目錄Ex內,列出本書各章習題之解答。
7
基礎圖文動畫 (Basic Graphics / Animation)
第一篇 基礎圖文動畫 (Basic Graphics / Animation)
8
第二章 文字繪製(Words) 2-1 簡介 2-2 Frame Class 2-3 執行緒繪圖流程 2-4 Font Class
2-5 Color Class 2-6 中文處理 2-7 習題(Exercises)
9
2-1 簡介 本書探討的是動畫遊戲,有文字、有影像、有動畫,這些都需要一個環境來顯示,常用的顯示工具為框架(Frame) 與瀏覽器(Browser),前者可用於單機顯示或多機網路對陣;後者可用於單機網路顯示,兩者本書都將詳細介紹。本章將使用框架來顯示基礎圖文繪製。
10
2-2 Frame Class java.awt.Frame繼承(extends) 自Window→Container→Component→Object,此類別物件可建立一個視窗外框,配合Java程式,提供單機圖文或多機網路對陣圖文之顯示。
11
範例1:設計檔案Ex2_2_1.java其功能為解釋視窗框架之建立。
01 import java.awt.*; 02 class Ex2_2_1 { 03 public Ex2_2_1() { Frame frame = new Frame("Ex2_2_1"); frame.setSize(350, 350); frame.setVisible(true); 07 } 08 public static void main(String[] args) { Ex2_2_1 workstart = new Ex2_2_1(); 10 } 11 }
12
範例2:比較範例1,設計檔案Ex2_2_2.java其功能為解釋繼承類別Frame之使用方法。
01 import java.awt.*; 02 public class Ex2_2_2 extends Frame { 03 public Ex2_2_2() { super("Ex2_2_2"); setSize(350, 350); setVisible(true); 07 } 08 public static void main(String args[]) { Ex2_2_2 workStart=new Ex2_2_2(); 10 } 11 }
13
2-3 執行緒繪圖流程 在繪圖或遊戲應用上,發生事件的區域範圍不僅廣大、且數量也多不勝數,在程式設計中,我們必須考量事件執行緒同步並行之安排,在CPU能力允許下,各執行緒競爭進入CPU執行,以使提高執行效率。為了達到這些之要求,我們使用Thread類別、Runnable介面、Component類別。
14
執行緒繪圖程式流程格式 01 public class myWork extends Frame implements Runnable {
02 public static void main(String args[]) { myWork workStart=new myWork (); 04 } 05 public myWork () { super("myWork"); setSize(350, 350); setVisible(true); new Thread(this).start(); 10 } 11 public void run() { … … … repaint(); 14 } 15 public void paint(Graphics g) { … … … 17 } 18 }
15
2-4 Font Class java.awt.Font繼承(extends) 自Object,此類別物件定義字型的規格,包括字型名稱(如Times New Roman)、字型樣式(如Bold)、字型大小(如10)。
16
範例3:設計檔案Ex2_4_1.java其功能為解釋如何以類別Font建立字型物件?
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 public class Ex2_4_1 extends Frame implements Runnable { 06 Font messageFont; 07 String message; 08 public static void main(String args[]) { Ex2_4_1 workStart=new Ex2_4_1(); 10 }
17
範例3續 11 public Ex2_4_1() { 12 super("Ex2_4_1"); 13 setSize(350, 350);
setVisible(true); new Thread(this).start(); 16 } 17 public void run() { messageFont = new Font("TimesRoman", Font.PLAIN, 20); message = "This is a test string"; repaint(); 21 } 22 public void paint(Graphics g) { g.setFont(messageFont); g.drawString(message, 5, 50); 25 } 26 }
18
範例4:設計檔案Ex2_4_2.java其功能為解釋類別Font各生存實體方法程序之應用?
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 public class Ex2_4_2 extends Frame implements Runnable { 06 Font messageFont; 07 String message; 08 public static void main(String args[]) { Ex2_4_2 workStart=new Ex2_4_2(); 10 } 11 public Ex2_4_2() { super("Ex2_4_2"); setSize(350, 350); setVisible(true); new Thread(this).start(); 16 }
19
範例4續 17 public void run() { messageFont = new Font("TimesRoman", Font.PLAIN, 20); message = "This is a test string"; System.out.println("getName() : " + messageFont.getName()); System.out.println("getStyle() : " + messageFont.getStyle()); System.out.println("getSize() : " + messageFont.getSize()); System.out.println("isBold() : " + messageFont.isBold()); System.out.println("isItalic() : " + messageFont.isItalic()); System.out.println("isPlain() : " + messageFont.isPlain()); repaint(); 27 } 28 public void paint(Graphics g) { g.setFont(messageFont); g.drawString(message, 5, 50); 31 } 32 }
20
2-5 Color Class java.awt.Color繼承(extends) 自Object,為public final Class,此類別物件用於定義顏色值,因是final類別,故不得被繼承使用。
21
範例5:設計檔案Ex2_5_1.java其功能為解釋類別Color建構子參數顏色值之設定。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 public class Ex2_5_1 extends Frame implements Runnable { 06 Font messageFont; 07 String message; 08 Color color1, color2, color3; 09 public static void main(String args[]) { Ex2_5_1 workStart=new Ex2_5_1(); 11 }
22
範例5 續1 12 public Ex2_5_1() { 13 super("Ex2_5_1");
範例5 續1 12 public Ex2_5_1() { super("Ex2_5_1"); setSize(350, 350); setVisible(true); new Thread(this).start(); 17 } 18 public void run() { color1 = new Color(212, 255, 0); color2 = new Color(0xd4ff00); color3 = new Color(0.83f, 1.0f, 0.0f); messageFont = new Font("TimesRoman", Font.PLAIN, 20); message = "This is a test string"; repaint(); 25 }
23
範例5 續2 26 public void paint(Graphics g) { 27 g.setFont(messageFont);
範例5 續2 26 public void paint(Graphics g) { g.setFont(messageFont); g.setColor(color1); g.drawString(message, 5, 50); g.setFont(messageFont); g.setColor(color2); g.drawString(message, 5, 100); g.setFont(messageFont); g.setColor(color3); g.drawString(message, 5, 150); 36 } 37 }
24
範例6:設計檔案Ex2_5_2.java其功能為解釋Color Class之類別常數。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 import java.awt.Color; 06 public class Ex2_5_2 extends Frame implements Runnable { 07 Font messageFont; 08 String message; 09 public static void main(String args[]) { Ex2_5_2 workStart=new Ex2_5_2(); 11 } 12 public Ex2_5_2() { super("Ex2_5_2"); setSize(350, 350); setVisible(true); new Thread(this).start(); 17 }
25
範例6 續 18 public void run() { messageFont = new Font("TimesRoman", Font.PLAIN, 30); message = "This is a test string"; repaint(); 22 } 23 public void paint(Graphics g) { g.setFont(messageFont); g.setColor(Color.blue); g.drawString(message, 5, 50); 27 } 28 }
26
範例6.5:設計檔案Ex2_5_3.java其功能為解釋Color Class讀取顏色值之實體方法程序,執行時必須配合新物件之生存才可使用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 public class Ex2_5_3 extends Frame implements Runnable { 06 Font messageFont; 07 String message; 08 Color color; 09 public static void main(String args[]) { Ex2_5_3 workStart=new Ex2_5_3(); 11 }
27
範例6.5 續1 12 public Ex2_5_3() { 13 super("Ex2_5_3");
範例6.5 續1 12 public Ex2_5_3() { super("Ex2_5_3"); setSize(350, 350); setVisible(true); new Thread(this).start(); 17 } 18 public void run() { color = new Color(212, 255, 0); messageFont = new Font("TimesRoman", Font.PLAIN, 20); message = "This is a test string";
28
範例6.5 續2 22 System.out.println("getRed() : " + color.getRed());
範例6.5 續2 System.out.println("getRed() : " + color.getRed()); System.out.println("getGreen() : " + color.getGreen()); System.out.println("getBlue() : " + color.getBlue()); System.out.println("getRGB() : " + color.getRGB()); repaint(); 27 } 28 public void paint(Graphics g) { g.setFont(messageFont); g.setColor(color); g.drawString(message, 5, 50); 32 } 33 }
29
2-6 中文處理 於2-4節曾述及類別Font之功能,其產生的物件可定義字型之格式,建構子參數name為字型名稱,如Batang、Times New Roman、標楷體等;參數style為字型樣式,如BOLD(粗體)、ITALIC(斜體)、PLAIN(標準);參數size為字型大小,如10、12等。
30
範例7:設計檔案Ex2_6_1.java其功能為解釋中文字型之繪製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 import java.awt.Color; 06 public class Ex2_6_1 extends Frame implements Runnable { 07 Font messageFont1; 08 Font messageFont2; 09 String message; 10 public static void main(String args[]) { Ex2_6_1 workStart=new Ex2_6_1(); 12 }
31
範例7 續1 13 public Ex2_6_1() { 14 super("Ex2_6_1");
範例7 續1 13 public Ex2_6_1() { super("Ex2_6_1"); setSize(350, 350); setVisible(true); new Thread(this).start(); 18 } 19 public void run() { messageFont1 = new Font("新細明體", Font.PLAIN, 30); messageFont2 = new Font("標楷體", Font.PLAIN, 30); message = "中文字串"; repaint(); 24 }
32
範例7 續2 25 public void paint(Graphics g) { 26 g.setFont(messageFont1);
範例7 續2 25 public void paint(Graphics g) { g.setFont(messageFont1); g.setColor(Color.blue); g.drawString(message, 5, 50); g.setFont(messageFont2); g.setColor(Color.green); g.drawString(message, 5, 100); 32 } 33 }
33
範例8:設計檔案Ex2_6_2.java其功能為解釋同時設定中文/英文之字型名稱、或兩種字型樣式。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 import java.awt.Font; 05 import java.awt.Color; 06 public class Ex2_6_2 extends Frame implements Runnable { Font messageFont1, messageFont2, messageFont3, messageFont4; String message; public static void main(String args[]) { Ex2_6_2 workStart=new Ex2_6_2(); } public Ex2_6_2() { super("Ex2_6_2"); setSize(350, 350);
34
範例8 續1 15 setVisible(true); 16 new Thread(this).start(); 17 }
範例8 續1 setVisible(true); new Thread(this).start(); } public void run() { messageFont1 = new Font("新細明體"+"TimesRoman", Font.PLAIN, 30); messageFont2 = new Font("新細明體"+"Monotype Corsiva", messageFont3 = new Font("Monotype Corsiva", messageFont4 = new Font("標楷體"+"Monotype Corsiva", Font.ITALIC + Font.PLAIN, 30); message = "中文字串 English String"; repaint(); }
35
範例8 續2 26 public void paint(Graphics g) { 27 g.setFont(messageFont1);
範例8 續2 public void paint(Graphics g) { g.setFont(messageFont1); g.setColor(Color.blue); g.drawString(message, 5, 50); g.setFont(messageFont2); g.setColor(Color.gray); g.drawString(message, 5, 100); g.setFont(messageFont3); g.setColor(Color.red); g.drawString(message, 5, 150); g.setFont(messageFont4); g.setColor(Color.yellow); g.drawString(message, 5, 200); 39 } 40 }
36
第三章 基礎繪圖(Basic Graphics)
3-1 簡介 3-2 Graphics Class 3-3 直線繪製 3-4 長方形繪製 3-5 橢圓形繪製 3-6 弧線繪製 3-7 多邊形繪製 3-8圖形剪裁 3-9圖形複製 3-10 習題(Exercises)
37
3-1 簡介 在基礎圖文繪製上,除了前章所探討的文字繪製之外,本章將介紹基礎圖形之繪製。類別Graphics提供了各類圖形之繪製方法程序,包括直線繪製、長方形繪製、橢圓形繪製、弧線繪製、多邊形繪製、圖片處理等。
38
3-2 Graphics Class java.awt.Graphics繼承(extends) 自Object,為abstract public class(有關抽象類別之用法,請參考本書系列叢書第一冊基礎入門程式9-6節),此類別提供了所有Java的繪圖功能。因是抽象類別,無法借助建構子產生實體新物件直接繪圖,但可借由類別Component之方法程序pant() 參與繪製。
39
3-3 直線繪製 依類別Graphics之方法程序public abstract void drawLine(int x1, int y1, int x2, int y2) 繪製直線,其中參數(x1, y1) 為直線起始點座標,(x2, y2) 為直線終止點座標。
40
範例9:設計檔案Ex3_3.java其功能為解釋直線繪製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_3 extends Frame implements Runnable { 05 public static void main(String args[]) { Ex3_3 workStart=new Ex3_3(); 07 } 08 public Ex3_3() { super("Ex3_3"); setSize(350, 350); setVisible(true); new Thread(this).start(); 13 }
41
範例9 續 14 public void run() { 15 repaint(); 16 }
範例9 續 14 public void run() { repaint(); 16 } 17 public void paint(Graphics g) { g.drawLine(50,50,120,100); g.drawLine(10,250,50,200); g.drawLine(50,200,120,270); g.drawLine(120,270,200,160); 22 } 23 }
42
3-4 長方形繪製 依類別Graphics之方法程序public abstract void drawRect(int x, int y, int width, int height) 繪製長方形,其中參數(x, y) 為長方形左上角之座標,width為長方形之寬距,height為長方形之高距。
43
範例10:設計檔案Ex3_4.java其功能為解釋長方形繪製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_4 extends Frame implements Runnable { 05 public static void main(String args[]) { Ex3_4 workStart=new Ex3_4(); 07 } 08 public Ex3_4() { super("Ex3_4"); setSize(350, 350); setVisible(true); new Thread(this).start(); 13 }
44
範例10 續 14 public void run() { 15 repaint(); 16 }
範例10 續 14 public void run() { repaint(); 16 } 17 public void paint(Graphics g) { g.drawRect(90,50,150,100); g.fillRect(90,200,150,100); 20 } 21 }
45
3-5 橢圓形繪製 依類別Graphics之方法程序public abstract void drawOval(int x, int y, int width, int height) 繪製橢圓形,其中參數(x, y) 為橢圓形長方形框左上角之座標,width為橢圓形之寬距,height為橢圓形之高距。
46
範例11:設計檔案Ex3_5.java其功能為解釋橢圓形繪製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_5 extends Frame implements Runnable { 05 public static void main(String args[]) { Ex3_5 workStart=new Ex3_5(); 07 } 08 public Ex3_5() { super("Ex3_5"); setSize(350, 350); setVisible(true); new Thread(this).start(); 13 }
47
範例11 續 14 public void run() { 15 repaint(); 16 }
範例11 續 14 public void run() { repaint(); 16 } 17 public void paint(Graphics g) { g.drawOval(35,65,100,60); g.drawOval(170,65,100,60); g.drawRect(170,65,100,60); g.fillOval(35,200,100,60); 22 } 23 }
48
3-6 弧線繪製 依類別Graphics之方法程序public abstract void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) 繪製弧線,其中參數(x, y) 比照長方形左上角座標,width為橢圓形之寬距,height為橢圓形之高距,startAngle為弧線起始角度,arcAngle為截取橢圓形邊緣之角度。
49
範例12:設計檔案Ex3_6.java其功能為解釋弧形繪製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_6 extends Frame implements Runnable { 05 public static void main(String args[]) { Ex3_6 workStart=new Ex3_6(); 07 } 08 public Ex3_6() { super("Ex3_6"); setSize(350, 350); setVisible(true); new Thread(this).start(); 13 }
50
範例12 續 14 public void run() { 15 repaint(); 16 }
範例12 續 14 public void run() { repaint(); 16 } 17 public void paint(Graphics g) { g.drawArc(35,65,100,60,10,90); g.drawArc(170,65,100,60,10,90); g.drawRect(170,65,100,60); g.fillArc(35,200,100,60,10,90); 22 } 23 }
51
3-7 多邊形繪製 依類別Graphics之方法程序public abstract void drawPolygon(int[] xpoints, int[] ypoints, int npoints) 繪製多邊形,其中參數xpoints為各點之x軸座標、ypoints為各點之y軸座標、npoint為多邊形點的數量。
52
範例13:設計檔案Ex3_7.java其功能為解釋多邊形繪製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_7 extends Frame implements Runnable { 05 int[] pgx = {90,130,180,230,180,130,90}; 06 int[] pgy = {100,60,60,100,150,150,100}; 07 int pgn = 7; 08 int[] fpgx = {90,130,180,230,180,130,90}; 09 int[] fpgy = {250,210,210,250,300,300,250}; 10 int fpgn = 7; 11 public static void main(String args[]) { Ex3_7 workStart=new Ex3_7(); 13 }
53
範例13 續 14 public Ex3_7() { 15 super("Ex3_7"); 16 setSize(350, 350);
範例13 續 14 public Ex3_7() { super("Ex3_7"); setSize(350, 350); setVisible(true); new Thread(this).start(); 19 } 20 public void run() { repaint(); 22 } 23 public void paint(Graphics g) { g.drawPolygon(pgx, pgy, pgn); g.fillPolygon(fpgx, fpgy, fpgn); 26 } 27 }
54
3-8圖形剪裁 依類別Graphics之方法程序 public abstract void clipRect(int x, int y, int width, int height) 裁取長方形區域內之圖形,其中參數(x, y) 為長方形左上角之座標,width為長方形之寬距,height為長方形之高距。
55
範例14:參考範例13,設計檔案Ex3_8.java其功能為解釋圖形剪裁。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_8 extends Frame implements Runnable { 05 int[] pgx = {90,130,180,230,180,130,90}; 06 int[] pgy = {100,60,60,100,150,150,100}; 07 int pgn = 7; 08 int[] fpgx = {90,130,180,230,180,130,90}; 09 int[] fpgy = {250,210,210,250,300,300,250}; 10 int fpgn = 7; 11 public static void main(String args[]) { Ex3_8 workStart=new Ex3_8(); 13 }
56
範例14 續 14 public Ex3_8() { 15 super("Ex3_8"); 16 setSize(350, 350);
範例14 續 14 public Ex3_8() { super("Ex3_8"); setSize(350, 350); setVisible(true); new Thread(this).start(); 19 } 20 public void run() { repaint(); 22 } 23 public void paint(Graphics g) { g.drawRect(100,80,120,50); g.drawPolygon(pgx, pgy, pgn); g.clipRect(100,230,120,50); g.fillPolygon(fpgx, fpgy, fpgn); 28 } 29 }
57
3-9圖形複製 依類別Graphics之方法程序public abstract void copyArea(int x, int y, int width, int height, int dx, int dy) 複製長方形區域內之圖形到(x+dx, y+dy),其中參數(x, y) 為原長方形左上角之座標,(x+dx, y+dy) 為複製長方形左上角之座標,width為長方形之寬距,height為長方形之高距。
58
範例15:參考範例11,設計檔案Ex3_9.java其功能為解釋圖形複製。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex3_9 extends Frame implements Runnable { 05 public static void main(String args[]) { Ex3_9 workStart=new Ex3_9(); 07 } 08 public Ex3_9() { super("Ex3_9"); setSize(350, 350); setVisible(true); new Thread(this).start(); 13 }
59
範例15 續 14 public void run() { 15 repaint(); 16 }
16 } 17 public void paint(Graphics g) { g.drawOval(35,65,100,60); g.drawOval(170,65,100,60); g.drawRect(170,65,100,60); g.fillOval(35,200,100,60); g.copyArea(35,200,100,60,135,0); 23 } 24 }
60
3-10 習題(Exercises) 01、Graphics Class之功能為何?
03、drawString(String str, int x, int y) 繪製字串,其中參數之意為何? 04、drawLine(int x1, int y1, int x2, int y2) 繪製直線,其中參數之意為何? 05、drawRect(int x, int y, int width, int height) 繪製長方形,其中參數之意為何? 06、drawOval(int x, int y, int width, int height) 繪製橢圓形,其中參數之意為何? 07、drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) 繪製弧線,其中參數之意為何? 08、drawPolygon(int[] xpoints, int[] ypoints, int npoints) 繪製多邊形,其中參數之意為何? 09、clearRect(int x, int y, int width, int height) 以背景色清除長方形區域內之圖形,其中參數之意為何? 10、clipRect(int x, int y, int width, int height) 裁取長方形區域內之圖形,其中參數之意為何? 11、copyArea(int x, int y, int width, int height, int dx, int dy) 複製長方形區域內之圖形到(x+dx, y+dy),其中參數之意為何?
61
第四章 影像檔引用(Image) 4-1 簡介 4-2 影像檔格式(File Formats) 4-3 影像讀取與Toolkit Class
4-4 影像繪製與Graphics Class 4-5 習題(Exercises)
62
4-1 簡介 於前述各章節、我們探討的圖文來自臨場自行繪製,本章探討的圖文是來自已經完成之影像檔案(Images),包括文字、圖案、照片等檔案。依程式碼功能,讀取影像檔案、顯示影像檔畫面。
63
4-2 影像檔格式(File Formats) 一般顯示在電腦螢幕上的影像檔格式有兩類:點陣格式(Raster)、與向量格式(Vector)。前者以點陣描繪影像,每一個像素有其對應影像之位置,當影像放大時,密度降低,影像趨於模糊;後者以幾何圖形描繪影像,當影響放大時,亦不會改變品質。Java目前可使用的影像檔格式為點陣格式,包括GIF、PNG、JPEG(JPG)。
64
4-4 影像繪製與Graphics Class 於3-2節、我們曾詳述Graphics Class之繪圖功能,除了基礎繪圖方法程序之外,類別Graphics也支援影像圖片繪製
65
範例16:設計檔案Ex4_4_1.java其功能為解釋drawImage(Image img, int x, int y, this) 之繪製功能。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex4_4_1 extends Frame implements Runnable { 05 Image image; 06 public static void main(String args[]) { Ex4_4_1 workStart=new Ex4_4_1(); 08 }
66
範例16 續 09 public Ex4_4_1() { 10 super("Ex4_4_1");
範例16 續 09 public Ex4_4_1() { super("Ex4_4_1"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); image = tk.getImage("Sunset.jpg"); setVisible(true); new Thread(this).start(); 16 } 17 public void run() { repaint(); 19 } 20 public void paint(Graphics g) { g.drawImage(image, 0, 0, this); 22 } 23 }
67
範例16.5:設計檔案Ex4_4_1.java其功能為解釋drawImage(Image img, int x, inty, int width, int height, this) 之繪製功能。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex4_4_2 extends Frame implements Runnable { 05 Image image; 06 public static void main(String args[]) { Ex4_4_2 workStart=new Ex4_4_2(); 08 }
68
範例16.5 續 09 public Ex4_4_2() { 10 super("Ex4_4_2");
範例16.5 續 09 public Ex4_4_2() { super("Ex4_4_2"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); image = tk.getImage("Sunset.jpg"); setVisible(true); new Thread(this).start(); 16 } 17 public void run() { repaint(); 19 } 20 public void paint(Graphics g) { g.drawImage(image, 0, 0, 200, 200, this); 22 } 23 }
69
4-5 習題(Exercises) 01、Java目前可使用的影像檔格式有那些? 02、GIF格式之特性為何? 03、PNG格式之特性為何?
04、JPEG(JPG) 格式之特性為何? 05、影像之讀取/繪環境有那兩類? 06、類別Toolkit 如何執行讀取影像檔? 07、方法程序drawImage(Image img, int x, int y, this) 在繪圖上有何特性? 08、方法程序drawImage(Image img, int x, inty, int width, int height, this)在繪圖上有何特性?
70
第五章 基礎動畫(Animation) 5-1 簡介 5-2 動態圖案 5-3 動態影像 5-4 矩陣與動畫
5-5 習題(Exercises)
71
5-1 簡介 於前述各章節,我們探討的是靜態圖案,本章將介紹動態圖案之設計,以為爾後線上遊戲設計之先驅,包括在框架中使用繪製圖案、或影像圖片;執行單幅動畫、或多幅動畫。
72
5-2 動態圖案 驅使一幅動態的圖案,在程式設計上要克服:(1) 不斷地改變圖案座標,(2) 當於新位置顯示圖案時,要清除舊位置圖案。
73
範例17:設計檔案Ex5_2_1.java其功能為解釋圖案移動之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex5_2_1 extends Frame implements Runnable { 05 int x=0, y=160; 06 int dx=5, dy=0; 07 public static void main(String args[]) { Ex5_2_1 workStart=new Ex5_2_1(); 09 } 10 public Ex5_2_1() { super("Ex5_2_1"); setSize(350, 350); setVisible(true); new Thread(this).start(); 15 }
74
範例17 續 16 public void run() { 17 while(true) { 18 x = x + dx;
範例17 續 16 public void run() { while(true) { x = x + dx; y = y + dy; repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} } 24 } 25 public void paint(Graphics g) { g.setColor(Color.red); g.fillOval(x, y, 50, 50); 28 } 29 }
75
範例18:設計檔案Ex5_2_2.java其功能為解釋圖案回彈之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex5_2_2 extends Frame implements Runnable { 05 int x=0, y=160; 06 int dx=5, dy=0; 07 public static void main(String args[]) { Ex5_2_2 workStart=new Ex5_2_2(); 09 } 10 public Ex5_2_2() { super("Ex5_2_2"); setSize(350, 350); setVisible(true); new Thread(this).start(); 15 }
76
範例18 續 16 public void run() { 17 while(true) { 18 x = x + dx;
範例18 續 16 public void run() { while(true) { x = x + dx; y = y + dy; repaint(); if(x<=0) dx = 5; else if((x + 50) >= getWidth()) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 28 } 29 public void paint(Graphics g) { g.setColor(Color.red); g.fillOval(x, y, 50, 50); 32 } 33 }
77
5-3 動態影像 參考4-4節,以類別Toolkit借助類別方法程序getDefaultToolkit() 建立新物件,再以實體方法程序getImage() 讀取影像。以類別Graphics支援影像圖片繪製。設計範例18,讀取單幅影像檔並執行動畫移動。
78
範例18.5:設計檔案Ex5_3_1.java其功能為解釋單幅動畫之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex5_3_1 extends Frame implements Runnable { 05 int x=0, y=100; 06 int dx=5, dy=5; 07 Image img; 08 public static void main(String args[]) { Ex5_3_1 workStart=new Ex5_3_1(); 10 } 11 public Ex5_3_1() { super("Ex5_3_1"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("fly.gif"); setVisible(true); new Thread(this).start(); 18 }
79
範例18.5 續 19 public void run() { 20 while(true) { 21 x = x + dx;
範例18.5 續 19 public void run() { while(true) { x = x + dx; y = y + dy; repaint(); if(x<=0) dx = 5; else if((x + 50) >= getWidth()) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 31 } 32 public void paint(Graphics g) { g.drawImage(img, x, y, this); 34 } 35 }
80
範例19:設計檔案Ex5_3_2.java其功能為解釋多幅動畫之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex5_3_2 extends Frame implements Runnable { 04 int num=0, flag; 05 int x=0, y=100, dx=5, dy=5; 06 Image img0, img1, img2; 07 public static void main(String args[]) { Ex5_3_2 workStart=new Ex5_3_2(); 09 }
81
範例19 續1 10 public Ex5_3_2() { 11 super("Ex5_3_2");
範例19 續1 10 public Ex5_3_2() { super("Ex5_3_2"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); img0 = tk.getImage("fly0.gif"); img1 = tk.getImage("fly1.gif"); img2 = tk.getImage("fly2.gif"); setVisible(true); new Thread(this).start(); 19 } 20 public void run() { while(true) { x = x + dx; y = y + dy; flag = num % 3; repaint(); num = num + 1; if(x <= 0) dx = 5; else if((x+60) >= 350) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5;
82
範例19 續2 31 try{Thread.sleep(250);}
範例19 續2 try{Thread.sleep(250);} catch(InterruptedException e) {;} } 34 } 35 public void paint(Graphics g) { if(flag == 0) g.drawImage(img0, x, y, this); else if(flag == 1) g.drawImage(img1, x, y, this); else if(flag == 2) g.drawImage(img2, x, y, this); 42 } 43 }
83
5-4 矩陣與動畫 在處理生動的動畫時,都需讀取/繪製多幅影像檔,為了程式簡潔且高可讀性,我們應選擇使用矩陣,詳細方式請讀者參本系列叢書第一冊基礎入門程式4-3節。設計範例20,以矩陣讀取10幅數字檔,並以靜態方式,依序繪出該10個數字。
84
範例20:設計檔案Ex5_4_1.java其功能為解釋矩陣與靜態動畫之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex5_4_1 extends Frame implements Runnable { 04 int num=0, flag, i; 05 Image[] img; 06 public static void main(String args[]) { Ex5_4_1 workStart=new Ex5_4_1(); 08 } 09 public Ex5_4_1() { super("Ex5_4_1"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); img = new Image[10]; for(i=0; i<10; i++) { img[i] = tk.getImage("char" + i + ".png"); } setVisible(true); new Thread(this).start(); 19 }
85
範例20 續 20 public void run() { 21 while(true) { 22 flag = num % 10;
範例20 續 20 public void run() { while(true) { flag = num % 10; repaint(); num = num + 1; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 28 } 29 public void paint(Graphics g) { g.drawImage(img[flag], 50, 50, this); 31 } 32 }
86
範例21:參考範例19,設計檔案Ex5_4_2.java其功能為解釋以矩陣設計多幅動畫之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex5_4_2 extends Frame implements Runnable { 04 int num=0, flag, i; 05 int x=0, y=100, dx=5, dy=5; 06 Image[] img; 07 public static void main(String args[]) { Ex5_4_2 workStart=new Ex5_4_2(); 09 } 10 public Ex5_4_2() { super("Ex5_4_2"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); img = new Image[3]; for(i=0; i<3; i++) { img[i] = tk.getImage("fly" + i + ".gif"); } setVisible(true); new Thread(this).start(); 20 }
87
範例21 續 21 public void run() { 22 while(true) { 23 x = x + dx;
範例21 續 21 public void run() { while(true) { x = x + dx; y = y + dy; flag = num % 3; repaint(); num = num + 1; if(x <= 0) dx = 5; else if((x+60) >= 350) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 35 } 36 public void paint(Graphics g) { g.drawImage(img[flag], x, y, this); 38 } 39 }
88
5-5 習題(Exercises) 01、設計動態圖案,在程式設計上要克服那些問題? 02、如何取得框架之寬距與高距? 03、如何讀取影像?
04、設計多幅動畫之觀念有那些重點?
89
第二篇 事件處理(Events)
90
第六章 低階事件(Low Level Events)
6-1 簡介 6-2 Java事件架構 6-3 AWTEvent Class 6-4 ComponentEvent Class 6-5 KeyEvent Class 6-6 MouseEvent Class 6-7 ContainerEvent Class 6-8 FocusEvent Class 6-9 WindowEvent Class 6-10 習題(Exercises)
91
6-1 簡介 於前述各章節,我們探討到如何繪圖?如何引用影像圖片?但仍缺少精彩之互動性,譬如使用滑鼠點選位置;使用鍵盤鍵改變移動方向,這些都是令人興奮的互動操作。雖然讀者可能已是熟練玩家,如果再能徹底了解其中程式設計之淵源,那將更是有意義。
92
6-2 Java事件架構 當預設事件(Event) 發生時,此時無論程式在何處、作何種執行,都將暫停,由系統將適當的資源(包括CPU與時間) 優先支援該事件之執行。Java所支援的低階事件類別有: 1、ComponentEvent 2、ContainerEvent 3、FocusEvent 4、KeyEvent 5、MouseEvent 6、WindowEvent
93
圖6-2
94
6-3 AWTEvent Class java.awt.AWTEvent繼承(extends) 自EventObject → Object,此類別是所有低階事件類別之基礎類別。
95
6-4 ComponentEvent Class
java.awt.event.ComponentEvent繼承(extends) 自AWTEvent → EventObject → Object,此類別將元件層次的事件(Events) 包裝起來,如調整移動(Move)、改變大小(Resize)、隱藏(Hid)、或顯示(Show) 等。
96
範例22:設計檔案Ex6_4_1.java其功能為解釋移動框架之Component事件。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_4_1 extends Frame { 04 public Ex6_4_1() { super("Ex6_4_1"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.COMPONENT_EVENT_MASK); 09 } 10 public void processComponentEvent(ComponentEvent e) { if(e.getID() == ComponentEvent.COMPONENT_MOVED) { System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); } 15 }
97
範例22 續 16 public static void main(String args[]) {
範例22 續 16 public static void main(String args[]) { Ex6_4_1 workStart=new Ex6_4_1(); 18 } 19 }
98
範例23:設計檔案Ex6_4_2.java其功能為解釋改變框架大小之Component之事件。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_4_2 extends Frame { 04 public Ex6_4_2() { super("Ex6_4_2"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.COMPONENT_EVENT_MASK); 09 } 10 public void processComponentEvent(ComponentEvent e) { if(e.getID() == ComponentEvent.COMPONENT_RESIZED) { System.out.println("getSource() : " + e.getSource()); System.out.println(“getID() : ” + e.getID()); } 15 }
99
範例22 續 16 public static void main(String args[]) {
範例22 續 16 public static void main(String args[]) { Ex6_4_2 workStart=new Ex6_4_2(); 18 } 19 }
100
6-5 KeyEvent Class java.awt.event.KeyEvent繼承(extends) 自InputEvent → ComponentEvent → AWTEvent → EventObject → Object,此類別將與按鍵有關的事件包裝起來,按下鍵盤鍵是按鍵事件的來源(KeyEvent Source),並實作至KeyListener介面,即一個KeyEvent會被送到KeyListener作指定事件的處理。
101
範例24:設計檔案Ex6_5_1.java其功能為解釋當以鍵 ‘A’ 為KEY_PRESSED按鍵事件時,如何讀取按鍵事件建構子參數之內容?
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_5_1 extends Frame { 04 public Ex6_5_1() { super("Ex6_5_1"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.KEY_EVENT_MASK); 09 }
102
範例24 續 10 public void processKeyEvent(KeyEvent e) {
範例24 續 10 public void processKeyEvent(KeyEvent e) { if(e.getID() == KeyEvent.KEY_PRESSED) { System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); System.out.println("getWhen() : " + e.getWhen()); System.out.println("getKeyCode() : " + e.getKeyCode()); } 17 } 18 public static void main(String args[]) { Ex6_5_1 workStart=new Ex6_5_1(); 20 } 21 }
103
範例25:設計檔案Ex6_5_2.java其功能為解釋KeyEvent Class類別常數VK_A與事件之關係。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_5_2 extends Frame { 04 public Ex6_5_2() { super("Ex6_5_2"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.KEY_EVENT_MASK); 09 }
104
範例25 續 10 public void processKeyEvent(KeyEvent e) {
範例25 續 10 public void processKeyEvent(KeyEvent e) { if(e.getID() == KeyEvent.KEY_PRESSED) { if(e.getKeyCode() == KeyEvent.VK_A) System.out.println("This is VK_A event"); else System.out.println("This is not VK_A event"); } 17 } 18 public static void main(String args[]) { Ex6_5_2 workStart=new Ex6_5_2(); 20 } 21 }
105
6-6 MouseEvent Class java.awt.event.MouseEvent繼承(extends) 自InputEvent → ComponentEvent → AWTEvent → EventObject → Object,此類別將與滑鼠有關的事件包裝起來,包括點擊、移動等。滑鼠為滑鼠事件的來源(MouseEvent Source),並實作至MouseListener介面,即一個MouseEvent會被送到MouseListener作指定事件的處理。
106
範例26:設計檔案Ex6_6_1.java其功能為解釋MouseEvent Class類別常數MOUSE_PRESSED與如何讀取滑鼠事件建構子參數之內容?
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_6_1 extends Frame { 04 public Ex6_6_1() { super("Ex6_6_1"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.MOUSE_EVENT_MASK); 09 }
107
範例26 續 10 public void processMouseEvent(MouseEvent e) {
範例26 續 10 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); System.out.println("getWhen() : " + e.getWhen()); System.out.println("getX() : " + e.getX()); System.out.println("getY() : " + e.getY()); System.out.println("getClickCount() : " + e.getClickCount()); } 19 } 20 public static void main(String args[]) { Ex6_6_1 workStart=new Ex6_6_1(); 22 } 23 }
108
範例27:設計檔案Ex6_6_2.java其功能為解釋類別常數MOUSE_ENTERED、MOUSE_EXITED之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_6_2 extends Frame { 04 public Ex6_6_2() { super("Ex6_6_2"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.MOUSE_EVENT_MASK); 09 }
109
範例27 續1 10 public void processMouseEvent(MouseEvent e) {
範例27 續1 10 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_ENTERED) { System.out.println("ENTER DATA ::"); System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); System.out.println("getWhen() : " + e.getWhen()); System.out.println("getX() : " + e.getX()); System.out.println("getY() : " + e.getY()); System.out.println("getClickCount() : " + e.getClickCount()); }
110
範例27 續2 20 if(e.getID() == MouseEvent.MOUSE_EXITED) {
範例27 續2 if(e.getID() == MouseEvent.MOUSE_EXITED) { System.out.println("EXIT DATA ::"); System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); System.out.println("getWhen() : " + e.getWhen()); System.out.println("getX() : " + e.getX()); System.out.println("getY() : " + e.getY()); System.out.println("getClickCount() : " + e.getClickCount()); } 29 } 30 public static void main(String args[]) { Ex6_6_2 workStart=new Ex6_6_2(); 32 } 33 }
111
6-7 ContainerEvent Class
java.awt.event.ContainerEvent繼承(extends) 自ComponentEvent → AWTEvent → EventObject → Object,此類別將容器有關的事件包裝起來,包括元件的新增與移除,當此類事件發生時,立即啟動ContainerEvent,並配合ContainerListener執行。
112
6-8 FocusEvent Class java.awt.event.FocusEvent繼承(extends) 自ComponentEvent → AWTEvent → EventObject → Object,此類別將輸入焦點有關的事件包裝起來,當此類事件發生時,立即啟動FocusEvent,並配合FocusListener執行。
113
範例28:設計檔案Ex6_8_1.java其功能為解釋點選使用框架之Focus事件。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_8_1 extends Frame { 04 public Ex6_8_1() { super("Ex6_8_1"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.FOCUS_EVENT_MASK); 09 }
114
範例28 續 10 public void processFocusEvent(FocusEvent e) {
範例28 續 10 public void processFocusEvent(FocusEvent e) { if(e.getID() == FocusEvent.FOCUS_GAINED) { System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); System.out.println("isTemporary() : " + e.isTemporary()); } 16 } 17 public static void main(String args[]) { Ex6_8_1 workStart=new Ex6_8_1(); 19 } 20 }
115
範例29:設計檔案Ex6_8_2.java其功能為解釋點選使用其他框架之Focus事件。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_8_2 extends Frame { 04 public Ex6_8_2() { super("Ex6_8_2"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.FOCUS_EVENT_MASK); 09 }
116
範例29 續 10 public void processFocusEvent(FocusEvent e) {
範例29 續 10 public void processFocusEvent(FocusEvent e) { if(e.getID() == FocusEvent.FOCUS_LOST) { System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); System.out.println("isTemporary() : " + e.isTemporary()); } 16 } 17 public static void main(String args[]) { Ex6_8_2 workStart=new Ex6_8_2(); 19 } 20 }
117
6-9 WindowEvent Class java.awt.event.WindowEvent繼承(extends) 自ComponentEvent → AWTEvent → EventObject → Object,此類別將視窗有關的事件包裝起來,當此類事件發生時,立即啟動WindowEvent,並配合WindowListener執行。
118
範例30:設計檔案Ex6_9_1.java其功能為解釋使用類別常數WINDOW_CLOSING、與點選框架右上方符號 “X” 之執行結果。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_9_1 extends Frame { 04 public Ex6_9_1() { super("Ex6_9_1"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.WINDOW_EVENT_MASK); 09 }
119
範例30 續 10 public void processWindowEvent(WindowEvent e) {
if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.out.println("getSource() : " + e.getSource()); System.out.println("getID() : " + e.getID()); } 15 } 16 public static void main(String args[]) { Ex6_9_1 workStart=new Ex6_9_1(); 18 } 19 }
120
範例31:設計檔案Ex6_9_2.java其功能為解釋點選框架右上方符號 “X” 執行視窗關閉。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex6_9_2 extends Frame { 04 public Ex6_9_2() { super("Ex6_9_2"); setSize(350, 350); setVisible(true); enableEvents(AWTEvent.WINDOW_EVENT_MASK); 09 } 10 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 14 }
121
範例31 續 15 public static void main(String args[]) {
範例31 續 15 public static void main(String args[]) { Ex6_9_2 workStart=new Ex6_9_2(); 17 } 18 }
122
範例32:設計檔案Ex6_9_3.java其功能為解釋點選框架右上方符號 “X” 執行視窗關閉。(參考本系列叢書第二冊網路入門程式20-5節)
01 import java.awt.*; 02 import java.awt.event.*; 03 class Ex6_9_3 extends Frame { 04 public Ex6_9_3() { super("Ex6_9_3"); setSize(350, 350); setVisible(true); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { System.exit(0); } }); 13 } 14 public static void main(String[] args) { Ex6_9_3 workstart = new Ex6_9_3(); 16 } 17 }
123
6-10 習題(Exercises) 01、Java所支援的低階事件類別有那些? 02、AWTEvent Class之功能為何?
03、ComponentEvent Class之功能為何? 04、KeyEvent Class之功能為何? 05、MouseEvent Class之功能為何? 06、ContainerEvent Class之功能為何? 07、FocusEvent Class之功能為何? 08、WindowEvent Class之功能為何?
124
第七章 滑鼠事件應用(MouseEvent Applications)
7-1 簡介 7-2 移動(Moved) 7-3 拖曳(Dragged) 7-4 選擇(Selected) 7-5 隨動(Followed) 7-6 執行緒並行(Threads) 7-7 棋盤(Chessboard) 7-8 習題(Exercises)
125
7-1 簡介 於前章6-6節,常用的滑鼠事件行為有按鍵(PRESSED)、點撃(CLICKED)、釋鍵(RELEASED)。每次發生滑鼠事件時,我們即可立即讀取該事件之來源(Source)、識別碼(ID)、發生時間(When)、x軸座標、y軸座標。
126
7-2 移動(Moved) 回顧範例26,當在視窗框架內任一點按下滑鼠左鍵時,發生滑鼠事件,此時我們可讀取滑鼠游標當時之位置座標(x, y)。再參考範例16,我們可將影像對應長方形之左上端畫製於該新位置座標。如此之執行,影像圖案將隨滑鼠游標之位置而移動。
127
範例33:設計檔案Ex7_2.java其功能為解釋影像圖案移動(Moved) 之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_2 extends Frame implements Runnable { 04 int x=50, y=50; 05 Image img; 06 public static void main(String args[]) { Ex7_2 workStart=new Ex7_2(); 08 }
128
範例33 續1 09 public Ex7_2() { 10 super("Ex7_2"); 11 setSize(350,350);
範例33 續1 09 public Ex7_2() { super("Ex7_2"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("img7_2.jpg"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 18 } 19 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 23 }
129
範例33 續2 24 public void processMouseEvent(MouseEvent e) {
範例33 續2 24 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { x = e.getX(); y = e.getY(); } 29 } 30 public void run() { while(true) { repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} } 36 } 37 public void paint(Graphics g) { g.drawImage(img, x, y, this); 39 } 40 }
130
7-3 拖曳(Dragged) 在影像上按下滑鼠左鍵,拖曳後、鬆釋滑鼠左鍵,影像圖案隨滑鼠游標此時之位置而移動,如此之執行是謂拖曳。
131
範例34:設計檔案Ex7_3.java其功能為解釋影像圖案拖曳(Dragged) 之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_3 extends Frame implements Runnable { 04 int x=50, y=50, flag=0; 05 Image img; 06 public static void main(String args[]) { Ex7_3 workStart=new Ex7_3(); 08 }
132
範例34 續1 09 public Ex7_3() { 10 super("Ex7_3"); 11 setSize(350,350);
範例34 續1 09 public Ex7_3() { super("Ex7_3"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("img7_3.jpg"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 18 } 19 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 23 }
133
範例34 續2 24 public void processMouseEvent(MouseEvent e) {
範例34 續2 24 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) if(((e.getX() >= x) && (e.getX() <= (x+40))) && ((e.getY() >= y) && (e.getY() <= (y+40)))) { flag = 1; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag == 1)) { x = e.getX(); y = e.getY(); flag = 0; } }
134
範例34 續3 36 public void run() { 37 while(true) { 38 repaint();
範例34 續3 36 public void run() { while(true) { repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} } 42 } 43 public void paint(Graphics g) { g.drawImage(img, x, y, this); 45 } 46 }
135
7-4 選擇(Selected) 於前節(7-3節),我們已實作點選一幅影像圖案,執行拖曳移動。本節將另介紹如何於多幅影像圖案中,點選其中一幅,執行拖曳移動,而不干擾其他影像圖案。即在多幅影像圖案中,選擇出需要的圖案,執行需要的動作。
136
範例35:設計檔案Ex7_4.java其功能為解釋選擇影像圖案(Selected) 與拖曳(Dragged) 之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_4 extends Frame implements Runnable { 04 int x1=50, y1=50, x2=100, y2=100, x3=150, y3=150; 05 int flag=0; 06 Image img1, img2, img3; 07 public static void main(String args[]) { Ex7_4 workStart=new Ex7_4(); 09 }
137
範例35 續1 10 public Ex7_4() { 11 super("Ex7_4"); 12 setSize(350,350);
範例35 續1 10 public Ex7_4() { super("Ex7_4"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img1 = tk.getImage("img7_4_01.jpg"); img2 = tk.getImage("img7_4_02.jpg"); img3 = tk.getImage("img7_4_03.jpg"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 21 }
138
範例35 續2 22 public void processWindowEvent(WindowEvent e) {
範例35 續2 22 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 26 } 27 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) if(((e.getX() >= x1) && (e.getX() <= (x1+40))) && ((e.getY() >= y1) && (e.getY() <= (y1+40)))) { flag = 1; } if(((e.getX() >= x2) && (e.getX() <= (x2+40))) && ((e.getY() >= y2) && (e.getY() <= (y2+40)))) { flag = 2; }
139
範例35 續3 37 if(((e.getX() >= x3) && (e.getX() <= (x3+40))) &&
範例35 續3 if(((e.getX() >= x3) && (e.getX() <= (x3+40))) && ((e.getY() >= y3) && (e.getY() <= (y3+40)))) { flag = 3; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag == 1)) { x1 = e.getX(); y1 = e.getY(); Flag = 0; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag == 2)) { x2 = e.getX(); y2 = e.getY(); Flag = 0; }
140
範例35 續4 if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag == 3)) { x3 = e.getX(); y3 = e.getY(); Flag = 0; } 56 } 57 public void run() { while(true) { repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} } 63 }
141
範例35 續5 64 public void paint(Graphics g) {
範例35 續5 64 public void paint(Graphics g) { g.drawImage(img1, x1, y1, this); g.drawImage(img2, x2, y2, this); g.drawImage(img3, x3, y3, this); 68 } 69 }
142
7-5 隨動(Followed) 按下滑鼠左鍵點選影像,拖曳滑鼠後,鬆釋滑鼠時,讀取滑鼠游標當時之Follow位置座標(fx, fy)。比較原始座標(x, y) 與隨動座標(fx, fy),將其間差距除以n,用以設定(dx, dy),當影像圖案隨動至座標(fx, fy) 時,設定(dx, dy) = (0, 0),使圖案不再移動,參考範例18,於每一階段之新位置座標(x, y) 更新繪製影像圖案,如此之執行是謂隨動(Followed)。
143
範例36:設計檔案Ex7_5_1.java其功能為解釋單幅影像圖案與隨動(Followed) 之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_5_1 extends Frame implements Runnable { 04 int x=50, y=50, fx=0, fy=0, dx=0, dy=0, flag=0; 05 Image img; 06 public static void main(String args[]) { Ex7_5_1 workStart=new Ex7_5_1(); 08 }
144
範例36 續1 09 public Ex7_5_1() { 10 super("Ex7_5_1");
範例36 續1 09 public Ex7_5_1() { super("Ex7_5_1"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("fly.GIF"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 18 } 19 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 23 }
145
範例36 續2 24 public void processMouseEvent(MouseEvent e) {
範例36 續2 24 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) if(((e.getX() >= x) && (e.getX() <= (x+60))) && ((e.getY() >= y) && (e.getY() <= (y+50)))) { flag = 1; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag == 1)) { fx = e.getX(); fy = e.getY(); dx = (fx - x) / 10; dy = (fy - y) / 10; flag = 0; } }
146
範例36 續3 38 public void run() { 39 while(true) { 40 x = x + dx;
範例36 續3 38 public void run() { while(true) { x = x + dx; y = y + dy; if(((dx > 0) && ((x+30) >= fx)) || ((dx < 0) && ((x+30) <= fx))) dx = 0; if(((dy > 0) && ((y+25) >= fy)) || ((dy < 0) && ((y+25) <= fy))) dy = 0; repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} } 48 } 49 public void paint(Graphics g) { g.drawImage(img, x, y, this); 51 } 52 }
147
範例37:設計檔案Ex7_5_2.java其功能為解釋多幅影像圖案與隨動(Followed) 之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_5_2 extends Frame implements Runnable { 04 int x=50, y=50, fx=0, fy=0, dx=0, dy=0; 05 int flag_type_img=0, flag_num_img=0, num=0; 06 Image img0, img1, img2; 07 public static void main(String args[]) { Ex7_5_2 workStart=new Ex7_5_2(); 09 }
148
範例37 續1 10 public Ex7_5_2() { 11 super("Ex7_5_2");
範例37 續1 10 public Ex7_5_2() { super("Ex7_5_2"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img0 = tk.getImage("fly0.GIF"); img1 = tk.getImage("fly1.GIF"); img2 = tk.getImage("fly2.GIF"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 21 }
149
範例37 續2 22 public void processWindowEvent(WindowEvent e) {
範例37 續2 22 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 26 } 27 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) if(((e.getX() >= x) && (e.getX() <= (x+80))) && ((e.getY() >= y) && (e.getY() <= (y+60)))) { flag_type_img = 1; }
150
範例37 續3 32 if((e.getID() == MouseEvent.MOUSE_RELEASED) &&
範例37 續3 if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag_type_img == 1)) { fx = e.getX(); fy = e.getY(); dx = (fx - x) / 10; dy = (fy - y) / 10; flag_type_img = 0; } }
151
範例37 續4 40 public void run() { 41 while(true) { 42 x = x + dx;
範例37 續4 40 public void run() { while(true) { x = x + dx; y = y + dy; flag_num_img = num % 3; if(((dx > 0) && ((x+40) >= fx)) || ((dx < 0) && ((x+40) <= fx))) dx = 0; if(((dy > 0) && ((y+30) >= fy)) || ((dy < 0) && ((y+30) <= fy))) dy = 0; repaint(); num = num + 1; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 52 }
152
範例37 續5 53 public void paint(Graphics g) {
範例37 續5 53 public void paint(Graphics g) { if(flag_num_img == 0) g.drawImage(img0, x, y, this); if(flag_num_img == 1) g.drawImage(img1, x, y, this); if(flag_num_img == 2) g.drawImage(img2, x, y, this); 57 } 58 }
153
7-6 執行緒並行(Threads) 我們一再強調,設計Java動畫遊戲必須參考2-3節之 “執行緒繪圖流程格式”,原因在可同時執行多個影像圖案之移動,多個圖案並行各自動作,互不干擾。
154
範例38:參考範例36,設計檔案Ex7_6_1.java其功能為解釋多個單幅影像圖案與並行之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_6_1 extends Frame implements Runnable { 04 int x1=50, y1=50, fx1=0, fy1=0, dx1=0, dy1=0, flag1=0; 05 int x2=100, y2=100, fx2=0, fy2=0, dx2=0, dy2=0, flag2=0; 06 Image img1, img2; 07 public static void main(String args[]) { Ex7_6_1 workStart=new Ex7_6_1(); 09 }
155
範例38 續1 10 public Ex7_6_1() { 11 super("Ex7_6_1");
範例38 續1 10 public Ex7_6_1() { super("Ex7_6_1"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img1 = tk.getImage("fly1.GIF"); img2 = tk.getImage("fly2.GIF"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 20 } 21 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 25 }
156
範例38 續2 26 public void processMouseEvent(MouseEvent e) {
範例38 續2 26 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) if(((e.getX() >= x1) && (e.getX() <= (x1+80))) && ((e.getY() >= y1) && (e.getY() <= (y1+60)))) { flag1 = 1; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag1 == 1)) { fx1 = e.getX(); fy1 = e.getY(); dx1 = (fx1 - x1) / 50; dy1 = (fy1 - y1) / 50; flag1 =0; }
157
範例38 續3 39 if(((e.getX() >= x2) && (e.getX() <= (x2+80))) &&
範例38 續3 if(((e.getX() >= x2) && (e.getX() <= (x2+80))) && ((e.getY() >= y2) && (e.getY() <= (y2+60)))) { flag2 = 1; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag2 == 1)) { fx2 = e.getX(); fy2 = e.getY(); dx2 = (fx2 - x2) / 50; dy2 = (fy2 - y2) / 50; flag2 = 0; } 50 }
158
範例38 續4 51 public void run() { 52 while(true) { 53 x1 = x1 + dx1;
範例38 續4 51 public void run() { while(true) { x1 = x1 + dx1; y1 = y1 + dy1; if(((dx1 > 0) && ((x1+40) >= fx1)) || ((dx1 < 0) && ((x1+40) <= fx1))) dx1 = 0; if(((dy1 > 0) && ((y1+30) >= fy1)) || ((dy1 < 0) && ((y1+30) <= fy1))) dy1 = 0; x2 = x2 + dx2; y2 = y2 + dy2; if(((dx2 > 0) && ((x2+40) >= fx2)) || ((dx2 < 0) && ((x2+40) <= fx2))) dx2 = 0; if(((dy2 > 0) && ((y2+30) >= fy2)) || ((dy2 < 0) && ((y2+30) <= fy2))) dy2 = 0; repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} } 65 }
159
範例38 續5 66 public void paint(Graphics g) {
範例38 續5 66 public void paint(Graphics g) { g.drawImage(img1, x1, y1, this); g.drawImage(img2, x2, y2, this); 69 } 70 }
160
範例39:參考範例37,設計檔案Ex7_6_2.java其功能為解釋多個多幅影像圖案與並行之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_6_2 extends Frame implements Runnable { 04 int x1=50, y1=50, fx1=0, fy1=0, dx1=0, dy1=0; 05 int flag1_type_img=0, flag1_num_img=0, num1=0; 06 Image img10, img11, img12; 07 int x2=100, y2=100, fx2=0, fy2=0, dx2=0, dy2=0; 08 int flag2_type_img=0, flag2_num_img=0, num2=0; 09 Image img20, img21, img22; 10 public static void main(String args[]) { 11 Ex7_6_2 workStart=new Ex7_6_2(); 12 }
161
範例39 續1 13 public Ex7_6_2() { 14 super("Ex7_6_2");
範例39 續1 13 public Ex7_6_2() { 14 super("Ex7_6_2"); 15 setSize(350,350); 16 Toolkit tk = Toolkit.getDefaultToolkit(); 17 img10 = tk.getImage("fly10.GIF"); 18 img11 = tk.getImage("fly11.GIF"); 19 img12 = tk.getImage("fly12.GIF"); 20 img20 = tk.getImage("fly20.GIF"); 21 img21 = tk.getImage("fly21.GIF"); 22 img22 = tk.getImage("fly22.GIF"); 23 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 24 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 25 setVisible(true); 26 new Thread(this).start(); 27 }
162
範例39 續2 28 public void processWindowEvent(WindowEvent e) {
範例39 續2 28 public void processWindowEvent(WindowEvent e) { 29 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 31 } 32 } 33 public void processMouseEvent(MouseEvent e) { 34 if(e.getID() == MouseEvent.MOUSE_PRESSED) if(((e.getX() >= x1) && (e.getX() <= (x1+80))) && ((e.getY() >= y1) && (e.getY() <= (y1+60)))) { flag1_type_img = 1; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag1_type_img == 1)) { fx1 = e.getX(); fy1 = e.getY(); dx1 = (fx1 - x1) / 50; dy1 = (fy1 - y1) / 50; flag1_type_img = 0; }
163
範例39 續3 46 if(((e.getX() >= x2) && (e.getX() <= (x2+80))) &&
範例39 續3 if(((e.getX() >= x2) && (e.getX() <= (x2+80))) && ((e.getY() >= y2) && (e.getY() <= (y2+60)))) { flag2_type_img = 1; } if((e.getID() == MouseEvent.MOUSE_RELEASED) && (flag2_type_img == 1)) { fx2 = e.getX(); fy2 = e.getY(); dx2 = (fx2 - x2) / 50; dy2 = (fy2 - y2) / 50; flag2_type_img = 0; } 57 }
164
範例39 續4 58 public void run() { 59 while(true) { 60 x1 = x1 + dx1;
範例39 續4 58 public void run() { 59 while(true) { x1 = x1 + dx1; y1 = y1 + dy1; flag1_num_img = num1 % 3; if(((dx1 > 0) && ((x1+40) >= fx1)) || ((dx1 < 0) && ((x1+40) <= fx1))) dx1 = 0; if(((dy1 > 0) && ((y1+30) >= fy1)) || ((dy1 < 0) && ((y1+30) <= fy1))) dy1 = 0; x2 = x2 + dx2; y2 = y2 + dy2; flag2_num_img = num2 % 3; if(((dx2 > 0) && ((x2+40) >= fx2)) || ((dx2 < 0) && ((x2+40) <= fx2))) dx2 = 0; if(((dy2 > 0) && ((y2+30) >= fy2)) || ((dy2 < 0) && ((y2+30) <= fy2))) dy2 = 0; repaint(); num1 = num1 + 1; num2 = num2 + 1; try{Thread.sleep(250);} catch(InterruptedException e) {;} 75 } 76 } 85 }
165
範例39 續5 77 public void paint(Graphics g) {
範例39 續5 77 public void paint(Graphics g) { 78 if(flag1_num_img == 0) g.drawImage(img10, x1, y1, this); 79 if(flag1_num_img == 1) g.drawImage(img11, x1, y1, this); 80 if(flag1_num_img == 2) g.drawImage(img12, x1, y1, this); 81 if(flag2_num_img == 0) g.drawImage(img20, x2, y2, this); 82 if(flag2_num_img == 1) g.drawImage(img21, x2, y2, this); 83 if(flag2_num_img == 2) g.drawImage(img22, x2, y2, this); 84 }
166
7-7 棋盤(Chessboard) 在弈棋遊戲設計上可略分為兩類:(1) 落子弈棋(如井字棋、五子棋、圍棋等),於棋盤預定位置顯示落放之棋子,在設計上、我們可參考範例33,在滑鼠點選之位置繪製棋子;(2) 叫吃弈棋(如象棋、西洋棋等),選定一個棋子,從原位置拖曳至新位置,如果新位置與敵方棋子衝突時,比較此兩個棋子之優勢,勝者保存、敗者消失,在設計上、我們可參考範例35,以滑鼠選擇棋子,拖曳至新位置。
167
範例40:設計檔案Ex7_7.java其功能為解釋井字棋盤之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex7_7 extends Frame implements Runnable { 04 int x, y; 05 Image img; 06 int[] area_flag = new int[9]; 07 int i; 08 public static void main(String args[]) { Ex7_7 workStart=new Ex7_7(); 10 }
168
範例40 續1 11 public Ex7_7() { 12 super("Ex7_7"); 13 setSize(250,280);
範例40 續1 11 public Ex7_7() { super("Ex7_7"); setSize(250,280); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("Circle.GIF"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); new Thread(this).start(); 20 } 21 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 25 }
169
範例40 續2 26 public void processMouseEvent(MouseEvent e) {
範例40 續2 26 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { x = e.getX(); y = e.getY(); } 31 } 32 public void run() { while(true) { if((x>30)&&(x<90)&&(y>70)&&(y<130)&&(area_flag[0]==0)) {area_flag[0] = 1;} else if ((x>90)&&(x<150)&&(y>70)&&(y<130)&&(area_flag[1]==0)) {area_flag[1] = 1;} else if ((x>150)&&(x<210)&&(y>70)&&(y<130)&&(area_flag[2]==0)) {area_flag[2] = 1;} else if((x>30)&&(x<90)&&(y>130)&&(y<190)&&(area_flag[3]==0)) {area_flag[3] = 1;}
170
範例40 續3 else if((x>90)&&(x<150)&&(y>130)&&(y<190)&&(area_flag[4]==0)) {area_flag[4] = 1;} else if((x>150)&&(x<210)&&(y>130)&&(y<190)&&(area_flag[5]==0)) {area_flag[5] = 1;} else if((x>30)&&(x<90)&&(y>190)&&(y<250)&&(area_flag[6]==0)) {area_flag[6] = 1;} else if((x>90)&&(x<150)&&(y>190)&&(y<250)&&(area_flag[7]==0)) {area_flag[7] = 1;} else if((x>150)&&(x<210)&&(y>190)&&(y<250)&&(area_flag[8]==0)) {area_flag[8] = 1;} repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} 46 } 47 }
171
範例40 續4 48 public void paint(Graphics g) {
範例40 續4 48 public void paint(Graphics g) { g.drawLine(90,50,90,230); g.drawLine(150,50,150,230); g.drawLine(30,110,210,110); g.drawLine(30,170,210,170); for (i=0; i<9; i++) { if (area_flag[0] == 1) g.drawImage(img, 42, 60, this); if (area_flag[1] == 1) g.drawImage(img, 102, 60, this); if (area_flag[2] == 1) g.drawImage(img, 164, 60, this); if (area_flag[3] == 1) g.drawImage(img, 42, 125, this);
172
範例40 續5 58 if (area_flag[4] == 1) g.drawImage(img, 102, 125, this);
範例40 續5 if (area_flag[4] == 1) g.drawImage(img, 102, 125, this); if (area_flag[5] == 1) g.drawImage(img, 164, 125, this); if (area_flag[6] == 1) g.drawImage(img, 42, 187, this); if (area_flag[7] == 1) g.drawImage(img, 102, 187, this); if (area_flag[8] == 1) g.drawImage(img, 164, 187, this); } 64 } 65 }
173
7-8 習題(Exercises) 01、何謂滑鼠事件之移動(Moved)? 02、何謂滑鼠事件之拖曳(Dragged)?
03、如何設定影像點選範圍? 04、何謂滑鼠事件之選擇(Selected)? 05、何謂滑鼠事件之隨動(Followed)? 06、在弈棋遊戲設計上可略分那兩類?
174
第八章 鍵盤事件應用(KeyEvent Applications)
8-1 簡介 8-2 鍵盤資料(Information of keys) 8-3 靜態方向控制(Static Directions Controlled) 8-4 動態方向控制(Dynamic Directions Controlled) 8-5 基礎射擊(Shooting) 8-6 習題(Exercises)
175
8-1 簡介 於前6-5節,常用的鍵盤鍵事件有按鍵(PRESSED)、釋鍵(RELEASED)。每次發生鍵盤鍵事件時,我們即可立即讀取該事件之來源(Source)、識別碼(ID)、發生時間(When)、與鍵之ASCII碼。
176
8-2 鍵盤資料(Information of keys)
回顧6-5節,類別KeyEvent將與按鍵有關的事件包裝起來,按下鍵盤鍵是按鍵事件的來源(KeyEvent Source),再實作至KeyListener介面,即將一個KeyEvent送到KeyListener作指定事件的處理。
177
8-3 靜態方向控制(Static Directions Controlled)
當按下鍵盤鍵時,鍵盤事件隨之發生,此時可讀取按鍵的資訊,不同的鍵有不同之KeyCode,我們可利用這些不同的資訊來執行不同的動作。如範例41,當按→鍵時,圖案向右移一步;當按←鍵時,圖案向左移一步;當按↑鍵時,圖案向上移一步;當按↓鍵時,圖案向下移一步。
178
範例41:設計檔案Ex8_3_1.java其功能為解釋單幅靜態方向控制之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex8_3_1 extends Frame implements Runnable { 04 int x=150, y=235, dx, dy; 05 Image img; 06 public static void main(String args[]) { 07 Ex8_3_1 workStart=new Ex8_3_1(); 08 }
179
範例41 續1 09 public Ex8_3_1() { 10 super("Ex8_3_1");
範例41 續1 09 public Ex8_3_1() { 10 super("Ex8_3_1"); 11 setSize(350,350); 12 Toolkit tk = Toolkit.getDefaultToolkit(); 13 img = tk.getImage("img8_3_1.JPG"); 14 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 15 enableEvents(AWTEvent.KEY_EVENT_MASK); 16 setVisible(true); 17 new Thread(this).start(); 18 }
180
範例41 續2 19 public void processWindowEvent(WindowEvent e) {
範例41 續2 19 public void processWindowEvent(WindowEvent e) { 20 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 22 } 23 } 24 public void processKeyEvent(KeyEvent e) { 25 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; break; case KeyEvent.VK_UP: dx = 0; dy = -5; break;
181
範例41 續3 36 case KeyEvent.VK_DOWN: 37 dx = 0; dy = 5; 38 break; 39 }
範例41 續3 case KeyEvent.VK_DOWN: dx = 0; dy = 5; break; } x = x + dx; y = y + dy; 42 } 43 } 44 public void run() { 45 while(true) { repaint(); try{Thread.sleep(10);} catch(InterruptedException e) {;} 49 } 50 }
182
範例41 續4 51 public void paint(Graphics g) {
範例41 續4 51 public void paint(Graphics g) { g.drawImage(img, x, y, this); 53 } 54 }
183
範例42:設計檔案Ex8_3_2.java其功能為解釋多幅靜態方向控制之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex8_3_2 extends Frame implements Runnable { 04 int x=150, y=235, dx=0, dy=0, flag_direction=0; 05 Image img0, img1, img2, img3; 06 public static void main(String args[]) { 07 Ex8_3_2 workStart=new Ex8_3_2(); 08 }
184
範例42 續1 09 public Ex8_3_2() { 10 super("Ex8_3_2");
範例42 續1 09 public Ex8_3_2() { 10 super("Ex8_3_2"); 11 setSize(350,350); 12 Toolkit tk = Toolkit.getDefaultToolkit(); 13 img0 = tk.getImage("img000.JPG"); 14 img1 = tk.getImage("img090.JPG"); 15 img2 = tk.getImage("img180.JPG"); 16 img3 = tk.getImage("img270.JPG"); 17 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 18 enableEvents(AWTEvent.KEY_EVENT_MASK); 19 setVisible(true); 20 new Thread(this).start(); 21 }
185
範例42 續2 22 public void processWindowEvent(WindowEvent e) {
範例42 續2 22 public void processWindowEvent(WindowEvent e) { 23 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 25 } 26 } 27 public void processKeyEvent(KeyEvent e) { 28 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; flag_direction = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; flag_direction = 2; break;
186
範例42 續3 38 case KeyEvent.VK_UP: 39 dx = 0; dy = -5;
範例42 續3 case KeyEvent.VK_UP: dx = 0; dy = -5; flag_direction = 1; break; case KeyEvent.VK_DOWN: dx = 0; dy = 5; flag_direction = 3; break; } x = x + dx; y = y + dy; 49 } 50 }
187
範例42 續4 51 public void run() { 52 while(true) { 53 repaint();
範例42 續4 51 public void run() { 52 while(true) { repaint(); try{Thread.sleep(10);} catch(InterruptedException e) {;} 56 } 57 } 58 public void paint(Graphics g) { if(flag_direction == 0) g.drawImage(img0, x, y, this); if(flag_direction == 1) g.drawImage(img1, x, y, this); if(flag_direction == 2) g.drawImage(img2, x, y, this); if(flag_direction == 3) g.drawImage(img3, x, y, this); 63 } 64 }
188
8-4 動態方向控制 (Dynamic Directions Controlled)
所謂動態是以迴圈,令圖案的位置座標不停地變換,每迴圈一次,即更新圖案座標一次,圖案也隨著不停地移動。
189
範例43:設計檔案Ex8_4_1.java其功能為解釋單幅動態方向控制之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex8_4_1 extends Frame implements Runnable { 04 int x=0, y=100; 05 int dx=0, dy=0; 06 Image img; 07 public static void main(String args[]) { 08 Ex8_4_1 workStart=new Ex8_4_1(); 09 }
190
範例43 續1 10 public Ex8_4_1() { 11 super("Ex8_4_1");
範例43 續1 10 public Ex8_4_1() { 11 super("Ex8_4_1"); 12 setSize(350, 350); 13 Toolkit tk = Toolkit.getDefaultToolkit(); 14 img = tk.getImage("fly.gif"); 15 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 16 enableEvents(AWTEvent.KEY_EVENT_MASK); 17 setVisible(true); 18 new Thread(this).start(); 19 } 20 public void processWindowEvent(WindowEvent e) { 21 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 23 } 24 }
191
範例43 續2 25 public void processKeyEvent(KeyEvent e) {
範例43 續2 25 public void processKeyEvent(KeyEvent e) { 26 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; break; case KeyEvent.VK_UP: dx = 0; dy = -5; break; case KeyEvent.VK_DOWN: dx = 0; dy = 5; break; } 41 } 42 }
192
範例43 續3 43 public void run() { 44 while(true) { 45 x = x + dx;
範例43 續3 43 public void run() { 44 while(true) { x = x + dx; y = y + dy; repaint(); if(x<=0) dx = 5; else if((x + 50) >= getWidth()) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} 54 } 55 } 56 public void paint(Graphics g) { g.drawImage(img, x, y, this); 58 } 59 }
193
範例44:設計檔案Ex8_4_2.java其功能為解釋多幅動態方向控制之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex8_4_2 extends Frame implements Runnable { 04 int x=0, y=170, dx=0, dy=0; 05 int num=1, flag; 06 Image img0, img1, img2; 07 public static void main(String args[]) { 08 Ex8_4_2 workStart=new Ex8_4_2(); 09 }
194
範例44 續1 10 public Ex8_4_2() { 11 super("Ex8_4_2");
範例44 續1 10 public Ex8_4_2() { 11 super("Ex8_4_2"); 12 setSize(350, 350); 13 Toolkit tk = Toolkit.getDefaultToolkit(); 14 img0 = tk.getImage("fly0.gif"); 15 img1 = tk.getImage("fly1.gif"); 16 img2 = tk.getImage("fly2.gif"); 17 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 18 enableEvents(AWTEvent.KEY_EVENT_MASK); 19 setVisible(true); 20 new Thread(this).start(); 21 }
195
範例43 續3 22 public void processWindowEvent(WindowEvent e) {
範例43 續3 22 public void processWindowEvent(WindowEvent e) { 23 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 25 } 26 } 27 public void processKeyEvent(KeyEvent e) { 28 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; break; case KeyEvent.VK_UP: dx = 0; dy = -5; break;
196
範例44 續3 39 case KeyEvent.VK_DOWN: 40 dx = 0; dy = 5; 41 break; 42 }
範例44 續3 case KeyEvent.VK_DOWN: dx = 0; dy = 5; break; } 43 } 44 } 45 public void run() { 46 while(true) { x = x + dx; y = y + dy; flag = num % 3; repaint(); num = num + 1; if(x <= 0) dx = 5; else if((x+60) >= 350) dx = -5; if((y-10) <= 0) dy = 5; else if((y+50) >= 350) dy = -5;
197
範例44 續4 56 try{Thread.sleep(170);}
範例44 續4 try{Thread.sleep(170);} catch(InterruptedException e) {;} 58 } 59 } 60 public void paint(Graphics g) { 61 if(flag == 0) g.drawImage(img0, x, y, this); 62 if(flag == 1) g.drawImage(img1, x, y, this); 63 if(flag == 2) g.drawImage(img2, x, y, this); 64 } 65 }
198
8-5 基礎射擊(Shooting) 如果能設計弈棋遊戲、與射擊遊戲,即可設計大部份之電腦遊戲,我們已於7-7節簡單介紹弈棋之設計概念,本節將介紹基礎射擊之概念。這些基礎概念均將使用於本書第三篇網路線上遊戲之設計。
199
範例45:設計檔案Ex8_5.java其功能為解釋基礎射擊之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex8_5 extends Frame implements Runnable { 04 int x=150, y=235, dx, dy; 05 int bx, by, dbx=0, dby=-5, bflag=0; 06 Image img; 07 public static void main(String args[]) { 08 Ex8_5 workStart=new Ex8_5(); 09 }
200
範例45 續1 10 public Ex8_5() { 11 super("Ex8_5"); 12 setSize(350,350);
範例45 續1 10 public Ex8_5() { 11 super("Ex8_5"); 12 setSize(350,350); 13 Toolkit tk = Toolkit.getDefaultToolkit(); 14 img = tk.getImage("car090.gif"); 15 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 16 enableEvents(AWTEvent.KEY_EVENT_MASK); 17 setVisible(true); 18 new Thread(this).start(); 19 } 20 public void processWindowEvent(WindowEvent e) { 21 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 23 } 24 }
201
範例45 續2 25 public void processKeyEvent(KeyEvent e) {
範例45 續2 25 public void processKeyEvent(KeyEvent e) { 26 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; break; case KeyEvent.VK_UP: dx = 0; dy = -5; break; case KeyEvent.VK_DOWN: dx = 0; dy = 5; break;
202
範例45 續3 40 case KeyEvent.VK_SPACE: 41 dx = 0; dy = 0; 42 bx = x + 30;
範例45 續3 case KeyEvent.VK_SPACE: dx = 0; dy = 0; bx = x + 30; by = y - 5; bflag = 1; break; default: dx = 0; dy = 0; } x = x + dx; y = y + dy; 51 } 52 }
203
範例45 續4 53 public void run() { 54 while(true) {
範例45 續4 53 public void run() { 54 while(true) { if(by <= 0) bflag = 0; if(bflag == 1) by = by + dby; repaint(); try{Thread.sleep(30);} catch(InterruptedException e) {;} 60 } 61 } 62 public void paint(Graphics g) { 63 g.drawImage(img, x, y, this); 64 g.fillRect(bx, by, 3, 5); 65 } 66 }
204
8-6 習題(Exercises) 01、如何讀取鍵盤事件之資訊? 02、如何以鍵盤鍵控制圖案之移動方向? 03、如何設計使圖案動態移動?
04、處理發射器與子彈之概念為何?
205
第九章 消除影像閃爍(Rid of Flicker)
9-1 簡介 9-2 設計方法(Methods) 9-3 消除動畫閃爍(Rid of Animation Flicker) 9-4 消除棋盤閃爍(Rid of Chessboard Flicker) 9-5 消除射擊影像閃爍(Rid of Shooting Flicker) 9-6 習題(Exercises)
206
9-1 簡介 細心的讀者可能已經注意到,前述各章節之影像動畫都有閃爍飄浮的畫面,那是因為新的影像隨時抽換舊的影像,如此才可呈現影像在視窗上作位置變化或姿態變化。如果影像抽換太快,其速度超過電腦系統的負荷,有些部份先到位、有些後到位,影像將會顯得閃爍而不穩定,這亦是電腦動畫設計不希望看到的。本章將介紹如何消除如是之閃爍飄浮的畫面。
207
9-2 設計方法(Methods) 為了消除影像閃爍,我們可設定一個緩衝頁(Buffer Page),將新影像先置入緩衝頁,等待影像各部份全部繪製完成後,再將緩衝頁顯示於視窗,如此即可消除影像之閃爍。程式設計過程中我們將使用Image、Component、Graphics等類別之方法程序。
208
9-3 消除動畫閃爍(Rid of Animation Flicker)
為了消除影像閃爍,我們可設定一個緩衝頁(Buffer Page),將新影像先置入緩衝頁,等待影像各部份全部繪製完成後,再將緩衝頁顯示於視窗,如此即可消除影像之閃爍。依9-2-4節之消除影像閃爍程式碼格式,設計範例46,執行消除單幅動畫閃爍之現象。
209
範例46:比較範例18,設計檔案Ex9_3_1.java其功能為解釋消除單幅動畫閃爍之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex9_3_1 extends Frame implements Runnable { 04 int x=0, y=100; 05 int dx=5, dy=5; 06 Image img, bufferPage=null; 07 public static void main(String args[]) { Ex9_3_1 workStart=new Ex9_3_1(); 09 }
210
範例46 續1 10 public Ex9_3_1() { 11 super("Ex9_3_1");
範例46 續1 10 public Ex9_3_1() { super("Ex9_3_1"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("fly.gif"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); setVisible(true); new Thread(this).start(); 18 } 19 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 23 }
211
範例46 續2 24 public void run() { 25 while(true) { 26 x = x + dx;
範例46 續2 24 public void run() { while(true) { x = x + dx; y = y + dy; repaint(); if(x<=0) dx = 5; else if((x + 50) >= getWidth()) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 36 }
212
範例46 續3 37 public void update(Graphics g) { 38 paint(g); 39 }
範例46 續3 37 public void update(Graphics g) { paint(g); 39 } 40 public void paint(Graphics g) { Graphics bufferg; if(bufferPage == null) bufferPage = createImage(350, 350); bufferg =bufferPage.getGraphics(); bufferg.drawImage(img, x, y, this); bufferg.dispose(); g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 48 } 49 }
213
範例47:參考範例19,設計檔案Ex9_3_2.java其功能為解釋消除多幅動畫閃爍之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex9_3_2 extends Frame implements Runnable { 04 int num=0, flag; 05 int x=0, y=100, dx=5, dy=5; 06 Image img0, img1, img2, bufferPage = null; 07 public static void main(String args[]) { Ex9_3_2 workStart=new Ex9_3_2(); 09 }
214
範例47 續1 10 public Ex9_3_2() { 11 super("Ex9_3_2");
範例47 續1 10 public Ex9_3_2() { super("Ex9_3_2"); setSize(350, 350); Toolkit tk = Toolkit.getDefaultToolkit(); img0 = tk.getImage("fly0.gif"); img1 = tk.getImage("fly1.gif"); img2 = tk.getImage("fly2.gif"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); setVisible(true); new Thread(this).start(); 20 }
215
範例47 續2 21 public void processWindowEvent(WindowEvent e) {
範例47 續2 21 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 25 } 26 public void run() { while(true) { x = x + dx; y = y + dy; flag = num % 3; repaint(); num = num + 1;
216
範例47 續3 33 if(x <= 0) dx = 5; 34 else if((x+60) >= 350) dx = -5;
範例47 續3 if(x <= 0) dx = 5; else if((x+60) >= 350) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 40 } 41 public void update(Graphics g) { paint(g); 43 }
217
範例47 續4 44 public void paint(Graphics g) { 45 Graphics bufferg;
範例47 續4 44 public void paint(Graphics g) { Graphics bufferg; if(bufferPage == null) bufferPage = createImage(350, 350); bufferg = bufferPage.getGraphics(); if(flag == 0) bufferg.drawImage(img0, x, y, this); else if(flag == 1) bufferg.drawImage(img1, x, y, this); else if(flag == 2) bufferg.drawImage(img2, x, y, this); bufferg.dispose(); g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 57 } 58 }
218
9-4 消除棋盤閃爍(Rid of Chessboard Flicker)
依9-2-4節之消除影像閃爍程式碼格式,設計範例48,執行消除棋盤閃爍之現象,包括棋盤格線、與棋子圖案。
219
範例48:參考範例40,設計檔案Ex9_4.java其功能為解釋消除棋盤閃爍之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex9_4 extends Frame implements Runnable { 04 int x, y; 05 Image img, bufferPage; 06 int[] area_flag = new int[9]; 07 int i; 08 public static void main(String args[]) { 09 Ex9_4 workStart=new Ex9_4(); 10 }
220
範例48 續1 11 public Ex9_4() { 12 super("Ex9_4"); 13 setSize(250,280);
範例48 續1 11 public Ex9_4() { 12 super("Ex9_4"); 13 setSize(250,280); 14 Toolkit tk = Toolkit.getDefaultToolkit(); 15 img = tk.getImage("Circle.GIF"); 16 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 17 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 18 setVisible(true); 19 new Thread(this).start(); 20 } 21 public void processWindowEvent(WindowEvent e) { 22 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 24 } 25 }
221
範例48 續2 26 public void processMouseEvent(MouseEvent e) {
範例48 續2 26 public void processMouseEvent(MouseEvent e) { 27 if(e.getID() == MouseEvent.MOUSE_PRESSED) { x = e.getX(); y = e.getY(); 30 } 31 } 32 public void run() { 33 while(true) { if((x>30)&&(x<90)&&(y>70)&&(y<130)&&(area_flag[0]==0)) {area_flag[0] = 1;} else if ((x>90)&&(x<150)&&(y>70)&&(y<130)&&(area_flag[1]==0)) {area_flag[1] = 1;} else if ((x>150)&&(x<210)&&(y>70)&&(y<130)&&(area_flag[2]==0)) {area_flag[2] = 1;} else if((x>30)&&(x<90)&&(y>130)&&(y<190)&&(area_flag[3]==0)) {area_flag[3] = 1;}
222
範例48 續3 else if((x>90)&&(x<150)&&(y>130)&&(y<190)&&(area_flag[4]==0)) {area_flag[4] = 1;} else if((x>150)&&(x<210)&&(y>130)&&(y<190)&&(area_flag[5]==0)) {area_flag[5] = 1;} else if((x>30)&&(x<90)&&(y>190)&&(y<250)&&(area_flag[6]==0)) {area_flag[6] = 1;} else if((x>90)&&(x<150)&&(y>190)&&(y<250)&&(area_flag[7]==0)) {area_flag[7] = 1;} else if((x>150)&&(x<210)&&(y>190)&&(y<250)&&(area_flag[8]==0)) {area_flag[8] = 1;} repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} 46 } 47 } 48 public void update(Graphics g) { paint(g); 50 } 51 public void paint(Graphics g) { 52 Graphics bufferg; 53 if(bufferPage == null) bufferPage = createImage(250, 250); 54 bufferg = bufferPage.getGraphics(); 55 bufferg.drawLine(90,50,90,230); 56 bufferg.drawLine(150,50,150,230); 57 bufferg.drawLine(30,110,210,110); 58 bufferg.drawLine(30,170,210,170); 59 for (i=0; i<9; i++) { if (area_flag[0] == 1) bufferg.drawImage(img, 42, 60, this); if (area_flag[1] == 1) bufferg.drawImage(img, 102, 60, this); if (area_flag[2] == 1) bufferg.drawImage(img, 164, 60, this); if (area_flag[3] == 1) bufferg.drawImage(img, 42, 125, this); if (area_flag[4] == 1) bufferg.drawImage(img, 102, 125, this); if (area_flag[5] == 1) bufferg.drawImage(img, 164, 125, this); if (area_flag[6] == 1) bufferg.drawImage(img, 42, 187, this); if (area_flag[7] == 1) bufferg.drawImage(img, 102, 187, this); if (area_flag[8] == 1) bufferg.drawImage(img, 164, 187, this); 69 } 70 bufferg.dispose(); 71 g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 72 } 73 }
223
範例48 續4 51 public void paint(Graphics g) { 52 Graphics bufferg;
範例48 續4 51 public void paint(Graphics g) { 52 Graphics bufferg; 53 if(bufferPage == null) bufferPage = createImage(250, 250); 54 bufferg = bufferPage.getGraphics(); 55 bufferg.drawLine(90,50,90,230); 56 bufferg.drawLine(150,50,150,230); 57 bufferg.drawLine(30,110,210,110); 58 bufferg.drawLine(30,170,210,170); 59 for (i=0; i<9; i++) { if (area_flag[0] == 1) bufferg.drawImage(img, 42, 60, this); if (area_flag[1] == 1) bufferg.drawImage(img, 102, 60, this);
224
範例48 續5 62 if (area_flag[2] == 1)
範例48 續5 if (area_flag[2] == 1) bufferg.drawImage(img, 164, 60, this); if (area_flag[3] == 1) bufferg.drawImage(img, 42, 125, this); if (area_flag[4] == 1) bufferg.drawImage(img, 102, 125, this); if (area_flag[5] == 1) bufferg.drawImage(img, 164, 125, this); if (area_flag[6] == 1) bufferg.drawImage(img, 42, 187, this); if (area_flag[7] == 1) bufferg.drawImage(img, 102, 187, this); if (area_flag[8] == 1) bufferg.drawImage(img, 164, 187, this); 69 } 70 bufferg.dispose(); 71 g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 72 } 73 }
225
9-5 消除射擊影像閃爍(Rid of Shooting Flicker)
依9-2-4節之消除影像閃爍程式碼格式,設計範例49,執行消除射擊影像閃爍之現象,包括發射器、與子彈。
226
範例49:參考範例45,設計檔案Ex9_5.java其功能為解釋消除射擊影像閃爍之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 public class Ex9_5 extends Frame implements Runnable { 04 int x=150, y=235, dx, dy; 05 int bx, by, dbx=0, dby=-5, bflag=0; 06 Image img, bufferPage=null; 07 public static void main(String args[]) { 08 Ex9_5 workStart=new Ex9_5(); 09 }
227
範例49 續1 10 public Ex9_5() { 11 super("Ex9_5"); 12 setSize(350,350);
範例49 續1 10 public Ex9_5() { 11 super("Ex9_5"); 12 setSize(350,350); 13 Toolkit tk = Toolkit.getDefaultToolkit(); 14 img = tk.getImage("car090.gif"); 15 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 16 enableEvents(AWTEvent.KEY_EVENT_MASK); 17 setVisible(true); 18 new Thread(this).start(); 19 } 20 public void processWindowEvent(WindowEvent e) { 21 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 23 } 24 }
228
範例49 續2 25 public void processKeyEvent(KeyEvent e) {
範例49 續2 25 public void processKeyEvent(KeyEvent e) { 26 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; break; case KeyEvent.VK_UP: dx = 0; dy = -5; break; case KeyEvent.VK_DOWN: dx = 0; dy = 5; break;
229
範例49 續3 40 case KeyEvent.VK_SPACE: 41 dx = 0; dy = 0; 42 bx = x + 30;
範例49 續3 case KeyEvent.VK_SPACE: dx = 0; dy = 0; bx = x + 30; by = y - 5; bflag = 1; break; default: dx =0; dy = 0; } x = x + dx; y = y + dy; 51 } 52 }
230
範例49 續4 53 public void run() { 54 while(true) {
範例49 續4 53 public void run() { 54 while(true) { if(by <= -10) bflag = 0; if(bflag == 1) by = by + dby; repaint(); try{Thread.sleep(30);} catch(InterruptedException e) {;} 60 } 61 } 62 public void update(Graphics g) { paint(g); 64 }
231
範例49 續5 65 public void paint(Graphics g) { 66 Graphics bufferg;
範例49 續5 65 public void paint(Graphics g) { 66 Graphics bufferg; 67 if(bufferPage == null) bufferPage = createImage(350, 350); 68 bufferg = bufferPage.getGraphics(); 69 bufferg.drawImage(img, x, y, this); 70 bufferg.fillRect(bx, by, 3, 5); 71 bufferg.setColor(Color.white); 72 bufferg.fillRect(bx, by+5, 3, 5); 73 bufferg.dispose(); 74 g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 75 } 76 }
232
9-6 習題(Exercises) 01、為何會產生閃爍影像畫面? 02、如何消除閃爍影像畫面? 03、消除閃爍影像畫面之程式碼格式為何?
233
第十章 音效處理(Sound Effects)
10-1 簡介 10-2 音效方法(Methods) 10-3 背景音效(Background Music) 10-4 音效控制(Sound Control) 10-5 弈棋音效 10-6 射擊音效 10-7 習題(Exercises)
234
10-1 簡介 如果動畫與電玩無音效陪襯,將使畫面失色許多,昇陽(Sun) 在設計Applet網路瀏覽器系列時,已發展出音效之使用,但在當時並不適用於視窗框架之使用。 為了使視窗框架也有適當之音響效果,昇陽(Sun) 特別將音效由Applet網路瀏覽器系列,延伸用於視窗框架系列。
235
10-2 音效方法(Methods) Java提供AIFF、AU、及WAV三種格式的音效檔案,本書暫採用AU格式,因其比較為簡單好用。
236
10-3 背景音效(Background Music)
依10-2節所述,以Applet.newAudioClip(getClass().getResource("sound.au")) 讀取音效檔案sound.au,設計範例50,以sound.loop( ) 連續播放音效檔案之音樂,形成背景音樂。
237
範例50:參考範例47,設計檔案Ex10_3.java其功能為解釋動畫背景音效之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.applet.*; 04 public class Ex10_3 extends Frame implements Runnable { 05 int num=0, flag; 06 int x=0, y=100, dx=5, dy=5; 07 Image img0, img1, img2, bufferPage = null; 08 AudioClip sound; 09 public static void main(String args[]) { 10 Ex10_3 workStart=new Ex10_3(); 11 }
238
範例50 續1 12 public Ex10_3() { 13 super("Ex10_3"); 14 setSize(350, 350);
範例50 續1 12 public Ex10_3() { 13 super("Ex10_3"); 14 setSize(350, 350); 15 Toolkit tk = Toolkit.getDefaultToolkit(); 16 img0 = tk.getImage("fly0.gif"); 17 img1 = tk.getImage("fly1.gif"); 18 img2 = tk.getImage("fly2.gif"); 19 sound = Applet.newAudioClip(getClass().getResource("sound.au")); 20 sound.loop(); 21 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 22 setVisible(true); 23 new Thread(this).start(); 24 }
239
範例50 續2 25 public void processWindowEvent(WindowEvent e) {
範例50 續2 25 public void processWindowEvent(WindowEvent e) { 26 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 28 } 29 } 30 public void run() { 31 while(true) { x = x + dx; y = y + dy; flag = num % 3; repaint(); num = num + 1; if(x <= 0) dx = 5; else if((x+60) >= 350) dx = -5;
240
範例50 續3 if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} 43 } 44 } 45 public void update(Graphics g) { paint(g); 47 } 48 public void paint(Graphics g) { 49 Graphics bufferg; 50 if(bufferPage == null) bufferPage = createImage(350, 350); 52 bufferg = bufferPage.getGraphics();
241
範例50 續4 53 if(flag == 0) 54 bufferg.drawImage(img0, x, y, this);
範例50 續4 53 if(flag == 0) bufferg.drawImage(img0, x, y, this); 55 else if(flag == 1) bufferg.drawImage(img1, x, y, this); 57 else if(flag == 2) bufferg.drawImage(img2, x, y, this); 59 bufferg.dispose(); 60 g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 61 } 62 }
242
10-4音效控制(Sound Control) 依10-2節所述,以Applet.newAudioClip(getClass().getResource("sound.au")) 讀取音效檔案sound.au,設計範例51,以鍵盤鍵控制音樂流程,當按下鍵 “L” 時,執行連續播放(Loop);當按下鍵 “P” 時,執行一首播放(Play);當按下鍵 “S” 時,執行停止播放(Stop)。
243
範例51:參考範例50,設計檔案Ex10_4.java其功能為解釋動畫音效控制之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.applet.*; 04 public class Ex10_4 extends Frame implements Runnable { 05 int num=0, flag; 06 int x=0, y=100, dx=5, dy=5; 07 Image img0, img1, img2, bufferPage = null; 08 AudioClip sound; 09 public static void main(String args[]) { 10 Ex10_4 workStart=new Ex10_4(); 11 }
244
範例51 續1 12 public Ex10_4() { 13 super("Ex10_4"); 14 setSize(350, 350);
範例51 續1 12 public Ex10_4() { 13 super("Ex10_4"); 14 setSize(350, 350); 15 Toolkit tk = Toolkit.getDefaultToolkit(); 16 img0 = tk.getImage("fly0.gif"); 17 img1 = tk.getImage("fly1.gif"); 18 img2 = tk.getImage("fly2.gif"); 19 sound = Applet.newAudioClip(getClass().getResource("sound.au")); 20 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 21 enableEvents(AWTEvent.KEY_EVENT_MASK); 22 setVisible(true); 23 new Thread(this).start(); 24 }
245
範例51 續2 25 public void processWindowEvent(WindowEvent e) {
範例51 續2 25 public void processWindowEvent(WindowEvent e) { 26 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 28 } 29 } 30 public void processKeyEvent(KeyEvent e) { 31 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_L: sound.loop(); break; case KeyEvent.VK_P: sound.play(); break; case KeyEvent.VK_S: sound.stop(); break; } 43 } 44 }
246
範例51 續3 45 public void run() { 46 while(true) { 47 x = x + dx;
範例51 續3 45 public void run() { 46 while(true) { x = x + dx; y = y + dy; flag = num % 3; repaint(); num = num + 1; if(x <= 0) dx = 5; else if((x+60) >= 350) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} 58 } 59 }
247
範例51 續4 60 public void update(Graphics g) { 61 paint(g); 62 }
範例51 續4 60 public void update(Graphics g) { 61 paint(g); 62 } 63 public void paint(Graphics g) { 64 Graphics bufferg; 65 if(bufferPage == null) bufferPage = createImage(350, 350); 66 bufferg = bufferPage.getGraphics(); 67 if(flag == 0) bufferg.drawImage(img0, x, y, this); 68 else if(flag == 1) bufferg.drawImage(img1, x, y, this); 69 else if(flag == 2) bufferg.drawImage(img2, x, y, this); 70 bufferg.dispose(); 71 g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 72 } 73 }
248
10-5弈棋音效 依10-2節所述,以Applet.newAudioClip(getClass().getResource("sound.au")) 讀取音效檔案sound.au,設計範例52,當按下滑鼠左鍵落棋子時,播放音效;當3枚棋子成一直線時為嬴,此時播放音效、並印出勝利訊息。
249
範例52:參考範例40,設計檔案Ex10_5.java其功能為解釋弈棋音效之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.applet.*; 04 public class Ex10_5 extends Frame implements Runnable { 05 int x, y; 06 Image img, bufferPage; 07 int[] area_flag = new int[9]; 08 int i; 09 AudioClip soundchess1, soundwin; 10 String message = ""; 11 public static void main(String args[]) { 12 Ex10_5 workStart=new Ex10_5(); 13 }
250
範例52 續1 14 public Ex10_5() { 15 super("Ex10_5"); 16 setSize(250,280);
範例52 續1 14 public Ex10_5() { 15 super("Ex10_5"); 16 setSize(250,280); 17 Toolkit tk = Toolkit.getDefaultToolkit(); 18 img = tk.getImage("Circle.GIF"); 19 soundchess1 = Applet.newAudioClip(getClass().getResource("soundchess1.au")); 20 soundwin = Applet.newAudioClip(getClass().getResource("soundwin.au")); 21 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 22 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 23 setVisible(true); 24 new Thread(this).start(); 25 }
251
範例52 續2 26 public void processWindowEvent(WindowEvent e) {
範例52 續2 26 public void processWindowEvent(WindowEvent e) { 27 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 29 } 30 } 31 public void processMouseEvent(MouseEvent e) { 32 if(e.getID() == MouseEvent.MOUSE_PRESSED) { x = e.getX(); y = e.getY(); soundchess1.play(); 36 } 37 }
252
範例52 續3 38 public void run() { 39 while(true) {
範例52 續3 38 public void run() { 39 while(true) { if((x>30)&&(x<90)&&(y>70)&&(y<130)&&(area_flag[0]==0)) {area_flag[0] = 1;} else if ((x>90)&&(x<150)&&(y>70)&&(y<130)&&(area_flag[1]==0)) {area_flag[1] = 1;} else if ((x>150)&&(x<210)&&(y>70)&&(y<130)&&(area_flag[2]==0)) {area_flag[2] = 1;} else if((x>30)&&(x<90)&&(y>130)&&(y<190)&&(area_flag[3]==0)) {area_flag[3] = 1;} else if((x>90)&&(x<150)&&(y>130)&&(y<190)&&(area_flag[4]==0)) {area_flag[4] = 1;} else if((x>150)&&(x<210)&&(y>130)&&(y<190)&&(area_flag[5]==0)) {area_flag[5] = 1;} else if((x>30)&&(x<90)&&(y>190)&&(y<250)&&(area_flag[6]==0)) {area_flag[6] = 1;} else if((x>90)&&(x<150)&&(y>190)&&(y<250)&&(area_flag[7]==0)) {area_flag[7] = 1;} else if((x>150)&&(x<210)&&(y>190)&&(y<250)&&(area_flag[8]==0)) {area_flag[8] = 1;}
253
範例52 續4 if ((area_flag[0]==1)&&(area_flag[1]==1)&&(area_flag[2]==1)&&(message=="")) { message = "User win!!"; soundwin.play(); } else if ((area_flag[3]==1)&&(area_flag[4]==1)&&(area_flag[5]==1)&&(message=="")) { else if ((area_flag[6]==1)&&(area_flag[7]==1)&&(area_flag[8]==1)&&(message=="")) { else if ((area_flag[0]==1)&&(area_flag[3]==1)&&(area_flag[6]==1)&&(message=="")) {
254
範例52 續5 else if ((area_flag[1]==1)&&(area_flag[4]==1)&&(area_flag[7]==1)&&(message=="")) { message = "User win!!"; soundwin.play(); } else if ((area_flag[2]==1)&&(area_flag[5]==1)&&(area_flag[8]==1)&&(message=="")) { else if ((area_flag[0]==1)&&(area_flag[4]==1)&&(area_flag[8]==1)&&(message=="")) { else if ((area_flag[2]==1)&&(area_flag[4]==1)&&(area_flag[6]==1)&&(message=="")) {
255
範例52 續6 57 repaint(); 58 try{Thread.sleep(250);}
範例52 續6 repaint(); try{Thread.sleep(250);} catch(InterruptedException e) {;} 60 } 61 } 62 public void update(Graphics g) { 63 paint(g); 64 } 65 public void paint(Graphics g) { 66 Graphics bufferg; 67 if(bufferPage == null) bufferPage = createImage(250, 250); 69 bufferg = bufferPage.getGraphics();
256
範例52 續7 70 bufferg.drawString(message, 20, 20);
範例52 續7 70 bufferg.drawString(message, 20, 20); 71 bufferg.drawLine(90,50,90,230); 72 bufferg.drawLine(150,50,150,230); 73 bufferg.drawLine(30,110,210,110); 74 bufferg.drawLine(30,170,210,170); 75 for (i=0; i<9; i++) { if (area_flag[0] == 1) bufferg.drawImage(img, 42, 60, this); if (area_flag[1] == 1) bufferg.drawImage(img, 102, 60, this); if (area_flag[2] == 1) bufferg.drawImage(img, 164, 60, this); if (area_flag[3] == 1) bufferg.drawImage(img, 42, 125, this);
257
範例52 續8 80 if (area_flag[4] == 1)
範例52 續8 if (area_flag[4] == 1) bufferg.drawImage(img, 102, 125, this); if (area_flag[5] == 1) bufferg.drawImage(img, 164, 125, this); if (area_flag[6] == 1) bufferg.drawImage(img, 42, 187, this); if (area_flag[7] == 1) bufferg.drawImage(img, 102, 187, this); if (area_flag[8] == 1) bufferg.drawImage(img, 164, 187, this); } bufferg.dispose(); g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 88 } 89 }
258
10-6射擊音效 依10-2節所述,以Applet.newAudioClip(getClass().getResource("sound.au")) 讀取音效檔案shoot1.au、explode.au,設計範例53,按←→↑↓鍵移動發射器;當發射子彈時,播放射擊音效;按空白鍵發射子彈,當子彈進入靶車範圍、播放擊中音效、將靶車圖案改成爆炸圖案、印出中彈訊息。
259
範例53:參考範例45,設計檔案Ex10_6.java其功能為解釋射擊音效之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.applet.*; 04 public class Ex10_6 extends Frame implements Runnable { 05 int x1=150, y1=225, dx1, dy1, x2=150, y2=10; 06 int bx, by, dbx=0, dby=-5, bflag=0, hitflag2=0; 07 Image img1, img2, img3, bufferPage=null; 08 AudioClip shoot1, explode; 09 String message=""; 10 public static void main(String args[]) { 11 Ex10_6 workStart=new Ex10_6(); 12 }
260
範例53 續1 13 public Ex10_6() { 14 super("Ex10_6"); 15 setSize(350,350);
範例53 續1 13 public Ex10_6() { 14 super("Ex10_6"); 15 setSize(350,350); 16 Toolkit tk = Toolkit.getDefaultToolkit(); 17 img1 = tk.getImage("car090.gif"); 18 img2 = tk.getImage("car180.gif"); 19 img3 = tk.getImage("hit2.gif"); 20 shoot1 = Applet.newAudioClip(getClass().getResource("shoot1.au")); 21 explode = Applet.newAudioClip(getClass().getResource("explode.au")); 22 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 23 enableEvents(AWTEvent.KEY_EVENT_MASK); 24 setVisible(true); 25 new Thread(this).start(); 26 }
261
範例53 續2 27 public void processWindowEvent(WindowEvent e) {
範例53 續2 27 public void processWindowEvent(WindowEvent e) { 28 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 30 } 31 } 32 public void processKeyEvent(KeyEvent e) { 33 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx1 = 5; dy1 = 0; break; case KeyEvent.VK_LEFT: dx1 = -5; dy1 = 0; break; case KeyEvent.VK_UP: dx1 = 0; dy1 = -5; break;
262
範例53 續3 44 case KeyEvent.VK_DOWN: 45 dx1 = 0; dy1 = 5; 46 break;
範例53 續3 case KeyEvent.VK_DOWN: dx1 = 0; dy1 = 5; break; case KeyEvent.VK_SPACE: dx1 = 0; dy1 = 0; bx = x1 + 30; by = y1 - 5; bflag = 1; shoot1.play(); break; default: dx1 = 0; dy1 = 0; } x1 = x1 + dx1; y1 = y1 + dy1; 59 } 60 }
263
範例53 續4 61 public void run() { 62 while(true) {
範例53 續4 61 public void run() { 62 while(true) { if(by <= -10) bflag = 0; if(bflag == 1) by = by + dby; if ((bx>=x2)&&(bx<=x2+60)&&(by<=y2)&&(message=="")){ hitflag2=1; explode.play(); message="User Win!!"; } repaint(); try{Thread.sleep(30);} catch(InterruptedException e) {;} 73 } 74 }
264
範例53 續5 75 public void update(Graphics g) { 76 paint(g); 77 }
範例53 續5 75 public void update(Graphics g) { paint(g); 77 } 78 public void paint(Graphics g) { Graphics bufferg; if(bufferPage == null) bufferPage = createImage(350, 350); bufferg = bufferPage.getGraphics(); if (hitflag2==1) img2=img3; bufferg.drawString(message, 20, 20); bufferg.drawImage(img1, x1, y1, this); bufferg.drawImage(img2, x2, y2, this);
265
範例53 續6 86 bufferg.fillRect(bx, by, 3, 5);
範例53 續6 bufferg.fillRect(bx, by, 3, 5); bufferg.setColor(Color.white); bufferg.fillRect(bx, by+5, 3, 5); bufferg.dispose(); g.drawImage(bufferPage, getInsets().left, getInsets().top, this); 91 } 92 }
266
10-7 習題(Exercises) 01、Java提供那三種格式的音效檔案? 02、AU格式原本為何種環境而設計?
03、於視窗框架使用AU格式時,為何必須自由系統匯入applet包裹? 04、如何為框架視窗(Frame) 設計的音效介面,用於讀取音效檔案? 05、方法程序sound.play( ) 之功能為何? 06、方法程序sound.loop( ) 之功能為何? 07、方法程序sound.stop( ) 之功能為何?
267
第三篇 網路線上遊戲(On Line Games)
268
第十一章 線上指令訊息(On Line Message)
11-1 簡介 11-2 線上指令串流 11-3 滑鼠指令串流 11-4 鍵盤指令串流 11-5 習題(Exercises)
269
11-1 簡介 回顧本系列叢書第二冊網路入門程式,其中第三篇網路群播之內容正是線上遊戲之設計架構,使用者A由Client端向Server端報到,使用者B由另一Client端向Server端報到,使用者A與B即可在網路上對陣以較高下。
270
11-2 線上指令串流 當多個動畫指令進入網路後,因網路之路徑複雜,指令將不會依一定次序抵達目的地,此時我們將無法識別這些指令。本書以餘數方法建立識別系數(Factor),在傳遞指令之前,以識別系數包裝(Pack) 指令;抵達目的地之後,辨認指令、解除(Unpack) 識別系數、使用指令。
271
11-3 滑鼠指令串流 如圖11-2,於Client1、Client2端建立相同之動畫元件,以線上串流將滑鼠動畫指令傳遞至對方,指揮各動畫元件作同步運動。其執行步驟為:(1) Client1與Client2分別向Server報到;(2) Client1將本機之滑鼠動畫指令,經由Server傳遞給予Client2;(3) Client2將本機之滑鼠動畫指令,經由Server傳遞至Client1;(4) Client1與Client2 影像動畫同步。
272
範例54:參考範例33,設計檔案Server11_3. java、Client11_3_user1
範例54:參考範例33,設計檔案Server11_3.java、Client11_3_user1.java、Client11_3_user2.java其功能為解釋滑鼠指令串流與同步影像之應用。 檔案Server11_3.java:(建立:(1)網站平台(ServerSocket)/連接平台 (Socket)、(2)輸入/輸出串流物件(DataStream)、(3)雜湊表(Hash Table)、(4) 並行執行緒(Synchronized Threads) 001 import java.net.*; 002 import java.io.*; 003 import java.util.*; 004 public class Server11_3 { 005 private static ServerSocket SS; 006 private static int port; 007 private Hashtable ht = new Hashtable(); 008 Socket socket; 009 public Server11_3() { try { SS = new ServerSocket(port); System.out.println("Server is created and waiting Client to connect...");
273
範例54 續1 013 while (true) { 014 socket = SS.accept();
範例54 續1 while (true) { socket = SS.accept(); System.out.println("Client IP = " + socket.getInetAddress().getHostAddress()); DataOutputStream outstream = new DataOutputStream(socket.getOutputStream()); ht.put(socket, outstream); Thread thread = new Thread(new ServerRunnable(socket, ht)); thread.start(); } } catch (IOException e) { System.out.println(e.getMessage()); 024 } } 026 public static void main(String[] args) { if (args.length < 1) { System.out.println("Usage: java Server11_3 [port]"); System.exit(1); }
274
範例54 續2 031 port=Integer.parseInt(args[0]) ;
範例54 續2 port=Integer.parseInt(args[0]) ; Server11_3 ServerStart=new Server11_3(); 033 } 034 } 035 class ServerRunnable implements Runnable { 036 private Socket socket; 037 private Hashtable ht; 038 public ServerRunnable(Socket socket, Hashtable ht) { this.socket = socket; this.ht = ht; 041 } 042 public void run() { DataInputStream instream;
275
範例54 續3 try { instream = new DataInputStream(socket.getInputStream()); while (true) { int message = instream.readInt(); synchronized(ht) { Enumeration en = ht.elements(); while(en.hasMoreElements()) { DataOutputStream outstream = (DataOutputStream)en.nextElement(); try { outstream.writeInt(message); } catch (IOException e) { System.out.println(e.getMessage()); 058 } } } } } 063 catch (IOException e) { System.out.println(e.getMessage()); }
276
範例54 續4 066 finally { 067 synchronized(ht) {
範例54 續4 finally { synchronized(ht) { System.out.println("Remove connection: " + socket); ht.remove(socket); try { socket.close(); } catch (IOException e) { } } } 078 } 079 }
277
範例54 續5 檔案Client11_3_user1.java:(讀取游標之座標(x, y);以識別系數包裝(x, y) 成線上指令訊息;以網路輸出串流物件將指令訊息輸出至網路;讀取從網路傳來之線上指令訊息;解除線上指令訊息之識別系數;繪製影像圖案) 080 import java.awt.*; 081 import java.awt.event.*; 082 import java.math.*; 083 import java.io.*; 084 import java.net.*; 085 import java.util.*; 086 public class Client11_3_user1 extends Frame implements Runnable { 087 int x=50, y=50; 088 Image img;
278
範例54 續6 089 Socket socket; 090 static String iaddr;
範例54 續6 089 Socket socket; 090 static String iaddr; 091 static int port; 092 DataOutputStream outstream; 093 DataInputStream instream; 094 int x_send, y_send, rcv; 095 public static void main(String args[]) { if (args.length < 2){ System.out.println("USAGE: java Client11_3_User1 [iaddr] [port]"); System.exit(1); } iaddr = args[0]; port=Integer.parseInt(args[1]); Client11_3_user1 workStart=new Client11_3_user1(); 103 }
279
範例54 續7 104 public Client11_3_user1() { 105 super("Client11_3_user1");
範例54 續7 104 public Client11_3_user1() { super("Client11_3_user1"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("img11_3.jpg"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); try{ socket=new Socket(InetAddress.getByName(iaddr),port); outstream = new DataOutputStream(socket.getOutputStream()); instream = new DataInputStream(socket.getInputStream()); new Thread(this).start(); } catch (Exception e) { e.printStackTrace(); } 121 }
280
範例54 續8 122 public void processWindowEvent(WindowEvent e) {
範例54 續8 122 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 127 } 128 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { try{ x = e.getX(); y = e.getY(); x_send = x * ; y_send = y * ; outstream.writeInt(x_send); outstream.writeInt(y_send); }
281
範例54 續9 138 catch (Exception f) { 139 f.printStackTrace(); 140 } }
範例54 續9 catch (Exception f) { f.printStackTrace(); 140 } } 143 public void run() { while(true) { try{ rcv = instream.readInt(); if(rcv % 100 == 0) x = (rcv-0)/100; if(rcv % 100 == 1) y = (rcv-1)/100; repaint(); } catch (Exception f) { f.printStackTrace(); 153 } } 156 public void paint(Graphics g) { g.drawImage(img, x, y, this); 158 } 159 }
282
11-4 鍵盤指令串流 如圖11-2,於Client1、Client2端建立相同之動畫元件,以線上串流將鍵盤動畫指令傳遞至對方,指揮各動畫元件作同步運動。其執行步驟為:(1) Client1與Client2分別向Server報到;(2) Client1將本機之鍵盤動畫指令,經由Server傳遞給予Client2;(3) Client2將本機之鍵盤動畫指令,經由Server傳遞至Client1;(4) Client1與Client2 影像動畫同步。
283
範例55:參考範例41,設計檔案Server11_4. java、Client11_4_user1
範例55:參考範例41,設計檔案Server11_4.java、Client11_4_user1.java、Client11_4_user2.java其功能為解釋鍵盤指令串流與同步影像之應用。 檔案Server11_4.java:內容與Server11_3.java相同,請參考範例54。 檔案Client11_4_user1.java:(以鍵盤鍵事件改變影像圖案之移動方向) 080 import java.awt.*; 081 import java.awt.event.*; 082 import java.math.*; 083 import java.io.*; 084 import java.net.*; 085 import java.util.*; 086 public class Client11_4_user1 extends Frame implements Runnable { 087 int x=150, y=235, dx, dy; 088 Image img;
284
範例55 續1 089 Socket socket; 090 static String iaddr;
範例55 續1 089 Socket socket; 090 static String iaddr; 091 static int port; 092 DataOutputStream outstream; 093 DataInputStream instream; 094 int x_send, y_send, rcv; 095 public static void main(String args[]) { if (args.length < 2){ System.out.println("USAGE: java Client11_4_User1 [iaddr] [port]"); System.exit(1); } iaddr = args[0]; port=Integer.parseInt(args[1]); Client11_4_user1 workStart=new Client11_4_user1(); 103 }
285
範例55 續2 104 public Client11_4_user1() { 105 super("Client11_4_user1");
範例55 續2 104 public Client11_4_user1() { super("Client11_4_user1"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("img11_4.JPG"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.KEY_EVENT_MASK); setVisible(true); try{ socket=new Socket(InetAddress.getByName(iaddr),port); outstream = new DataOutputStream(socket.getOutputStream()); instream = new DataInputStream(socket.getInputStream()); new Thread(this).start(); }
286
範例55 續3 118 catch (Exception e) { 119 e.printStackTrace(); 120 } 121 }
範例55 續3 catch (Exception e) { e.printStackTrace(); } 121 } 122 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 126 } 127 public void processKeyEvent(KeyEvent e) { if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break;
287
範例55 續4 133 case KeyEvent.VK_LEFT: 134 dx = -5; dy = 0; 135 break;
範例55 續4 case KeyEvent.VK_LEFT: dx = -5; dy = 0; break; case KeyEvent.VK_UP: dx = 0; dy = -5; break; case KeyEvent.VK_DOWN: dx = 0; dy = 5; break; default: dx = 0; dy = 0; } try{ x = x + dx; y = y + dy; x_send = x * ; y_send = y * ;
288
範例55 續5 150 outstream.writeInt(x_send);
範例55 續5 outstream.writeInt(x_send); outstream.writeInt(y_send); } catch (Exception f) { f.printStackTrace(); } } 157 } 158 public void run() { while(true) { try { rcv = instream.readInt(); if (rcv % 100 == 0) x = (rcv - 0) / 100; else if (rcv % 100 == 1) y = (rcv - 1) / 100;
289
範例55 續6 164 repaint(); 165 } 166 catch (Exception f) {
範例55 續6 repaint(); } catch (Exception f) { f.printStackTrace(); } } 170 } 171 public void paint(Graphics g) { g.drawImage(img, x, y, this); 173 } 174 }
290
11-5 習題(Exercises) 01、線上遊戲最首要的問題為何? 02、為了維護對陣各端之同步畫面,為何不可由傳遞圖案而完成?
03、如何建立對陣各端之同步畫面? 04、如何辨識網路指令? 05、如何以餘數方法建立識別系數? 06、網路對陣中,而最少建立那些網路連接平台?
291
第十二章 網路線上弈棋對陣(On Line Chess)
12-1 簡介 12-2 網路指令串流 12-3 對陣同步影像 12-4 輸贏評定與音效 12-5 消除閃爍 12-6 習題(Exercises)
292
12-1 簡介 一般來言弈棋為兩人對陣(本章稱User1與User2),網路上兩人各據一台電腦,兩台電腦螢幕顯示相同的棋盤,相同的棋子位置。在設計的關鍵處是當User1落子於某棋盤位置時,在遠處的User2也可在其電腦螢幕上看到同樣的動作,反之亦是。棋盤畫面同步、音效處理、消除閃爍等問題之解決,是本章之核心內容。
293
12-2 網路指令串流 於Client1、Client2端建立相同之棋盤元件,以線上串流將指令傳遞至對方,指揮同步落子位置。其執行步驟為:(1) Client1與Client2分別向Server報到;(2) Client1將本機之落子指令,經由Server傳遞給予Client2;(3) Client2將本機之落子指令,經由Server傳遞至Client1;(4) Client1與Client2同步落子。
294
範例56:參考範例40、54,設計檔案Server12_2
範例56:參考範例40、54,設計檔案Server12_2.java、Client12_2_user1、Client12_2_user2,其功能為解釋弈棋指令串流與同步影像之應用。 檔案Server12_2.java:(接受Client1端送來之指令資訊,再轉發送至Client2,反之亦然) 001 import java.net.*; 002 import java.io.*; 003 import java.util.*; 004 public class Server12_2 { 005 private static ServerSocket SS; 006 private static int port; 007 private Hashtable ht = new Hashtable(); 008 Socket socket; 009 public Server12_2() { try { SS = new ServerSocket(port); System.out.println("Server is created and waiting Client to connect...");
295
範例56 續1 013 while (true) { 014 socket = SS.accept();
範例56 續1 while (true) { socket = SS.accept(); System.out.println("Client IP = " + socket.getInetAddress().getHostAddress()); DataOutputStream outstream = new DataOutputStream(socket.getOutputStream()); ht.put(socket, outstream); Thread thread = new Thread(new ServerRunnable(socket, ht)); thread.start(); } } catch (IOException e) { System.out.println(e.getMessage()); 024 } } 026 public static void main(String[] args) { if (args.length < 1) { System.out.println("Usage: java Server12_2 [port]"); System.exit(1); }
296
範例56 續2 031 port=Integer.parseInt(args[0]) ;
範例56 續2 port=Integer.parseInt(args[0]) ; Server12_2 ServerStart=new Server12_2(); 033 } 034 } 035 class ServerRunnable implements Runnable { 036 private Socket socket; 037 private Hashtable ht; 038 public ServerRunnable(Socket socket, Hashtable ht) { this.socket = socket; this.ht = ht; 041 } 042 public void run() { DataInputStream instream;
297
範例56 續3 try { instream = new DataInputStream(socket.getInputStream()); while (true) { int message = instream.readInt(); synchronized(ht) { Enumeration en = ht.elements(); while(en.hasMoreElements()) { DataOutputStream outstream = (DataOutputStream)en.nextElement(); try { outstream.writeInt(message); } catch (IOException e) { System.out.println(e.getMessage()); 058 } } } } }
298
範例56 續4 063 catch (IOException e) {
範例56 續4 063 catch (IOException e) { System.out.println(e.getMessage()); } finally { synchronized(ht) { System.out.println("Remove connection: " + socket); ht.remove(socket); try { socket.close(); } catch (IOException e) { } } } 078 } 079 }
299
範例56 續5 檔案Client12_2_user1.java:(向Server報到,將指令訊息傳遞至遠端user2,執行棋盤同步落子)
範例56 續5 檔案Client12_2_user1.java:(向Server報到,將指令訊息傳遞至遠端user2,執行棋盤同步落子) 079 import java.awt.*; 080 import java.awt.event.*; 081 import java.math.*; 082 import java.io.*; 083 import java.net.*; 084 import java.util.*; 085 public class Client12_2_user1 extends Frame implements Runnable { 086 int x, y; 087 Image img; 088 int[] area_flag = new int[9]; 089 int i;
300
範例56 續6 090 Socket socket; 091 static String iaddr;
範例56 續6 090 Socket socket; 091 static String iaddr; 092 static int port; 093 DataOutputStream outstream; 094 DataInputStream instream; 095 int x_send, y_send, rcv, w; 096 public static void main(String args[]) { if (args.length < 2){ System.out.println("USAGE: java Client11_3_User1 [iaddr] [port]"); System.exit(1); } iaddr = args[0]; port=Integer.parseInt(args[1]); Client12_2_user1 workStart=new Client12_2_user1(); 104 }
301
範例56 續7 105 public Client12_2_user1() { 106 super("Client12_2_user1");
範例56 續7 105 public Client12_2_user1() { super("Client12_2_user1"); setSize(250,280); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("Circle.GIF"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); try{ socket=new Socket(InetAddress.getByName(iaddr),port); outstream = new DataOutputStream(socket.getOutputStream()); instream = new DataInputStream(socket.getInputStream()); new Thread(this).start(); } catch (Exception e) { e.printStackTrace(); } 122 }
302
範例56 續8 123 public void processWindowEvent(WindowEvent e) {
範例56 續8 123 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 127 } 128 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { try{ x = e.getX(); y = e.getY(); x_send = x * ; y_send = y * ; outstream.writeInt(x_send); outstream.writeInt(y_send); }
303
範例56 續9 138 catch (Exception f) { 139 f.printStackTrace(); 140 } 141 }
範例56 續9 catch (Exception f) { f.printStackTrace(); } } 142 } 143 public void run() { while(true) { try{ rcv = instream.readInt(); if(rcv % 100 == 0) x = (rcv-0)/100; if(rcv % 100 == 1) y = (rcv-1)/100; w = w+1; if((x>30)&&(x<90)&&(y>70)&&(y<130)&&(area_flag[0]==0)&&(w%2==0)) {area_flag[0] = 1;} else if ((x>90)&&(x<150)&&(y>70)&&(y<130)&&(area_flag[1]==0)&&(w%2==0)) {area_flag[1] = 1;}
304
範例56 續10 else if ((x>150)&&(x<210)&&(y>70)&&(y<130)&&(area_flag[2]==0)&&(w%2==0)) {area_flag[2] = 1;} else if((x>30)&&(x<90)&&(y>130)&&(y<190)&&(area_flag[3]==0)&&(w%2==0)) {area_flag[3] = 1;} else if((x>90)&&(x<150)&&(y>130)&&(y<190)&&(area_flag[4]==0)&&(w%2==0)) {area_flag[4] = 1;} else if((x>150)&&(x<210)&&(y>130)&&(y<190)&&(area_flag[5]==0)&&(w%2==0)) {area_flag[5] = 1;} else if((x>30)&&(x<90)&&(y>190)&&(y<250)&&(area_flag[6]==0)&&(w%2==0)) {area_flag[6] = 1;} else if((x>90)&&(x<150)&&(y>190)&&(y<250)&&(area_flag[7]==0)&&(w%2==0)) {area_flag[7] = 1;} else if((x>150)&&(x<210)&&(y>190)&&(y<250)&&(area_flag[8]==0)&&(w%2==0)) {area_flag[8] = 1;} repaint(); Thread.sleep(250); }
305
範例56 續11 162 catch (Exception f) { 163 f.printStackTrace(); 164 }
範例56 續11 catch (Exception f) { f.printStackTrace(); } } 166 } 167 public void paint(Graphics g) { g.drawLine(90,50,90,230); g.drawLine(150,50,150,230); g.drawLine(30,110,210,110); g.drawLine(30,170,210,170); for (i=0; i<9; i++) { if (area_flag[0] == 1) g.drawImage(img, 42, 60, this); if (area_flag[1] == 1) g.drawImage(img, 102, 60, this);
306
範例56 續12 175 if (area_flag[2] == 1) g.drawImage(img, 164, 60, this);
範例56 續12 if (area_flag[2] == 1) g.drawImage(img, 164, 60, this); if (area_flag[3] == 1) g.drawImage(img, 42, 125, this); if (area_flag[4] == 1) g.drawImage(img, 102, 125, this); if (area_flag[5] == 1) g.drawImage(img, 164, 125, this); if (area_flag[6] == 1) g.drawImage(img, 42, 187, this); if (area_flag[7] == 1) g.drawImage(img, 102, 187, this); if (area_flag[8] == 1) g.drawImage(img, 164, 187, this); } 183 } 184 }
307
12-3 對陣同步影像 於Client1、Client2端建立相同之棋盤元件,包括Circle棋子影像與Cross棋子影像,以線上串流將棋子指令傳遞至對方,指揮同步落子位置。其執行步驟為:(1) Client1與Client2分別向Server報到;(2) Client1將Circle棋子影像之落子指令,經由Server傳遞給予Client2;(3) Client2將Cross棋子影像之落子指令,經由Server傳遞至Client1;(4) Client1與Client2對陣同步落子。
308
範例57:參考範例56,設計檔案Server12_3. java、Client12_3_user1
範例57:參考範例56,設計檔案Server12_3.java、Client12_3_user1.java、Client12_3_user2.java,其功能為解釋弈棋指令串流與對陣之應用。 檔案Server12_3.java:內容與Server12_2.java相同,請參考範例56。 檔案Client12_3_user1.java:(向Server報到,將Cicle棋子指令訊息傳遞至遠端user2,亦接受user2傳來之Cross棋子指令,執行棋盤同步落子) 080 import java.awt.*; 081 import java.awt.event.*; 082 import java.math.*; 083 import java.io.*; 084 import java.net.*; 085 import java.util.*; 086 public class Client12_3_user1 extends Frame implements Runnable { 087 int x1, y1, x2, y2; 088 Image img1, img2; 089 int[] area_flag = new int[9]; 090 int i;
309
範例57 續1 091 Socket socket; 092 static String iaddr;
範例57 續1 091 Socket socket; 092 static String iaddr; 093 static int port; 094 DataOutputStream outstream; 095 DataInputStream instream; 096 int x1_send, y1_send, x2_send, y2_send, rcv, w; 097 public static void main(String args[]) { if (args.length < 2){ System.out.println("USAGE: java Client11_3_User1 [iaddr] [port]"); System.exit(1); } iaddr = args[0]; port=Integer.parseInt(args[1]); Client12_3_user1 workStart=new Client12_3_user1(); 105 }
310
範例57 續2 106 public Client12_3_user1() { 107 super("Client12_3_user1");
範例57 續2 106 public Client12_3_user1() { super("Client12_3_user1"); setSize(250,280); Toolkit tk = Toolkit.getDefaultToolkit(); img1 = tk.getImage("Circle.GIF"); img2 = tk.getImage("Cross.GIF"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); try{ socket=new Socket(InetAddress.getByName(iaddr),port); outstream = new DataOutputStream(socket.getOutputStream()); instream = new DataInputStream(socket.getInputStream()); new Thread(this).start(); } catch (Exception e) { e.printStackTrace(); } 124 }
311
範例57 續3 125 public void processWindowEvent(WindowEvent e) {
範例57 續3 125 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 129 } 130 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { try{ x1 = e.getX(); y1 = e.getY(); x1_send = x1 * ; y1_send = y1 * ; outstream.writeInt(x1_send); outstream.writeInt(y1_send); }
312
範例57 續4 140 catch (Exception f) { 141 f.printStackTrace(); 142 } 143 }
範例57 續4 catch (Exception f) { f.printStackTrace(); } } 144 } 145 public void run() { while(true) { try{ rcv = instream.readInt(); if(rcv % 100 == 0) x1 = (rcv-0)/100; if(rcv % 100 == 1) y1 = (rcv-1)/100; if(rcv % 100 == 2) x2 = (rcv-2)/100; if(rcv % 100 == 3) y2 = (rcv-3)/100; w = w+1;
313
範例57 續5 if((x1>30)&&(x1<90)&&(y1>70)&&(y1<130)&&(area_flag[0]==0)&&(w%2==0)) {area_flag[0] = 1;} else if((x1>90)&&(x1<150)&&(y1>70)&&(y1<130)&&(area_flag[1]==0)&&(w%2==0)) {area_flag[1] = 1;} else if ((x1>150)&&(x1<210)&&(y1>70)&&(y1<130)&&(area_flag[2]==0)&&(w%2==0)) {area_flag[2] = 1;} else if((x1>30)&&(x1<90)&&(y1>130)&&(y1<190)&&(area_flag[3]==0)&&(w%2==0)) {area_flag[3] = 1;} else if((x1>90)&&(x1<150)&&(y1>130)&&(y1<190)&&(area_flag[4]==0)&&(w%2==0)) {area_flag[4] = 1;} else if((x1>150)&&(x1<210)&&(y1>130)&&(y1<190)&&(area_flag[5]==0)&&(w%2==0)) {area_flag[5] = 1;} else if((x1>30)&&(x1<90)&&(y1>190)&&(y1<250)&&(area_flag[6]==0)&&(w%2==0)) {area_flag[6] = 1;} else if((x1>90)&&(x1<150)&&(y1>190)&&(y1<250)&&(area_flag[7]==0)&&(w%2==0)) {area_flag[7] = 1;} else if((x1>150)&&(x1<210)&&(y1>190)&&(y1<250)&&(area_flag[8]==0)&&(w%2==0)) {area_flag[8] = 1;}
314
範例57 續6 if((x2>30)&&(x2<90)&&(y2>70)&&(y2<130)&&(area_flag[0]==0)&&(w%2==0)) {area_flag[0] = 2;} else if ((x2>90)&&(x2<150)&&(y2>70)&&(y2<130)&&(area_flag[1]==0)&&(w%2==0)) {area_flag[1] = 2;} else if ((x2>150)&&(x2<210)&&(y2>70)&&(y2<130)&&(area_flag[2]==0)&&(w%2==0)) {area_flag[2] = 2;} else if((x2>30)&&(x2<90)&&(y2>130)&&(y2<190)&&(area_flag[3]==0)&&(w%2==0)) {area_flag[3] = 2;} else if((x2>90)&&(x2<150)&&(y2>130)&&(y2<190)&&(area_flag[4]==0)&&(w%2==0)) {area_flag[4] = 2;} else if((x2>150)&&(x2<210)&&(y2>130)&&(y2<190)&&(area_flag[5]==0)&&(w%2==0)) {area_flag[5] = 2;} else if((x2>30)&&(x2<90)&&(y2>190)&&(y2<250)&&(area_flag[6]==0)&&(w%2==0)) {area_flag[6] = 2;} else if((x2>90)&&(x2<150)&&(y2>190)&&(y2<250)&&(area_flag[7]==0)&&(w%2==0)) {area_flag[7] = 2;} else if((x2>150)&&(x2<210)&&(y2>190)&&(y2<250)&&(area_flag[8]==0)&&(w%2==0)) {area_flag[8] = 2;}
315
範例57 續7 178 repaint(); 179 Thread.sleep(250); 180 }
範例57 續7 repaint(); Thread.sleep(250); } catch (Exception f) { f.printStackTrace(); } } 185 } 186 public void paint(Graphics g) { g.drawLine(90,50,90,230); g.drawLine(150,50,150,230); g.drawLine(30,110,210,110); g.drawLine(30,170,210,170); for (i=0; i<9; i++) { if (area_flag[0] == 1) g.drawImage(img1, 42, 60, this);
316
範例57 續8 193 if (area_flag[1] == 1) g.drawImage(img1, 102, 60, this);
範例57 續8 if (area_flag[1] == 1) g.drawImage(img1, 102, 60, this); if (area_flag[2] == 1) g.drawImage(img1, 164, 60, this); if (area_flag[3] == 1) g.drawImage(img1, 42, 125, this); if (area_flag[4] == 1) g.drawImage(img1, 102, 125, this); if (area_flag[5] == 1) g.drawImage(img1, 164, 125, this); if (area_flag[6] == 1) g.drawImage(img1, 42, 187, this); if (area_flag[7] == 1) g.drawImage(img1, 102, 187, this); if (area_flag[8] == 1) g.drawImage(img1, 164, 187, this); }
317
範例57 續9 202 for (i=0; i<9; i++) { 203 if (area_flag[0] == 2)
範例57 續9 for (i=0; i<9; i++) { if (area_flag[0] == 2) g.drawImage(img2, 42, 60, this); if (area_flag[1] == 2) g.drawImage(img2, 102, 60, this); if (area_flag[2] == 2) g.drawImage(img2, 164, 60, this); if (area_flag[3] == 2) g.drawImage(img2, 42, 125, this); if (area_flag[4] == 2) g.drawImage(img2, 102, 125, this); if (area_flag[5] == 2) g.drawImage(img2, 164, 125, this); if (area_flag[6] == 2) g.drawImage(img2, 42, 187, this); if (area_flag[7] == 2) g.drawImage(img2, 102, 187, this); if (area_flag[8] == 2) g.drawImage(img2, 164, 187, this); 212 } } }
318
12-4 輸贏評定與音效 井字棋有9個棋盤格位置,可以0~8編號,當按下滑鼠左鍵落棋子時,播放落子音效,當弈棋任何一方先佔位一直線者,評定為勝方,此時播放勝利音效、並印出勝利訊息。
319
範例58:參考範例52、57,設計檔案Server12_4. java、Client12_4_user1
範例58:參考範例52、57,設計檔案Server12_4.java、Client12_4_user1.java、Client12_4_user2.java,其功能為解釋弈棋輸贏評定與音效之應用。 檔案Server12_4.java:內容與Server12_2.java相同,請參考範例56。 檔案Client12_4_user1.java:(向Server報到,將Cicle棋子指令訊息傳遞至遠端user2,落子時播放音效,當弈棋先佔位一直線時,播放勝利音效、並印出勝利訊息) 080 import java.awt.*; 081 import java.awt.event.*; 082 import java.math.*; 083 import java.io.*; 084 import java.net.*; 085 import java.util.*; 086 import java.applet.*;
320
範例58 續1 087 public class Client12_4_user1 extends Frame implements Runnable { 088 int x1, y1, x2, y2; 089 Image img1, img2; 090 int[] area_flag = new int[9]; 091 int i; 092 Socket socket; 093 static String iaddr; 094 static int port; 095 DataOutputStream outstream; 096 DataInputStream instream; 097 int x1_send, y1_send, x2_send, y2_send, rcv, w; 098 AudioClip sound1, sound2, soundwin; 099 String message = "";
321
範例58 續2 100 public static void main(String args[]) {
範例58 續2 100 public static void main(String args[]) { if (args.length < 2){ System.out.println("USAGE: java Client11_3_User1 [iaddr] [port]"); System.exit(1); } iaddr = args[0]; port=Integer.parseInt(args[1]); Client12_4_user1 workStart=new Client12_4_user1(); 108 } 109 public Client12_4_user1() { super("Client12_4_user1"); setSize(250,280); Toolkit tk = Toolkit.getDefaultToolkit(); img1 = tk.getImage("Circle.GIF"); img2 = tk.getImage("Cross.GIF");
322
範例58 續3 sound1 = Applet.newAudioClip(getClass().getResource("sound1.au")); sound2 = Applet.newAudioClip(getClass().getResource("sound2.au")); soundwin = Applet.newAudioClip(getClass().getResource("soundwin.au")); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.MOUSE_EVENT_MASK); setVisible(true); try{ socket=new Socket(InetAddress.getByName(iaddr),port); outstream = new DataOutputStream(socket.getOutputStream()); instream = new DataInputStream(socket.getInputStream()); new Thread(this).start(); } catch (Exception e) { e.printStackTrace(); } 130 }
323
範例58 續4 131 public void processWindowEvent(WindowEvent e) {
範例58 續4 131 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 134 }} 136 public void processMouseEvent(MouseEvent e) { if(e.getID() == MouseEvent.MOUSE_PRESSED) { try{ x1 = e.getX(); y1 = e.getY(); sound1.play(); x1_send = x1 * ; y1_send = y1 * ; outstream.writeInt(x1_send); outstream.writeInt(y1_send); } catch (Exception f) { f.printStackTrace(); 149 } } }
324
範例58 續5 152 public void run() { 153 while(true) { 154 try{
範例58 續5 152 public void run() { while(true) { try{ rcv = instream.readInt(); if(rcv % 100 == 0) x1 = (rcv-0)/100; if(rcv % 100 == 1) y1 = (rcv-1)/100; if(rcv % 100 == 2) x2 = (rcv-2)/100; if(rcv % 100 == 3) y2 = (rcv-3)/100; w = w+1; if((x1>30)&&(x1<90)&&(y1>70)&&(y1<130)&&(area_flag[0]==0)&&(w%2==0)) {area_flag[0] = 1;} else if ((x1>90)&&(x1<150)&&(y1>70)&&(y1<130)&&(area_flag[1]==0)&&(w%2==0)) {area_flag[1] = 1;} else if ((x1>150)&&(x1<210)&&(y1>70)&&(y1<130)&&(area_flag[2]==0)&&(w%2==0)) {area_flag[2] = 1;} else if((x1>30)&&(x1<90)&&(y1>130)&&(y1<190)&&(area_flag[3]==0)&&(w%2==0)) {area_flag[3] = 1;}
325
範例58 續6 else if((x1>90)&&(x1<150)&&(y1>130)&&(y1<190)&&(area_flag[4]==0)&&(w%2==0)) {area_flag[4] = 1;} else if((x1>150)&&(x1<210)&&(y1>130)&&(y1<190)&&(area_flag[5]==0)&&(w%2==0)) {area_flag[5] = 1;} else if((x1>30)&&(x1<90)&&(y1>190)&&(y1<250)&&(area_flag[6]==0)&&(w%2==0)) {area_flag[6] = 1;} else if((x1>90)&&(x1<150)&&(y1>190)&&(y1<250)&&(area_flag[7]==0)&&(w%2==0)) {area_flag[7] = 1;} else if((x1>150)&&(x1<210)&&(y1>190)&&(y1<250)&&(area_flag[8]==0)&&(w%2==0)) {area_flag[8] = 1;} if((x2>30)&&(x2<90)&&(y2>70)&&(y2<130)&&(area_flag[0]==0)&&(w%2==0)) {area_flag[0] = 2;} else if ((x2>90)&&(x2<150)&&(y2>70)&&(y2<130)&&(area_flag[1]==0)&&(w%2==0)) {area_flag[1] = 2;} else if ((x2>150)&&(x2<210)&&(y2>70)&&(y2<130)&&(area_flag[2]==0)&&(w%2==0)) {area_flag[2] = 2;} else if((x2>30)&&(x2<90)&&(y2>130)&&(y2<190)&&(area_flag[3]==0)&&(w%2==0)) {area_flag[3] = 2;}
326
範例58 續7 else if((x2>90)&&(x2<150)&&(y2>130)&&(y2<190)&&(area_flag[4]==0)&&(w%2==0)) {area_flag[4] = 2;} else if((x2>150)&&(x2<210)&&(y2>130)&&(y2<190)&&(area_flag[5]==0)&&(w%2==0)) {area_flag[5] = 2;} else if((x2>30)&&(x2<90)&&(y2>190)&&(y2<250)&&(area_flag[6]==0)&&(w%2==0)) {area_flag[6] = 2;} else if((x2>90)&&(x2<150)&&(y2>190)&&(y2<250)&&(area_flag[7]==0)&&(w%2==0)) {area_flag[7] = 2;} else if((x2>150)&&(x2<210)&&(y2>190)&&(y2<250)&&(area_flag[8]==0)&&(w%2==0)) {area_flag[8] = 2;} if ((area_flag[0]==1)&&(area_flag[1]==1)&&(area_flag[2]==1)&&(message=="")) { message = "User1 win!!"; soundwin.play(); } else if ((area_flag[3]==1)&&(area_flag[4]==1)&&(area_flag[5]==1)&&(message=="")){
327
範例58 續8 181 else if ((area_flag[6]==1)&&(area_flag[7]==1)&&(area_flag[8]==1)&&(message=="")) { message = "User1 win!!"; soundwin.play(); } 182 else if ((area_flag[0]==1)&&(area_flag[3]==1)&&(area_flag[6]==1)&&(message=="")) { 183 else if ((area_flag[1]==1)&&(area_flag[4]==1)&&(area_flag[7]==1)&&(message=="")) { 184 else if ((area_flag[2]==1)&&(area_flag[5]==1)&&(area_flag[8]==1)&&(message=="")) { 185 else if ((area_flag[0]==1)&&(area_flag[4]==1)&&(area_flag[8]==1)&&(message=="")) {
328
範例58 續9 186 else if ((area_flag[2]==1)&&(area_flag[4]==1)&&(area_flag[6]==1)&&(message=="")) { message = "User1 win!!"; soundwin.play(); } if ((area_flag[0]==2)&&(area_flag[1]==2)&&(area_flag[2]==2)&&(message=="")) { message = "User2 win!!"; 188 else if ((area_flag[3]==2)&&(area_flag[4]==2)&&(area_flag[5]==2)&&(message=="")) { 189 else if ((area_flag[6]==2)&&(area_flag[7]==2)&&(area_flag[8]==2)&&(message=="")) { 190 else if ((area_flag[0]==2)&&(area_flag[3]==2)&&(area_flag[6]==2)&&(message=="")) {
329
範例58 續10 191 else if ((area_flag[1]==2)&&(area_flag[4]==2)&&(area_flag[7]==2)&&(message=="")) { message = "User2 win!!"; soundwin.play(); } 192 else if ((area_flag[2]==2)&&(area_flag[5]==2)&&(area_flag[8]==2)&&(message=="")) { 193 else if ((area_flag[0]==2)&&(area_flag[4]==2)&&(area_flag[8]==2)&&(message=="")) { 194 else if ((area_flag[2]==2)&&(area_flag[4]==2)&&(area_flag[6]==2)&&(message=="")) { repaint(); Thread.sleep(250); 197 }
330
範例58 續11 198 catch (Exception f) { 199 f.printStackTrace(); 200 }
範例58 續11 catch (Exception f) { f.printStackTrace(); } } 202 } 203 public void paint(Graphics g) { g.drawString(message, 20, 45); g.drawLine(90,50,90,230); g.drawLine(150,50,150,230); g.drawLine(30,110,210,110); g.drawLine(30,170,210,170); for (i=0; i<9; i++) { if (area_flag[0] == 1) g.drawImage(img1, 42, 60, this); if (area_flag[1] == 1) g.drawImage(img1, 102, 60, this);
331
範例58 續12 212 if (area_flag[2] == 1) g.drawImage(img1, 164, 60, this);
範例58 續12 if (area_flag[2] == 1) g.drawImage(img1, 164, 60, this); if (area_flag[3] == 1) g.drawImage(img1, 42, 125, this); if (area_flag[4] == 1) g.drawImage(img1, 102, 125, this); if (area_flag[5] == 1) g.drawImage(img1, 164, 125, this); if (area_flag[6] == 1) g.drawImage(img1, 42, 187, this); if (area_flag[7] == 1) g.drawImage(img1, 102, 187, this); if (area_flag[8] == 1) g.drawImage(img1, 164, 187, this); }
332
範例58 續13 220 for (i=0; i<9; i++) { 221 if (area_flag[0] == 2)
範例58 續13 for (i=0; i<9; i++) { if (area_flag[0] == 2) g.drawImage(img2, 42, 60, this); if (area_flag[1] == 2) g.drawImage(img2, 102, 60, this); if (area_flag[2] == 2) g.drawImage(img2, 164, 60, this); if (area_flag[3] == 2) g.drawImage(img2, 42, 125, this); if (area_flag[4] == 2) g.drawImage(img2, 102, 125, this); if (area_flag[5] == 2) g.drawImage(img2, 164, 125, this); if (area_flag[6] == 2) g.drawImage(img2, 42, 187, this); if (area_flag[7] == 2) g.drawImage(img2, 102, 187, this); if (area_flag[8] == 2) g.drawImage(img2, 164, 187, this); 230 } }}
333
12-5 消除閃爍 如第九章曾述,因為新的影像隨時抽換舊的影像,如果抽換太快,有些部份先到位、有些後到位,影像將會顯得閃爍而不穩定。為了消除影像閃爍,我們可設定一個緩衝頁(Buffer Page),將新影像先置入緩衝頁,等待影像各部份全部繪製完成後,再將緩衝頁顯示於視窗,如此即可消除影像之閃爍。依9-2-4節之消除影像閃爍程式碼格式,設計範例59,執行消除線上弈棋圖案閃爍之現象。
334
12-6 習題(Exercises) 01、弈棋對陣同步影像之意義為何? 02、弈棋輸贏評定與音效之意義為何? 03、自行試作五子棋設計。
335
第十三章 網路線上射擊對陣(On Line Shooting)
13-1 簡介 13-2網路指令串流 13-3 對陣同步影像 13-4 輸贏評定與音效 13-5 消除閃爍 13-6 習題(Exercises)
336
13-1 簡介 網路線上對陣遊戲最重要的兩個項目就是:弈棋對陣與射擊對陣。讀者如果能將此兩種線上對陣透析,任何其他對陣型態均可輕易克服。於前篇我們已研習弈棋設計,本篇將繼續趁勝追擊,研討線上射擊對陣設計。
337
13-2網路指令串流 於Client1、Client2端建立相同之射擊體與子彈元件,以線上串流將指令傳遞至對方,指揮同步移動。其執行步驟為:(1) Client1與Client2分別向Server報到;(2) Client1將本機之射擊體與子彈移動指令,經由Server傳遞給予Client2;(3) Client2將本機之射擊體與子彈移動指令,經由Server傳遞至Client1;(4) Client1與Client2同步畫面移動。
338
範例60:參考範例45、55,設計檔案Server13_2
範例60:參考範例45、55,設計檔案Server13_2.java、Client13_2_user1、Client13_2_user2,其功能為解釋射擊指令串流與同步影像之應用。 檔案Server13_2.java:(接受Client1端送來之指令資訊,再轉發送至Client2,反之亦然) 檔案Client13_2_user1.java:(向Server報到,將指令訊息傳遞至遠端user2,執行射擊同步移動) 080 import java.awt.*; 081 import java.awt.event.*; 082 import java.math.*; 083 import java.io.*; 084 import java.net.*; 085 import java.util.*;
339
範例60 續1 086 public class Client13_2_user1 extends Frame implements Runnable { int x=150, y=235, dx, dy; int bx, by, dbx=0, dby=-5, bflag=0; Image img; Socket socket; static String iaddr; static int port; DataOutputStream outstream; DataInputStream instream; int x_send, y_send, bx_send, by_send, Ibflag=0, bflag_send, rcv; public static void main(String args[]) { if (args.length < 2){ System.out.println("USAGE: java Client11_4_User1 [iaddr] [port]"); System.exit(1); }
340
範例60 續2 101 iaddr = args[0]; 102 port=Integer.parseInt(args[1]);
範例60 續2 iaddr = args[0]; port=Integer.parseInt(args[1]); Client13_2_user1 workStart=new Client13_2_user1(); } public Client13_2_user1() { super("Client13_2_user1"); setSize(350,350); Toolkit tk = Toolkit.getDefaultToolkit(); img = tk.getImage("car090.gif"); enableEvents(AWTEvent.WINDOW_EVENT_MASK); enableEvents(AWTEvent.KEY_EVENT_MASK); setVisible(true);
341
範例60 續3 try{ socket=new Socket(InetAddress.getByName(iaddr),port); outstream = new DataOutputStream(socket.getOutputStream()); instream = new DataInputStream(socket.getInputStream()); new Thread(this).start(); } catch (Exception e) { e.printStackTrace(); } } public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } }
342
範例60 續4 128 public void processKeyEvent(KeyEvent e) {
範例60 續4 public void processKeyEvent(KeyEvent e) { if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_RIGHT: dx = 5; dy = 0; break; case KeyEvent.VK_LEFT: dx = -5; dy = 0; case KeyEvent.VK_UP: dx = 0; dy = -5; case KeyEvent.VK_DOWN: dx = 0; dy = 5;
343
範例60 續5 135 case KeyEvent.VK_SPACE: dx = 0; dy = 0; bx = x + 30;
範例60 續5 case KeyEvent.VK_SPACE: dx = 0; dy = 0; bx = x + 30; by = y - 5; Ibflag = 1; break; default: } try{ x = x + dx; y = y + dy; x_send = x * ; y_send = y * ; bx_send = bx * ; by_send = by * ; bflag_send = Ibflag * ;
344
範例60 續6 146 outstream.writeInt(x_send);
範例60 續6 outstream.writeInt(x_send); outstream.writeInt(y_send); outstream.writeInt(bx_send); outstream.writeInt(by_send); outstream.writeInt(bflag_send); } catch (Exception f) { f.printStackTrace(); } } } public void run() { while(true) { try { rcv = instream.readInt();
345
範例60 續7 161 if (rcv % 100 == 0) x = (rcv - 0) / 100;
範例60 續7 if (rcv % 100 == 0) x = (rcv - 0) / 100; else if (rcv % 100 == 1) y = (rcv - 1) / 100; else if (rcv % 100 == 2) bx = (rcv - 2) / 100; else if (rcv % 100 == 3) by = (rcv - 3) / 100; else if (rcv % 100 == 4) bflag = (rcv - 4) / 100; while(bflag==1) { if(by <= -10) bflag = 0; by = by + dby;
346
範例60 續8 169 repaint(); 170 Thread.sleep(5); 171 } 172 repaint(); 173 }
範例60 續8 repaint(); Thread.sleep(5); } repaint(); } catch (Exception f) { f.printStackTrace(); } } } public void paint(Graphics g) { g.drawImage(img, x, y, this); g.fillRect(bx, by, 3, 5); } 183 }
347
13-3 對陣同步影像 於Client1、Client2端建立相同之發射器元件,包括user1之綠色發射器與user2之紅色發射器,以線上串流將影像圖案指令傳遞至對方,指揮同步圖案移動。其執行步驟為:(1) Client1與Client2分別向Server報到;(2) Client1將綠色發射器與子彈之圖案移動指令,經由Server傳遞給予Client2;(3) Client2將紅色發射器與子彈之圖案移動指令,經由Server傳遞給予Client1;(4) Client1與Client2對陣影像圖案同步移動。
348
13-4 輸贏評定與音效 當按下←→↑↓鍵時,左右上下移動發射器,執行閃躲與追擊;當按下Space鍵時發射子彈、並播放發射音效;當子彈擊中對方發射器時,將對方換成爆炸圖案、播放爆炸音效、並印出勝利訊息。
349
13-5 消除閃爍 如第九章曾述,因為新的影像隨時抽換舊的影像,如果抽換太快,有些部份先到位、有些後到位,影像將會顯得閃爍而不穩定。為了消除影像閃爍,我們可設定一個緩衝頁(Buffer Page),將新影像先置入緩衝頁,等待影像各部份全部繪製完成後,再將緩衝頁顯示於視窗,如此即可消除影像之閃爍。
350
13-6 習題(Exercises) 1、試請設計射擊對陣,包括3個發射器。
2、試請設計射擊對陣現象,當user1之子彈觸及user2的子彈時,雙方子彈均被擊毀。
351
第四篇 2D繪圖設計(2D Graphics)
352
第十四章 螢幕/視景 座標 (Screen/View Coordinates)
14-1 簡介 14-2 螢幕座標(Screen Coordinates) 14-3 視景座標(View Coordinates) 14-4 包裹應用(Pachage) 14-5 習題(Exercises)
353
14-1 簡介 2D平面座標是建立在x軸與y軸之距離,螢幕上:原點在左上角,x座標是從左到右,y座標是從上到下,是謂 “螢幕座標(Screen Coordinates)”。在視覺上:則習慣原點在中央,x座標是從左到右,y座標是從下到上,是謂 “視景座標(View Coordinates)”。因此在2D平面繪圖中,設計時、以視景座標(View Coordinate) 作思考,設計完成後,再將視景座標改換成對應之螢幕座標,在螢幕上顯示圖案。
354
14-2 螢幕座標(Screen Coordinates)
在螢幕上、X軸座標距離是由左向右移動,Y軸座標距離是由上向下移動,左上方為原點(x=0, y=0)。本節將以螢幕座標舉例解述2D繪圖。
355
圖14-2
356
範例64:設計檔案Ex14_2.java,其功能為解釋螢幕座標之應用。
01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex14_2 extends Frame implements Runnable { 05 int xs=100, ys=100; 06 public static void main(String args[]) { 07 Ex14_2 workStart=new Ex14_2(); 08 } 09 public Ex14_2() { 10 super("Ex14_2"); 11 setSize(350, 350); 12 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 13 setVisible(true); 14 new Thread(this).start(); 15 }
357
範例64 續1 16 public void processWindowEvent(WindowEvent e) {
範例64 續1 16 public void processWindowEvent(WindowEvent e) { 17 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 19 } 20 } 21 public void run() { 22 repaint(); 23 } 24 public void paint(Graphics g) { 25 g.drawLine(0,0,xs,ys); 26 } 27 }
358
14-3 視景座標(View Coordinates)
在視覺上,我們習慣以視景座標作思考,原點在視窗中央,原點右端之x座標為正,從左到右增加距離正值,原點左端之x座標為負,從右到左增加距離負值;原點上端之y座標為正,從下到上增加距離正值,原點下端之y座標為負,從上到下增加距離負值。如此標示是謂 “視景座標(View Coordinates)”。
359
圖14-3-1
360
範例65:如果以視景座標(View Coordinates) 考量將原點(xv1, yv1)與a點(xv2, yv2)繪一直線,其中原點為(0, 0)、a點為(100, 100)如圖14-3-2,則其螢幕座標為何?並設計程式Ex14_3.java驗證之。 01 import java.awt.*; 02 import java.awt.event.*; 03 import java.awt.Graphics; 04 public class Ex14_3 extends Frame implements Runnable { 05 int xv1=0, yv1=0, xv2=100, yv2=100; 06 int xs1, ys1, xs2, ys2; 07 public static void main(String args[]) { 08 Ex14_3 workStart=new Ex14_3(); 09 } 10 public Ex14_3() { 11 super("Ex14_3"); 12 setSize(350, 350); 13 xs1 = 350/2; 14 ys1 = 350/2;
361
範例65 續1 15 xs2 = xv2 + 350/2; 16 ys2 = -yv2 + 350/2;
範例65 續1 15 xs2 = xv /2; 16 ys2 = -yv /2; 17 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 18 setVisible(true); 19 new Thread(this).start(); 20 } 21 public void processWindowEvent(WindowEvent e) { 22 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 24 } 25 } 26 public void run() { 27 repaint(); 28 } 29 public void paint(Graphics g) { 30 g.drawLine(xs1, ys1, xs2, ys2); 31 } 32 }
362
14-4 包裹應用(Pachage) 當程式功能增加,其中程式碼亦將隨之增多,為了考量程式之可讀性,我們可將某些特別功能的一群程式碼,另闢建於包裹程式。
363
範例66:設計檔案Ex14_4.java,其功能為解釋使用包裹math2D、與視景座標改換成螢幕座標之應用。
包裹檔案view_To_screen_coordinates.java:(將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch14\14_4\math2D) 01 package math2D; 02 import java.awt.*; 03 public class view_To_screen_coordinates { 04 public int viewX_To_screenX(int x) { 05 return x + 350/2; 06 } 07 public int viewY_To_screenY(int y) { 08 return -y + 350/2; 09 } 10 }
364
範例66 續1 檔案Ex14_4.java:(使用列01~10之包裹,將視景座標改換成螢幕座標,再以螢幕座標繪出直線)
範例66 續1 檔案Ex14_4.java:(使用列01~10之包裹,將視景座標改換成螢幕座標,再以螢幕座標繪出直線) 11 import math2D.*; 12 import java.awt.*; 13 import java.awt.event.*; 14 import java.awt.Graphics; 15 public class Ex14_4 extends Frame implements Runnable { 16 int xv1=0, yv1=0, xv2=100, yv2=100; 17 int xs1, ys1, xs2, ys2; 18 public static void main(String args[]) { Ex14_4 workStart=new Ex14_4(); 20 } 21 public Ex14_4() { super("Ex14_4"); setSize(350, 350);
365
範例66 續2 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); xs1 = vTs.viewX_To_screenX(xv1); ys1 = vTs.viewY_To_screenY(yv1); xs2 = vTs.viewX_To_screenX(xv2); ys2 = vTs.viewY_To_screenY(xv2); enableEvents(AWTEvent.WINDOW_EVENT_MASK); setVisible(true); new Thread(this).start(); 32 } 33 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 36 } } 38 public void run() { repaint(); 40 } 41 public void paint(Graphics g) { g.drawLine(xs1, ys1, xs2, ys2); 43 } }
366
14-5 習題(Exercises) 01、螢幕座標之意義為何? 02、視景座標之意義為何? 03、視景座標與螢幕座標之關係為何?
04、螢幕座標與視景座標之使用時機為何? 05、為何要使用包裹?其優點為何?
367
第十五章 繪圖概念(Graphic Math)
15-1 簡介 15-2 點/線(Point/Line) 15-3 向量(Vector) 15-4 內積(Inner Product)/法線(Normal) 15-5 多邊形(Polygon) 15-6 習題(Exercises)
368
15-1 簡介 對讀者來言,2D平面繪圖並不是一件困難的問題,而3D繪圖則並非那麼簡單。本書在繪圖方面的描述,最終是要將讀者導引到3D繪圖之認識,在3D繪圖設計上,需要一些高中程度的數學式來支援。
369
15-2 點/線(Point/Line) 點/線(Point/Line) 是繪圖元件中最基本也是最重要的兩個元件,通常點以座標(x, y) 表示如圖15-2-1、線以兩點座標((x1, y1), (x2, y2)) 表示如圖15-2-2。
370
圖15-2-1 Point a(xa, ya) Point b(xb, yb)
371
15-3 向量(Vector) 向量(Vector) 為兩點之連線,但與 “線(Line)” 不同的是起始點為原點(0,0),含有方向與長度。如圖15-3-1: (1) 向量A:長度為 (xa2+ya2)1/2。表示符號為 A(xa, ya)。 (2) 向量B:長度為 (xb2+yb2)1/2。表示符號為 B(xb, yb)。 (3) 向量AB:長度為 ((xb-xa)2+(yb-ya)2)1/2。表示符號為 AB(xb-xa, yb-ya)。
372
圖15-3-1
373
圖15-3-2
374
範例67:設計檔案Ex15_3.java,其功能為解釋使用包裹math2D、與執行向量和之應用。
包裹檔案1 view_To_screen_coordinates.java:(將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch15\15_3\math2D) 01 package math2D; 02 import java.awt.*; 03 public class view_To_screen_coordinates { 04 public int viewX_To_screenX(int x) { 05 return x + 350/2; 06 } 07 public int viewY_To_screenY(int y) { 08 return -y + 350/2; 09 } 10 }
375
範例67 續1 包裹檔案2 vector_calculator.java:(將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch15\15_3\math2D) 11 package math2D; 12 import java.awt.*; 13 public class vector_calculator { 14 private int x, y; 15 public void setPt(int x, int y) { this.x = x; this.y = y; 17 } 18 public void vectAdd(int x, int y) { this.x += x; this.y += y; 20 } 21 public void vectSubtract(int x, int y) { this.x -= x; this.y -= y; 23 }
376
範例67 續2 24 public void vectMultiply(int x, int y) {
範例67 續2 24 public void vectMultiply(int x, int y) { this.x *= x; this.y *= y; 26 } 27 public void vectDivide(int x, int y) { this.x /= x; this.y /= y; 29 } 30 public int getPtX() {return x;} 31 public int getPtY() {return y;} 32 }
377
範例67 續3 檔案Ex15_3.java:(繪出向量C=A+B) 33 import math2D.*;
範例67 續3 檔案Ex15_3.java:(繪出向量C=A+B) 33 import math2D.*; 34 import java.awt.*; 35 import java.awt.event.*; 36 import java.awt.Graphics; 37 public class Ex15_3 extends Frame implements Runnable { 38 int x0=0, y0=0, xa=20, ya=40, xb=100, yb=10, xc, yc; 39 int x0s, y0s, xas, yas, xbs, ybs, xcs, ycs; 40 public static void main(String args[]) { 41 Ex15_3 workStart=new Ex15_3(); 42 } 43 public Ex15_3() { 44 super("Ex15_3"); 45 setSize(350, 350);
378
範例67 續4 46 vector_calculator vc = new vector_calculator();
範例67 續4 46 vector_calculator vc = new vector_calculator(); 47 vc.setPt(xa, ya); 48 vc.vectAdd(xb, yb); 49 xc = vc.getPtX(); 50 yc = vc.getPtY(); 51 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 52 x0s = vTs.viewX_To_screenX(x0); 53 y0s = vTs.viewY_To_screenY(y0); 54 xas = vTs.viewX_To_screenX(xa); 55 yas = vTs.viewY_To_screenY(ya); 56 xbs = vTs.viewX_To_screenX(xb); 57 ybs = vTs.viewY_To_screenY(yb); 58 xcs = vTs.viewX_To_screenX(xc); 59 ycs = vTs.viewY_To_screenY(yc); 60 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 61 setVisible(true); 62 new Thread(this).start(); 63 }
379
範例67 續5 64 public void processWindowEvent(WindowEvent e) {
範例67 續5 64 public void processWindowEvent(WindowEvent e) { 65 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 67 } 68 } 69 public void run() { repaint(); 71 } 72 public void paint(Graphics g) { 73 g.drawLine(x0s, y0s, xas, yas); 74 g.drawLine(x0s, y0s, xbs, ybs); 75 g.drawLine(x0s, y0s, xcs, ycs); 76 } 77 }
380
15-4 內積(Inner Product)/法線(Normal)
讀者也許會好奇,2D、3D、4D(移動的立體)繪圖與內積(Inner Product)、法線(Normal)有何關係?將來於第五篇3D立體繪圖時,內積與法線在消除隱藏線上,都是極為重要的辨認機制。
381
圖15-4-1
382
圖15-4-2
383
範例68:設計檔案Ex15_4.java,其功能為解釋使用包裹math2D、與繪出線段之法線向量。
包裹檔案1 view_To_screen_coordinates.java:(如範例67,將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch15\15_4\math2D) 包裹檔案2 vector_calculator.java:(如範例67,將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch15\15_4\math2D) 檔案Ex15_4.java:(繪出線段ab之向量AB、與其法線向量N(上端N1與下端N2)) 33 import math2D.*; 34 import java.awt.*; 35 import java.awt.event.*; 36 import java.awt.Graphics; 37 public class Ex15_4 extends Frame implements Runnable { 38 int x0=0, y0=0, xa=20, ya=40, xb=100, yb=10, xc, yc; 39 int xN1, yN1, xN2, yN2; 40 int x0s, y0s, xcs, ycs, xN1s, yN1s, xN2s, yN2s; 41 public static void main(String args[]) { 42 Ex15_4 workStart=new Ex15_4(); 43 }
384
範例68 續1 44 public Ex15_4() { 45 super("Ex15_4"); 46 setSize(350, 350);
範例68 續1 44 public Ex15_4() { 45 super("Ex15_4"); 46 setSize(350, 350); 47 vector_calculator vc = new vector_calculator(); 48 vc.setPt(xb, yb); 49 vc.vectSubtract(xa, ya); 50 xc = vc.getPtX(); 51 yc = vc.getPtY(); 52 xN1 = -yc; yN1 = xc; 53 xN2 = yc; yN2 = -xc; 54 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 55 x0s = vTs.viewX_To_screenX(x0); 56 y0s = vTs.viewY_To_screenY(y0); 57 xcs = vTs.viewX_To_screenX(xc); 58 ycs = vTs.viewY_To_screenY(yc); 59 xN1s = vTs.viewX_To_screenX(xN1); 60 yN1s = vTs.viewY_To_screenY(yN1); 61 xN2s = vTs.viewX_To_screenX(xN2); 62 yN2s = vTs.viewY_To_screenY(yN2);
385
範例68 續2 63 enableEvents(AWTEvent.WINDOW_EVENT_MASK);
範例68 續2 63 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 64 setVisible(true); 65 new Thread(this).start(); 66 } 67 public void processWindowEvent(WindowEvent e) { 68 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 70 } 71 } 72 public void run() { repaint(); 74 } 75 public void paint(Graphics g) { 76 g.drawLine(x0s, y0s, xcs, ycs); 77 g.drawLine(x0s, y0s, xN1s, yN1s); 78 g.drawLine(x0s, y0s, xN2s, yN2s); 79 } 80 }
386
15-5 多邊形(Polygon) 三個(含)以上的點所包含之範圍均可謂 “多邊形(Polygon)”。在2D、3D繪圖上,多邊形將是不可或缺的繪圖元件,為圖案畫面的基本單元(Basic Unit),每一幅圖案都是以不同的多個多邊形所組成,掌握住多邊形的特性,亦即掌握住圖案的特性,例如旋轉(Rotate)、縮放(Zoom)、隱藏(Hide) 等。
387
圖15-5
388
範例69:設計檔案Ex15_5.java,其功能為解釋使用包裹math2D、與繪出多邊形之應用。
包裹檔案1 view_To_screen_coordinates.java:(如範例67,將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch15\15_5\math2D) 檔案Ex15_5.java:(繪出多邊形abcd) 11 import math2D.*; 12 import java.awt.*; 13 import java.awt.event.*; 14 import java.awt.Graphics; 15 public class Ex15_5 extends Frame implements Runnable { 16 int xa=20, ya=40, xb=30, yb=10, xc=100, yc=20, xd=80, yd=60; 17 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 18 public static void main(String args[]) { 19 Ex15_5 workStart=new Ex15_5(); 20 }
389
範例69 續1 21 public Ex15_5() { 22 super("Ex15_5"); 23 setSize(350, 350);
範例69 續1 21 public Ex15_5() { 22 super("Ex15_5"); 23 setSize(350, 350); 24 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 25 xas = vTs.viewX_To_screenX(xa); 26 yas = vTs.viewY_To_screenY(ya); 27 xbs = vTs.viewX_To_screenX(xb); 28 ybs = vTs.viewY_To_screenY(yb); 29 xcs = vTs.viewX_To_screenX(xc); 30 ycs = vTs.viewY_To_screenY(yc); 31 xds = vTs.viewX_To_screenX(xd); 32 yds = vTs.viewY_To_screenY(yd); 33 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 34 setVisible(true); 35 new Thread(this).start(); 36 }
390
範例69 續2 37 public void processWindowEvent(WindowEvent e) {
範例69 續2 37 public void processWindowEvent(WindowEvent e) { 38 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 40 } 41 } 42 public void run() { repaint(); 44 } 45 public void paint(Graphics g) { 46 int pgx[] = {xas,xbs,xcs,xds}; 47 int pgy[] = {yas,ybs,ycs,yds}; 48 g.fillPolygon(pgx, pgy, 4); 49 } 50 }
391
15-6 習題(Exercises) 01、點/線(Point/Line) 如何表示?
02、向量(Vector) 與線(Line) 有何不同? 03、設有向量A(xa, ya),其長度為何? 04、設有向量A(xa, ya)、向量B(xb, yb),則向量AB為何? 05、繪圖與內積(Inner Product)、法線(Normal)有何關係? 06、兩向量內積與其夾角有何關係? 07、何謂多邊形? 08、繪圖與多邊形有何關係?
392
第十六章 縮放與旋轉(Zoom/Rotate)
16-1 簡介 16-2 多邊形縮放(Polygon Zoom) 16-3 多邊形旋轉(Polygon Rotate) 16-4 習題(Exercises)
393
16-1 簡介 筆者一再強調,本書之繪圖焦點是在3D立體繪圖,本篇標題雖然是2D平面繪圖,但所列出的章節項目均是為著3D立體繪圖作準備,為了如何設計3D立體圖形之縮放與旋轉,本篇先就2D圖形之縮放與旋轉,以較簡易之層次讓讀者先了解其中部份原理及程式設計方法。
394
16-2 多邊形縮放(Polygon Zoom) 從視覺上來言,縮放即為在觀察物件時,觀察點與物件間距離之變化;從繪圖設計上來言,縮放却是將物件之座標乘以某系數,使物件變大或變小。本節多邊形縮放是配合鍵盤事件來執行,鍵↑控制放大圖案、鍵↓控制縮小圖案。
395
範例70:設計檔案Ex16_2.java,其功能為解釋使用包裹math2D、與多邊形縮放之應用。
包裹檔案1 view_To_screen_coordinates.java:(將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch16\16_2\math2D) 01 package math2D; 02 import java.awt.*; 03 public class view_To_screen_coordinates { 04 public int viewX_To_screenX(int x) { 05 return x + 350/2; 06 } 07 public int viewY_To_screenY(int y) { 08 return -y + 350/2; 09 } 10 }
396
包裹檔案2 zoom_calculator.java:(以縮放係數執行多邊形縮放,本例目錄為C:\BookJavaVol_3\Program\ch16\16_2\math2D)
11 package math2D; 12 import java.awt.*; 13 public class zoom_calculator { 14 private double x, y; 15 public void zoomChange(double x, double y, double zf) { this.x = x * zf; this.y = y * zf; 18 } 19 public double getPtX() {return x;} 20 public double getPtY() {return y;} 21 }
397
範例70 續1 包裹檔案2 zoom_calculator.java:(以縮放係數執行多邊形縮放,本例目錄為C:\BookJavaVol_3\Program\ch16\16_2\math2D) 11 package math2D; 12 import java.awt.*; 13 public class zoom_calculator { 14 private double x, y; 15 public void zoomChange(double x, double y, double zf) { this.x = x * zf; this.y = y * zf; 18 } 19 public double getPtX() {return x;} 20 public double getPtY() {return y;} 21 }
398
範例70 續2 檔案Ex16_2.java:(繪出縮放多邊形,鍵↑控制放大圖案、鍵↓控制縮小圖案) 22 import math2D.*;
範例70 續2 檔案Ex16_2.java:(繪出縮放多邊形,鍵↑控制放大圖案、鍵↓控制縮小圖案) 22 import math2D.*; 23 import java.awt.*; 24 import java.awt.event.*; 25 import java.awt.Graphics; 26 public class Ex16_2 extends Frame implements Runnable { 27 double xa=20, ya=40, xb=30, yb=10, xc=100, yc=20, xd=80, yd=60; 28 int xam, yam, xbm, ybm, xcm, ycm, xdm, ydm; 29 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 30 double zf=1; 31 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 32 zoom_calculator zc = new zoom_calculator(); 33 public static void main(String args[]) { 34 Ex16_2 workStart=new Ex16_2(); 35 }
399
範例70 續2 37 super("Ex16_2"); 38 setSize(350, 350);
範例70 續2 37 super("Ex16_2"); 38 setSize(350, 350); 39 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 40 enableEvents(AWTEvent.KEY_EVENT_MASK); 41 xam=(int)xa; yam=(int)ya; 42 xbm=(int)xb; ybm=(int)yb; 36 public Ex16_2() { 43 xcm=(int)xc; ycm=(int)yc; 44 xdm=(int)xd; ydm=(int)yd; 45 setVisible(true); 46 new Thread(this).start(); 47 } 48 public void processWindowEvent(WindowEvent e) { 49 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 51 } 52 }
400
範例70 續3 54 if(e.getID() == KeyEvent.KEY_PRESSED) {
範例70 續3 54 if(e.getID() == KeyEvent.KEY_PRESSED) { switch(e.getKeyCode()) { case KeyEvent.VK_UP: if(zf<1) zf = 1; 53 public void processKeyEvent(KeyEvent e) { zf = zf ; break; case KeyEvent.VK_DOWN: if(zf>0) zf = 1; zf = zf ; break; } zc.zoomChange(xa,ya,zf); xa=zc.getPtX(); ya=zc.getPtY(); zc.zoomChange(xb,yb,zf); xb=zc.getPtX(); yb=zc.getPtY(); zc.zoomChange(xc,yc,zf); xc=zc.getPtX(); yc=zc.getPtY();
401
範例70 續4 71 zc.zoomChange(xd,yd,zf); 72 xd=zc.getPtX(); yd=zc.getPtY();
範例70 續4 zc.zoomChange(xd,yd,zf); xd=zc.getPtX(); yd=zc.getPtY(); xam=(int)xa; yam=(int)ya; xbm=(int)xb; ybm=(int)yb; xcm=(int)xc; ycm=(int)yc; xdm=(int)xd; ydm=(int)yd; } 78 } 79 public void run() { 80 while(true) { xas = vTs.viewX_To_screenX(xam); yas = vTs.viewY_To_screenY(yam); xbs = vTs.viewX_To_screenX(xbm); ybs = vTs.viewY_To_screenY(ybm); xcs = vTs.viewX_To_screenX(xcm); ycs = vTs.viewY_To_screenY(ycm); xds = vTs.viewX_To_screenX(xdm); yds = vTs.viewY_To_screenY(ydm);
402
範例70 續5 89 repaint(); 90 try{Thread.sleep(150);}
範例70 續5 repaint(); try{Thread.sleep(150);} catch(InterruptedException e) {;} 92 } 93 } 94 public void paint(Graphics g) { 95 int pgx[] = {xas, xbs, xcs, xds}; 96 int pgy[] = {yas, ybs, ycs, yds}; 97 g.drawPolygon(pgx, pgy, 4); 98 } 99 }
403
16-3 多邊形旋轉(Polygon Rotate)
在繪製圖形上,旋轉亦是一種重要的座標轉換。如圖16-3,如何從點a座標旋轉至點b座標?在此、我們將使用三角形之複角定理求得之。
404
圖16-3
405
範例71:設計檔案Ex16_3.java,其功能為解釋使用包裹math2D、與多邊形圍繞中心點旋轉之應用。
包裹檔案1 view_To_screen_coordinates.java:(如範例70,將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch16\16_3\math2D) 包裹檔案2 rotate_calculator.java:(將視景座標以三角形複角定理,建立座標旋轉變換,本例目錄為C:\BookJavaVol_3\Program\ch16\16_3\math2D) 11 package math2D; 12 import java.awt.*; 13 public class rotate_calculator { 14 private double x, y; 15 private double sinAngle, cosAngle; 16 public void rotateChange(double x, double y, double angle) { 17 this.sinAngle = (double)Math.sin(angle); 18 this.cosAngle = (double)Math.cos(angle); 19 this.x = x*cosAngle - y*sinAngle; 20 this.y = x*sinAngle + y*cosAngle; 21 } 22 public double getPtX() {return x;} 23 public double getPtY() {return y;} 24 }
406
範例71 續1 檔案Ex16_3.java:(繪出旋轉多邊形) 25 import math2D.*;
範例71 續1 檔案Ex16_3.java:(繪出旋轉多邊形) 25 import math2D.*; 26 import java.awt.*; 27 import java.awt.event.*; 28 import java.awt.Graphics; 29 public class Ex16_3 extends Frame implements Runnable { 30 double xa=20, ya=40, xb=30, yb=10, xc=100, yc=20, xd=80, yd=60; 31 int xam, yam, xbm, ybm, xcm, ycm, xdm, ydm; 32 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 33 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 34 rotate_calculator rc = new rotate_calculator(); 35 public static void main(String args[]) { 36 Ex16_3 workStart=new Ex16_3(); 37 }
407
範例71 續2 38 public Ex16_3() { 39 super("Ex16_3"); 40 setSize(350, 350);
範例71 續2 38 public Ex16_3() { 39 super("Ex16_3"); 40 setSize(350, 350); 41 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 42 xam=(int)xa; yam=(int)ya; 43 xbm=(int)xb; ybm=(int)yb; 44 xcm=(int)xc; ycm=(int)yc; 45 xdm=(int)xd; ydm=(int)yd; 46 setVisible(true); 47 new Thread(this).start(); 48 } 49 public void processWindowEvent(WindowEvent e) { 50 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 52 } 53 }
408
範例71 續3 54 public void run() { 55 while(true) {
範例71 續3 54 public void run() { 55 while(true) { rc.rotateChange(xa,ya,0.03); xa=rc.getPtX(); ya=rc.getPtY(); rc.rotateChange(xb,yb,0.03); xb=rc.getPtX(); yb=rc.getPtY(); rc.rotateChange(xc,yc,0.03); xc=rc.getPtX(); yc=rc.getPtY(); rc.rotateChange(xd,yd,0.03); xd=rc.getPtX(); yd=rc.getPtY(); xam=(int)xa; yam=(int)ya; xbm=(int)xb; ybm=(int)yb; xcm=(int)xc; ycm=(int)yc; xdm=(int)xd; ydm=(int)yd;
409
範例71 續4 68 xas = vTs.viewX_To_screenX(xam);
範例71 續4 xas = vTs.viewX_To_screenX(xam); yas = vTs.viewY_To_screenY(yam); xbs = vTs.viewX_To_screenX(xbm); ybs = vTs.viewY_To_screenY(ybm); xcs = vTs.viewX_To_screenX(xcm); ycs = vTs.viewY_To_screenY(ycm); xds = vTs.viewX_To_screenX(xdm); yds = vTs.viewY_To_screenY(ydm); repaint(); try{Thread.sleep(150);} catch(InterruptedException e) {;} 79 } 80 } 81 public void paint(Graphics g) { 82 int pgx[] = {xas, xbs, xcs, xds}; 83 int pgy[] = {yas, ybs, ycs, yds}; 84 g.drawPolygon(pgx, pgy, 4); 85 } 86 }
410
16-4 習題(Exercises) 01、從視覺上來言,縮放之意義為何? 02、從繪圖設計上來言,縮放之意義為何?
03、何謂三角形之複角定理? 04、如何利用三角形之複角定理來產生旋轉座標?
411
第五篇 3D繪圖設計(3D Graphics)
412
第十七章 3D座標(3D Coordinates)
17-1 簡介 17-2 立體空間座標(3D Coordinates) Z軸座標(Z axis) 視景視窗(View Window) 投影座標(Project Coordinates) 視景寬與視距(View Width and Distance) 17-3 繪製3D水平多邊形 17-4 繪製3D立體多邊形 17-5 習題(Exercises)
413
17-1 簡介 圖案落置之處是一張紙、是螢幕、是相片、或是…等等,無論是何物,都有一共通特徵,是2D平面。如果要繪製3D立體圖案,首先要解決的就是如何將3D座標投影轉換成2D座標,本節將就其中關鍵處,擇重解述。
414
17-2 立體空間座標(3D Coordinates)
3D物件之繪製,是將物件各點的立體座標 (x,y,z),投影到只有二維空間之平面座標 (x’,y’)。本節將列出有關的原理及公式,同時以範例繪製3D水平多邊形、3D立方形,讀者將會輕易地了解其中的奧秘。
415
圖17-2-1
416
圖
417
圖
418
圖
419
圖
420
圖
421
17-3 繪製3D水平多邊形 依照17-2節所列各觀念原理,本節將其溶入程式設計,使用Java繪出第一個3D圖形。為了解述方便,本節檢選一簡易的3D水平多邊形如圖17-3-1,作為第一個3D圖形之繪製範例。
422
圖17-3-1
423
範例72:設計檔案Ex17_3.java,其功能為解釋使用包裹math3D、與繪製立體之水平多邊形(執行結果將如圖17-3-2)。
包裹檔案1 view_To_screen_coordinates.java:(將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch17\17_3\math3D) 01 package math3D; 02 import java.awt.*; 03 public class view_To_screen_coordinates { 04 public int viewX_To_screenX(int x) { 05 return x + 400/2; 06 } 07 public int viewY_To_screenY(int y) { 08 return -y + 400/2; 09 } 10 }
424
範例72 續1 包裹檔案2 project3D_To_2D.java:(將物件3D各點之v (x,y,z) 從投影到2D平面之各點v’ (x’,y’),本例目錄為C:\BookJavaVol_3\Program\ch17\17_3\math3D) 11 package math3D; 12 import java.awt.*; 13 public class project3D_To_2D { 14 private double x, y; 15 private double d; 16 public double projectPtX(double x, double y, double z) { 17 d = ((double)400/2)/(double)Math.tan(80/2); 18 this.x = d*x/(d-(-z)); 19 return this.x; 20 } 21 public double projectPtY(double x, double y, double z) { 22 d = ((double)400/2)/(double)Math.tan(80/2); 23 this.y = d*y/(d-(-z)); 24 return this.y; 25 } 26 }
425
範例72 續2 檔案Ex17_3.java:(繪出立體之水平多邊形) 27 import math3D.*;
範例72 續2 檔案Ex17_3.java:(繪出立體之水平多邊形) 27 import math3D.*; 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.awt.Graphics; 31 public class Ex17_3 extends Frame implements Runnable { 32 double xa=-50, ya=50, za=50, xb=-50, yb=50, zb=-50; 33 double xc=50, yc=50, zc=-50, xd=50, yd=50, zd=50; 34 int xam, yam, xbm, ybm, xcm, ycm, xdm, ydm; 35 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 36 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 37 project3D_To_2D pr = new project3D_To_2D(); 38 public static void main(String args[]) { 39 Ex17_3 workStart=new Ex17_3(); 40 }
426
範例72 續3 41 public Ex17_3() { 42 super("Ex17_3"); 43 setSize(400, 400);
範例72 續3 41 public Ex17_3() { 42 super("Ex17_3"); 43 setSize(400, 400); 44 enableEvents(AWTEvent.WINDOW_EVENT_MASK); 45 xam = (int)pr.projectPtX(xa,ya,za); 46 yam = (int)pr.projectPtY(xa,ya,za); 47 xbm = (int)pr.projectPtX(xb,yb,zb); 48 ybm = (int)pr.projectPtY(xb,yb,zb); 49 xcm = (int)pr.projectPtX(xc,yc,zc); 50 ycm = (int)pr.projectPtY(xc,yc,zc); 51 xdm = (int)pr.projectPtX(xd,yd,zd); 52 ydm = (int)pr.projectPtY(xd,yd,zd); 53 xas = vTs.viewX_To_screenX(xam); 54 yas = vTs.viewY_To_screenY(yam); 55 xbs = vTs.viewX_To_screenX(xbm);
427
範例72 續4 56 ybs = vTs.viewY_To_screenY(ybm);
範例72 續4 56 ybs = vTs.viewY_To_screenY(ybm); 57 xcs = vTs.viewX_To_screenX(xcm); 58 ycs = vTs.viewY_To_screenY(ycm); 59 xds = vTs.viewX_To_screenX(xdm); 60 yds = vTs.viewY_To_screenY(ydm); 61 setVisible(true); 62 new Thread(this).start(); 63 } 64 public void processWindowEvent(WindowEvent e) { 65 if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); 67 } 68 } 69 public void run() { repaint(); 71 } 72 public void paint(Graphics g) { 73 int pgx[] = {xas, xbs, xcs, xds}; 74 int pgy[] = {yas, ybs, ycs, yds}; 75 g.drawPolygon(pgx, pgy, 4); 76 } 77 }
428
17-4 繪製3D立體多邊形 繼前節繪製水平多邊形後,本節再引介繪製3D立方形,在設計啟示上,當完成3D立方形之繪製時,即已進入完整簡易3D立體的設計。如圖17-4-1,原點(0,0,0)位於立方體中央,各軸距為50單位 (如a點為(-50,50,50)、b點為(-50,50,-50)、餘類推)。
429
圖17-4-1
430
範例73:設計檔案Ex17_4.java,其功能為解釋使用包裹math3D、與繪製3D立方形(執行結果將如圖17-4-2)。
包裹檔案1 view_To_screen_coordinates.java:(如範例72,將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch17\17_4\math3D) 包裹檔案2 project3D_To_2D.java:(如範例72,將物件3D各點之v (x,y,z) 從投影到2D平面之各點v’ (x’,y’),本例目錄為C:\BookJavaVol_3\Program\ch17\17_4\math3D) 檔案Ex17_4.java:(繪出立體之水平多邊形) 027 import math3D.*; 028 import java.awt.*; 029 import java.awt.event.*; 030 import java.awt.Graphics; 031 public class Ex17_4 extends Frame implements Runnable { 032 double xa=-50, ya=50, za=50, xb=-50, yb=50, zb=-50; 033 double xc=50, yc=50, zc=-50, xd=50, yd=50, zd=50; 034 double xe=-50, ye=-50, ze=50, xf=-50, yf=-50, zf=-50; 035 double xg=50, yg=-50, zg=-50, xh=50, yh=-50, zh=50; 036 int xam, yam, xbm, ybm, xcm, ycm, xdm, ydm; 037 int xem, yem, xfm, yfm, xgm, ygm, xhm, yhm; 038 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 039 int xes, yes, xfs, yfs, xgs, ygs, xhs, yhs;
431
範例73 續1 040 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 041 project3D_To_2D pr = new project3D_To_2D(); 042 public static void main(String args[]) { Ex17_4 workStart=new Ex17_4(); 044 } 045 public Ex17_4() { super("Ex17_4"); setSize(400, 400); enableEvents(AWTEvent.WINDOW_EVENT_MASK); xam = (int)pr.projectPtX(xa,ya,za); yam = (int)pr.projectPtY(xa,ya,za); xbm = (int)pr.projectPtX(xb,yb,zb); ybm = (int)pr.projectPtY(xb,yb,zb); xcm = (int)pr.projectPtX(xc,yc,zc); ycm = (int)pr.projectPtY(xc,yc,zc); xdm = (int)pr.projectPtX(xd,yd,zd); ydm = (int)pr.projectPtY(xd,yd,zd);
432
範例73 續2 057 xem = (int)pr.projectPtX(xe,ye,ze);
範例73 續2 xem = (int)pr.projectPtX(xe,ye,ze); yem = (int)pr.projectPtY(xe,ye,ze); xfm = (int)pr.projectPtX(xf,yf,zf); yfm = (int)pr.projectPtY(xf,yf,zf); xgm = (int)pr.projectPtX(xg,yg,zg); ygm = (int)pr.projectPtY(xg,yg,zg); xhm = (int)pr.projectPtX(xh,yh,zh); yhm = (int)pr.projectPtY(xh,yh,zh); xas = vTs.viewX_To_screenX(xam); yas = vTs.viewY_To_screenY(yam); xbs = vTs.viewX_To_screenX(xbm); ybs = vTs.viewY_To_screenY(ybm); xcs = vTs.viewX_To_screenX(xcm); ycs = vTs.viewY_To_screenY(ycm); xds = vTs.viewX_To_screenX(xdm); yds = vTs.viewY_To_screenY(ydm); xes = vTs.viewX_To_screenX(xem);
433
範例73 續3 074 yes = vTs.viewY_To_screenY(yem);
範例73 續3 yes = vTs.viewY_To_screenY(yem); xfs = vTs.viewX_To_screenX(xfm); yfs = vTs.viewY_To_screenY(yfm); xgs = vTs.viewX_To_screenX(xgm); ygs = vTs.viewY_To_screenY(ygm); xhs = vTs.viewX_To_screenX(xhm); yhs = vTs.viewY_To_screenY(yhm); setVisible(true); new Thread(this).start(); } 084 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 088 }
434
範例73 續4 089 public void run() { 090 repaint(); 091 }
範例73 續4 089 public void run() { repaint(); 091 } 092 public void paint(Graphics g) { g.drawLine(xas, yas, xbs, ybs); g.drawLine(xbs, ybs, xcs, ycs); g.drawLine(xcs, ycs, xds, yds); g.drawLine(xds, yds, xas, yas); g.drawLine(xes, yes, xfs, yfs); g.drawLine(xfs, yfs, xgs, ygs); g.drawLine(xgs, ygs, xhs, yhs); g.drawLine(xhs, yhs, xes, yes); g.drawLine(xas, yas, xes, yes); g.drawLine(xbs, ybs, xfs, yfs); g.drawLine(xcs, ycs, xgs, ygs); g.drawLine(xds, yds, xhs, yhs); 105 } 106 }
435
17-5 習題(Exercises) 01、試述3D繪圖之意義。 02、試述3D之X軸、Y軸、Z軸之意義。
03、何謂視覺景深(Field Depth)? 04、外端投影3D座標v (x,y,z) 與2D座標v’ (x’,y’) 之關係為何? 05、內端投影3D座標v (x,y,z) 與2D座標v’ (x’,y’) 之關係為何? 06、如何求取視距?
436
第十八章 3D圖形旋轉(3D Rotating)
18-1 簡介 18-2 立體圖形之旋轉 18-3 圖繞Y軸旋轉 18-4 圖繞X軸旋轉 18-5 習題(Exercises)
437
18-1 簡介 於前章,我們已探討如何將3D圖形投影到2D平面上,其表現方式是靜態的,本章將討論動態的3D圖形,因其是隨著時間作變化,因此也有人稱之謂4D圖形,依三角函數運算式,求取圍繞X、Y、Z軸作旋轉之位置座標。
438
18-2 立體圖形之旋轉 於16-3節,我們曾就2D平面多邊形之旋轉作過詳盡的探討,本節將就立體圖形之旋轉作進一步的討論。參考16-3節的計算公式作推論,如圖18-2可視為圍繞Z軸作旋轉,因xa移至xb、ya移至yb、zb=za (即z軸座標無改變)。
439
圖18-2
440
18-3 圖繞Y軸旋轉 以18-2節之演算式設計範例74,建立檔案Ex18_3.java繪製3D立方形圍繞Y軸旋轉。於包裹math3D建立檔案view_To_screen..java,將視景座標更換成螢幕座標,建立檔案project3D_To_2D.java,將3D圖形之各點座標投影到2D平面上,建立檔案rotate_calculator.java,將3D圖形各點圍繞X軸(或Y軸、或Z軸) 旋轉α 角度。
441
範例74:設計檔案Ex18_3.java,其功能為解釋使用包裹math3D、與繪製3D立方形圍繞Y軸旋轉。
包裹檔案1 view_To_screen_coordinates.java:(將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch18\18_3\math3D) 01 package math3D; 02 import java.awt.*; 03 public class view_To_screen_coordinates { 04 public int viewX_To_screenX(int x) { 05 return x + 400/2; 06 } 07 public int viewY_To_screenY(int y) { 08 return -y + 400/2; 09 } 10 }
442
範例74 續1 包裹檔案2 project3D_To_2D.java:(將物件3D各點之v (x,y,z) 從投影到2D平面之各點v’ (x’,y’),本例目錄為C:\BookJavaVol_3\Program\ch18\18_3\math3D) 11 package math3D; 12 import java.awt.*; 13 public class project3D_To_2D { 14 private double x, y; 15 private double d; 16 public double projectPtX(double x, double y, double z) { 17 d = ((double)400/2)/(double)Math.tan(80/2); 18 this.x = d*x/(d-(-z)); 19 return this.x; 20 } 21 public double projectPtY(double x, double y, double z) { 22 d = ((double)400/2)/(double)Math.tan(80/2); 23 this.y = d*y/(d-(-z)); 24 return this.y; 25 } 26 }
443
範例74 續2 包裹檔案3 rotate_calculator.java:(將3D圖形各點圍繞X軸(或Y軸、或Z軸) 旋轉α 角度,本例目錄為C:\BookJavaVol_3\Program\ch18\18_3\math3D) 27 package math3D; 28 import java.awt.*; 29 public class rotate_calculator { 30 private double x, y, z; 31 private double sinAngle, cosAngle; 32 public void rotateX(double x, double y, double z, double angle) { 33 this.sinAngle = (double)Math.sin(angle); 34 this.cosAngle = (double)Math.cos(angle); 35 this.y = y*cosAngle - z*sinAngle; 36 this.z = y*sinAngle + z*cosAngle; 37 this.x = x; 38 }
444
範例74 續3 39 public void rotateY(double x, double y, double z, double angle) { 40 this.sinAngle = (double)Math.sin(angle); 41 this.cosAngle = (double)Math.cos(angle); 42 this.z = z*cosAngle - x*sinAngle; 43 this.x = z*sinAngle + x*cosAngle; 44 this.y = y; 45 } 46 public void rotateZ(double x, double y, double z, double angle) { 47 this.sinAngle = (double)Math.sin(angle); 48 this.cosAngle = (double)Math.cos(angle); 49 this.x = x*cosAngle - y*sinAngle; 50 this.y = x*sinAngle + y*cosAngle; 51 this.z = z; 52 } 53 public double getPtX() {return x;} 54 public double getPtY() {return y;} 55 public double getPtZ() {return z;} 56 }
445
範例74 續4 檔案Ex18_3.java:(繪製3D立方形圍繞Y軸旋轉) 057 import math3D.*;
範例74 續4 檔案Ex18_3.java:(繪製3D立方形圍繞Y軸旋轉) 057 import math3D.*; 058 import java.awt.*; 059 import java.awt.event.*; 060 import java.awt.Graphics; 061 public class Ex18_3 extends Frame implements Runnable { 062 double xa=-50, ya=50, za=50, xb=-50, yb=50, zb=-50; 063 double xc=50, yc=50, zc=-50, xd=50, yd=50, zd=50; 064 double xe=-50, ye=-50, ze=50, xf=-50, yf=-50, zf=-50; 065 double xg=50, yg=-50, zg=-50, xh=50, yh=-50, zh=50; 066 int xam, yam, xbm, ybm, xcm, ycm, xdm, ydm; 067 int xem, yem, xfm, yfm, xgm, ygm, xhm, yhm; 068 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 069 int xes, yes, xfs, yfs, xgs, ygs, xhs, yhs; 070 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 071 project3D_To_2D pr = new project3D_To_2D(); 072 rotate_calculator rc = new rotate_calculator(); 073 public static void main(String args[]) { Ex18_3 workStart=new Ex18_3(); 075 }
446
範例74 續5 076 public Ex18_3() { 077 super("Ex18_3");
範例74 續5 076 public Ex18_3() { super("Ex18_3"); setSize(400, 400); enableEvents(AWTEvent.WINDOW_EVENT_MASK); setVisible(true); new Thread(this).start(); } 083 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 087 } 088 public void run() { while(true) { rc.rotateY(xa,ya,za, 0.03); xa=rc.getPtX(); ya=rc.getPtY(); za=rc.getPtZ();
447
範例74 續6 092 rc.rotateY(xb,yb,zb, 0.03);
範例74 續6 rc.rotateY(xb,yb,zb, 0.03); xb=rc.getPtX(); yb=rc.getPtY(); zb=rc.getPtZ(); rc.rotateY(xc,yc,zc, 0.03); xc=rc.getPtX(); yc=rc.getPtY(); zc=rc.getPtZ(); rc.rotateY(xd,yd,zd, 0.03); xd=rc.getPtX(); yd=rc.getPtY(); zd=rc.getPtZ(); rc.rotateY(xe,ye,ze, 0.03); xe=rc.getPtX(); ye=rc.getPtY(); ze=rc.getPtZ(); rc.rotateY(xf,yf,zf, 0.03); xf=rc.getPtX(); yf=rc.getPtY(); zf=rc.getPtZ(); rc.rotateY(xg,yg,zg, 0.03); xg=rc.getPtX(); yg=rc.getPtY(); zg=rc.getPtZ(); rc.rotateY(xh,yh,zh, 0.03); xh=rc.getPtX(); yh=rc.getPtY(); zh=rc.getPtZ();
448
範例74 續7 106 xam = (int)pr.projectPtX(xa,ya,za);
範例74 續7 xam = (int)pr.projectPtX(xa,ya,za); yam = (int)pr.projectPtY(xa,ya,za); xbm = (int)pr.projectPtX(xb,yb,zb); ybm = (int)pr.projectPtY(xb,yb,zb); xcm = (int)pr.projectPtX(xc,yc,zc); ycm = (int)pr.projectPtY(xc,yc,zc); xdm = (int)pr.projectPtX(xd,yd,zd); ydm = (int)pr.projectPtY(xd,yd,zd); xem = (int)pr.projectPtX(xe,ye,ze); yem = (int)pr.projectPtY(xe,ye,ze); xfm = (int)pr.projectPtX(xf,yf,zf); yfm = (int)pr.projectPtY(xf,yf,zf); xgm = (int)pr.projectPtX(xg,yg,zg); ygm = (int)pr.projectPtY(xg,yg,zg); xhm = (int)pr.projectPtX(xh,yh,zh); yhm = (int)pr.projectPtY(xh,yh,zh);
449
範例74 續8 122 xas = vTs.viewX_To_screenX(xam);
範例74 續8 xas = vTs.viewX_To_screenX(xam); yas = vTs.viewY_To_screenY(yam); xbs = vTs.viewX_To_screenX(xbm); ybs = vTs.viewY_To_screenY(ybm); xcs = vTs.viewX_To_screenX(xcm); ycs = vTs.viewY_To_screenY(ycm); xds = vTs.viewX_To_screenX(xdm); yds = vTs.viewY_To_screenY(ydm); xes = vTs.viewX_To_screenX(xem); yes = vTs.viewY_To_screenY(yem); xfs = vTs.viewX_To_screenX(xfm); yfs = vTs.viewY_To_screenY(yfm); xgs = vTs.viewX_To_screenX(xgm); ygs = vTs.viewY_To_screenY(ygm); xhs = vTs.viewX_To_screenX(xhm); yhs = vTs.viewY_To_screenY(yhm); repaint(); try{Thread.sleep(150);} catch(InterruptedException e) {;} 141 } }
450
18-5 習題(Exercises) 01、3D圖形圍繞X軸作旋轉的運算式為何? 02、3D圖形圍繞Y軸作旋轉的運算式為何?
03、3D圖形圍繞Z軸作旋轉的運算式為何? 04、試設計一立方體圍繞Z軸作旋轉之程式。
451
第十九章 法線與隱藏線(Normal and Hidden Lines)
19-1 簡介 19-2 隱藏線(Hidden Lines) 立體圖像之法線(Nomal) 隱藏線處理 19-3 視角誤差 19-4 修正視角誤差 19-5 光影變化 19-6 習題(Exercises)
452
19-1 簡介 實體物件並非都是透明體,我們只能看到其正面,無法看到被遮蓋的背面,在繪圖上稱之謂隱藏部份,因此於繪製時,須將隱藏部份作消除或淺細處理。 在設計上、須先判別何者是正面顯示部份,何者是背面隱藏部份。本章將以此為著手點,以範例詳盡解述。
453
19-2 隱藏線(Hidden Lines) 立體物件圖像之顯示與平面物件圖像之顯示有所不同,平面圖案是盡收眼底,而立體圖案因是有層次,無法一覽而盡,亦即,我們只能看到前端的圖案,無法看到後端被遮蓋的圖案,如圖19-2,如果我們沿Z軸凝視,我們只能看到前端平面daeh,而無法看到被遮蓋的後端平面bcgf (讀者是否注意到:筆者在描述平面各點時,面向平面,逆時針描述,這是為了配合法線之方向應用,將於19-2-1述說)。
454
圖19-2
455
圖19-2-1
456
範例76:設計檔案Ex19_2.java,其功能為解釋使用包裹math3D、與繪製3D立方形圍繞Y軸旋轉,並將隱藏線作淺細處理。
包裹檔案1 view_To_screen_coordinates.java:(如範例74,將視景座標改換成螢幕座標,本例目錄為C:\BookJavaVol_3\Program\ch19\19_2\math3D) 包裹檔案2 project3D_To_2D.java:(如範例74,將物件3D各點之v (x,y,z) 從投影到2D平面之各點v’ (x’,y’),本例目錄為C:\BookJavaVol_3\Program\ch19\19_2\math3D) 包裹檔案3 rotate_calculator.java:(如範例74,將3D圖形各點圍繞X軸(或Y軸、或Z軸) 旋轉α 角度,本例目錄為C:\BookJavaVol_3\Program\ch19\19_2\math3D) 包裹檔案4 normal_hidden.java:(計算3D圖形之隱藏條件Nz,當Nz >= 0 時,Ø <= 90 o,此時我們可看到3D圖案,否則我們將看不到3D圖案,應作隱藏處理,本例目錄為C:\BookJavaVol_3\Program\ch19\19_2\math3D) 57 package math3D; 58 import java.awt.*;
457
範例76 續1 59 public class normal_hidden { 60 private double Nx, Ny, Nz;
範例76 續1 59 public class normal_hidden { 60 private double Nx, Ny, Nz; double xb, double yb, double zb, double xc, double yc, double zc) { 62 double Ux=xa-xb; double Uy=ya-yb; double Uz=za-zb; 63 double Vx=xb-xc; double Vy=yb-yc; double Vz=zb-zc; 61 public void vectNormal(double xa, double ya, double za, 64 this.Nx=Uy*Vz - Uz*Vy; 65 this.Ny=Uz*Vx - Ux*Vz; 66 this.Nz=Ux*Vy - Uy*Vx; 67 } 68 public double getHidden() {return Nz;} 69 }
458
範例76 續2 檔案Ex19_2.java:(繪製3D立方形圍繞Y軸旋轉,並將隱藏線作淺細處理) 070 import math3D.*;
範例76 續2 檔案Ex19_2.java:(繪製3D立方形圍繞Y軸旋轉,並將隱藏線作淺細處理) 070 import math3D.*; 071 import java.awt.*; 072 import java.awt.event.*; 073 import java.awt.Graphics; 074 public class Ex19_2 extends Frame implements Runnable { 075 double xa=-50, ya=50, za=50, xb=-50, yb=50, zb=-50; 076 double xc=50, yc=50, zc=-50, xd=50, yd=50, zd=50; 077 double xe=-50, ye=-50, ze=50, xf=-50, yf=-50, zf=-50; 078 double xg=50, yg=-50, zg=-50, xh=50, yh=-50, zh=50; 079 int xam, yam, xbm, ybm, xcm, ycm, xdm, ydm; 080 int xem, yem, xfm, yfm, xgm, ygm, xhm, yhm; 081 int xas, yas, xbs, ybs, xcs, ycs, xds, yds; 082 int xes, yes, xfs, yfs, xgs, ygs, xhs, yhs; 083 view_To_screen_coordinates vTs = new view_To_screen_coordinates(); 084 project3D_To_2D pr = new project3D_To_2D();
459
範例76 續3 085 rotate_calculator rc = new rotate_calculator();
範例76 續3 085 rotate_calculator rc = new rotate_calculator(); 086 normal_hidden P1 = new normal_hidden(); 087 normal_hidden P2 = new normal_hidden(); 088 normal_hidden P3 = new normal_hidden(); 089 normal_hidden P4 = new normal_hidden(); 090 normal_hidden P5 = new normal_hidden(); 091 normal_hidden P6 = new normal_hidden(); 092 public static void main(String args[]) { Ex19_2 workStart=new Ex19_2(); 094 } 095 public Ex19_2() { super("Ex19_2"); setSize(400, 400); enableEvents(AWTEvent.WINDOW_EVENT_MASK); setVisible(true); new Thread(this).start(); }
460
範例76 續4 102 public void processWindowEvent(WindowEvent e) {
範例76 續4 102 public void processWindowEvent(WindowEvent e) { if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } 106 } 107 public void run() { while(true) { rc.rotateY(xa,ya,za, 0.03); xa=rc.getPtX(); ya=rc.getPtY(); za=rc.getPtZ(); rc.rotateY(xb,yb,zb, 0.03); xb=rc.getPtX(); yb=rc.getPtY(); zb=rc.getPtZ(); rc.rotateY(xc,yc,zc, 0.03); xc=rc.getPtX(); yc=rc.getPtY(); zc=rc.getPtZ(); rc.rotateY(xd,yd,zd, 0.03); xd=rc.getPtX(); yd=rc.getPtY(); zd=rc.getPtZ();
461
範例76 續5 117 rc.rotateY(xe,ye,ze, 0.03);
範例76 續5 rc.rotateY(xe,ye,ze, 0.03); xe=rc.getPtX(); ye=rc.getPtY(); ze=rc.getPtZ(); rc.rotateY(xf,yf,zf, 0.03); xf=rc.getPtX(); yf=rc.getPtY(); zf=rc.getPtZ(); rc.rotateY(xg,yg,zg, 0.03); xg=rc.getPtX(); yg=rc.getPtY(); zg=rc.getPtZ(); rc.rotateY(xh,yh,zh, 0.03); xh=rc.getPtX(); yh=rc.getPtY(); zh=rc.getPtZ(); P1.vectNormal(xd, yd, zd, xa, ya, za, xe, ye, ze); P2.vectNormal(xc, yc, zc, xd, yd, zd, xh, yh, zh); P3.vectNormal(xb, yb, zb, xc, yc, zc, xg, yg, zg); P4.vectNormal(xa, ya, za, xb, yb, zb, xf, yf, zf); P5.vectNormal(xc, yc, zc, xb, yb, zb, xa, ya, za); P6.vectNormal(xh, yh, zh, xe, ye, ze, xf, yf, zf);
462
範例76 續6 131 xam = (int)pr.projectPtX(xa,ya,za);
範例76 續6 xam = (int)pr.projectPtX(xa,ya,za); yam = (int)pr.projectPtY(xa,ya,za); xbm = (int)pr.projectPtX(xb,yb,zb); ybm = (int)pr.projectPtY(xb,yb,zb); xcm = (int)pr.projectPtX(xc,yc,zc); ycm = (int)pr.projectPtY(xc,yc,zc); xdm = (int)pr.projectPtX(xd,yd,zd); ydm = (int)pr.projectPtY(xd,yd,zd); xem = (int)pr.projectPtX(xe,ye,ze); yem = (int)pr.projectPtY(xe,ye,ze); xfm = (int)pr.projectPtX(xf,yf,zf); yfm = (int)pr.projectPtY(xf,yf,zf); xgm = (int)pr.projectPtX(xg,yg,zg); ygm = (int)pr.projectPtY(xg,yg,zg); xhm = (int)pr.projectPtX(xh,yh,zh); yhm = (int)pr.projectPtY(xh,yh,zh);
463
範例76 續7 147 xas = vTs.viewX_To_screenX(xam);
範例76 續7 xas = vTs.viewX_To_screenX(xam); yas = vTs.viewY_To_screenY(yam); xbs = vTs.viewX_To_screenX(xbm); ybs = vTs.viewY_To_screenY(ybm); xcs = vTs.viewX_To_screenX(xcm); ycs = vTs.viewY_To_screenY(ycm); xds = vTs.viewX_To_screenX(xdm); yds = vTs.viewY_To_screenY(ydm); xes = vTs.viewX_To_screenX(xem); yes = vTs.viewY_To_screenY(yem); xfs = vTs.viewX_To_screenX(xfm); yfs = vTs.viewY_To_screenY(yfm); xgs = vTs.viewX_To_screenX(xgm); ygs = vTs.viewY_To_screenY(ygm); xhs = vTs.viewX_To_screenX(xhm); yhs = vTs.viewY_To_screenY(yhm); repaint(); try{Thread.sleep(150);} catch(InterruptedException e) {;} 166 } }
464
範例76 續8 168 public void paint(Graphics g) {
範例76 續8 168 public void paint(Graphics g) { g.setColor(new Color(225,225,225)); g.drawLine(xas, yas, xbs, ybs); g.drawLine(xbs, ybs, xcs, ycs); g.drawLine(xcs, ycs, xds, yds); g.drawLine(xds, yds, xas, yas); g.drawLine(xes, yes, xfs, yfs); g.drawLine(xfs, yfs, xgs, ygs); g.drawLine(xgs, ygs, xhs, yhs); g.drawLine(xhs, yhs, xes, yes); g.drawLine(xas, yas, xes, yes); g.drawLine(xbs, ybs, xfs, yfs); g.drawLine(xcs, ycs, xgs, ygs); g.drawLine(xds, yds, xhs, yhs); g.setColor(new Color(0,0,0));
465
範例76 續9 183 if (P1.getHidden() > 0.0) {
範例76 續9 if (P1.getHidden() > 0.0) { g.drawLine(xds, yds, xas, yas); g.drawLine(xas, yas, xes, yes); g.drawLine(xes, yes, xhs, yhs); g.drawLine(xhs, yhs, xds, yds); } if (P2.getHidden() > 0.0) { g.drawLine(xcs, ycs, xds, yds); g.drawLine(xds, yds, xhs, yhs); g.drawLine(xhs, yhs, xgs, ygs); g.drawLine(xgs, ygs, xcs, ycs); } if (P3.getHidden() > 0.0) { g.drawLine(xbs, ybs, xcs, ycs); g.drawLine(xcs, ycs, xgs, ygs); g.drawLine(xgs, ygs, xfs, yfs); g.drawLine(xfs, yfs, xbs, ybs); }
466
範例76 續10 201 if (P4.getHidden() > 0.0) {
範例76 續10 if (P4.getHidden() > 0.0) { g.drawLine(xas, yas, xbs, ybs); g.drawLine(xbs, ybs, xfs, yfs); g.drawLine(xfs, yfs, xes, yes); g.drawLine(xes, yes, xas, yas); } if (P5.getHidden() > 0.0) { g.drawLine(xcs, ycs, xbs, ybs); g.drawLine(xbs, ybs, xas, yas); g.drawLine(xas, yas, xds, yds); g.drawLine(xds, yds, xcs, ycs); } if (P6.getHidden() > 0.0) { g.drawLine(xhs, yhs, xes, yes); g.drawLine(xes, yes, xfs, yfs); g.drawLine(xfs, yfs, xgs, ygs); g.drawLine(xgs, ygs, xhs, yhs); 218 } }}
467
19-3 視角誤差 如圖19-3,當我們沿Z軸垂直觀看3D圖案時,是以N1為法線(如圖)。但事實上、我們是以N2為法線(如圖),故在視覺上必有一視角誤差α,隨Ø角與視距之變動而變動,因此在消除隱藏線的設計上需作些許修正。
468
圖19-3
469
19-4 修正視角誤差 於前節(19-2-2範例76) 最後兩個執行結果圖案,當某平面已旋轉到立方形之背面,且已達隱藏線部位時,按理我們不應再看到該平面有深色線段,其實則不然,究其原因、正如19-3節所討論的視角誤差,因並非垂直視線,平面部份看似已旋轉至背面,其實其法線向量之Nz仍大於0,亦即若以垂直視線觀看,部份尚未旋轉至背面。
470
19-5 光影變化 於前節、我們以Nz值為立方形之遮蓋判別式,本節仍再以其為光影之亮度條件式。在不同的環境有不同的Nz值,以本章19-4節範例77之觀測為例、可見平面之Nz介於2400與10,000之間,假設光線是從正面射入,則Nz值愈大、該平面應愈為明亮。
471
19-6 習題(Exercises) 01、立體物件圖像之顯示與平面物件圖像之顯示有何不同? 02、何謂法線向量?
03、如何求取多邊形之法線向量? 04、設有一個由向量U與V組成之平面,則該平面之法線為何? 05、在視覺上,法線代表何種意義? 06、如何以法線設定隱藏條件? 07、為何要作視角修正? 08、試修改範例78,消除其影像閃爍。
472
第六篇 Java Applet 與網頁
473
第二十章 第一個Java Applet 20-1 簡介 20-2編輯Java Applet與HTML程式 20-3網站網頁應用
網站架設 查驗本機之IP 網路瀏覽器 20-4 習題(Exercises)
474
20-1 簡介 昇陽開發Java Applet用於嵌入在網頁應用上,當網頁被使用者瀏覽時,網頁的文字、影像圖案,隨同Applet自動經由網路執行,具有互動功能。也即、當動畫在Server端網站建立完成後,使用者可在網路之任一台Client端電腦開啟瀏覽器,鍵入該網站網址,以鍵盤或滑鼠從Client端互動控制Server端之動畫。
475
20-2編輯Java Applet與HTML程式
在網路上執行Java Applet程式,須配合HTML程式之帶引,亦即是利用HTML之網路能力作網路螢幕畫面上之安排(Screen Manager)。本節將介紹如何編輯一個最基礎的Java Applet與HTML程式。
476
範例79:設計檔案Ex20_2.java、Ex20_2.html,其功能為解釋Java Applet網路執行應用。
檔案firstApplet.java:(編輯於記事本,功能為印出字串) 01 import java.awt.Graphics; 02 import java.applet.*; 03 public class firstApplet extends Applet { 04 String message = "My firstApplet WWW Program"; 05 public void paint(Graphics g){ g.drawString(message,100,45); 07 } 08 }
477
範例79 續 檔案Ex20_2.html:(編輯於記事本,功能為帶引Applet.class於網路執行) 09 <HTML>
範例79 續 檔案Ex20_2.html:(編輯於記事本,功能為帶引Applet.class於網路執行) 09 <HTML> 10 <BODY BGCOLOR="#eeeeee"> 11 <CENTER> 12 <APPLET CODE="firstApplet" WIDTH=350 HEIGHT=350> 13 </APPLET> 14 </HTML>
478
20-3網站網頁應用 於完成Java Applet與HTML程式之編輯後,須在WinXP作業系統建立網站虛擬目錄,將設計內容以網頁形式傳播出去,亦即所謂 “架網站”。
479
20-4 習題(Exercises) 01、在影像圖案顯示上,框架(Frame) 與瀏覽器(Browser) 之功能有何差異?
02、Java Applet程式需配合HTML程式使用,試述連接兩者之程式碼為何? 03、如何以WinXP作業系統建立網站? 04、於任意網路電腦開啟瀏覽器後,為使用新建網站之網頁,應如何鍵入網址?
480
第二十一章 基礎圖文處理 21-1 簡介 21-2 文字處理 21-3 圖案繪製 21-4 圖檔引用 21-5 習題(Exercises)
481
21-1 簡介 參考本書第二、三章,我們曾以框架環境執行圖文處理,本章再以網路瀏覽器執行Java Applet程式作基礎圖文處理,讀者可觀察兩者間之異同,前者強調本機執行,後者強調網路廣播。
482
21-2 文字處理 參考第二章,設計範例80,執行文字處理,建立Java Applet程式,用於印出字串;另建立HTML程式,用於帶引於網路執行(參考第二十章)。
483
範例80:設計檔案Ex21_2.java、Ex21_2.html,其功能為解釋Java Applet於網路執行文字處理。
01 import java.awt.Graphics; 02 import java.awt.Font; 03 import java.awt.Color; 04 import java.applet.*; 05 public class Ex21_2 extends Applet { 06 Font messageFont1 = new Font("TimesRoman", Font.PLAIN, 30); 07 Font messageFont2 = new Font("新細明體", Font.PLAIN, 30); 08 Font messageFont3 = new Font("標楷體", Font.PLAIN, 30); 09 String messageEnglish = "English test string"; 10 String messageChinese = "中文測試字串";
484
範例80 續1 11 public void paint(Graphics g) { 12 g.setFont(messageFont1);
範例80 續1 11 public void paint(Graphics g) { 12 g.setFont(messageFont1); 13 g.setColor(Color.red); 14 g.drawString(messageEnglish, 10, 50); 15 g.setFont(messageFont2); 16 g.setColor(Color.green); 17 g.drawString(messageChinese, 10, 100); 18 g.setFont(messageFont3); 19 g.setColor(Color.blue); 20 g.drawString(messageChinese, 10, 150); 21 } 22 }
485
範例80 續2 檔案Ex21_2.html:(編輯於記事本,功能為帶引Ex21_2.class於網路執行) <HTML>
範例80 續2 檔案Ex21_2.html:(編輯於記事本,功能為帶引Ex21_2.class於網路執行) <HTML> <BODY BGCOLOR="#eeeeee"> <CENTER> <APPLET CODE="Ex21_2" WIDTH=350 HEIGHT=350> </APPLET> </HTML>
486
21-3 圖案繪製 參考第三章,設計範例81,執行圖案繪製,建立Java Applet程式,用於繪製基礎圖形;另建立HTML程式,用於帶引於網路執行(參考第二十章)。
487
範例81:設計檔案Ex21_3.java、Ex21_3.html,其功能為解釋Java Applet於網路執行圖案繪製。
01 import java.awt.Graphics; 02 import java.applet.Applet; 03 public class Ex21_3 extends Applet { 04 public void paint(Graphics g) { 05 g.drawLine(100,35,250,35); 06 g.drawRect(60,70,100,50); 07 g.fillRect(200,70,100,50); 08 g.drawOval(30,160,80,60); 09 g.drawOval(140,160,80,60); 10 g.drawRect(140,160,80,60); 11 g.fillOval(250,160,80,60); 12 g.drawArc(30,260,80,60,10,90); 13 g.drawArc(140,260,80,60,10,90); 14 g.drawRect(140,260,80,60); 15 g.fillArc(250,260,80,60,10,90); 16 } 17 }
488
21-4 圖檔引用 參考第四章,設計範例82,執行圖檔引用,建立Java Applet程式,用於顯示圖檔;另建立HTML程式,用於帶引於網路執行(參考第二十章)。
489
範例82:設計檔案Ex21_4.java、Ex21_4.html,其功能為解釋Java Applet於網路執行圖檔引用。
01 import java.awt.Graphics; 02 import java.awt.Image; 03 import java.applet.Applet; 04 public class Ex21_4 extends Applet { 05 Image Img; 06 public void init() { 07 Img = getImage(getDocumentBase(), "pic.JPG"); 08 } 09 public void paint(Graphics g) { 10 g.drawImage(Img, 170, 170, this); 11 } 12 }
490
21-5 習題(Exercises) 01、試以Java Applet設計多邊形繪製。
02、無論是長方形、橢圓形、弧形、或圖檔,其繪製座標在圖案的那個部位? 03、當設計Java Applet時,其讀取圖檔之程式碼為何?
491
第二十二章 動畫與事件(Animation and Events)
22-1 簡介 22-2 執行緒工作流程 22-3 動畫設計 22-4 滑鼠事件 22-5 鍵盤事件 22-6 習題(Exercises)
492
22-1 簡介 於框架環境,本書已於第五章討論框架動畫,於第七章討論框架滑鼠事件,於第八章討論框架鍵盤事件。本章將以網路瀏覽器環境再探討動畫與事件,我們將看到遠處Client端如何以事件(Events) 指揮Server端之圖案。
493
於2-3節,我們曾談到框架執行緒工作流程,並描述流程之格式。本章將就Applet與網頁瀏器之環境,討論其執行緒工作流程,並描述流程之格式。
22-2執行緒工作流程 於2-3節,我們曾談到框架執行緒工作流程,並描述流程之格式。本章將就Applet與網頁瀏器之環境,討論其執行緒工作流程,並描述流程之格式。
494
如同2-3節有框架環境流程格式,Applet網頁瀏器環境也有流程格式:
01 import java.awt.*; 02 import java.applet.*; 03 public class testProgram extends Applet implements Runnable { 04 … … … 05 public void init () { … … … 07 } 08 public void start() { … … … new Thread(this).start(); 11 } 12 public void run () { … … … repaint(); 15 } 16 public void paint(Graphics g) { … … … 18 } 19 }
495
22-3動畫設計 參考範例18,設計Applet網頁瀏器環境單幅動畫之應用,並考量到當影像圖案移動觸及邊線時,立即折返移動。
496
範例83:設計檔案Ex22_3_1.java、Ex22_3_1.html,其功能為解釋Java Applet於網路執行單幅動畫之應用。
01 import java.awt.*; 02 import java.applet.*; 03 public class Ex22_3_1 extends Applet implements Runnable { 04 int x=0, y=100; 05 int dx=5, dy=5; 06 Image img; 07 public void init() { img = getImage(getDocumentBase(), "fly.gif"); 09 } 10 public void start() { new Thread(this).start(); 12 }
497
範例83 續 13 public void run() { 14 while(true) { 15 x = x + dx;
範例83 續 13 public void run() { while(true) { x = x + dx; y = y + dy; repaint(); if(x<=0) dx = 5; else if((x + 50) >= getWidth()) dx = -5; if(y<=0) dy = 5; else if((y + 50) >= getHeight()) dy = -5; try{Thread.sleep(250);} catch(InterruptedException e) {;} } 25 } 26 public void paint(Graphics g) { g.drawImage(img, x, y, this); 28 } 29 }
498
22-4滑鼠事件 回顧第六章6-6節,每次發生滑鼠事件時,我們即可立即讀取該事件之來源(Source)、識別碼(ID)、發生時間(When)、x軸座標、y軸座標。因這些資訊是伴隨事件的發生而產生,我們可利用這些資訊,導引我們想要的後續執行動作,如移動(Moved)、拖曳(Dragged)、選擇(Selected)、隨動(Followed) 等。本節再以此用於Applet網頁設計。
499
22-5鍵盤事件 當按下鍵盤鍵時,鍵盤事件隨之發生,此時可讀取按鍵的資訊,不同的鍵有不同之KeyCode,我們可利用這些不同的資訊來執行不同的動作。
500
22-6 習題(Exercises) 01、Applet網頁瀏器環境之設計流程格式為何?
Similar presentations