送給初學者的禮物:遊戲程式設計起源連載二(轉)

post0發表於2007-08-12
送給初學者的禮物:遊戲程式設計起源連載二(轉)[@more@]

  三、建立視窗

  好訊息,建立視窗你所要做的只是呼叫一個CreateWindowEx()函式。壞訊息是,這個函式有好多的引數。以下便是函式原形:

  

  HWND CreateWindowEx(

  DWORD dwExStyle, // extended window style

  LPCTSTR lpClassName, // pointer to registered class name

  LPCTSTR lpWindowName, // pointer to window name

  DWORD dwStyle, // window style

  int x, // horizontal position of window

  int y, // vertical position of window

  int nWidth, // window width

  int nHeight, // window height

  HWND hWndParent, // handle to parent or owner window

  HMENU hMenu, // handle to menu, or child-window identifier

  HINSTANCE hInstance, // handle to application instance

  LPVOID lpParam // pointer to window-creation data

  );

  

  首先:函式的返回值。也就是函式的型別。是不是所有建立視窗用的函式的型別的討厭樣子都感覺親切了一點兒?不要緊,你會習慣的,肯定比你想象的速度要快。這裡返回的型別是HWND,是一個視窗的控制程式碼(控制程式碼就是視窗的識別符號)。你將把CreateWindowEx()的返回值傳遞給一個視窗的控制程式碼,就像一個引數一樣。現在,我們來琢磨一下這些引數,很多根據名字就知道它是幹什麼的了。

  

  ※ DWORD dwExStyle:擴充的視窗風格。你將很少使用擴充的視窗風格,所以多數時間你會把它設定為NULL。如果有興趣,查一下幫助檔案,可以一試由WS_EX_打頭的擴充風格。

  

  ※ LPCTSTR lpClassName:還記得你的視窗類的名稱嗎?再用一次。

  

  ※ LPCTSTR lpWindowName:將顯示在視窗的標題欄裡的簡短文字。

  

  ※ DWORD dwStyle:視窗的風格。它將允許你詳細的描繪你所要建立的視窗的風格。有很多風格你可以利用哦,都是以WS_打頭的,你可以利用(|)符號組合利用它們。我將在這兒介紹幾個常用的。

  

  ◎ WS_POPUP 指定一個彈出的視窗。

  

  ◎ WS_OVERLAPPED 指定一個具有標題欄和邊界的重疊視窗。

  

  ◎ WS_OVERLAPPEDWINDOW 指定一個具有所有標準控制元件的視窗。

  

  ◎ WS_VISIBLE 指定一個初始時可見的視窗。

  

  看得出,WS_OVERLAPPEDWINDOW是一個組合體。簡單的說,你可以按照如下規律:如果你要建立一個可以最大化、最小化、隨意改變大小等等地視窗,就選擇WS_OVERLAPPEDWINDOW;如果你只想要一個具有標題欄、可改變大小的視窗,就選擇WS_OVERLAPPED;如果你只想要一個光禿禿的視窗,就選擇WS_POPUP;如果你只想顯示一個黑色的大方框,可能你要用它寫一個全屏的遊戲,選擇WS_VISIBLE是沒錯的。

  

  ※ int x,y:你所要建立的視窗的左上角的座標。

  

  ※ int nWidth,nHeight:猜也猜到了,是視窗的長和高。

  

  ※ HWND hWndParent:指向父視窗的控制程式碼。你若想在視窗下再建立一個視窗,那麼第一個視窗就叫父視窗。我們先建立一個主視窗,所以設定為NULL,也就意味著Windows桌面是父視窗。

  

  ※ HMENU hMenu:這是用在視窗上的選單控制程式碼。若你學會建立和使用資源,即建立自己的選單,你可以用LoadMenu()函式呼叫自己的選單資源。目前,先設為NULL。

  

  ※ HINSTANCE hInstance:是一個名柄,它指向由Windows傳遞給WinMain()的例項。

  

  ※ LPVOID lpParam:對於遊戲程式設計來說,沒有什麼用的東西,只有簡單的視窗程式用到它。設定為NULL好了。

  

  我們現在萬事具備,東風也有了。我先給個示例:

  

  HWND hwnd;

  if (!(hwnd = CreateWindowEx(NULL,         // extended style, not needed

  "Sample Class",    // class identifier

  "Sample Window",   // window title

  WS_POPUP | WS_VISIBLE,// parameters

  0, 0, 320, 240,    // initial position, size

  NULL,         // handle to parent (the desktop)

  NULL,         // handle to menu (none)

  hinstance,      // application instance handle

  NULL)))        // who needs it?

  return(0);

  

  你可能會在遊戲程式設計中用上這這段程式碼,因為它是一個彈出式視窗。注意,我用了if形式,目的是一旦CreateWindowsEX()函式失靈,返回一個NULL,也就意味著如果視窗由於某種原因不能被建立,那麼WinMain()就被簡單的返回,程式結束。現在我們學會了足夠的知識建立一個小有功能的視窗了。還記得我們建立視窗類“sample class”時,一個指向“CALLBACK”型別函式的指標嗎?對,是“lpfnWndProc”。要想讓你的視窗真正做點事兒,我們還得來處理一下它指向的“視窗過程”函式。

  

  四、顯示視窗

  CreateWindowEx()從內部建立視窗,但並不顯示它。要顯示這個視窗,必須呼叫另外兩個函式:ShowWindow()和UpdateWindow()。頭一個設定視窗的顯示狀態,後一個則更新視窗的客戶區。對於程式的主視窗,ShowWindow()必須被呼叫一次,呼叫程式碼如下:

  

  ShowWindow(hwnd,nCmdShow);

  

  第一個引數是由CreateWindowEx()函式返回的視窗控制程式碼;第二個引數就是視窗的顯示模式引數,在☆WinMain()函式中提到過,就不重複了。UpdateWindow()函式的呼叫程式碼如下:

  

  UpdateWindow(hwnd);

  

  引數hwnd同ShowWindow()函式的hwnd一樣。

  

  五、訊息的處理

  我已經說過訊息在視窗裡的作用了,下面讓我們來仔細學習一下它。處理訊息的函式結構如下:

  

  LRESULT CALLBACK MsgHandler(

  HWND hwnd,   // window handle

  UINT msg,   // the message identifier

  WPARAM wparam, // message parameters

  LPARAM lparam // more message parameters

  );

  

  這個LRESULT型別要求返回一個32位的整數。實際取值依賴於訊息,但是這個值很少在應用程式程式碼中得到應用。以前我們談到過一點CALLBACK協定,它的引數很簡單:

  

  ※ HWND hwnd:是接收訊息的視窗的控制程式碼,也是由CreateWindowEx()函式返回的控制程式碼。

  

  ※ UINT msg:這是一個訊息識別符號,都是以WM_打頭的符號常量,意思是“Windows Message”。很多的,這裡只介紹一些常用的:

  

  ◎ WM_ACTIVATE:一個新視窗被啟用。

  

  ◎ WM_CLOSE:一個視窗被關閉。

  

  ◎ WM_COMMAND:一個選單功能被選擇。

  

  ◎ WM_CREATE:一個視窗被建立。

  

  ◎ WM_LBUTTONDBLCLK:滑鼠左鍵被雙擊。

  

  ◎ WM_LBUTTONDOWN:滑鼠左鍵被按下。

  

  ◎ WM_MOUSEMOVE:滑鼠被移動。

  

  ◎ WM_MOVE:一個視窗被移動。

  

  ◎ WM_PAINT:視窗的一部分需要重畫。

  

  ◎ WM_RBUTTONDBLCLK:滑鼠的右鍵被雙擊。

  

  ◎ WM_RBUTTONDOWN:滑鼠的右鍵被按下。

  

  ◎ WM_SIZE:視窗的大小被改變。

  

  ◎ WM_USER:做你想做的。

  

  ※ WPARAM wparam,LPARAM lparam:訊息引數。它們提供有關訊息的附加資訊,這兩個值對於每條訊息來說都是特定的。

  

  你要把所有要發生的訊息都寫程式序程式碼的話,我想你可能已經累瘋了。我想我會的。感謝上帝,Windows提供了預設訊息處理,如果你沒有任何特殊的訊息需要處理了,你總是要用DefWindowPorc()函式的,下面給一個最簡單的例子,沒有任何特定的訊息要處理的例子:

  

  LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)

  {

  return(DefWindowProc(hwnd, msg, wparam, lparam));

  }

  

  簡單吧!但通常你都需要處理一些自己的訊息,你要寫自己的程式程式碼,然後返回0,來告訴程式你幹完了。下面是一個例子,當視窗建立時,你呼叫了一個初始化的函式Initialize_Game(),然後返回0,最後告訴程式自己處理那些預設的訊息吧:

  

  LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)

  {

  if (msg == WM_CREATE)

  {

  Initialize_Game();

  return(0);

  }

  

  return(DefWindowProc(hwnd, msg, wparam, lparam));

  }

  

  你很可能需要一個“switch”結構來手動完成你想要控制的訊息,然後把剩下的交給DefWindowProc()去做。大功告成前,我不得不提醒您一件事,就是怎樣使你的訊息控制得到響應呢?

  

  六、讀取訊息佇列

  這裡先給你一個switch結構的例子吧:

  

  LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)

  {

  switch(msg)

  {

  case WM_CREAT:

  [初始化遊戲]

  return 0;

  case WM_PAINT:

  [畫一架飛機]

  return 0;

  case ……………………

  ……………………

  }

  

  return(DefWindowProc(hwnd, msg, wparam, lparam));

  }

  

  在進入程式的主迴圈前,你需要看看你的訊息控制(就是你在switch結構裡編的那些),尤其是還沒有用到的訊息控制是否被機器存了起來,以備一旦用到,馬上響應。做到正確的響應,你需要做幾件事。首先你需要PeekMessage()函式。下面是它的原形:

  

  BOOL PeekMessage(

  LPMSG lpMsg,     // pointer to structu

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-951677/,如需轉載,請註明出處,否則將追究法律責任。

相關文章