閱讀導航:
指法練習Applet遊戲介紹
我首先介紹一下這個遊戲的界面,這個Applet共由11個組件組成,左邊的主界面是畫布Canvas組件,被分隔爲10個欄。程序會隨機在這些欄中產生下落的字母,用戶按下匹配的字母鍵盤按鍵後,即爲擊中。
程序框架
打開JBuilder2005,創建一個工程(File->New...->Project->雙擊Project頁中的Project圖標),我們將工程名取爲game,然後利用下面的步驟,調用Applet嚮導生成TypeTrainApplet。
主體程序
遊戲界面中每一個下落的字母對應一個字母下落線程DropCharThread的實例,這個線程負責將一個隨機的字母在指定的畫布欄中從上至下落下。在TypeTrainApplet內部定義這個線程類,之所以要將其作爲成員內部類來定義,是因爲這樣可以減少類和類之間的通信,降低調用接口的複雜度。
控制遊戲
至此,我們已經完成了Applet主要功能的開發,剩下的工作是如何通過按鈕控制遊戲。在編寫控制代碼之前,先爲開始/暫停按鈕(jButton1)和停止按鈕(jButton2) 裝飾一下,再編寫控制代碼。
打包並進行數字簽名
瀏覽器對Applet的數字簽名支持並沒有一個統一的標準,但是一些著名的瀏覽器如IE和Navigator對進行數字簽名的Applet都可以開放大部分的權限。要對Applet進行數據簽名必須先將Applet類和資源打成一個JAR包。
使用插件下載JRE
如果客戶端遊覽器還未安裝JRE或已安裝的JRE版本低於你Applet的要求,或瀏覽器自帶的JRE不是Sun公司標準的Applet,你Applet都可能無法正常運行。可以通過JDK自帶的HtmlConverter.exe工具對帶Applet的HTML文件進行轉換。
總結
我們講述瞭如何在JBuilder開發一個簡單的Applet指法練習遊戲程序的過程,雖然這個遊戲在功能上屬於不敢見公婆型,但它涵蓋了Applet開發的大部分內容和技巧。我們特在Applet中設置了一個不安全的功能:在客戶機器中保存文件,瀏覽器事先毫不留情地阻截了它,爾後我們通過數字簽名技術曉之以情,動之以理"說服"了瀏覽器取消安全限制。
大千世界,紛繁複雜,客戶端瀏覽器的JRE版本和廠家百家爭鳴,百花齊放,爲了使我們的Applet能夠在Sun標準的JRE1.5.0的版本上運行,我們動用了JDK自帶的轉換器對原html進行轉換,這樣標準的JER1.5.0將作爲插件的形式下載並安裝以支持這個難伺候Applet。
指法練習Applet遊戲介紹
1、界面及功能
指法練習的Applet遊戲的界面如下圖所示:
圖 1 指法練習的用戶界面 |
如上圖所示,這個Applet共由11個組件組成,左邊的主界面是畫布Canvas組件,被分隔爲10個欄。程序會隨機在這些欄中產生下落的字母,用戶按下匹配的字母鍵盤按鍵後,即爲擊中,相應的字母將消失,正確數遞增1;字母落到畫布底端後,還沒有被擊中,失敗數遞增1;每產生一個下落的字母,總數遞增1。
遊戲提供了3個JButton的按鈕,分別用於控制遊戲的開始/暫停、結束以及保存遊戲成績。在未啓動遊戲前第一個按鈕顯示爲三角箭頭的圖標,點擊後啓動遊戲,隨後按鈕圖標切換爲暫停的圖標。而第二個爲停止按鈕,其上顯示結束的圖標,當遊戲處於運行或暫停的狀態時,點擊該按鈕將停止遊戲以便重新開始。而第三個按鈕保存遊戲的成績到客戶端的D:/result.txt文件中。
整個界面採用BorderLayout佈局管理器,畫布位於BorderLayout.CENTER區,而右邊的控制檯JPanel位於BorderLayout.EAST區。控制檯的JPanel採用GridLayout佈局管理器。
2、程序組成
每個下落的字母對應一個線程實例,稱爲DropCharThread線程,它由一個產生器定時產生出來,這個產生器也是一個線程稱爲GenerateDropThread線程,下面是這個Applet的類圖:
圖 2 Applet類圖 |
TypeTrainApplet類繼承了JApplet,是遊戲的主類,DropCharThread和GenerateDropThread都是其內部類,後兩者都繼承Thread,以線程的方式運行,下面對這3個類重要的成員變量和成員方法進行說明。
1).TypeTrainApplet
繼承JApplet的Applet主類,負責構造用戶界面、響應用戶操作事件、更新遊戲統計數據等。
· 重要成員變量
統計數據變量
volatile int totalCount = 0;//生產下落字母的總數。 volatile int rightCount = 0;//正確擊中的字母數。 volatile int errorCount = 0;//未被擊中且到達畫布底部的字母數。 |
這3個變量用volatile作了修飾,這是因爲這3個變量會被每個字母下落線程更改,爲防止各個線程通過各自的緩存更改變量值造成線程間值的不同步,需要將這3個變量設置爲volatile的類型,這樣這些變量的更改值對於其他線程馬上可見。
字母下落速率控制變量
private static int stepLen = 2; //每次下落的步長,即字母每移一步的象素。 private static int stepInterval = 50; //每兩步之間的時間間隔,以毫秒爲單位。 private static int columnCount = 10; //畫布被分隔爲多個欄 private static int generateInterval = 500; //創建一個新的下落字母線程的時間間隔,以毫秒爲單位 |
Applet通過通過這4個變量達到控制產生字母的快慢和字母下落的速度及欄數,可以進一步規劃這些值,以形成遊戲的難度級別。有鑑於此,我們特地將這些參數的值通過HTML的<Applet>參數傳入。這樣,只需要更改HTML的<applet>參數值就可以達到控制遊戲難度級別的目標,而不需更改Applet程序。
其他
int colWidth; //下落字母每欄的寬度,在運行期才獲取這個變量值,它由畫布的寬度和欄數決定。 volatile char pressKeyChar; //記錄當前按鍵對應的字母。 int statusCode = 0; //記錄遊戲所處的狀態,其中1:運行態、,2:暫停態 0:停止態。 |
· 重要成員方法
private void drawResult()//將統計結果寫到界面的對應JLabel中。 private void resetGame()//重置遊戲現場 |
2) DropCharThread
是一個線程,將一個隨機的字母在畫布的特定欄中往下落下,並實時檢測是否被擊中,如果擊中馬上消失,否則一直落到畫布的底部。
·重要成員變量
char c; //對應的字母 int colIndex; //對應畫布的欄序號,第一欄爲1,第二欄爲2,以此類推 int x, y; //當前字母在畫布中的座標 |
·動作類型常量
private static final int ACTION_DRAW_FONT = 1; //表示畫字符 private static final int ACTION_CLEAR_FONT = 2; //表示清除字符 |
不應當直接用1或2表示動作的類型,而應該定義一個更有意義的常量,這樣不但於理解,也便於以後的維護。
·重要成員方法
public DropCharThread(char c, int colIndex)//構造函數,傳入特定的字母和欄序號 private void draw(int actionType)//在畫布中特寫的位置上畫字母 |
3) GenerateDropThread
·重要成員變量
Random random = new Random(); //負責產生隨機數 |
·重要成員方法
private char getRandomChar()//獲取一個隨機的字母 |
負責定時產生一個DropCharThread線程實例,通過generateInterval成員變量控制產生DropCharThread線程實例的頻率。
當遊戲玩家點擊Applet的開始按鈕後,Applet將啓動遊戲,這3個類之間的交互關係可以通過以下的順序圖來描述,如下圖所示:
圖 3 開始遊戲的順序圖 |
1)當用戶按下Applet的開始按鈕後激發一個事件。
2) Applet響應這個事件,調用事件響應方法,在方法中實例化一個GenerateDropThread線程,並啓動這個線程。
3) GenerateDropThread線程定時產生一個DropCharThread線程,並讓賦予一個隨機的字母和欄序號。
4)DropCharThread線程啓動,將字母在特定的欄中從上至下落下。
程序框架
1、利用嚮導生成Applet
首先創建一個工程(File->New...->Project->雙擊Project頁中的Project圖標),我們將工程名取爲game,然後利用下面的步驟,調用Applet嚮導生成TypeTrainApplet。
1) 啓動Applet嚮導
File->New...->Web->雙擊Web頁中Applet的圖標啓動共4步的Applet嚮導。
2) 嚮導第1步,填寫Applet的詳細信息。
圖 4 Applet嚮導第1步 |
·ClassName:Applet的類名,填入TypeTrainApplet
·Package:包名,接受默認值
·Base Class:父類,有兩個選項,一個是java.applet.Applet,另一個是javax.swing.JApplet。前者以AWT爲基礎,而後者以Swing爲基礎。如果客戶端瀏覽器的JRE版本很低,且你不希望客戶下載額外的插件,則需要考慮用java.applet.Applet,且不能應用高版本JDK中的特性,這裏我們用javax.swing.JApplet。
·Generate header comments:在產生Applet代碼時,產生類標題頭的註釋說明,你大可不必生成這些註釋。
·Can run standalone:是否將Applet設置爲可獨立運行,如果勾選,JBuilder爲其生成了一個main函數,這樣就可以在脫離瀏覽器或AppletViewer的情況下,像一般可運行類一樣運行這個Applet中的功能,我們不勾選它。
·Generate standard methods:是否生成Applet的標準函數,大家都知道Applet通過4個函數管理着Applet的生命週期,它們分別是init()、start()、stop()、destroy()。如果不勾選這個選項,嚮導只會生成init()方法,而其他3個方法不會生成。在我們的例子中,需要用到其他3個方法,所以需要勾選。
按Next到下一步。
3) 定義Applet的參數
Applet的參數是指通過網頁中<applet>標籤的<param>指定的參數值,這些值可以在Applet類中引用到。這樣就允許在不改變Applet程序的情況下,僅通過網頁中<applet>屬性值的更改控制Applet的表現。我們在這一步中爲Applet設置4個控制變量參數,如下圖所示:
圖 5 爲Applet設置參數 |
這一步的設置,不但爲網頁生成了參數聲明,還爲Applet程序生成了從網頁獲取參數值的方法,在Applet初始化時,即將網頁中的參數值賦給Applet的成員變量。
點擊Add Parameter新增一行,聲明一個新的參數,其中Name*爲網頁中參數的名字,而Variable*爲Applet類成員變量名,通過Type*欄設置成員變量的數據類型。你還可以爲參數在Default欄中指定一個默認的值,在Desc中給出描述說明信息,其中帶*的欄是必填的欄。
點擊Next到下一步。
4) 設置包含這個Applet的網頁
在這一步裏,我們指定包含這個Applet網頁的<applet>標籤的一些屬性,如下圖所示:
圖 6 設置引用Applet的網頁 |
JBuilder會生成一個引用Applet的HTML網頁,網頁名字和Applet的類名相同,網頁通過<applet>標籤引用Applet,網頁的標題及<applet>屬性值在這一步中設置。
我們除將Height從默認的300調整爲400,其他的都保持不變。按Next到最後一步。
5) 創建運行配置項
在這一步裏JBuilder允許你決定是否爲Applet生成一個運行配置項,運行配置項是允許你配置運行時的有關屬性,如運行的入口類,在運行時是否重新編譯等,你也可以通過Project->Project Properties...->Run來維護運行配置項。
圖 7 設置Applet運行配置信息 |
點擊Finish完成Applet的創建嚮導。此時JBuilder爲這個Applet生成了兩個文件,一個是TypeTrainApplet.java程序文件,而另一個是引用這個Applet的TypeTrainApplet.html網頁。我們來看這兩個文件的主要結構。
代碼清單 1 TypeTrainApplet.html 引用Applet的網頁表
1. <html> 2. <head> 3. <meta http-equiv="Content-Type" content="text/html; charset=GBK"> 4. <title> 5. HTML Test Page 6. </title> 7. </head> 8. <body> 9. game.TypeTrainApplet will appear below in a Java enabled browser.<br> 10. <applet 11. codebase = "." 12. code = "game.TypeTrainApplet.class" 13. name = "TestApplet" 14. width = "400" 15. height = "400" 16. hspace = "0" 17. vspace = "0" 18. align = "middle" 19. > 20. <param name = "stepLen" value = "2"> 21. <param name = "stepInterval" value = "50"> 22. <param name = "columnCount" value = "10"> 23. <param name = "generateInterval" value = "500"> 24. </applet> 25. </body> 26. </html> |
在嚮導第2步所設置的Applet參數悉數在網頁中定義(第20~23行),在嚮導第3步中設置的Applet屬性反映在第11~18行中。
Applet類的TypeTrainApplet.java文件代碼如下所示:
代碼清單 2 TypeTrainApplet.java
1. package game; 2. 3. import java.awt.*; 4. import java.awt.event.*; 5. import java.applet.*; 6. import javax.swing.*; 7. 8. public class TypeTrainApplet1 extends JApplet { 9. boolean isStandalone = false; 10. BorderLayout borderLayout1 = new BorderLayout(); 11. int stepLen; 12. int stepInterval; 13. int columnCount; 14. int generateInterval; 15. 16. //Get a parameter value 17. public String getParameter(String key, String def) { 18. return isStandalone ? System.getProperty(key, def) : 19. (getParameter(key) != null ? getParameter(key) : def); 20. } 21. 22. //Construct the applet 23. public TypeTrainApplet1() { 24. } 25. 26. //Initialize the applet 27. public void init() { 28. try { 29. stepLen = Integer.parseInt(this.getParameter("stepLen", "2")); 30. } catch (Exception e) { 31. e.printStackTrace(); 32. } 33. try { 34. stepInterval = Integer.parseInt(this.getParameter("stepInterval", 35. "50")); 36. } catch (Exception e) { 37. e.printStackTrace(); 38. } 39. try { 40. columnCount = Integer.parseInt(this.getParameter("columnCount", 41. "10")); 42. } catch (Exception e) { 43. e.printStackTrace(); 44. } 45. try { 46. generateInterval = Integer.parseInt(this.getParameter( 47. "generateInterval", "500")); 48. } catch (Exception e) { 49. e.printStackTrace(); 50. } 51. try { 52. jbInit(); 53. } catch (Exception e) { 54. e.printStackTrace(); 55. } 56. } 57. 58. //Component initialization 59. private void jbInit() throws Exception { 60. this.setSize(new Dimension(400, 300)); 61. this.getContentPane().setLayout(borderLayout1); 62. } 63. 64. //Get Applet information 65. public String getAppletInfo() { 66. return "Applet Information"; 67. } 68. 69. //Get parameter info 70. public String[][] getParameterInfo() { 71. java.lang.String[][] pinfo = { { 72. "stepLen", "int", "每次下落的步長"}, { 73. "stepInterval", "int", "每移動一個像素的間隔時間,以毫秒爲單位"}, { 74. "columnCount", "int", 分成多少列"}, { 75. "generateInterval", "int", 分成多少列"}, 76. }; 77. return pinfo; 78. } 79. 80. //static initializer for setting look & feel 81. static { 82. try { 83. //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 84. } catch (Exception e) { 85. } 86. } 87. } |
其中第11~14行定義了對應嚮導第2步所定義的參數變量,第70~78行獲取參數的註釋信息。在Applet通過init()初始化,在init()中調用方法將網頁中參數的值賦給Applet類的成員變量,以初始化變量的值。在第59~62行設定了Applet的大小,其值應該和網頁中<applet>的width和height屬性值一致。
2、設計Applet界面
打開TypeTrainApplet.java,切換到Design視圖頁面中,設計如下的Applet界面
圖 8 Applet界面設計 |
承繼JApplet的Applet其默認的佈局管理器是BorderLayout,首先在其東區(BorderLayout.EAST)放置一個infoPnl的JPanel組件,將infoPnl的佈局管理器設置爲GridLayout,9行1列,即在jbInit()方法中通過infoPnl.setLayout(new GridLayout(9, 1));設置。再在infoPnl上依次放置9個組件,這些組件的類型和用途分別說明如表所示:
表 2 組件說明
組件名 | 組件類型 | 用途 |
jButton1 | JButton | 開始/暫停按鈕 |
jButton2 | JButton | 結束按鈕 |
jButton3 | JButton | 保存按鈕 |
totalLbl_1 | JLabel | 總數標籤 |
totalLbl_2 | JLabel | 總數值顯示標籤 |
rightLbl_1 | JLabel | 正確數標籤 |
rightLbl_2 | JLabel | 正確數值顯示標籤 |
errorLbl_1 | JLabel | 錯誤數標籤 |
errorLbl_2 | JLabel | 錯誤數值顯示標籤 |
你只要從設計器窗口左邊的組件庫中用鼠標將組件拖到設計窗口的相應位置,並放開鼠標就可以了相應生成用戶界面的代碼了。
由於畫布組件沒有java.awt.Canvas並沒有列在JBuilder的組件面板中,你可以直接通過編碼的方式把畫布組件放到Applet的中區(BorderLayout.CENTER),了可以點擊JBuilder可視化設計器的Bean選擇器圖標 (位於組件庫的上方),在彈出的Bean Chooser對話框中選擇java.awt.Canvas,如下圖所示:
圖 9 通過Bean選擇器選擇Canvas組件 |
在Bean Chooser對話框中有一棵以包組織的類樹,選擇Canvas類,再點擊OK按鈕,在可視化設計器的Bean選擇器的下拉菜單中將出現java.awt.Canvas的類,如下圖所示:
圖 10 Bean選擇器中的Canvas組件類 |
點擊下拉菜單中的java.awt.Canvas,鼠標移到Applet設計界面的中心點擊一下,一個畫布組件就被添加到Applet的中區去了。在組件樹中選中這個Applet中,將其命名爲canvas,並確認其Constaints屬性是CENTER(即位於中區)。
3、遊戲的統計數據
遊戲包括3個統計數據,即已產生字母的總數,被正確擊中的字母數及未被擊中的字母數。需要有3個變量來保存這些統計數據,同時還需要一個方法,將數據寫到Applet界面的統計標籤組件中去。當用戶點擊開始按鈕時調用resetGame()方法將這些統計數據歸0。
我們在TypeTrainApplet中添加以下粗體的代碼。
代碼清單 3 統計數據
1. … 2. public class TypeTrainApplet extends JApplet 3. { 4. … 5. volatile int totalCount = 0;//總數計數器 6. volatile int rightCount = 0;//正確數計數器 7. volatile int errorCount = 0;//錯誤數計數器 8. public TypeTrainApplet() 9. {} 10. … 11. //將統計結果畫到界面上 12. private void drawResult() 13. { 14. totalLbl_2.setText("" + totalCount); 15. rightLbl_2.setText("" + rightCount); 16. errorLbl_2.setText("" + errorCount); 17. } 18. //重置現場 19. private void resetGame() 20. { 21. totalCount = 0; 22. rightCount = 0; 23. errorCount = 0; 24. drawResult(); 25. } 26. … 27. } |
drawResult()方法以下兩種情況下都應被調用:
·擊中一個字母。
·一個字母下落到底端。
而resetGame()方法在點擊開始按鈕後調用,將3個統計變量歸零,以便重新開始統計。