從 C++ 向 C# 遷移
John Kennedy
Microsoft Corporation
本頁內容
紙牌遊戲 | |
圖形和 SDE | |
聲音效果 |
與生活中的許多事情一樣,有時要學習某些知識的唯一方式就是親自去嘗試。當然,也有一些很顯著的例外。我可不建議通過這種方式去學習外科手術(哈哈),但是對於學習智能設備擴展 (SDE) 和用 C# 編寫適用於 Pocket PC 的程序,這絕對是一個很好的方式。
誠然,我們會越來越多地接觸到 Microsoft .NET Compact Framework,不管是在本專欄還是在實際的開發環節中,這一點不容置疑。更加不容置疑的是,C# 是 .NET Compact Framework 用得最多的一種語言。用 C# 開發了一些程序之後,我對它的易用性、靈活性和優雅得體有着越來越深的印象。當然,當前發行的 Beta 版在某些地方還存在着所謂的操作“欠佳”的情況,但這不久就會得到解決。我堅信,一旦您繼續瞭解一些新概念,您就會真正地喜歡使用 C# 和 .NET Compact Framework。
紙牌遊戲
我們以前在本專欄中討論過 XML Web 服務,但有些讀者希望我能夠使用智能設備擴展來創建其他一些連接性不大的編程項目。並不是每個軟件都需要連接到 Internet,因此這個新平臺中有些很強大的功能往往會被忽視。
所以在本月的專欄中,我們將演練一下如何開發一個適用於 Pocket PC 的更加傳統的應用程序 — 實際上是一個遊戲,它採用 C# 編寫並使用 .NET Compact Framework。使用 C# 進行開發與使用 eMbedded Visual C++ 進行開發之間存在着一些很明顯的差異,對於在編寫這個程序時所碰到的一些很明顯的問題,我希望拿出來與大家分享一下。
所要討論的遊戲非常簡單。一箇舊版本的紙牌匹配遊戲 — Pelmanism。然而,要闡明一些重要的區別和方法還是很複雜的,所以即使您認爲它很容易,也不妨試試。如果您要加載並試玩,可以下載源代碼。
這個遊戲的設計十分簡單,正如您在以下屏幕截圖中所看到的。(這些照片是我的團隊(即 Visual C++ 小組)的所有成員。您可能會認爲,現在當我找他們要求拍照時,他們會更加謹慎了。當然,我更願意使用 Drew Barrymore 的照片,但有着嚴格的流程……)。屏幕上有十六張紙牌,玩家通過點擊指示筆來“翻轉它們”。如果紙牌相匹配,它們就會從屏幕中消失。我將留給讀者一個練習 — 添加代碼以便統計翻轉次數、保存成績等等。是的,這表明我在偷懶。不過要知道,我也忙乎了一天呢!第一個人在問我花多少時間在 Xbox 上玩 Halo 時懷着一種很鄙夷的目光。
圖 1. 您知道,我被 Xbox 團隊逼着討論這張圖的許可權。當然,實際上這是個謊言。
圖形和 SDE
C# 程序與 eMbedded Visual C++ 的工作方式有着很大的不同,它從 Visual Basic 繼承了窗體 的概念。從技術角度看,改變並不大,但我希望能夠引起您的注意。您可能也知道,窗體其實就是一個頁面,您可以在其中添加控件、顯示文本、從中接收屏幕點擊消息等等。當我們在 Visual Studio .NET 中創建一個默認項目時,系統會自動爲我們提供一個默認窗體。
圖 2. 一個空白窗體,準備讓我們展現自己的想象力和才幹。
您可以發現,窗體的左邊是一組控件,我們可以將它們拖放到窗體中,也就是拖放到我們的程序中。與使用 eMbedded Visual C++ 工具箱相比,您會發現,編輯器中控件的可視化外觀與因此而創建的源代碼之間的聯繫更加緊密。試試一些控件並觀察代碼。Visual Studio 保持跟蹤的方式給人的印象很深。
一句話警告源代碼中有些地方不要手動進行更改。由於某種原因我忽視了這個告誡,當我在設計器中調整窗體時就丟失了代碼,所以要謹記。
可以在窗體中添加的一個控件是 PictureBox,這是一個顯示圖像的控件。我打算對遊戲所顯示的每張紙牌都使用一個 PictureBox。它與最初採用 C++ 編寫這個程序的方式有所不同。如果用 C++ 創建,我就需要創建窗口(現在我們稱之爲窗體)並獲得 HDC(顯示上下文句柄),然後使用 BitBlt 將一大堆圖形數據變爲實際的圖形。這次,我打算使用十六個 PictureBox 控件,並使用它們的方法來定義它們顯示什麼圖像。您將會看到,這種方式是相當得體的。
當然,知道我們要顯示十六張紙牌,您可能會試着將十六個 PictureBox 拖到窗體中,然後調整它們的大小並將它們排列整齊。這是個好主意,但通過編程方式處理每個控件很快就會變成一項很瑣碎的事情。更好的做法是創建一組 PictureBox,然後使用索引對它們進行處理,一會兒您將看到,我就是這樣做的。所以,儘管設計器十分有用,但我們並不一定要使用它來添加 PictureBox 控件。
讓我們來簡單地看一些源代碼。C# 程序在大部分地方很像 C++ 程序。比如函數、if() 語句、do/while 和大量圓括號。然而,也有一些細微(而非很顯著)的區別。
舉一些基本的例子,比如很好而又老舊的 #define 語句。我如何向您說明呢?它已經過時了。當然,它也沒有完全過時,只是做了點更改。在 C# 程序中,#define 只能用於創建標識符。您不能使用它來創建常量,比如 #define X_WIDTH 100。
瞭解這一點之後,我們再看一些數組聲明。需要二維整數數組嗎?不要再認爲是 int grid[4][4];,而應該是 int[,] grid = new int[4,4] ; 。
瞭解此信息之後,您應該能夠明白遊戲開頭的這段代碼有何用處:
圖 3. 通過編程方式創建控件很容易,而且它們在編輯器中顯示了所有方法以提示您下一步做什麼。
這段代碼聲明瞭一些數組:一個用於存儲玩紙牌時用到的 9 張不同的圖像,一個用於存儲可能存有紙牌的 16 個位置中的紙牌類型,一個用於存儲 PictureBox 控件本身。
然後,我們按相同的方式處理所有這些 PictureBox 對象,創建它們、將它們添加到窗體中,並調整它們的大小和位置。請注意應用於每個對象的方法和屬性(例如 Location)如何通過句點與對象分開。Visual Studio 集成開發環境 (IDE) 非常智能,一旦您鍵入句點,它就會彈出一個列表,其中包含您可以對這個對象執行的所有正確操作。這只是一個方面,說明了 C# 讓人感到編寫代碼是一件很愉快的事。
如果您也和我一樣,您就會喜歡在程序中添加 MessageBox 調用,以便隨時瞭解發生的事情。使用智能設備擴展也可以擁有這樣的能力:只需使用以下語法:
Messagebox.Show("Hello");
當然還有更多的選項,有關完整的列表,請參閱聯機幫助。
考慮圖像
想知道當我們玩紙牌時如何加載以顯示一些圖像嗎?我也很想知道,因爲 .NET Compact Framework 缺少 .NET Framework 的一些功能,連我首選的方法都沒有。不過它仍然十分簡單,如果您在通讀示例源代碼時稍加註意,就會發現它與 Image 對象有點關係。
下面是另一段代碼。這是一個函數,通過調用它,可以從磁盤(其實是內存,只要您明白我的意思)加載圖像,並將前面聲明的圖像數組中的元素分配給它。
圖 4. 從內存中加載圖像非常容易。
.bmp 文件是我在一個塗鴉程序中創建的圖像集合,並預先對它進行縮放以適合 PictureBox 控件的大小。我習慣於使用 .bmp 格式,不過該控件也支持其他常見的文件格式。
我相信您已經很熟悉 try/catch 異常處理了,它是一種可選方式,不過建議您養成使用它的習慣。如果圖像加載失敗,就會執行 catch() { } 中的代碼。這明顯能使代碼更加健壯可靠,不過如果能多做些事情(而不只是警告用戶程序將要終止),那就更有用了。
如果您使用小巧的 Pocket PC 模擬器(它附帶智能設備擴展)來試運行這個示例程序,您可能不知道如何將 .bmp 圖像文件從開發用的計算機移到模擬的 Pocket PC 中。讓我告訴你吧,因爲這個過程與早期的 Pocket PC 開發工具相比有了一些更改 — 您一猜就知道我對它有很深的印象。
將文件複製到模擬器
1. |
啓動模擬器(如果尚未啓動)。 |
2. |
從 Start 菜單中,選擇 Settings,然後選擇 System 選項卡。 |
3. |
單擊 About,然後單擊 DeviceID 標籤。 |
4. |
將設備名稱從 Pocket_PC 改成其他的名稱。 圖 5. 更改設備 ID,否則當模擬器與桌面計算機通信時會報錯。 |
5. |
現在啓動 Pocket PC 上的文件瀏覽器程序。 |
6. |
點擊網絡共享圖標。該圖標位於屏幕底部的最右邊。 |
7. |
輸入通過 LAN 訪問您的計算機時所需的詳細信息。 |
8. |
瀏覽至桌面計算機上的共享文件夾,然後選擇要複製的文件。 |
9. |
點擊 Pocket PC 上的文件並按住不放。 注 模擬器存在一個 bug,您可能需要按住鼠標鍵 30 秒鐘纔會看到紅點以及隨後的彈出菜單。 |
10. |
複製文件,返回到 Pocket PC 自己的目錄下並粘貼。 圖 6. 瀏覽至桌面計算機上的共享文件夾,然後複製文件並將其粘貼到您的(模擬的)Pocket PC。 |
這就是所有的步驟。
鄭重宣佈,這就是這個小程序的全部內容了。當然,我還添加了一些菜單,使用窗體設計器,要多簡單就有多簡單。有一件事令我考慮了許久,那就是如何處理用戶的指示筆點擊操作。
我對 Visual C++ 程序的理論分析是這樣的:佔據整個屏幕並接受鼠標按下的消息,解出 X 和 Y 座標,然後算出用戶點擊的紙牌。在這種情況下,每張紙牌都是一個控件,而每個控件都會單獨地響應鼠標按下的消息。我無法包含那種具有全局性質的“點擊屏幕並獲得消息”代碼。
然而,爲每個控件指定處理程序是很簡單的。這就是源代碼的第一個代碼塊中的行:
this.pictureBox[x,y].Click += new System.EventHandler(this.UserTapsScreen);
您可以看到,我對每個控件都添加了相同的函數 (UserTapsScreen)。這明顯有個疑問,那就是“在該函數中,我如何知道消息由哪個控件發出?”以下就是實現這一點的源代碼:
private void UserTapsScreen(object sender, System.EventArgs e) { int cardx = -1; int cardy = -1; // Find the card that send the message for (int x=0; x<4; x++) for (int y=0; y<4; y++) if (sender.Equals(pictureBox[x,y])) { cardx = x; cardy = y; y=4;x=4; }...
請注意,這段代碼使用了傳遞給 UserTapsScreen 函數的 sender 參數的一個屬性,以及它的 Equals 方法。
聲音效果
一個遊戲要是沒有聲音效果那就毫無價值。這就是我們下個月要介紹的內容。同時,請下載源代碼並試玩一下。如果您有任何問題,請隨時給我發送電子郵件。但是,如果您是專門發送垃圾郵件的公司,打算一天要給我發送十二次電子郵件來向我提供一些衣着、電子和色情信息,那就另當別論了!
John Kennedy 白天是 Visual C++ 小組的技術作家/編程人員;而夜晚,他享受着 Pocket PC 開發人員的祕密生活。
Larry Roof 創立了 larryroof.com,這是一家專門從事移動項目諮詢和 eMbedded Visual Basic、智能設備擴展和 SQL Server CE 方面培訓的公司。