编程初学者:游戏编程起源(初学者)Ⅵ



☆ 关闭你应用(关闭窗口)

有 3个消息看起来差不多都是处理关闭事情它们是WM_DESTROYWM_CLOSE和WM_QUIT它们确很相似但你需要知道它们的间区别!个窗口或者应用应该被关闭时发出WM_CLOSE消息当接收到WM_CLOSE消息时如果你愿意向用户提出是否真退出你知道让用户作确认或有出现或有什么应该注意事情发生时候往往弹出个消息框消息框制作是很容易由于它用途广泛我们还是介绍下:

MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);

这些参数尤其是最后需要些解释:
HWND hWnd:过会儿我将向你介绍个不含有它我保证
LPCTSTR lpText:这是将要显示在消息框里文本你可以用\\n等调整下格式
LPCCTSTR lpCaption:这是显示在消息框标题栏里文本
UINT uType:这个参数可以联合使用几个区别标记这些标记可以根据你选择有好多MB_打头标记供你选择联合使用时要用“|”分隔开下面列出了些常用:

……按钮类
◎ MB_ABORTRETRYIGNORE:建立有“Abort”、“Retry”、“Ignore”按钮消息框
◎ MB_OK:建立有“OK”按钮消息框
◎ MB_OKCANCEL:建立有“OK”和“Cancel”按钮消息框
◎ MB_RETRYCANCEL:建立有“Retry”、和“Cancel”按钮消息框
◎ MB_YESNO:建立有“Yes”和“NO”按钮消息框
◎ MB_YESNOCANCEL:建立有“Yes”、“No”和“Cancel”按钮消息框

……图标类
◎ MB_ICONEXCLAMATION:加个惊叹号图标
◎ MB_ICONINFORMATION:加个消息图标(好像是个问号)
◎ MB_ICONQUESTION:加个问号图标
◎ MB_ICONSTOP:加个停止图标

……默认按钮标志
◎ MB_DEFBUTTON1:设置第个按钮为默认按钮(默认按钮即消息框弹出后直接敲回车就被按下那个按钮)
◎ MB_DEFBUTTON2:第 2个为默认按钮
◎ MB_DEFBUTTON3:第 3个为默认按钮
◎ MB_DEFBUTTON4:第 4个为默认按钮

……其它标志
◎ MB_HELP:添加个帮助按钮通常按下该按钮或者敲F1键都将产生WM_HELP消息
◎ MB_RIGHT:文本右对齐
◎ MB_TOPMOST:设置消息框总在窗口最上面

我不知道你是如何想但是我想Microsoft定有员除了件事其它什么都不做那就是全天写#声明!^_^ 如果消息框建立失败返回值为0否则是下列任个值:
◎ IDABORT:“Abort”按钮被选择
◎ IDCANCEL:“Cancel”按钮被选择
◎ IDIGNORE:“Ignore”按钮被选择
◎ IDNO:“No”按钮被选择
◎ IDOK:“OK”按钮被选择
◎ IDRETRY:“Retry”按钮被选择
◎ IDYES:“Yes”按钮被选择
以上说了这么多我几乎都忘了我们原来话题了总的当收到WM_CLOSE消息你可以做两件事儿件是你接受默认处理返回个值你若这样做了应用或窗口按照计划关闭;再者你返回0应用或窗口将保持原样以下是代码基本部分:

