说起游戏,大家都不陌生,大家很快就能想到LOL,DNF,QQ炫舞啊等一系列热门网游,那么它们是如何开发出来的呢?不得不说,这里面涉及到的东西太多太多,其中有:计算机图形学,计算机物理,计算机碰撞检测,计算机多媒体处理,计算机网络技术等。那么如今游戏开发都用什么语言呢?当然非C++莫属,为什么呢?不解释,好了,我们就从最简单的图形GDI开始吧(什么是GDI?下一章介绍)。 (PS:本文内容均为个人理解学习,获取有误 - - !)
做游戏首先得有一个窗体吧,那么这节我们就简单的来创建一个窗体吧,怎么创建呢?可能你会想到MFC,但是这里我们不用它,为什么呢?因为它包含了很多对于游戏来说没有用的东西,影响效率。那么我们该怎么创建呢?
我们先来认识一下主函数吧,你可能马上会想到int main(){}函数,但是这里不是,他是windows窗体,所以主函数为WinMain函数,该函数的功能是被系统调用,作为一个32位应用程序的入口点。
//WinMain入口函数
//第一个参数为当前窗体实列
//第二窗体为父窗体实列
//第三个参数为传递给应用程序的命令行参数
//第四个参数指明窗口如何显示
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrivInstance,LPSTR lpCmdLine,int nShowCmd)
{}
创建一个窗口的步骤如下:
第一步:设计窗体
第二步:注册窗体
第三步:创建窗体
第四步:显示与更新
第五步:窗体消息队列
第六步:窗体过程函数
一,设计窗体:
WNDCLASSEX wndClass = {0};//描述窗口的结构体,{}表示初始化这个结构体的一个参数,就像创建一个对象需要new一样,不然就变成声明了
wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗体被上下左右拉伸时重新绘制
wndClass.cbSize = sizeof(WNDCLASSEX);//结构体大小
wndClass.cbClsExtra = 0;//类的附加内存,一般为0
wndClass.cbWndExtra = 0;//窗体的附加内存,一般为0
wndClass.hInstance = hInstance;//窗体的实列
wndClass.hIcon = (HICON)LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_LOADFROMFILE|LR_DEFAULTSIZE);//窗体的图标文件,必须为ico格式的
wndClass.hCursor = LoadCursor(NULL,IDC_ARROW);//窗体的光标
wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);//窗体的背景颜色
wndClass.lpszClassName = L"wndClass";//结构体名称
wndClass.lpszMenuName = 0;//指定菜单资源的名字
wndClass.lpfnWndProc = WndProc;//窗体的过程函数
通过RegisterClassEx函数来注册我们已经设计好的窗体,注册成功返回true,失败时,返回false
//注册窗体,如果注册失败,返回-1
if (!RegisterClassEx(&wndClass))
{
return -1;
}
通过CreateWindow函数来创建窗体,创建成功,返回HWND窗口句柄,失败,返回NULL
HWND hWnd = CreateWindow(L"wndClass",//结构体的名称,也就是Name属性
L"This is a Windows!",//窗体的标题
WS_OVERLAPPEDWINDOW,//窗体的层叠样式,通常为<span style="font-family: Arial, Helvetica, sans-serif;">WS_OVERLAPPEDWINDOW</span>
CW_USEDEFAULT,//窗口位置,X坐标,可以给具体的的值,一般为CW_USEDFAULT即可
CW_USEDEFAULT,//窗口位置,Y坐标,可以给具体的的值,一般为<span style="font-family: Arial, Helvetica, sans-serif;">CW_USEDFAULT即可</span>
800,//窗口的宽
600,//窗口的高
NULL,//窗体的父窗体,为NULL,意思就是如果一个窗体打开两次,那么第一次的就是父窗体
NULL,//<span style="font-family: Arial, Helvetica, sans-serif;">窗体的菜单资源,这里给NULL,表示没有</span>
hInstance,//窗体的实列,直接给WinMain函数里面的参数就行了
NULL);//附加参数,为NULL就好了
四,显示与更新
通过前面的步骤,我们已经可以显示窗体了,通过ShowWindow函数,它有两个参数,第一个是窗体的句柄HWND,第二个也就是显示状态,这个参数直接给WinMain函数里面的参数就行了。通过UpdateWindow更新窗体,它有一个参数,即窗体句柄HWND
<pre name="code" class="cpp"> //显示与更新
ShowWindow(hWnd,nShowCmd);
UpdateWindow(hWnd);
五,窗口消息队列
顾名思义,它是用来获取窗体的操作信息的,比如拖动,重绘,键盘,鼠标的操作等信息。通过PeekMessage函数读取消息队列,它有5个参数
//读取窗体消息队列
MSG msg = {0};//描述消息队列的结构体
while (msg.message!=WM_QUIT)//判断是否退出程序,如果没有退出,就一直循环读取消息
{
if (PeekMessage(&msg,//存消息信息的结构体
0,//窗体的句柄,0表示获取所有消息
0,//消息最小值,为0即可
0,//消息最大值,为0即可
PM_REMOVE)//读取后,如果存在,发送消息,并移除该条信息
)//读取消息
{
TranslateMessage(&msg);//将虚拟键消息转换为字符消息
DispatchMessage(&msg);//把这个消息发给窗口程序
}
}
前面有了消息队列,那么读取的消息在哪里处理呢?比如拖动,重绘,键盘按下,鼠标点击等,对,就是这个过程函数,它是负责处理消息队列发送过来的消息的。
//窗口过程函数处理
//第一个参数,窗体的句柄
//第二参数,消息ID(比如重绘,键盘按下等)
//第三个参数与第四个参数为附加消息
//通常我们通过第三个参数可以知道用户按下了某个键,或抬起某个键
//第四个参数通常用作取鼠标的XY坐标
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wparam,LPARAM lparam)
{
switch(message)//根据消息ID判断需要处理的消息
{
case WM_PAINT://重绘窗体
ValidateRect(hWnd,NULL);//使整个窗体的无效区变成有效区,第一参数为窗体句柄
//第二参数为NULL,表示所有区域,也可以是Rect的一个结构体,来限定窗体的有效区的区域
break;
case WM_DESTROY://窗口的关闭消息
PostQuitMessage(0);//退出程序
break;
default://如果没有需要处理的消息,就调用默认的过程函数
return DefWindowProc(hWnd,message,wparam,lparam);
}
return 0;
}
有了以上步骤,一个窗体就算是完成创建了。下面是完整代码:
#include <Windows.h>
//过程函数的声明
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wparam,LPARAM lparam);
//入口函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrivInstance,LPSTR lpCmdLine,int nShowCmd)
{
//设计窗体
WNDCLASSEX wndClass = {0};
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = (HICON)LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_LOADFROMFILE|LR_DEFAULTSIZE);
wndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wndClass.lpszClassName = L"wndClass";
wndClass.lpszMenuName = 0;
wndClass.lpfnWndProc = WndProc;
//注册窗体
if (!RegisterClassEx(&wndClass))
{
return -1;
}
//创建窗体
HWND hWnd = CreateWindow(L"wndClass",
L"This is a Windows!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
hInstance,
NULL);
//显示与更新
ShowWindow(hWnd,nShowCmd);
UpdateWindow(hWnd);
//读取窗体消息队列
MSG msg = {0};//描述消息队列的结构体
while (msg.message!=WM_QUIT)//判断是否退出程序,如果没有退出,就一直循环读取消息
{
if (PeekMessage(&msg,//存消息信息的结构体
0,//窗体的句柄,0表示获取所有消息
0,//消息最小值,为0即可
0,//消息最大值,为0即可
PM_REMOVE)//读取后,如果存在,发送消息,并移除该条信息
)//读取消息
{
TranslateMessage(&msg);//将虚拟键消息转换为字符消息
DispatchMessage(&msg);//把这个消息发给窗口程序
}
}
UnregisterClass(L"wndClass",hInstance);
}
//窗口过程函数处理
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wparam,LPARAM lparam)
{
switch(message)//根据消息ID判断需要处理的消息
{
case WM_PAINT://重绘窗体
ValidateRect(hWnd,NULL);
break;
case WM_DESTROY://窗口的关闭消息
PostQuitMessage(0);
break;
default://如果没有需要处理的消息,就调用默认的过程函数
return DefWindowProc(hWnd,message,wparam,lparam);
}
return 0;
}
运行效果图: