首页 > 代码库 > 《逐梦旅程 WINDOWS游戏编程之从零开始》源码分析2——GDI
《逐梦旅程 WINDOWS游戏编程之从零开始》源码分析2——GDI
GDI: 图形设备接口
1. 取得设备环境的句柄(如屏幕)
使用BeginPaint和EndPaint这两个函数,或者使用GetDC和ReleaseDC这两个函数。关于函数的具体说明可以参考mdsn文档。
一个GDI程序通用框架:
1 #include <windows.h> 2 3 #define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度 4 #define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度 5 #define WINDOW_TITLE L"【致我们永不熄灭的游戏开发梦想】程序核心框架" //为窗口标题定义的宏 6 7 HDC g_hdc=NULL; //全局设备环境句柄 8 9 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); //窗口过程函数 10 BOOL Game_Init(HWND hwnd); //在此函数中进行资源的初始化 11 VOID Game_Paint(HWND hwnd); //进行绘图代码的书写 12 BOOL Game_CleanUp(HWND hwnd); //资源的清理 13 14 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) 15 { 16 //【1】窗口创建四步曲之一:开始设计一个完整的窗口类 17 WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体 18 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小 19 wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口的样式 20 wndClass.lpfnWndProc = WndProc; //设置指向窗口过程函数的指针 21 wndClass.cbClsExtra = 0; //窗口类的附加内存,取0就可以了 22 wndClass.cbWndExtra = 0; //窗口的附加内存,依然取0就行了 23 wndClass.hInstance = hInstance; //指定包含窗口过程的程序的实例句柄。 24 wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //本地加载自定义ico图标 25 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。 26 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个灰色画刷句柄 27 wndClass.lpszMenuName = NULL; //用一个以空终止的字符串,指定菜单资源的名字。 28 wndClass.lpszClassName = L"ForTheDreamOfGameDevelop"; //用一个以空终止的字符串,指定窗口类的名字。 29 30 //【2】窗口创建四步曲之二:注册窗口类 31 if( !RegisterClassEx( &wndClass ) ) //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口 32 return -1; 33 34 //【3】窗口创建四步曲之三:正式创建窗口 35 HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE, //喜闻乐见的创建窗口函数CreateWindow 36 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, 37 WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); 38 39 //【4】窗口创建四步曲之四:窗口的移动、显示与更新 40 MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整窗口显示时的位置,使窗口左上角位于(250,80)处 41 ShowWindow( hwnd, nShowCmd ); //调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态 42 UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样 43 44 //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE 45 if(!(Game_Init(hwnd))) 46 { 47 MessageBox(hwnd,L"资源初始化失败",L"消息窗口",0); 48 return FALSE; 49 } 50 51 //【5】消息循环过程 52 MSG msg = { 0 }; //定义并初始化msg 53 while( msg.message != WM_QUIT ) //使用while循环,如果消息不是WM_QUIT消息,就继续循环 54 { 55 if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。 56 { 57 TranslateMessage( &msg ); //将虚拟键消息转换为字符消息 58 DispatchMessage( &msg ); //分发一个消息给窗口程序。 59 } 60 } 61 62 //【6】窗口类的注销 63 UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance); //程序准备结束,注销窗口类 64 return 0; 65 } 66 69 // 描述:窗口过程函数WndProc,对窗口消息进行处理 70 //------------------------------------------------------------------------------------------------ 71 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 72 { 73 PAINTSTRUCT paintStruct; //定义一个PAINTSTRUCT结构体来记录一些绘制信息 74 75 switch( message ) //switch语句开始 76 { 77 case WM_PAINT: // 若是客户区重绘消息 78 g_hdc=BeginPaint(hwnd,&paintStruct); //指定窗口进行绘图工作的准备,并将和绘图有关的信息填充到paintstruct结构体中 79 Game_Paint(hwnd); 80 EndPaint(hwnd,&paintStruct); //EndPaint函数标记指定窗口的绘画过程结束 81 ValidateRect(hwnd, NULL); // 更新客户区的显示 82 break; //跳出该switch语句 83 84 case WM_KEYDOWN: // 若是键盘按下消息 85 if (wParam == VK_ESCAPE) // 如果被按下的键是ESC 86 DestroyWindow(hwnd); // 销毁窗口, 并发送一条WM_DESTROY消息 87 break; //跳出该switch语句 88 89 case WM_DESTROY: //若是窗口销毁消息 90 PostQuitMessage( 0 ); //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息 91 break; //跳出该switch语句 92 93 default: //若上述case条件都不符合,则执行该default语句 94 return DefWindowProc( hwnd, message, wParam, lParam ); //调用缺省的窗口过程 95 } 96 97 return 0; //正常退出 98 } 99 100 //初始化函数,进行一些简单的初始化 101 BOOL Game_Init(HWND hwnd) 102 { 103 g_hdc=GetDC(hwnd); 104 Game_Paint(hwnd); 105 ReleaseDC(hwnd,g_hdc); //一个窗口句柄,一个设备上下文环境句柄,注意区别 106 return TRUE; 107 } 108 109 //绘制函数 110 VOID Game_Paint(HWND hwnd) 111 { 112 //我们自己的绘制逻辑 113 } 114 115 //清理资源 116 BOOL Game_CleanUp(HWND hwnd) 117 { 118 return TRUE; 119 }
2. GDI基本几何绘图
使用画笔HPEN,画刷HBRUSH。然后是使用SelectObject函数在设备上下文中选中它们。GDI对象(我感觉还是称之为结构体比较合适)一经创建便会占用部分内存,不用时务必使用DeleteObject函数删除掉。
创建玩画笔和画刷后便可以绘制了,绘制线条通常使用LineTo与MoveToEx函数。
示例程序1:
1 #include <windows.h> 2 #include <time.h> //使用获取系统时间time函数需要包含的头文件 3 4 #pragma comment(lib,"winmm.lib") //调用PlaySound函数所需库文件 5 6 #define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度 7 #define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度 8 #define WINDOW_TITLE L"【致我们永不熄灭的游戏开发梦想】程序核心框架" //为窗口标题定义的宏 9 10 HDC g_hdc=NULL; //全局设备环境句柄 11 HPEN g_hPen[7]={0}; //定义画笔句柄的数组 12 HBRUSH g_hBrush[7]={0}; //定义画刷句柄的数组 13 int g_iPenStyle[7] = {PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASHDOTDOT,PS_NULL,PS_INSIDEFRAME}; //定义画笔样式数组并初始化 14 int g_iBrushStyle[6] = {HS_VERTICAL,HS_HORIZONTAL,HS_CROSS,HS_DIAGCROSS,HS_FDIAGONAL,HS_BDIAGONAL}; //定义画刷样式数组并初始化 15 16 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); //窗口过程函数 17 BOOL Game_Init(HWND hwnd); //在此函数中进行资源的初始化 18 VOID Game_Paint(HWND hwnd); //进行绘图代码的书写 19 BOOL Game_CleanUp(HWND hwnd); //资源的清理 20 21 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) 22 { 23 //【1】窗口创建四步曲之一:开始设计一个完整的窗口类 24 WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体 25 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小 26 wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口的样式 27 wndClass.lpfnWndProc = WndProc; //设置指向窗口过程函数的指针 28 wndClass.cbClsExtra = 0; //窗口类的附加内存,取0就可以了 29 wndClass.cbWndExtra = 0; //窗口的附加内存,依然取0就行了 30 wndClass.hInstance = hInstance; //指定包含窗口过程的程序的实例句柄。 31 wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //本地加载自定义ico图标 32 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。 33 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个灰色画刷句柄 34 wndClass.lpszMenuName = NULL; //用一个以空终止的字符串,指定菜单资源的名字。 35 wndClass.lpszClassName = L"ForTheDreamOfGameDevelop"; //用一个以空终止的字符串,指定窗口类的名字。 36 37 //【2】窗口创建四步曲之二:注册窗口类 38 if( !RegisterClassEx( &wndClass ) ) //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口 39 return -1; 40 41 //【3】窗口创建四步曲之三:正式创建窗口 42 HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE, //喜闻乐见的创建窗口函数CreateWindow 43 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, 44 WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); 45 46 //【4】窗口创建四步曲之四:窗口的移动、显示与更新 47 MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整窗口显示时的位置,使窗口左上角位于(250,80)处 48 ShowWindow( hwnd, nShowCmd ); //调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态 49 UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样 50 51 //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE 52 if(!(Game_Init(hwnd))) 53 { 54 MessageBox(hwnd,L"资源初始化失败",L"消息窗口",0); 55 return FALSE; 56 } 57 PlaySound(L"AIR - 夏影.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐 58 59 //【5】消息循环过程 60 MSG msg = { 0 }; //定义并初始化msg 61 while( msg.message != WM_QUIT ) //使用while循环,如果消息不是WM_QUIT消息,就继续循环 62 { 63 if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。 64 { 65 TranslateMessage( &msg ); //将虚拟键消息转换为字符消息 66 DispatchMessage( &msg ); //分发一个消息给窗口程序。 67 } 68 } 69 70 //【6】窗口类的注销 71 UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance); //程序准备结束,注销窗口类 72 return 0; 73 } 74 75 76 //-----------------------------------【WndProc( )函数】-------------------------------------- 77 // 描述:窗口过程函数WndProc,对窗口消息进行处理 78 //------------------------------------------------------------------------------------------------ 79 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 80 { 81 PAINTSTRUCT paintStruct; //定义一个PAINTSTRUCT结构体来记录一些绘制信息 82 83 switch( message ) //switch语句开始 84 { 85 case WM_PAINT: // 若是客户区重绘消息 86 g_hdc=BeginPaint(hwnd,&paintStruct); //指定窗口进行绘图工作的准备,并将和绘图有关的信息填充到paintstruct结构体中 87 Game_Paint(hwnd); 88 EndPaint(hwnd,&paintStruct); //EndPaint函数标记指定窗口的绘画过程结束 89 ValidateRect(hwnd, NULL); // 更新客户区的显示 90 break; //跳出该switch语句 91 92 case WM_KEYDOWN: // 若是键盘按下消息 93 if (wParam == VK_ESCAPE) // 如果被按下的键是ESC 94 DestroyWindow(hwnd); // 销毁窗口, 并发送一条WM_DESTROY消息 95 break; //跳出该switch语句 96 97 case WM_DESTROY: //若是窗口销毁消息 98 PostQuitMessage( 0 ); //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息 99 break; //跳出该switch语句 100 101 default: //若上述case条件都不符合,则执行该default语句 102 return DefWindowProc( hwnd, message, wParam, lParam ); //调用缺省的窗口过程 103 } 104 105 return 0; //正常退出 106 } 107 108 //初始化函数,进行一些简单的初始化 109 BOOL Game_Init(HWND hwnd) 110 { 111 g_hdc=GetDC(hwnd); 112 srand((unsigned)time(NULL)); //用系统时间初始化随机种子 113 114 //随机初始化画笔和画刷的颜色值 115 for(int i=0;i<=6;i++) 116 { 117 g_hPen[i] = CreatePen(g_iPenStyle[i],1,RGB(rand()%256,rand()%256,rand()%256)); 118 if(i==6) 119 g_hBrush[i] = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); 120 else 121 g_hBrush[i] = CreateHatchBrush(g_iBrushStyle[i],RGB(rand()%256,rand()%256,rand()%256)); 122 } 123 Game_Paint(hwnd); 124 ReleaseDC(hwnd,g_hdc); //一个窗口句柄,一个设备上下文环境句柄,注意区别 125 return TRUE; 126 } 127 128 //绘制函数 129 VOID Game_Paint(HWND hwnd) 130 { 131 //定义一个y坐标值 132 int y=0; 133 134 //一个for循环,用7种不同的画笔绘制线条 135 for(int i=0;i<=6;i++) 136 { 137 y = (i+1) * 70; 138 139 SelectObject(g_hdc,g_hPen[i]);//将对应的画笔选好 140 MoveToEx(g_hdc,30,y,NULL); //“光标”移动到对应的(30,y)坐标处 141 LineTo(g_hdc,100,y); //从(30,y)坐标处向(100,y)绘制线段 142 } 143 144 /*注意上面画完后y=420,下面画矩形的时候还有用*/ 145 //定义两个x坐标值 146 int x1 = 120; 147 int x2 = 190; 148 149 //用7种不同的画刷填充矩形 150 for(int i=0;i<=6;i++) 151 { 152 SelectObject(g_hdc,g_hBrush[i]); //选用画刷 153 Rectangle(g_hdc,x1,70,x2,y); //画出一个封闭的矩形,矩形左上角坐标为(x1,50),右下角坐标为(x2,y) 154 x1 += 90; 155 x2 += 90; 156 } 157 } 158 159 //清理资源 160 BOOL Game_CleanUp(HWND hwnd) 161 { 162 //一个for循环,释放掉所有的画笔和画刷句柄 163 for (int i=0;i<=6;i++) 164 { 165 DeleteObject(g_hPen[i]); 166 DeleteObject(g_hBrush[i]); 167 } 168 return TRUE; 169 }
上面的程序除了基本的GDI几何绘图之外,还需要注意的是随机数的生成:
因为用到了time()函数,因为它在Windows.h中没有声明,所以要包含声明它的time.h头文件
3. 文字的输出
最常用的的是TextOut函数,进阶文字输出函数是DrawText,使用SetTextColor来设置文字的颜色,使用SetBkMode函数来设置文字背景透明,使用CreateFont函数来创建字体。
示例程序2:
1 #include <windows.h> 2 #include <time.h> //使用获取系统时间time函数需要包含的头文件 3 4 #pragma comment(lib,"winmm.lib") //调用PlaySound函数所需库文件 5 6 #define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度 7 #define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度 8 #define WINDOW_TITLE L"【致我们永不熄灭的游戏开发梦想】程序核心框架" //为窗口标题定义的宏 9 10 HDC g_hdc=NULL; //全局设备环境句柄 11 12 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); //窗口过程函数 13 BOOL Game_Init(HWND hwnd); //在此函数中进行资源的初始化 14 VOID Game_Paint(HWND hwnd); //进行绘图代码的书写 15 BOOL Game_CleanUp(HWND hwnd); //资源的清理 16 17 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) 18 { 19 //【1】窗口创建四步曲之一:开始设计一个完整的窗口类 20 WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体 21 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小 22 wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口的样式 23 wndClass.lpfnWndProc = WndProc; //设置指向窗口过程函数的指针 24 wndClass.cbClsExtra = 0; //窗口类的附加内存,取0就可以了 25 wndClass.cbWndExtra = 0; //窗口的附加内存,依然取0就行了 26 wndClass.hInstance = hInstance; //指定包含窗口过程的程序的实例句柄。 27 wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //本地加载自定义ico图标 28 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。 29 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个灰色画刷句柄 30 wndClass.lpszMenuName = NULL; //用一个以空终止的字符串,指定菜单资源的名字。 31 wndClass.lpszClassName = L"ForTheDreamOfGameDevelop"; //用一个以空终止的字符串,指定窗口类的名字。 32 33 //【2】窗口创建四步曲之二:注册窗口类 34 if( !RegisterClassEx( &wndClass ) ) //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口 35 return -1; 36 37 //【3】窗口创建四步曲之三:正式创建窗口 38 HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE, //喜闻乐见的创建窗口函数CreateWindow 39 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, 40 WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); 41 42 //【4】窗口创建四步曲之四:窗口的移动、显示与更新 43 MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整窗口显示时的位置,使窗口左上角位于(250,80)处 44 ShowWindow( hwnd, nShowCmd ); //调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态 45 UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样 46 47 //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE 48 if(!(Game_Init(hwnd))) 49 { 50 MessageBox(hwnd,L"资源初始化失败",L"消息窗口",0); 51 return FALSE; 52 } 53 PlaySound(L"AIR - 夏影.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐 54 55 //【5】消息循环过程 56 MSG msg = { 0 }; //定义并初始化msg 57 while( msg.message != WM_QUIT ) //使用while循环,如果消息不是WM_QUIT消息,就继续循环 58 { 59 if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。 60 { 61 TranslateMessage( &msg ); //将虚拟键消息转换为字符消息 62 DispatchMessage( &msg ); //分发一个消息给窗口程序。 63 } 64 } 65 66 //【6】窗口类的注销 67 UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance); //程序准备结束,注销窗口类 68 return 0; 69 } 70 75 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 76 { 77 PAINTSTRUCT paintStruct; //定义一个PAINTSTRUCT结构体来记录一些绘制信息 78 79 switch( message ) //switch语句开始 80 { 81 case WM_PAINT: // 若是客户区重绘消息 82 g_hdc=BeginPaint(hwnd,&paintStruct); //指定窗口进行绘图工作的准备,并将和绘图有关的信息填充到paintstruct结构体中 83 Game_Paint(hwnd); 84 EndPaint(hwnd,&paintStruct); //EndPaint函数标记指定窗口的绘画过程结束 85 ValidateRect(hwnd, NULL); // 更新客户区的显示 86 break; //跳出该switch语句 87 88 case WM_KEYDOWN: // 若是键盘按下消息 89 if (wParam == VK_ESCAPE) // 如果被按下的键是ESC 90 DestroyWindow(hwnd); // 销毁窗口, 并发送一条WM_DESTROY消息 91 break; //跳出该switch语句 92 93 case WM_DESTROY: //若是窗口销毁消息 94 PostQuitMessage( 0 ); //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息 95 break; //跳出该switch语句 96 97 default: //若上述case条件都不符合,则执行该default语句 98 return DefWindowProc( hwnd, message, wParam, lParam ); //调用缺省的窗口过程 99 } 100 101 return 0; //正常退出 102 } 103 104 //初始化函数,进行一些简单的初始化 105 BOOL Game_Init(HWND hwnd) 106 { 107 g_hdc=GetDC(hwnd); 108 Game_Paint(hwnd); 109 ReleaseDC(hwnd,g_hdc); //一个窗口句柄,一个设备上下文环境句柄,注意区别 110 return TRUE; 111 } 112 113 //绘制函数 114 VOID Game_Paint(HWND hwnd) 115 { 116 HFONT hFont=CreateFont(30,0,0,0,0,0,0,0,GB2312_CHARSET,0,0,0,0,L"微软雅黑"); //创建一种字体 117 SelectObject(g_hdc,hFont); //将字体选入设备环境中 118 SetBkMode(g_hdc, TRANSPARENT); //设置输出文字背景色为透明 119 120 //定义三段文字 121 wchar_t text1[]=L"我们所有的梦想都可以成真,只要我们有勇气去追求它们。"; 122 wchar_t text2[]=L"All our dreams can come true, if we have the courage to pursue them. "; 123 wchar_t text3[]=L"--------沃尔特 迪斯尼"; 124 125 //设置文字颜色并输出第一段文字 126 SetTextColor(g_hdc,RGB(50,255,50)); 127 TextOut(g_hdc,30,150,text1,wcslen(text1)); 128 //设置文字颜色并输出第二段文字 129 SetTextColor(g_hdc,RGB(50,50,255)); 130 TextOut(g_hdc,30,200,text2,wcslen(text2)); 131 //设置文字颜色并输出第三段文字 132 SetTextColor(g_hdc,RGB(255,150,50)); 133 TextOut(g_hdc,500,250,text3,wcslen(text3)); 134 135 DeleteObject(hFont);//释放字体对象 136 } 137 138 //清理资源 139 BOOL Game_CleanUp(HWND hwnd) 140 { 141 return TRUE; 142 }
4. 位图绘制基础
位图对象常常用位图句柄(HBITMAP)来表示。因为在游戏中角色位图是不断变化的,所以设备上下文中的合成位图的内容也是不断变化的。为了实现游戏画面的刷新,通常是利用缓存设备环境来保存下一帧的合成位图对象。利用位图绘图的大体思路如下:加载位图——>建立兼容DC——>选用位图对象——>进行贴图
LoadImage函数不仅可以加载位图,还可以用于加载图标,光标等图像资源。加载位图之后,要建立兼容DC。
内存DC起到中转站的作用,这个DC要做到与窗口DC的无缝衔接。利用CreateCompatibleDC函数可以创建与指定DC相兼容的内存DC,内存DC使用后也必须进行释放操作,这里要注意的是释放内存DC所调用的函数是DeleteDC,而不是ReleaseDC
使用SelcetObject选用位图对象,使用BitBlt,TransparentBlt或者StretchBlt函数来进行贴图。
示例程序3:
1 #include <windows.h> 2 #include <time.h> //使用获取系统时间time函数需要包含的头文件 3 4 #pragma comment(lib,"winmm.lib") //调用PlaySound函数所需库文件 5 6 #define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度 7 #define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度 8 #define WINDOW_TITLE L"【致我们永不熄灭的游戏开发梦想】程序核心框架" //为窗口标题定义的宏 9 10 HDC g_hdc=NULL,g_mdc=NULL; //全局设备环境句柄 11 HBITMAP g_hBitmap=NULL; //定义一个位图句柄 12 13 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); //窗口过程函数 14 BOOL Game_Init(HWND hwnd); //在此函数中进行资源的初始化 15 VOID Game_Paint(HWND hwnd); //进行绘图代码的书写 16 BOOL Game_CleanUp(HWND hwnd); //资源的清理 17 18 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) 19 { 20 //【1】窗口创建四步曲之一:开始设计一个完整的窗口类 21 WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体 22 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小 23 wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口的样式 24 wndClass.lpfnWndProc = WndProc; //设置指向窗口过程函数的指针 25 wndClass.cbClsExtra = 0; //窗口类的附加内存,取0就可以了 26 wndClass.cbWndExtra = 0; //窗口的附加内存,依然取0就行了 27 wndClass.hInstance = hInstance; //指定包含窗口过程的程序的实例句柄。 28 wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //本地加载自定义ico图标 29 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。 30 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个灰色画刷句柄 31 wndClass.lpszMenuName = NULL; //用一个以空终止的字符串,指定菜单资源的名字。 32 wndClass.lpszClassName = L"ForTheDreamOfGameDevelop"; //用一个以空终止的字符串,指定窗口类的名字。 33 34 //【2】窗口创建四步曲之二:注册窗口类 35 if( !RegisterClassEx( &wndClass ) ) //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口 36 return -1; 37 38 //【3】窗口创建四步曲之三:正式创建窗口 39 HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE, //喜闻乐见的创建窗口函数CreateWindow 40 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, 41 WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); 42 43 //【4】窗口创建四步曲之四:窗口的移动、显示与更新 44 MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整窗口显示时的位置,使窗口左上角位于(250,80)处 45 ShowWindow( hwnd, nShowCmd ); //调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态 46 UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样 47 48 //游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE 49 if(!(Game_Init(hwnd))) 50 { 51 MessageBox(hwnd,L"资源初始化失败",L"消息窗口",0); 52 return FALSE; 53 } 54 PlaySound(L"AIR - 夏影.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐 55 56 //【5】消息循环过程 57 MSG msg = { 0 }; //定义并初始化msg 58 while( msg.message != WM_QUIT ) //使用while循环,如果消息不是WM_QUIT消息,就继续循环 59 { 60 if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。 61 { 62 TranslateMessage( &msg ); //将虚拟键消息转换为字符消息 63 DispatchMessage( &msg ); //分发一个消息给窗口程序。 64 } 65 } 66 67 //【6】窗口类的注销 68 UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance); //程序准备结束,注销窗口类 69 return 0; 70 } 71 72 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 73 { 74 PAINTSTRUCT paintStruct; //定义一个PAINTSTRUCT结构体来记录一些绘制信息 75 76 switch( message ) //switch语句开始 77 { 78 case WM_PAINT: // 若是客户区重绘消息 79 g_hdc=BeginPaint(hwnd,&paintStruct); //指定窗口进行绘图工作的准备,并将和绘图有关的信息填充到paintstruct结构体中 80 Game_Paint(hwnd); 81 EndPaint(hwnd,&paintStruct); //EndPaint函数标记指定窗口的绘画过程结束 82 ValidateRect(hwnd, NULL); // 更新客户区的显示 83 break; //跳出该switch语句 84 85 case WM_KEYDOWN: // 若是键盘按下消息 86 if (wParam == VK_ESCAPE) // 如果被按下的键是ESC 87 DestroyWindow(hwnd); // 销毁窗口, 并发送一条WM_DESTROY消息 88 break; //跳出该switch语句 89 90 case WM_DESTROY: //若是窗口销毁消息 91 PostQuitMessage( 0 ); //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息 92 break; //跳出该switch语句 93 94 default: //若上述case条件都不符合,则执行该default语句 95 return DefWindowProc( hwnd, message, wParam, lParam ); //调用缺省的窗口过程 96 } 97 98 return 0; //正常退出 99 } 100 101 //初始化函数,进行一些简单的初始化 102 BOOL Game_Init(HWND hwnd) 103 { 104 g_hdc = GetDC(hwnd); //获取设备环境句柄 105 106 //-----【位图绘制四步曲之一:加载位图】----- 107 g_hBitmap = (HBITMAP)LoadImage(NULL,L"Naruto.bmp",IMAGE_BITMAP,WINDOW_WIDTH,WINDOW_HEIGHT,LR_LOADFROMFILE); //加载位图 108 109 //-----【位图绘制四步曲之二:建立兼容DC】----- 110 g_mdc = CreateCompatibleDC(g_hdc); //建立兼容设备环境的内存DC 111 112 Game_Paint(hwnd); 113 ReleaseDC(hwnd,g_hdc); //释放设备环境 114 return TRUE; 115 } 116 117 //绘制函数 118 VOID Game_Paint(HWND hwnd) 119 { 120 //-----【位图绘制四步曲之三:选用位图对象 】----- 121 SelectObject(g_mdc,g_hBitmap); //将位图对象选入到g_mdc内存DC中 122 //-----【位图绘制四步曲之四:进行贴图】----- 123 BitBlt(g_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,g_mdc,0,0,SRCCOPY); //采用BitBlt函数贴图,参数设置为窗口大小 124 } 125 126 //清理资源 127 BOOL Game_CleanUp(HWND hwnd) 128 { 129 //释放资源对象 130 DeleteObject(g_hBitmap); 131 DeleteDC(g_mdc); 132 return TRUE; 133 }
《逐梦旅程 WINDOWS游戏编程之从零开始》源码分析2——GDI