(msg WM_CLOSE)
{
(MessageBox(hMainWindow, \"Are you sure want to quit?\", \"Notice\", MB_YESNO | MB_ICONEXCLAMATION) IDNO)
(0);

// otherwise, let the default handler take care of it
}

WM_DESTROY消息有点儿区别它是窗口正在关闭时发出当得到WM_DESTROY消息时候窗口已经从视觉上被删除个主窗口被关闭并不意味着应用结束了它将在没有窗口条件下继续运行然而个用户关闭了主窗口也就意味着他要结束应用所以如果你希望应用结束在收到WM_DESTROY消息时候你必须发出个WM_QUIT消息你可以使用PostMessage但由于这是个特殊情况就为它准备了个特殊:

VOID PostQuitMessage( nExitCode);

参数nExitCode是你应用返回给Windows个退出代码(通常是0)记住WinMain返回(实数)不是void(空)nExitCode参数值被赋值给wparamWM_QUIT消息表示要关闭应用所以得到这个消息后你应跳出循环把wparam返回给Windows下面是个简单WinMain例子:

WinMain(HINSTANCE hinstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine, nCmdShow)
{
// initialization stuff goes here

// loop - infinite!
while (TRUE)
{
// check the message queue
(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
(msg.message WM_QUIT) // exit loop _disibledevent=>

对不起罗罗嗦嗦讲了这么多但这些都是你必须要了解继续你对我耐心让我们看看基础GDI图形

☆ 绘制象素
终于到了本章戏肉儿!只要你取得了显示设备上下文句柄就可以用GDI绘制象素了记住GetDC()取得句柄绘制单个象素不出意外你就用SetPixel:

COLORREF SetPixel(
HDC hdc, // handle to device context
X, // x-coordinate of pixel
Y, // y-coordinate of pixel
COLORREF crColor // pixel color
);



返回类型COLORREF是我们没有讲过它不是个结构但是在表格0x00bbggrr里是个32位Bb是蓝色成分8位值gg是绿色rr是红色高字节00没有用总是00让我们看看里面参数:
HDC hdc:你将要GetDC()得到DC句柄(DC就是设备上下文)你只能GetDC()然后其它也可以用这个DC句柄了你每次绘制单个象素不用再取得新DC句柄了
XY:是象素x和y坐标是在客户区坐标(00)即是窗口客户区左上角坐标不是屏幕左上角坐标
COLORREF crColor:象素颜色设置象素颜色用RGB宏最简单RGB()括号中 3个值分别是红色、绿色和蓝色各个颜色值可从0-255间取区别值组合成区别颜色
如果成功将返回个颜色就是你要绘制象素颜色由于视频硬件限制返回颜色可能和时要求颜色并不如果失败返回-1如果你要设置窗口客户区左上角为红色将是这样:

SetPixel(hdc, 0, 0, RGB(255,0, 0));

是在假设你已经取得了DC句柄hdc后这个很简单是不是?这个更快版本是SetPixelV:

BOOL SetPixelV(
HDC hdc, // handle to device context
X, // x-coordinate of pixel
Y, // y-coordinate of pixel
COLORREF crColor // pixel color
);

参数相同但返回类型区别SetPixelV返回是布尔类型成功TRUE;失败FALSE如果你没有必要使用SetPixel提供额外信息那我们当然是选择使用SetPixelV(我们总是希望快些)
还有件事情我们需要得到绘制好象素值(颜色)没问题使用GetPixel就解决了:

COLORREF GetPixel(
HDC hdc, // handle to device context
XPos, // x-coordinate of pixel
nYPos // y-coordinate of pixel
);

很明显返回值是象素所在位置颜色如果坐标出了DC句柄控制区域返回值是CLR_INVALID参数同SetPixel差不多:DC句柄象素xy坐标绘制象素就说到这吧下面看看GDI文本输出

☆ GDI文本输出
有两个有关绘制文本其中简单点儿是TextOut原形是:

BOOL TextOut(
HDC hdc, // handle to device context
nXStart, // x-coordinate of starting position
nYStart, // y-coordinate of starting position
LPCTSTR lpString, // poer to
cbString // number of characters in
);

我们已经看过很多返回类型是布尔类型意思都样:成功TRUE失败FALSE参数如下:
HDC hdc:DC句柄它既可以使GetDC返回值也可以是在处理WM_PAINT消息时BeginPa返回值
nXStartnYStart:定义客户区内串开始位置串中第左上角位于坐标(xXStartnYStart)在默认DC中客户区左上角为(00)
LPCTSTR lpString:要显示由于下个参数给出长度所有串不需要空中止符
cbString:串中个数(长度)
TextOut使用当前文本颜色背景颜色和背景类型你若想自己改变这些就得用如下:

COLORREF SetTextColor(
HDC hdc, // handle to device context
COLORREF crColor // text color
);

COLORREF SetBkColor(
HDC hdc, // handle of device context
COLORREF crColor // background color value
);

SetTextColor设置当前文本颜色SetBkColor设置当前背景颜色参数解释是显而易见个是DC句柄第 2个是颜色你当然记得可以用RGB()宏来设置颜色例如设置文本为蓝色:SetTextColor(hdc,RGB(0,0,255))设置背景为白色:SetBkColor(hdc,RGB(255,255,255))最后设置背景类型用SetBkMode:

SetBkMode(
HDC hdc, // handle of device context
iBkMode // flag specying background mode
);

个参数就不讲了第 2个参数iBkMode可以分别取两个值:TRANSPARENT或OPAQUE如果取TRANSPARENT文本不会对背景造成影响如选择OPAQUE则相反返回值是背景模式
有关TextOut()还有点要说你可以控制文本排列方式用SetTextAlign实现:

UINT SetTextAlign(
HDC hdc, // handle to device context
UINT fMode // text-alignment flag
);

参数如下:
HDC hdc:DC句柄没有什么特别
UINT fMode:个标志或组标志(用“|”分开)将决定TextOut输出文本对齐模式你不可以随便组合要合理组合这些标志如下:
◎ TA_BASELINE:参考点将在文本基线
◎ TA_BOTTOM:参考点将在矩形范围底边
◎ TA_TOP:参考点将在矩形范围顶部
◎ TA_CENTER:参考点将在矩形范围中心
◎ TA_LEFT:参考点将在矩形范围左边
◎ TA_RIGHT:参考点将在矩形范围右边
◎ TA_NOUPDATECP:当前位置没有被文本输出参考点被每次传递
◎ TA_UPDATECP:当前位置被每次文本输出当前位置作为参考点
默认设置是TA_LEFT|TA_TOP|TA_NOUPDATECP如果你设置成TA_UPDATECP后来TextOut将忽略nXStart和nYStart这两个参数把文本放置在……OK段落我们来看看TextOut()兄弟DrawText():



DrawText(
HDC hDC, // handle to device context
LPCTSTR lpString, // poer to to draw
nCount, // length, in characters
LPRECT lpRect, // poer to struct with formatting dimensions
UINT uFormat // text-drawing flags
);

这个家伙有点儿复杂DrawText()能格式化文本多种排列方式返回值是文本象素高度返回0介绍说明失败让我们看看它参数:
HDC hDC:东东我们好朋友DC句柄
LPCTSTR lpString:要显示用双引号引起来
nCount:串中数量(串长度)
LPRECT lpRect:是RECT类型结构地址该结构包含了将要显示区域逻辑坐标
UINT uFormat:文本格式选项你可以用“|”符号组合下面列出最常用到标志:
◎ DT_BOTTOM:指定底部对齐文本必须和DT_SINGLELINE组合使用
◎ DT_CALCRECT:返回矩形宽度和高度在多文本行情况下DrawText()将使用lpRect所指向矩形宽度并扩展矩形底部直到包含文本最后在单文本行情况中DrawText()将改变矩形右边界使它包含最后不管在那种情况下DrawText()都返回格式化后文本高度但是不重新绘制文本
◎ DT_CENTER:文本水平居中
◎ DT_EXPANDATABS:扩充Tab键跳跃默认情况下每按次Tab键跳跃8个
◎ DT_LEFT:指定文本左对齐
◎ DT_NOCLIP:绘制屏幕时无需剪切当使用DT_NOCLIP后性能提高
◎ DT_RIGHT:指定文本右对齐
◎ DT_SINGLELINE:指定单行文本忽略回车和换行
◎ DT_TABSTOP:设置Tab键停止在uFormat低端字高阶字节(15-8)中存放Tab键每按次跳跃默认是8
◎ DT_TOP:顶部对齐文本(仅用于单行文本)
◎ DT_VCENTER:指定垂直居中(仅对单行文本)
还有些其它标志但你看到已经足够了有了这些你就可以轻松驾驭文本了但记住是以牺牲速度为代价你可以选择比较常用TextOut()文本输出就说道这吧让我们去学点儿令人兴奋东东吧!

☆ 用GDI显示位图
记得我告诉过你位图是很容易操纵Windows本身就是位图现在让我们看看到底有多容易吧!用GDI显示位图需要 4个基本步骤:
1、得到你要操作窗口DC句柄
2、获得位图句柄
3、为位图创建设备上下文
4、传送位图
你已经知道第步如何做了以前我也间接提到过第 2步做法但没有具体说我说过通过LoadBitmap可以得到位图资源句柄但它有些过时了个更灵活LoadImage取代了它现在让我们看看如何使用这个新原形如下:

HANDLE LoadImage(
HINSTANCE hinst, // handle of the instance containing the image
LPCTSTR lpszName, // name or identier of image
UINT uType, // type of image
cxDesired, // desired width
cyDesired, // desired height
UINT fuLoad // load flags
);

如果失败返回NULL成功你得到位图句柄意味着你就可以从资源或外部文件位图了注意这个还可以取得光标、图标句柄所以返回类型只是简单HANDLE在Visual C++6.0中你需要用HBITMAP类型定义位图句柄变量否则编译器会生气例如:

HBITMAP hBitmap;
hBitmap =LoadImage(……);

下面是LoadImage()参数介绍说明:
HINSTANCE hinst:如果你从资源位图这应该是你应用例子如果你要从外部文件位图就把它设置为NULL
LPCTSTR lpszName:这个要么是资源标识符记住用MAKEINTRESOURCE宏转变数字常量;要么就是你要图象完整文件名称
UINT uType:根据你对象来决定应该是IMAGE_BITMAP、IMAGE_CURSOR和IMAGE_ICON中
cxDesiredcyDesired:这是你希望图象尺寸如果你都设置为0将是图象真实尺寸
UINT fuLoad:这是可以组合用标志当然是用“|”来连接以下是些常用标志:
◎ LR_CREATEDIBSECTION:如果uType是IMAGE_BITMAP将导致返回个DIB(DIB设备无关位图)基本意思就是返回个不依赖于显示设备位图
◎ LR_DEFAULTSIZE:对于光标和图标如果cxDesired和cyDesired都设置为0这个标志将启用Windows默认尺寸而不是图形实际尺寸
◎ LR_LOADFROMFILE:如果你要从外部文件中调入图象你就必须用这个标志
条件允许你应当尽量使用LR_CREATEDIBSECTION和LR_LOADFROMFILE这两个标志现在你已经得到了图象(过去我们总说位图好像不太准确毕竟有时我们不从资源里)句柄步你必须建立设备上下文把图象放进去位图应该独有特点是:它只能被选入到内存设备上下文中内存设备上下文被定义为个具有显示表面设备上下文它只存在于内存中并且和特定设备上下文相关使用内存设备上下文你必须首先创建它CreateCompatibleDC正是用于这个目般形式如下:

HDC CreateCompatibleDC(HDC hdc);

参数是和内存设备上下文相兼容设备上下文句柄如果内存设备上下文和视频屏幕兼容则这个参数可以为NULL我们就用NULL如果失败返回NULL现在我们把位图(或图象)放入内存失败上下文我们用这个:

HGDIOBJ SelectObject(
HDC hdc, // handle to device context
HGDIOBJ hgdiobj // handle to object
);



返回类型HGDIOBJ是个比HBITMAP更通用类型不用担心HGDIOBJ和HBITMAP致性没有任何问题以下是参数介绍说明:
HDC hdc:是设备上下文句柄图象必须是内存设备上下文句柄
HGDIOBJ hgdiobj:要对象句柄有位图、画刷、字体、画笔等这里是位图(图象)句柄
返回值是要调入设备上下文中对象句柄这里是位图句柄如果失败返回NULL
现在你已经把位图装入设备上下文你还需要进行最后步:把内存DC里内容拷贝到显示设备上下文中然而我们首先要得到位图些信息如尺寸这是显示图象时必须所以我们还需要另GetObject它可以用于获得对象信息当然这里我们是要获得位图信息般形式如下:

GetObject(
HGDIOBJ hgdiobj, // handle to graphics object of erest
cbBuffer, // size of buffer for object information
LPVOID lpvObject // poer to buffer for object information
);

返回值是个字节数如果失败返回0当GetObject目标是位图时返回信息是和位图宽度、高度和颜色格式有关结构成员参数介绍说明如下:
HGDIOBJ hgdiobj:要得到信息对象句柄这里我们传送位图句柄
cbBuffer:存放返回信息缓冲区大小对于位图我们将得到BITMAP类型结构所以这里设置成(BITMAP)
LPVOID lpvObject:指向存放由返回信息缓冲区指针
你需要定义个BITMAP结构类型变量GetObject放入缓冲区信息由于BITMAP结构对我们来说是个新结构所以就介绍下:

typedef struct tagBITMAP { // bm
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP;

很多成员但我们实际上只对其中两个有兴趣但我们还是都介绍下:
LONG bmType:指定位图类型必须为0有用吧!
LONG bmWidthbmHeight:分别是位图宽度和高度以象素为单位必须都大于0
LONG bmWidthBytes:指定每行扫描线中字节数Windows假定位图是字对齐所以这个值必须能够被2整除
LONG bmPlanes:指定颜色面数目
LONG bmBitsPixel:指定表述象素颜色所需位数(好像没什么用)
LPVOID bmBits:如果你想存取实际位图数据这个指针指向位图位值得位置
OK差不多了旦位图被选入内存设备上下文且代码已经得到了位图宽度和高度必要信息后我们就可以将内存中存储位图通过位块传输到达屏幕然后在任意位置对它进行显示我说过处理位图是很容易对吧?(如果你觉得不容易只能怪我说不够好)有两个需要介绍说明先说第个:

BOOL BitBlt(
HDC hdcDest, // handle to destination device context
nXDest, // x-coordinate of destination rectangle\'s upper-left corner
nYDest, // y-coordinate of destination rectangle\'s upper-left corner
nWidth, // width of destination rectangle
nHeight, // height of destination rectangle
HDC hdcSrc, // handle to source device context
nXSrc, // x-coordinate of source rectangle\'s upper-left corner
nYSrc, // y-coordinate of source rectangle\'s upper-left corner
DWORD dwRop // raster operation code
);

BitBlt是执行位图显示操作最简单且最直接思路方法根据成功或失败返回值是TRUE或FALSE布尔都是这样有很多参数但很好理解:
HDC hdcDest:目标设备上下文句柄根据我们情况应该是显示设备上下文句柄
nXDestnYDest:目标矩形左上角x、y坐标也就是被显示位图左上角屏幕位置
nWidthnHeight:位图宽度和高度
HDC hdcSrc:原来设备上下文句柄根据我们情况应该是内存设备上下文句柄
nXSrcnYSrc:源位图x、y坐标由于进行位块传输矩形必须和在参数nWidth和参数nHeight中定义尺寸相同所以通常都设为0但不定总是这样例如你只想显示位图部分就不能都设置为0
※ DWORD dwRop:有很多光栅代码你可以选择但只有个我们感兴趣,SRCCOPY它直接把源DC内内容拷贝到目标DC中
以上就是第下面说说第 2个StretchBlt简单位图实际拉伸或压缩就是通过它来实现般形式如下:

BOOL StretchBlt(
HDC hdcDest, // handle to destination device context
nXOriginDest, // x-coordinate of upper-left corner of dest. rectangle
nYOriginDest, // y-coordinate of upper-left corner of dest. rectangle
nWidthDest, // width of destination rectangle
nHeightDest, // height of destination rectangle
HDC hdcSrc, // handle to source device context
nXOriginSrc, // x-coordinate of upper-left corner of source rectangle
nYOriginSrc, // y-coordinate of upper-left corner of source rectangle
nWidthSrc, // width of source rectangle
nHeightSrc, // height of source rectangle
DWORD dwRop // raster operation code
);



它比BitBlt复杂所以它比BitBlt它们参数差不多并且有了注释这里就不再重复了光栅代码也是选择SRCCOPY现在只剩下最后件事情——清除建立个设备上下文【CreateCompatibleDC(HDC hdc)】区别于得到设备上下文【(GetDC)】不能用ReleaseDC()要用:

BOOL DeleteDC(HDC hdc);

参数是建立DC句柄返回值是个布尔类型你知道布尔类型是如何回事对吧?All right感觉还不坏吧下面我们把以上步骤合并隆重推出个超级大包子(我觉得它有点象包子)我事先假设你已经定义了个全局应用例子句柄hinstance

ShowBitmapResource(HDC hDestDC, xDest, yDest, nResID)
{
HDC hSrcDC; // source DC - memory device context
HBITMAP hbitmap; // handle to the bitmap resource
BITMAP bmp; // structure for bitmap info
nHeight, nWidth; // bitmap dimensions

// first load the bitmap resource
((hbitmap = (HBITMAP)LoadImage(hinstance, MAKEINTRESOURCE(nResID), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)) NULL)
(FALSE);

// create a DC for the bitmap to use
((hSrcDC = CreateCompatibleDC(NULL)) NULL)
(FALSE);

// select the bitmap o the DC
(SelectObject(hSrcDC, hbitmap) NULL)
(FALSE);

// get image dimensions
(GetObject(hbitmap, (BITMAP), &bmp) 0)
(FALSE);

nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;

// copy image from _disibledevent=>(FALSE);

// kill the memory DC
DeleteDC(hSrcDC);

// success!
(TRUE);
}

☆ 最后件事
你可能还不太明白但你现在确实有足够知识用Windows GDI在窗口里建立个小游戏了!你能创建窗口显示图形游戏逻辑同DOS下C语言游戏逻辑即使你不知道DOS下C你玩过游戏吧玩过就应该有个初步认识你甚至能用鼠标处理产生消息但有件事儿我们漏了可能它不属于本章范围但我们不能不提它——键盘支持Windows提供了个非常好GetAsyncKeyState检测键盘状态它返回个16位高字节显示了个键子是否被按下原形如下:

SHORT GetAsyncKeyState( vKey);

参数是键子标识符都以VK_开头例如些最常用:VK_RETURNVK_ESCAPEVK_UPVK_LEFTVK_RIGHT和VK_DOWN你甚至可以用VK_LBUTTON和VK_RBUTTON标识鼠标按键多方便呀观察高字节如果是1键子就是正被按下我总是用个宏来做这些:

# KEYSTATE(vknum) ((GetAsyncKeyState(vknum) & 0x8000) ? TRUE : FALSE)

如果你不仔细可能还没有注意到这个条件符号(?)在C语言中是个 3元算子——评估它左边表达式如果表达式值为true表达式就是冒号左边如果false就是冒号右边明白了吗?好极了!

☆ 整理总结
现在你可以做GDI基础游戏了本章比我原打算要长由于讲内容较多我做了个例程给你借鉴它是类似于屏幕保护东东只生成个.EXE文件格式不是全屏个小窗口里它展示了出口创建和相应了几个键盘消息…………

Tags:  网络游戏的起源 网络游戏起源 游戏的起源 编程初学者

延伸阅读

最新评论

发表评论