首先声明一下,图图对风靡CSDN论坛的浅墨大哥比较崇拜,买了一本他的《windows编程从零开始》,图图写的有关游戏编程的博客里面借鉴了好多浅墨大哥书里的话,然后再加上一些图图自己的理解,所以请看过浅墨大哥文章或者他的书的人请不要喷图图写的博客,喜欢的话就提提图图博客里的不足之处,不喜欢的话就说说图图文章哪里写的不好。好了,下面进入正文。
GDI英文全称为Graphics Device Interface,也就是图形设备接口的意思。GDI是Windows绘图API,使得用户无需关心具体设备的细节,而只需在一个虚拟的环境中进行操作。我们利用GDI所提供的函数可以方便地在屏幕、打印机及其他输出设备上输出图形、文本等操作,实现程序开发与硬件设备的隔离,大大方便我们的开发工作。
虽然GDI可以让我们轻易进行图形程序开发,但是它的绘图效率跟专业的图形API比起来还是比较低的,我们使用的专业游戏图形API比如DirectX和OpenGL,它们是允许程序直接访问底层显卡内存的,可以实现硬件加速。在现在这个玩家注重游戏运行画面和运行速度的社会下,直接用GDI来进行游戏开发显然是不明智的。但是我们很难在完全不了解GDI的情况下就能直接利用专业的图形API(如DirectX和OpenGL)来进行游戏开发,DirectX中的DrawTex()函数就是对GDI中TextOut()函数的封装。所以如果我们不太注重画面和游戏速度的话,还是可以用GDI来玩玩的,当然了这个仅限于二维游戏,小时候我们玩的《魂斗罗》和《超级玛丽》就是用GDI写出来的。
GDI中一个最基础而且使用最频繁的概念就是设备上下文,英文全称Device Context,如果把我们的程序窗口比喻成画板的话,那么这个设备上下文就是画板上面的画纸(有点像Java中的JPanel面板的意思,学过Java的同学可以结合一下想想),所以我们在画图之前必须获得设备上下文的句柄(HDC),HDC即Handle of device context的简称,画完画后我们要释放这个设备上下文句柄。
下面介绍两种获得设备上下文句柄的方法:
1、这种方法我们只能在窗口过程函数里的WM_PAINT这个画面重绘消息里使用,这个方法我们使用BeginPaint和EndPaint这两个函数,这两个函数的参数都是一样的,第一个是HWND类型的我们当前激活的窗口实例,第二个参数是PAINTSTRUCT结构体指针类型的保存当前画纸信息的变量,给大家贴出使用代码
HDC g_hdc; //全局设备上下文变量(画纸)
......
int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )
{
......
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
PAINTSTRUCT paintStruct; //记录设备上下文(画纸)信息的变量
case WM_PAINT:
g_hdc = BeginPaint(hwnd, &paintStruct); //指定是哪个窗口进行画画,
//并把画纸信息保存进paintStruct变量中
Game_Paint(); //进行画画的函数
EndPaint(hwnd, &paintStruct); //画完画我们就关闭画纸
ValidateRect(hwnd); //更新窗口画面
break;
......
}
......
}
怎么样,使用还算简单吧,要注意BeginPaint和EndPaint这对好基友要成对出现哦,我们要养成及时释放资源的习惯,最好是我们一写好BeginPaint就立即写上EndPaint函数,这样我们就不太容易出错。
2、第二种获得HDC的方法是使用GetDC和ReleaseDC函数。GetDC函数里的唯一参数就是HWND类型的窗口句柄,Release函数里的第一个参数是HWND类型的窗口句柄,第二个是我们通过GetDC获得的HDC类型的设备上下文句柄,给出演示示例让小伙伴们看看它的简单实用
HDC g_hdc; //全局设备上下文变量(画纸)
g_hdc = GetDC(hwnd); //获得设备上下文句柄
Game_Paint(hwnd); //自定义的画画函数
ReleaseDC(hwnd, g_hdc); //释放设备上下文句柄
接下来给大家贴出一个GDI程序通用的模板,大家以后写GDI程序就可以直接套用这个模板了
//-----------------------------------------------------------
// 程序名称:GDIdemoCore
// 2014年4月by图图
// 描述:实现GDI游戏卡法所需的核心程序
//-----------------------------------------------------------
#include <Windows.h>
#define WINDOW_WDTH 800 //定义窗口宽度
#define WINDOW_HEIGHT 600 //定义窗口高度
#define WINDOW_CLASS L"ForTheDreamOfGameDevelop" //定义窗口类名称
#define WINDOW_NAME L"【致我们永不熄灭的游戏开发梦想】GDI程序核心框架" //定义窗口标题
//全局变量
HDC g_hdc = NULL; //全局设备上下文句柄
//全局函数声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //窗口过程函数
BOOL Game_Init(HWND hwnd); //资源初始化函数
VOID Game_Paint(HWND hwnd); //绘画函数
BOOL Game_CleanUp(HWND hwnd); //释放资源函数
//Windows应用程序入口函数
int WINAPI WinMain(
__in HINSTANCE hInstance,
__in_opt HINSTANCE hPrevInstance,
__in LPSTR lpCmdLine,
__in int nShowCmd
)
{
//设计一个窗口类
WNDCLASSEX wcex = { 0 };
wcex.cbClsExtra = 0;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.cbWndExtra = 0;
wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hIcon = (HICON)::LoadImage(NULL, L"icon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
wcex.hIconSm = NULL;
wcex.hInstance = hInstance;
wcex.lpfnWndProc = WindowProc;
wcex.lpszClassName = WINDOW_CLASS;
wcex.lpszMenuName = NULL;
wcex.style = CS_HREDRAW | CS_VREDRAW;
//如果注册失败就返回退出
if(!RegisterClassEx(&wcex))
return -1;
//创建窗口实例
HWND hwnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
//窗口的移动、显示和更新
MoveWindow(hwnd, 250, 80, WINDOW_WDTH, WINDOW_HEIGHT, true); //移动窗口到(250,80)处
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
//初始化游戏资源
if(!Game_Init(hwnd))
{
MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //创建消息窗口提示初始化资源失败
return FALSE;
}
//消息循环
MSG msg = { 0 };
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//注销窗口类
UnregisterClass(WINDOW_CLASS, wcex.hInstance);
return 0;
}
//窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct; //定义一个PAINTSTRUCT结构体来记录一些绘制信息
switch(uMsg)
{
case WM_PAINT: //重绘消息
g_hdc = BeginPaint(hwnd, &paintStruct);
Game_Paint(hwnd);
EndPaint(hwnd, &paintStruct);//绘画结束
ValidateRect(hwnd, NULL); //更新客户区显示
break;
case WM_KEYDOWN: //键盘按下消息
if(wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
case WM_DESTROY:
Game_CleanUp(hwnd); //释放资源
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
//初始化函数
BOOL Game_Init(HWND hwnd)
{
g_hdc = GetDC(hwnd);
Game_Paint(hwnd);
ReleaseDC(hwnd, g_hdc);
return TRUE;
}
//绘画函数
VOID Game_Paint(HWND hwnd)
{
}
//释放资源函数
BOOL Game_CleanUp(HWND hwnd)
{
return TRUE;
}
运行画面如下