消灭星星游戏程序设计【连载四】——图片的移动
大家每次都可以在页面中下载本节内容的实现代码,一步一步从简单开始,逐步完成游戏的各种功能,如果大家有任何问题也欢迎留言交流。
游戏整体效果展示:
1、图片可以开始动起来了
我们在前边讲过在游戏程序中显示出漂亮的图片的方法,游戏画面好看多了。但是,我们显示的图片是静止不动的,如果图片能够动起来,是不是会有更好的效果呢。我们能不能让图片动起来呢?
我们现在就来尝试一下。聪明的你一定会想到,我们只需要改变一下图片显示代码中的指定位置,图片应该就能够移动了。对了就是这样,我们这里尝试让图片跟随鼠标坐标位置的移动而移动,来感受一下效果。但首先,我们要怎样才能获取鼠标坐标位置呢,获取鼠标位置的方法不止一种,这里为了方便演示,我们采用一种特殊的方式,这种方式可以随时随地获取鼠标的位置,并将位置保存到POINT变量中。
//用于保存鼠标位置
POINT tempCursorPos;
//获取鼠标在显示器屏幕中的位置
GetCursorPos(&tempCursorPos);
//鼠标屏幕位置转换为程序内位置
ScreenToClient(hWnd,&tempCursorPos);
同时,将我们要显示图片的显示位置修改为刚刚获获取的鼠标位置,就能达到图片随鼠标移动的效果。你只需要在显示图片时,将TransparentBlt函数的第2个和第3个参数修改为鼠标的坐标位置tempCursorPos.x和tempCursorPos.y即可。
//显示图片
if(true)
{
//获取图像大小信息
BITMAP BM;
GetObject(hPopStar,sizeof(BITMAP),&BM);
//显示图像
HDC hTemDC=CreateCompatibleDC(hDC);
SelectObject(hTemDC,hPopStar);
//TransparentBlt(hDC,165-BM.bmWidth/2,100,BM.bmWidth,BM.bmHeight,hTemDC,0,0,BM.bmWidth,BM.bmHeight,RGB(0,0,0));
TransparentBlt(hDC,tempCursorPos.x,tempCursorPos.y,BM.bmWidth,BM.bmHeight,hTemDC,0,0,BM.bmWidth,BM.bmHeight,RGB(0,0,0));
DeleteDC(hTemDC);
}
但我们一定要注意,需要在WM_MOUSEMOVE消息处理中加入重新绘制窗口的命令,否则窗口不重绘就无法看到图片移动的效果。(这里需要注意,我们在WM_MOUSEMOVE中添加InvalidateRect刷新操作只是临时操作,后期添加完定时器后必须删除,否则会出现卡顿)
case WM_MOUSEMOVE:
//当鼠标移动式刷新窗口内容
InvalidateRect(hWnd,NULL,false);
return 0;
2、图片的移动效果出乎我们的意料
修改后运行了一下效果,我们发现图片是可以跟随鼠标移动了,但移动后的效果出乎我们的意料,图片的残影很快覆盖了整个窗口屏幕,这不是我们想要的结果,我们想要的只是在鼠标旁边只显示一个图片(说句题外话,这残影的效果还是挺不错的),效果图如下:
移动后会在程序界面内留下残影,这显然并不是我们想要的效果。发生这样的情况,主要是由于图片每次移动到新的位置后,原来位置上的图片并没有擦除掉,这需要我们对原来位置上的图片进行手动擦除。
3、图片残影的解决办法
我们可以将图片原来显示位置的区域用黑色进行擦除,这样就能始终保存画面的清洁,只在图片的最新位置显示图片。但由于我们这里是一个小游戏,为了方便操作,我们在每一帧窗口重新绘制前,将整个屏幕用黑色进行擦除,会简单很多。如果朋友们有需要逐个擦除的需求,可以自行进行相应处理,此处就不再研究。
解决办法是我们需要在WM_PAINT消息开始处理所有绘图前,用黑色擦除程序界面。
RECT tempClientRect;
GetClientRect(hWnd,&tempClientRect);
//用黑色擦除整个屏幕
BitBlt(hDC,0,0,tempClientRect.right,tempClientRect.bottom,NULL,0,0,BLACKNESS);
处理后显示界面恢复了正常,图片已经能够跟随鼠标移动正确的显示出来了。
4、又出现了问题,屏幕偶尔会出现闪烁
当图片跟随鼠标快速移动时,有没有感觉屏幕中的图片或文字会偶尔出现闪烁的问题,这种情况我们必须要进行处理,否则游戏的体验感会下降很多。
这主要时由于窗体在响应WM_PAINT消息的时候,要进行复杂的图像处理,重绘时由于过频的刷新操作而引起的闪烁现象。
5、屏幕闪烁的解决办法
这里需要使用双缓冲技术解决这个问题。我理解双缓冲技术相当于把原来直接在屏幕hDC上绘制各种图像的方式,改变成先在兼容的hMemDC中绘制各种图像,绘制完毕再直接将hMemDC整屏复制到用户屏幕hDC上,具体使用双缓冲的整个步骤大致如下:
(1)、定义内存hMemDC设备
(2)、创建并加载与原屏幕兼容的hMemDC设备
(2)、将所有绘图由原来的hDC设备变更为在hMemDC设备中绘制
(3)、在hMemDC设备中绘制完毕后,将hMemDC中的图像整屏复制到hDC设备中
完成以上操作后,图形绘制时屏幕没有任何闪烁。添加双缓存方案后整个WM_PAINT内的代码修改如下:
case WM_PAINT:
PAINTSTRUCT PS;
hDC=BeginPaint(hWnd,&PS);
//刷新整个程序界面
if(true)
{
//获取窗口大小
RECT tempClientRect;
GetClientRect(hWnd,&tempClientRect);
//双缓冲后每次创建的兼容设备都是初始化黑色平面,无需在手动重绘黑色,后续删除
//BitBlt(hDC,0,0,tempClientRect.right,tempClientRect.bottom,NULL,0,0,BLACKNESS);
//创建与原窗口兼容位图
HBITMAP hMemBM=CreateCompatibleBitmap(hDC,tempClientRect.right,tempClientRect.bottom);
//创建与原窗口兼容的DC设备
HDC hMemDC=CreateCompatibleDC(hDC);
//将位图载入内容DC设备
SelectObject(hMemDC,hMemBM);
//这里添加文字和图片
if(true)
{
char szTemp[1024]="PopStar";
SetTextColor(hMemDC,RGB(255,0,0));
TextOut(hMemDC,10,10,szTemp,strlen(szTemp));
}
if(true)
{
char szTemp[1024]="PopStar";
RECT tempRect={10,30,100,50};
SetTextColor(hMemDC,RGB(255,0,0));
DrawText(hMemDC,szTemp,strlen(szTemp),&tempRect,DT_LEFT);
}
//用于保存鼠标位置
POINT tempCursorPos;
//获取鼠标在显示器屏幕中的位置
GetCursorPos(&tempCursorPos);
//鼠标屏幕位置转换为程序内位置
ScreenToClient(hWnd,&tempCursorPos);
//显示图片
if(true)
{
//获取图像大小信息
BITMAP BM;
GetObject(hPopStar,sizeof(BITMAP),&BM);
//显示图像
HDC hTemDC=CreateCompatibleDC(hDC);
SelectObject(hTemDC,hPopStar);
//TransparentBlt(hDC,165-BM.bmWidth/2,100,BM.bmWidth,BM.bmHeight,hTemDC,0,0,BM.bmWidth,BM.bmHeight,RGB(0,0,0));
TransparentBlt(hMemDC,tempCursorPos.x,tempCursorPos.y,BM.bmWidth,BM.bmHeight,hTemDC,0,0,BM.bmWidth,BM.bmHeight,RGB(0,0,0));
DeleteDC(hTemDC);
}
//这里添加文字和图片
BitBlt(hDC,0,0,tempClientRect.right,tempClientRect.bottom,hMemDC,0,0,SRCCOPY);
//删除兼容DC设备
DeleteDC(hMemDC);
//删除位图资源
DeleteObject(hMemBM);
}
ReleaseDC(hWnd,hDC);
return 0;
6、小结
完成以上图片的移动后,游戏界面已经不在那么生硬了,具有一定的互动性。同时在解决了图片的显示问题后,游戏初期的基础工作已经铺垫完成,后续继续完善游戏内容。如果大家有任何问题也欢迎留言交流。