การเรียก include ตัว Library ของ DirectX
#include <d3dx9.h>
#include <dinput.h> #include <dinputd.h> #include <dsound.h> |
ที่ด้านบนสุดของโปรแกรม เราต้องเรียก include file ของ DirectX ใน header file เหล่านี้จะประกอบด้วยส่วนต่าง ๆ ของ DirectX นั้น ๆ ที่เราจะสามารถเรียกเข้ามาใช้ได้ เช่น หากเราต้องการเรียกใช้ Direct 3D เราต้องสร้างตัว device ขึ่นมาก่อน
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; |
โดยในตัวอย่าง จะเป็นการสร้างตัวแปร pointer ที่ชื่อไป object แบบ Direct 3D Device ซึ่ง object นี้จะอ้างอิงอยู่ที่ file d3dx9.h เป็นต้น
จะเขียนเกม 1 เกมบน Windows เราต้องมีอะไรบ้าง
ผมเริ่มต้นด้วยส่วนประกอบเหล่านี้เลยนะครับ เพื่อไม่ให้สับสนว่าการจะเขียนเกม ทำไมต้องมีอะไรยุ่งวุ่นวายด้วย ส่วนประกอบเหล่านี้มันจำเป็นต่อการสร้างเกม หรือโปรแกรมทั่ว ๆ ไปบน Windows ครับ และถึงแม้มันจะมีรายละเอียดคล้าย ๆ กัน แต่เราก็ควรจะเข้าใจเบื้องต้นไว้บ้างครับ
Function แรก คือ Winmain
ถ้าเราลองไปดูโปรแกรมตัวอย่างภาษา c++ ทั่วไปจะออกมาคล้าย ๆ กันครับ หน้าตามันจะประมาณนี้ครับ )แต่ใน C เก่า ๆ อาจจะมี function main บ้าง)
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{ } |
ตัว Winmain จะเป็นตัวเริ่มโปรแกรมของเราเลย ทุกอย่างจะเริ่มตรงนี้นะครับ Function นี้จะมี parameter อยู่หลายตัว แต่ในที่นี้จะกล่าวถึงส่วนสำคัญคือ HINSTANCE เจ้าตัวนี้จะเป็นเหมือนตัวเชื่อมตัวระหว่างโปรแกรมของเรากับตัว Window ที่เราจะสร้างขึ้นนะครับ
WNDCLASSEX winClass;
MSG uMsg; memset(&uMsg,0,sizeof(uMsg)); winClass.lpszClassName = "MY_WINDOWS_CLASS"; winClass.cbSize = sizeof(WNDCLASSEX); winClass.style = CS_HREDRAW | CS_VREDRAW; winClass.lpfnWndProc = WindowProc; winClass.hInstance = hInstance; winClass.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_DIRECTX_ICON); winClass.hIconSm = LoadIcon(hInstance, (LPCTSTR)IDI_DIRECTX_ICON); winClass.hCursor = LoadCursor(NULL, IDC_ARROW); winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); winClass.lpszMenuName = NULL; winClass.cbClsExtra = 0; winClass.cbWndExtra = 0; |
ในตัว Winmain นี้เราก็จะสร้าง object Window ขึ้นมา ให้ชื่อว่า winclass ครับ แล้วเราก็กำหนดรายละเอียดของมันลงไปประมาณนี้ครับ ตัวที่สำคัญคือ winClass.hInstance = hInstance; คือจะบอกว่าเมื่อเราสร้าง Window ขึ้นมาแล้ว ให้มันไปเชื่อมกับโปรแกรมของเรา
ต่อมาคือ winClass.lpfnWndProc = WindowProc; เป็นการตั้งค่า pointer function อันนี้เป็นฟังก์ชั่นที่เราจะเรียกใช้ ระบบ message ของ Windows ครับ ระบบ message นี้คือ ระบบที่มีการส่งข้อความไปมาว่ามีการกระทำอะไรกับ window ของเราผ่าน OS Windows บ้าง เช่น การปิด window การ focus window เป็นต้น เมื่อมีการกระทำใด ๆ แล้ว Windows จะส่งข้อความมาบอก โดยเราสามารถรับข้อความนั้นผ่านทาง Function ที่ชื่อ WindowProc ครับ
แต่ไม่ต้องงงครับว่า WindowProc อยู่ที่ไหน เพราะเราจะสร้างขึ้นต่อไปครับ
หลังจากเราสร้างตัวแปร winClass แล้วเราจะมาสร้างตัว window กัน
if( !RegisterClassEx(&winClass) )
return E_FAIL; WindowCanvasX=1280; WindowCanvasY=720; g_hWnd = CreateWindowEx( NULL, "MY_WINDOWS_CLASS", "game", WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, WindowCanvasX, WindowCanvasY, NULL, NULL, hInstance, NULL ); if( g_hWnd == NULL ) return E_FAIL; ShowWindow( g_hWnd, nCmdShow ); UpdateWindow( g_hWnd ); |
จากตัวอย่างเป็นการสร้าง window ขึ้นมาจากคำสั่ง CreateWindowEx โดยให้ ชื่อว่า game โดย ของ window จะเป็นแบบ POPUP (ถ้าเราเลือกให้เป็นแบบมี title bar จะมีชื่อเขียนว่า game) โดย window นี้จะมีขนาด 1280*720 จุด
มีข้อแนะนำอีกอย่างนะครับ ถ้าเราเพิ่ม style ให้เป็น window แบบมีขอบ ขนาดของมันอาจจะต้องหักส่วนที่เป็น ขอบ และ title bar ไปด้วย ซึ่งทำให้ canvas ที่แท้จริงอาจจะผิดได้ (มีผลทำให้ภาพที่แสดงผลมันเบี้ยว)
จะเห็นว่าเมื่อเราสร้าง window มาแล้ว เราจะใช้ตัวแปรชื่อ g_hWnd มารับเอาไว้ ตัวแปรนี้เราสามารถไปกำหนดใน global ได้
HWND g_hWnd = NULL; |
เจ้าตัวนี้เราจะเก็บไว้ใช้อีกที หากมีการเปลี่ยนแปลงขนาดของ window
Loop game
จริง ๆ แล้วในการเขียนเกมทุกแบบจะมีส่วนของ loop game อยู่นะครับ ผมจะยกตัวอย่างที่ผมใช้ให้ดูครับ
while( uMsg.message != WM_QUIT ) {
if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &uMsg ); DispatchMessage( &uMsg ); } else { g_dCurTime = timeGetTime(); g_fElpasedTime = (float)((g_dCurTime - g_dLastTime) * 0.001); g_dLastTime = g_dCurTime; if (g_fElpasedTime>=0.03) { g_dLastTime = g_dCurTime; Game_input(); Game_play(); } Game_render(); } } shutDown(); UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance ); return uMsg.wParam; |
ที่เราเรียกว่า loop game นั้นเพราะว่ามีจะมี loop ที่มันทำงานไม่จบสิ้นอยู่นะครับ ในที่นี้คือ loop while นั้นเอง loop นี้จะออกก็ต่อเมื่อมี message เข้ามาว่ามีการออกจากโปรแกรม โดยมีการตรวจสอบ message นี้อยู่ใน loop ด้วย เมื่อมีการส่ง message ว่า WM_QUIT ก็จะมีการสั่งเคลียร์ข้อมูลต่าง ๆ ต่อไป (ในที่นี้คือ shutDown();)
ใน loop ของเกมนั้นจะมีการจับเวลาอยู่ด้วย อันนี้เป็นเทคนิกในเกมโดยเฉพาะครับ นั่นคือ
Function สอง คือ WindowProc
LRESULT CALLBACK WindowProc( HWND hWnd,
UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_CLOSE: { ... } } } |
อันนี้เป็นตัวอย่างนะครับ ที่จะให้ดูคือ function นี้จะรับข้อความมาทางตัวแปร msg แล้วเราก็เอามา switch ดูว่ามันมีค่าอะไรบ้าง ค่าต่าง ๆ เหล่านี้ เช่น WM_CLOSE ลองหาได้จากเวปไซต์ของ Microsoft นะครับ มันมีหลายตัวมา ตั้งแต่พื้นฐาน ไปจนถึงการกดปุ่ม การ click mouse ที่เกิดขี้นใน window ของเรา (การรับค่า input สำหรับเกมเล็ก ๆ เราสามารถใช้ตัวนี้แทน Direct input ได้อยู่นะครับ แต่มันอาจจะช้ากว่าของ Direct input ครับ)
แต่ตัวที่สำคัญคือการปิด window ครับ คือถ้ามีการปิด window เพื่อปิดเกมของเรา แทนที่จะออกจากโปรแกรมแบบปกติ เราสามารถเขียนกรองตรงนี้เพื่อการคือค่าหน่วยความจำตรงนี้ได้
ตัว WindowProc นี้เป็น Callback function นะครับ หมายถึงว่ามันจะถูกเรียกผ่าน Windows เมื่อมี message ผ่านตัว window ที่เราสร้างขึ้น
จบครับ ในส่วนที่ผ่านมานี้ คนที่เคยเขียนโปรแกรมบน Windows อาจจะรู้มาบ้างแล้วครับ ผมจะอธิบายแค่คร่าว ๆ เท่านั้น ส่วนเรื่องรายละเอียดอื่น ๆ เช่น ค่าคงที่ต่าง ๆ ที่ใช้สร้าง window หรือ message ที่ Windows เป็นตัวส่งมานั้น หาอ่านเพิ่มเติมได้ครับ
ในส่วนต่อไป เราจะมาสร้าง object ของ DirectX กันครับ
(สำหรับใครที่รอ source code ขอให้ลองหาที่อื่นก่อนนะครับ พอดีว่าตัวที่ผมมี ไม่มีแบบที่เป็นตัวเบื้องต้นอยู่เลยครับ)
ไม่มีความเห็น