基于OK6410嵌入式WinCE 6.0的“街霸”游戏的设计与实现

来源:互联网 发布:c语言怎么打开特定文件 编辑:程序博客网 时间:2024/05/16 23:46

很久没写博客了。这几天在做嵌入式的课程设计,忙活了将近两周。实验平台的开发板子用的是OK6410,操作系统是WindowsCE,微软旗下一个嵌入式方向的产品。本打算做一个三维的游戏,结果学了一段时间DirectX的东西。悲哀的是WinCE目前还不支持DirectX的API。囧,只好做一个二维的游戏。上周末一兄弟去北京工作,在寝室为他饯行,喝了几杯白兰地,结果周一难受得在床上躺了一天,呵呵,其实我酒量很好的。周二开始动工,周四写得差不多了,周五又调了一天。时间不多,所以游戏做得也比较简单。不扯了,下面开始总结一下。

其实无论是二维的,还是三维的游戏开发,思路都是一样的。实现游戏,尤其是格斗这一类游戏,很重要的一个东西,就是游戏动画技术,怎么让游戏中的人物动起来,这个需要想办法。其实主流的方式有两种,一种是类似“定时器”的东西,每隔多少毫秒定时刷新屏幕,不幸的是,这种方式不太准确,尤其是在Windows下利用响应WM_TIMER消息来实现的这种方式更加不准确,因为这种消息机制不是异步的。换句话说,如果指定定时器的时间间隔是1s,在消息队列的这种机制下,你根本就不能保证程序每隔1s就会收到一个WM_TIMER消息;另外的一种技术就是“游戏循环”,这个比较准确,其实就是将Windows程序中的消息循环机制稍微地加以修改,具体的实现方法就是,判断队列中目前是否有要处理的消息,如果有则进行处理,否则的话,就按照设定的时间间隔来重绘画面。

开发游戏,不可或缺的一个步骤就是游戏模型的设计。比如说,格斗中的人物,怎么在电脑屏幕上画出来,这是个问题。两种方式,自己用写代码的方式把游戏人物画出来。在大型的游戏中,这种方式不太常见,因为这样做太耗时了,GPU的处理速度也跟不上。另外一种方式,就是自己用专门的绘图软件,比如说二维中的Photoshop,三维中的3ds Max,把游戏人物设计好以后,存成特定格式的文件,直接加载进去就可以了。这种方式比较通用,对于一些大型的三维游戏,一般都有专门的设计人员负责游戏模型的创建。此次的游戏模型设计也是采用这种方式,只不过,方式比较低级,用的是bmp位图的方式存储。把人物动作的每一帧存下来,为了编程方便,自然需要规定好每一帧的大小。比如说,对于下面这个已经设计好的模型(Ryu很经典的发大招时的动作):


怎么让设计的模型显示在屏幕合适的位置上,还是有点讲究的。这里用到一个映射技术,即如何将游戏模型中的每一个像素点映射到显示器上合适的位置。所以,这里需要API来获取屏幕的位置和游戏模型像素点的位置。在Windows中,这种API已经很成熟了,可以直接调用,比如说GetObject、GetClientRect等。绘制人物直接调用一些GDI函数就可以了,比如说BitBlt、TransparentBlt、StretchBlt等。

其实,游戏的绘制,三维中叫做渲染,远远没有想象中的那么简单,尤其是闪屏的问题,比较令人恼火,上面的思路多么清晰啊,怎么绘制出来的人物一直在闪烁?!仔细分析一下,也很容易知道问题的原因所在,因为显示器是按照行来刷新的,一行一行地进行绘制,你在写代码的时候,直接写到显示器中(实际的开发中是一个DC,Device Context,一般中文翻译成设备上下文),即直接把绘制的代码写到一个DC中,就可以直接显示屏幕上。要知道,程序的执行还是按照过程的,一行一行地执行,必定存在一个先后绘制的问题,所以屏幕的闪烁也是正常的。那么怎么解决这个问题呢?可以利用一种缓冲显示技术,比如说双缓冲(double buffer)、三缓冲技术(triple buffering)。这种技术,有一个更能描述其功能的名字,页面切换技术(page flipping)。就相当于,实现把所有的模型渲染完毕,存在某一个地方,要现实的时候直接拷贝过来就行了,这就跟切换的效果是一样的,切换肯定不存在先后绘制的闪屏问题。这次的游戏设计,用了三缓冲技术。也就是说新建立一个与g_hdc(g_hdc负责最终在显示器上的显示)兼容的两个DC,一个g_bufdc,一个是g_hdcMem。首先把所有的资源统一加载到g_bufdc,再进行透明化处理等渲染操作,将渲染处理完的结果从g_bufdc贴图到g_hdcMem(注:g_hdcMem可以在初始化的时候贴上一张与窗口大小相同的空位图),最后再将所有渲染处理完的画面整体拷贝显示到屏幕g_hdc中。这样就有效地避免了屏幕的闪烁问题。

到目前为止,实现游戏人物的绘制已经没有问题。然后,游戏开发所带来的细节问题还有很多,比如说游戏的物理建模、重力系统、粒子系统、碰撞检测、游戏AI等,这些方面也需要考虑,因为它们也是游戏设计不可或缺的一部分。此次的游戏设计也有所涉及。不扯了,先贴几张图,再贴代码。

开机画面,卷轴效果:




Ryu发绝招打出的球不太好截图,囧。




代码写得比较烂,勿喷!

主要结构体设计:

//动作枚举enum ActionType{ACTION_TYPE_BOXING=0,//拳击ACTION_TYPE_KICK=1,//踢腿ACTION_TYPE_WAVEBOXING=2,//绝招ACTION_TYPE_NULL=3,//无攻击动作};//绝招时发出的子弹struct WaveBullets {int x,y;//波的坐标bool exist;};//雪花粒子系统struct Snow {int x,y;//雪花的位置坐标BOOL exist;};

主要变量:

//窗口实例全局变量HINSTANCE g_hInst=NULL;//三缓冲思想的三个DCHDC g_hdc=NULL,g_hdcMem=NULL,g_bufdc=NULL;/************************************************************************//* Hero的相关变量                                                       *//************************************************************************//*g_hHeroDirection表示Hero的移动方向,分为wait,go back,go forward三种;g_hHeroHit存储Hero的出招图,分为拳打、脚踢和绝招三种;g_hHeroWave存储Hero发出绝招时推出的波形子弹;g_hHeroBeAttactedHeavy存储Hero被重击的效果图;g_hHeroBeAttactedLight存储Hero被轻击的效果图;g_hHeroBlood存储Hero的血量图;g_hHeroDie存储Hero阵亡以后的效果图,g_hHeroWin存储Hero胜利以后的效果图*/HBITMAP g_hHeroDirection[3]={NULL},g_hHeroHit[3]={NULL},g_hHeroWave=NULL,g_hHeroBeAttactedHeavy=NULL,g_hHeroBeAttactedLight=NULL,g_hHeroBlood=NULL,g_hHeroDie=NULL,g_hHeroWin=NULL;/*g_iHeroFileNum记录Hero画面帧号;g_iHeroX,g_iHeroY分别记录Hero的实时坐标位置;g_iHeroDirection记录方向,0,1,2分别表示前进、后退、wait三个方向g_iHeroStrength记录Hero的血量(体力值)*/int g_iHeroFileNum=0,g_iHeroX=0,g_iHeroY=0,g_iHeroDirection=0;float g_iHeroStrength=0.0;//Hero的出招类型ActionType g_HeroActionType;//heroWaves存储Hero打出的波形子弹的信息WaveBullets heroWaves[10];//g_iHeroWavesNum记录Hero的波形子弹的数目int g_iHeroWavesNum=0;/************************************************************************//* Robot的相关变量                                                      *//************************************************************************//*g_hRobotDirection存储Robot的移动方向,也分为wait,go back,go forward三种;g_hRobotHit存储Robot的出招图,也分为拳打、脚踢和绝招三种;g_hRobotWave存储Robot发出绝招时推出的波形子弹;g_hRobotBeAttactedHeavy存储Robot被重击的效果图;g_hRobotBeAttactedLight存储Robot被轻击的效果图;g_hRobotBlood存储Robot的血量图;g_hRobotDie存储Robot阵亡以后的效果图*/HBITMAP g_hRobotDirection[3]={NULL},g_hRobotWave=NULL,g_hRobotHit[3]={NULL},g_hRobotBeAttactedHeavy=NULL,g_hRobotBeAttactedLight=NULL,g_hRobotBlood=NULL,g_hRobotDie=NULL;/*g_iRobotFileNum存储Robot图片帧号,g_iRobotX、g_iRobotY记录Robot的位置;g_iRobotDirection记录Robot的移动方向,0/1/2分别表示前进、后退、wait;g_iRobotStrength记录Robot的体力值*/int g_iRobotFileNum=0,g_iRobotX=0,g_iRobotY=0,g_iRobotDirection=0;float g_iRobotStrength=0.0;//Robot的出招类型ActionType g_RobotActionType;//robotWaves存储Robot打出的波形子弹的信息WaveBullets robotWaves[10];//g_iRobotWaveNum记录Robot的子弹的数目int g_iRobotWaveNum=0;/************************************************************************//* 游戏公共资源部分                                                     *//************************************************************************//*g_hLogoKO存储血量图中间的KO标志;g_hBackGround存储游戏背景图;g_hLoading存储游戏初始加载的效果图;g_hStart存储游戏的开始画面;g_hFire存储火焰燃烧画面;g_hTryAgain存储游戏结束画面;g_hSnow存储雪花粒子图*/HBITMAP g_hLogoKO=NULL,g_hBackGround=NULL,g_hLoading=NULL,g_hStart=NULL,g_hFire=NULL,g_hTryAgain=NULL,g_hSnow=NULL;;//g_iFireFrameNum记录火焰图的帧号int g_iFireFrameNum=0;//存储Hero出招图记录的宽度和高度信息BITMAP g_bmpHeroHit;//存储Robot出招图记录的宽度和高度信息BITMAP g_bmpRobotHit;//存储Hero血量位图记录的宽度和高度信息BITMAP g_bmpHeroBlood;//存储Robot血量图记录的宽度和高度信息BITMAP g_bmpRobotBlood;//血量图中间的KO logo位图信息BITMAP g_bmpKO;/*声明两个变量来记录时间,g_tPre记录上一次绘图的时间;g_tNow记录此次准备绘图的时间*/DWORD g_tHeroPre=0,g_tHeroNow=0;//SnowFlowers存储雪花粒子信息Snow SnowFlowers[100];//g_SnowNum记录雪花的数目int g_SnowNum=0;/************************************************************************//* Flag标志部分                                                         *//************************************************************************//*g_heroActionFlag、g_robotActionFlag标识人物的是否出招,用于区别走动和出招两个不同的动作;g_heroBeAttactedLightFlag、g_heroBeAttactedHeavyFlag、g_robotBeAttactedLightFlag、g_robotBeAttactedHeavyFlag用于标识人物是否收到重击或者轻击;g_gameOverFlag标识游戏是否结束;first_start用于标识游戏是否第一次启动*/bool g_heroActionFlag;bool g_robotActionFlag;bool g_heroBeAttactedLightFlag;bool g_heroBeAttactedHeavyFlag;bool g_robotBeAttactedLightFlag;bool g_robotBeAttactedHeavyFlag;bool g_gameOverFlag;bool first_start;

几个核心函数:

WinMain主程序的入口点:

int WINAPI WinMain( __in HINSTANCE hInstance,    __in_opt HINSTANCE hPrevInstance,    __in_opt LPWSTR lpCmdLine,   __in int nShowCmd ){//1.创建一个窗体类WNDCLASS ws;g_hInst=hInstance;ws.cbClsExtra= 0;ws.cbWndExtra   = 0;ws.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);ws.hCursor= NULL;ws.hIcon= NULL;ws.hInstance= hInstance;ws.lpfnWndProc= WndProc;ws.lpszClassName = TEXT("Hello");ws.lpszMenuName  = NULL;ws.style= CS_VREDRAW | CS_HREDRAW;//2.注册窗体类if (! RegisterClass(&ws)) return -1;//3.创建窗体UINT sysWidth=GetSystemMetrics(SM_CXSCREEN);UINT sysHeight=GetSystemMetrics(SM_CYSCREEN);HWND hwnd = CreateWindow(TEXT("Hello"),TEXT("Street Fighter V1.0"),WS_VISIBLE | WS_BORDER | WS_SYSMENU /*| WS_MINIMIZEBOX*/ /*| WS_MAXIMIZEBOX*/ | WS_CAPTION,0,0,sysWidth,sysHeight,NULL,NULL,hInstance,NULL);//4.更新窗体内容UpdateWindow(hwnd);ShowWindow(hwnd,nShowCmd);if (!Game_Init (hwnd)) {MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0);return FALSE;}MSG msg={0};//5.获取系统消息while(msg.message!=WM_QUIT){if (PeekMessage(&msg,0,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else{g_tHeroNow=GetTickCount();//获取当前系统时间if ((g_tHeroNow-g_tHeroPre>=50)/*&&!first_start*/){Game_Paint(hwnd);}}}return 1;}

窗口过程函数:

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){RECT rect;GetClientRect(hwnd,&rect);switch (message){// case WM_TIMER:// Game_Paint(hwnd);// break;case WM_KEYDOWN:switch (wParam){case VK_P://开始游戏//first_start=false;break;case VK_Q://退出游戏DestroyWindow(hwnd);PostQuitMessage(0);break;case VK_ESCAPE:DestroyWindow(hwnd);PostQuitMessage(0);break;//方向控制(测试发现WINCE下不支持字母)case VK_LEFT://后退if(!g_gameOverFlag)//游戏未结束{g_heroActionFlag=false;g_HeroActionType=ACTION_TYPE_NULL;g_robotBeAttactedLightFlag=false;g_robotBeAttactedHeavyFlag=false;g_iHeroX-=10;g_iHeroDirection=0;if (g_iHeroX<=0)g_iHeroX=0;g_iRobotDirection=1;//Hero后退则Robot前进if (g_iRobotX>(g_iHeroX+g_bmpHeroHit.bmWidth/6))//未靠近Hero{g_iRobotX-=(10+rand()%5);//以10-15的像素向Hero靠近}else{g_iRobotX=(g_iHeroX+g_bmpHeroHit.bmWidth/6);}}else//游戏结束{g_iHeroDirection=2;g_heroActionFlag=false;g_HeroActionType=ACTION_TYPE_NULL;}break;case VK_RIGHT://前进if (!g_gameOverFlag)//游戏未结束{g_heroActionFlag=false;g_robotBeAttactedLightFlag=false;g_robotBeAttactedHeavyFlag=false;g_iHeroX+=10;g_iHeroDirection=1;if(g_iHeroX>=(g_iRobotX-g_bmpRobotHit.bmWidth/6))//Hero靠近Robot则不能再靠近g_iHeroX=g_iRobotX-g_bmpRobotHit.bmWidth/6;//g_RobotActionType=ACTION_TYPE_KICK;g_iRobotDirection=0;//Hero前进则Robot回退g_iRobotX+=(10+rand()%5);if(g_iRobotX>=(rect.right-116))g_iRobotX=rect.right-116;}else//游戏结束{g_iHeroDirection=2;g_heroActionFlag=false;g_HeroActionType=ACTION_TYPE_NULL;}break;//招式控制case VK_J:g_heroActionFlag=true;g_HeroActionType=ACTION_TYPE_BOXING;//g_RobotActionType=ACTION_TYPE_KICK;break;case VK_K:g_heroActionFlag=true;g_HeroActionType=ACTION_TYPE_KICK;//g_RobotActionType=ACTION_TYPE_BOXING;break;case VK_L:g_heroActionFlag=true;g_HeroActionType=ACTION_TYPE_WAVEBOXING;//g_robotBeAttactedHeavyFlag=true;for (int i=0;i<10;i++){if (!heroWaves[i].exist){heroWaves[i].x=g_iHeroX+50;heroWaves[i].y=g_iHeroY+20;//大概处于出手位置,更显真实heroWaves[i].exist=true;g_iHeroWavesNum++;//波的数目累加break;}}break;}break;case WM_DESTROY:Game_CleanUp(hwnd);PostQuitMessage( 0 );break;default://g_heroActionFlag=false;//g_robotActionFlag=true;g_iRobotDirection=2;//wait防守状态g_iHeroDirection=2;//wait的动作,处于防守状态return DefWindowProc(hwnd,message,wParam,lParam);}return 0;}

游戏资源初始化函数:

BOOL Game_Init( HWND hwnd ){HBITMAP wndBmp;g_hdc=GetDC(hwnd);//先创建内存DC,再创建一个缓冲DCg_hdcMem=CreateCompatibleDC(g_hdc);g_bufdc=CreateCompatibleDC(g_hdc);RECT wndRect;//窗体矩形GetClientRect(hwnd,&wndRect);//建一个和窗口大小相同的空的位图对象wndBmp=CreateCompatibleBitmap(g_hdc,wndRect.right,wndRect.bottom);//将空的位图放到g_hdcMem中SelectObject(g_hdcMem,wndBmp);//加载各种位图资源g_hBackGround=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_BKGRD));//g_hBlood=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_BLOOD));g_hHeroDirection[0]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_GOBACK));g_hHeroDirection[1]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_GOFORWARD));g_hHeroDirection[2]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_WAIT));g_hHeroHit[0]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_BOXING));g_hHeroHit[1]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_KICK));g_hHeroHit[2]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_WAVEBOXING));g_hHeroWave=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_WAVE));g_hHeroBeAttactedLight=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_BEATTACTED_LIGHT));g_hHeroBeAttactedHeavy=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_BEATTACTED_HEAVY));g_hHeroBlood=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_BLOOD));g_hHeroDie=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_DIE));g_hHeroWin=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R1_WIN));g_hRobotDirection[0]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_GOBACK));g_hRobotDirection[1]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_GOFORWARD));g_hRobotDirection[2]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_WAIT));g_hRobotHit[0]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_KICK));g_hRobotHit[1]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_BOXING));g_hRobotHit[2]=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_WAVEBOXING));g_hRobotWave=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_WAVE));g_hRobotBeAttactedHeavy=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_BEATTACTED_HEAVY));g_hRobotBeAttactedLight=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_BEATTACTED_LIGHT));g_hRobotBlood=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_BLOOD));g_hRobotDie=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_R2_DIE));g_hLogoKO=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_KO));g_hLoading=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_LOADING));g_hStart=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_START));g_hSnow=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_SNOW));g_hFire=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_FIRE));g_hTryAgain=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_TRY_AGAIN));GetObject(g_hHeroHit[0],sizeof(g_bmpHeroHit),&g_bmpHeroHit);GetObject(g_hRobotHit[0],sizeof(g_bmpRobotHit),&g_bmpRobotHit);GetObject(g_hHeroBlood,sizeof(g_bmpHeroBlood),&g_bmpHeroBlood);GetObject(g_hRobotBlood,sizeof(g_bmpRobotBlood),&g_bmpRobotBlood);GetObject(g_hLogoKO,sizeof(g_bmpKO),&g_bmpKO);//Hero贴图的起始坐标g_iHeroX=wndRect.right/4;g_iHeroY=wndRect.right/4;g_iHeroDirection=2;//wait状态g_HeroActionType=ACTION_TYPE_NULL;g_heroActionFlag=false;g_heroBeAttactedHeavyFlag=false;g_heroBeAttactedLightFlag=false;g_iHeroStrength=100.0;//Hero体力值初始化为100g_iHeroFileNum=0;g_iRobotFileNum=0;//Robot贴图的起始坐标g_iRobotX=wndRect.right/4*3;g_iRobotY=wndRect.right/4;g_iRobotDirection=2;g_RobotActionType=ACTION_TYPE_NULL;g_robotActionFlag=false;g_robotBeAttactedHeavyFlag=false;g_robotBeAttactedLightFlag=false;g_iRobotStrength=100.0;//Robot体力值初始化为100g_gameOverFlag=false;BITMAP bmpLoading;GetObject(g_hStart,sizeof(bmpLoading),&bmpLoading);//加载开始画面first_start=true;HDC start_hdc=NULL;start_hdc=CreateCompatibleDC(g_hdc);if (first_start){SelectObject(start_hdc,g_hStart);for (int i=0;i<bmpLoading.bmWidth;i++){BitBlt(g_hdc,(wndRect.right-bmpLoading.bmWidth)/2,(wndRect.bottom-bmpLoading.bmHeight)/2+i,bmpLoading.bmWidth,1,start_hdc,0,i,SRCCOPY);Sleep(10);}}g_iFireFrameNum=0;Game_Paint(hwnd);return TRUE;}
核心渲染函数:

VOID Game_Paint(HWND hwnd){RECT wndRect;BITMAP bmpBkGrd;//获取背景图片的大小GetObject(g_hBackGround,sizeof(bmpBkGrd),&bmpBkGrd);//获取窗口尺寸GetClientRect(hwnd,&wndRect);BITMAP bmpHero;//hero位图信息63*93GetObject(g_hHeroDirection[0],sizeof(bmpHero),&bmpHero);BITMAP bmpRobot;//robot位图信息,63*93GetObject(g_hRobotDirection[0],sizeof(bmpRobot),&bmpRobot);//开始界面BITMAP bmpStart;GetObject(g_hStart,sizeof(bmpStart),&bmpStart);BITMAP bmpSnow;//获取雪花位图的大小GetObject(g_hSnow,sizeof(bmpSnow),&bmpSnow);BITMAP bmpFire;GetObject(g_hFire,sizeof(bmpFire),&bmpFire);BITMAP bmpHeroWin;GetObject(g_hHeroWin,sizeof(bmpHeroWin),&bmpHeroWin);BITMAP bmpTryAgain;GetObject(g_hTryAgain,sizeof(bmpTryAgain),&bmpTryAgain);//绘制开始界面//MessageBox(hwnd, L"是否开始游戏?", L"消息窗口", 0);//先贴上背景图,将其存入内存DC g_hdcMem中SelectObject(g_bufdc,g_hBackGround);//拉伸的方式全屏贴背景图StretchBlt(g_hdcMem,0,0,wndRect.right,wndRect.bottom,g_bufdc,0,0,bmpBkGrd.bmWidth,bmpBkGrd.bmHeight,SRCCOPY);//火焰燃烧画面SelectObject(g_bufdc,g_hFire);TransparentBlt(g_hdcMem,0,wndRect.right/3,bmpFire.bmWidth/6,bmpFire.bmHeight,g_bufdc,g_iHeroFileNum*bmpFire.bmWidth/6,0,bmpFire.bmWidth/6,bmpFire.bmHeight,RGB(0,0,0));TransparentBlt(g_hdcMem,wndRect.right/20*19,wndRect.right/3,bmpFire.bmWidth/6,bmpFire.bmHeight,g_bufdc,(g_iHeroFileNum)*bmpFire.bmWidth/6,0,bmpFire.bmWidth/6,bmpFire.bmHeight,RGB(0,0,0));//创建雪花粒子系统if (g_SnowNum<100){SnowFlowers[g_SnowNum].x=rand()%wndRect.right;SnowFlowers[g_SnowNum].y=0;SnowFlowers[g_SnowNum].exist=true;g_SnowNum++;}//判断粒子是否存在,存在则进行透明贴图操作for (int i=0;i<100;i++){if (SnowFlowers[i].exist){//贴上粒子图SelectObject(g_bufdc,g_hSnow);TransparentBlt(g_hdcMem,SnowFlowers[i].x,SnowFlowers[i].y,bmpSnow.bmWidth,bmpSnow.bmHeight,g_bufdc,0,0,bmpSnow.bmWidth,bmpSnow.bmHeight,RGB(0,0,0));//随机决定横向的移动方向和偏移量if (rand()%2==0)SnowFlowers[i].x+=rand()%6;elseSnowFlowers[i].x-=rand()%6;//纵方向上做匀速运动SnowFlowers[i].y+=10;if (SnowFlowers[i].y>wndRect.bottom){SnowFlowers[i].x=rand()%wndRect.right;SnowFlowers[i].y=0;}}}BITMAP bmpHeroWave;GetObject(g_hHeroWave,sizeof(bmpHeroWave),&bmpHeroWave);//Hero攻击,招式控制,招式不为空,且处于攻击状态if((g_HeroActionType!=ACTION_TYPE_NULL)&&g_heroActionFlag&&!g_gameOverFlag){//获取攻击状态的位图信息BITMAP bmpHeroAction;GetObject(g_hHeroHit[0],sizeof(bmpHeroAction),&bmpHeroAction);if(g_HeroActionType==ACTION_TYPE_WAVEBOXING){//人物招式贴图SelectObject(g_bufdc,g_hHeroHit[g_HeroActionType]);TransparentBlt(g_hdcMem,g_iHeroX,g_iHeroY,bmpHeroAction.bmWidth/6,bmpHeroAction.bmHeight,g_bufdc,g_iHeroFileNum*bmpHeroAction.bmWidth/6,0,bmpHeroAction.bmWidth/6,bmpHeroAction.bmHeight,RGB(255,255,255));//绝招效果贴图SelectObject(g_bufdc,g_hHeroWave);if (g_iHeroWavesNum!=0){for (int i=0;i<10;i++)if (heroWaves[i].exist){TransparentBlt(g_hdcMem,heroWaves[i].x+45,heroWaves[i].y,bmpHeroWave.bmWidth,bmpHeroWave.bmHeight,g_bufdc,0,0,bmpHeroWave.bmWidth,bmpHeroWave.bmHeight,RGB(255,255,255));heroWaves[i].x+=20;//Hero打出的波距离Robot小于20个像素就认为是打中if((g_iRobotX-heroWaves[i].x)<20){if(g_iRobotStrength>0){g_robotBeAttactedHeavyFlag=true;//造成5-15之间的伤害float damage=5+(float)(rand()%10);g_iRobotStrength-=damage;}else{g_iRobotStrength=0;g_gameOverFlag=true;}g_RobotActionType=ACTION_TYPE_NULL;g_iHeroWavesNum--;heroWaves[i].exist=false;}//没打中到达窗口边缘自动消失if (heroWaves[i].x>wndRect.right-bmpHeroWave.bmWidth){g_iHeroWavesNum--;heroWaves[i].exist=false;}}}}else//普通招式贴图{if(!g_gameOverFlag){SelectObject(g_bufdc,g_hHeroHit[g_HeroActionType]);TransparentBlt(g_hdcMem,g_iHeroX,g_iHeroY,bmpHeroAction.bmWidth/6,bmpHeroAction.bmHeight,g_bufdc,g_iHeroFileNum*bmpHeroAction.bmWidth/6,0,bmpHeroAction.bmWidth/6,bmpHeroAction.bmHeight,RGB(255,255,255));//位图差距为116if((g_iRobotX-g_iHeroX)<=120){g_robotBeAttactedLightFlag=true;if (g_iRobotStrength>0){//造成0-5之间的伤害float damage=(float)(rand()%5);g_iRobotStrength-=damage;Sleep(10);}else{g_iRobotStrength=0;g_gameOverFlag=true;}}}}//g_heroActionFlag=false;}//Hero受到轻击打if (!g_gameOverFlag&&g_heroBeAttactedLightFlag&&g_RobotActionType!=ACTION_TYPE_NULL){g_heroActionFlag=false;SelectObject(g_bufdc,g_hHeroBeAttactedLight);TransparentBlt(g_hdcMem,g_iHeroX,g_iHeroY,bmpHero.bmWidth/6,bmpHero.bmHeight,g_bufdc,g_iHeroFileNum*bmpHero.bmWidth/6,0,bmpHero.bmWidth/6,bmpHero.bmHeight,RGB(255,255,255));g_heroBeAttactedLightFlag=false;}//Hero贴图,针对其各个方向进行贴图if (!g_gameOverFlag&&!g_heroActionFlag&&!g_heroBeAttactedLightFlag&&!g_heroBeAttactedHeavyFlag){SelectObject(g_bufdc,g_hHeroDirection[g_iHeroDirection]);TransparentBlt(g_hdcMem,g_iHeroX,g_iHeroY,bmpHero.bmWidth/6,bmpHero.bmHeight,g_bufdc,g_iHeroFileNum*bmpHero.bmWidth/6,0,bmpHero.bmWidth/6,bmpHero.bmHeight,RGB(255,255,255));}//Robot受到轻击打if (!g_gameOverFlag&&g_robotBeAttactedLightFlag/*&&g_HeroActionType!=ACTION_TYPE_NULL*/){g_robotActionFlag=false;SelectObject(g_bufdc,g_hRobotBeAttactedLight);TransparentBlt(g_hdcMem,g_iRobotX,g_iRobotY,bmpRobot.bmWidth/6,bmpRobot.bmHeight,g_bufdc,g_iRobotFileNum*bmpRobot.bmWidth/6,0,bmpRobot.bmWidth/6,bmpRobot.bmHeight,RGB(255,255,255));//g_robotBeAttactedLightFlag=false;}//Robot受到波的重击if(g_robotBeAttactedHeavyFlag){g_robotActionFlag=false;SelectObject(g_bufdc,g_hRobotBeAttactedHeavy);TransparentBlt(g_hdcMem,g_iRobotX,g_iRobotY,g_bmpRobotHit.bmWidth/6,g_bmpRobotHit.bmHeight,g_bufdc,g_iRobotFileNum*g_bmpRobotHit.bmWidth/6,0,g_bmpRobotHit.bmWidth/6,g_bmpRobotHit.bmHeight,RGB(255,255,255));g_robotBeAttactedHeavyFlag=false;}//Sleep(500);//g_robotActionFlag=true;if ((!g_gameOverFlag)&&(!g_heroActionFlag)&&(g_HeroActionType==ACTION_TYPE_NULL)){g_robotActionFlag=true;//int probability=rand()%2;if(1==rand()%2){g_RobotActionType=ACTION_TYPE_KICK;}else{g_RobotActionType=ACTION_TYPE_BOXING;}}//Robot攻击if(!g_gameOverFlag&&g_robotActionFlag/*&&(g_heroActionFlag==false)*/){SelectObject(g_bufdc,g_hRobotHit[g_RobotActionType]);TransparentBlt(g_hdcMem,g_iRobotX,g_iRobotY,g_bmpRobotHit.bmWidth/6,g_bmpRobotHit.bmHeight,g_bufdc,g_iRobotFileNum*g_bmpRobotHit.bmWidth/6,0,g_bmpRobotHit.bmWidth/6,g_bmpRobotHit.bmHeight,RGB(255,255,255));if((g_iRobotX-g_iHeroX)<=110){g_heroBeAttactedLightFlag=true;if (g_iHeroStrength>0){//造成5-10之间的伤害float damage=(float)(5+rand()%5);g_iHeroStrength-=damage;//Sleep(10);}else{g_iHeroStrength=0;g_gameOverFlag=true;}}}//Robot贴图if (!g_gameOverFlag&&!g_robotActionFlag&&!g_robotBeAttactedLightFlag&&!g_robotBeAttactedHeavyFlag){SelectObject(g_bufdc,g_hRobotDirection[g_iRobotDirection]);TransparentBlt(g_hdcMem,g_iRobotX,g_iRobotY,bmpRobot.bmWidth/6,bmpRobot.bmHeight,g_bufdc,g_iRobotFileNum*bmpRobot.bmWidth/6,0,bmpRobot.bmWidth/6,bmpRobot.bmHeight,RGB(255,255,255));// g_robotBeAttactedLightFlag=false;// g_robotBeAttactedHeavyFlag=false;}//血量位图中间的KO logo贴图SelectObject(g_bufdc,g_hLogoKO);StretchBlt(g_hdcMem,wndRect.right/2-g_bmpKO.bmWidth/2,0,g_bmpKO.bmWidth,g_bmpKO.bmHeight,g_bufdc,0,0,g_bmpKO.bmWidth,g_bmpKO.bmHeight,SRCCOPY);//贴上Hero血量图,根据体力值实时计算贴图位置SelectObject(g_bufdc,g_hHeroBlood);StretchBlt(g_hdcMem,(int)(wndRect.right/2*(float)(1.0-(float)(g_iHeroStrength/CHARACTER_MAX_STRENGTH))),0,(int)((float)(g_iHeroStrength/CHARACTER_MAX_STRENGTH)*wndRect.right/2)-g_bmpKO.bmWidth/2,g_bmpHeroBlood.bmHeight,g_bufdc,0,0,(int)(g_bmpHeroBlood.bmWidth*(float)(g_iHeroStrength/CHARACTER_MAX_STRENGTH)),g_bmpHeroBlood.bmHeight,SRCCOPY);//贴上Robot血量图,根据体力值实时计算贴图起始位置SelectObject(g_bufdc,g_hRobotBlood);StretchBlt(g_hdcMem,wndRect.right/2+g_bmpKO.bmWidth/2,0,(int)(wndRect.right/2*(float)(g_iRobotStrength/CHARACTER_MAX_STRENGTH)),g_bmpRobotBlood.bmHeight,g_bufdc,0,0,g_bmpRobotBlood.bmWidth,g_bmpRobotBlood.bmHeight,SRCCOPY);if(g_gameOverFlag)//游戏结束{if (g_iHeroStrength<=0)//Hero阵亡{//Hero倒地SelectObject(g_bufdc,g_hHeroDie);TransparentBlt(g_hdcMem,g_iHeroX,g_iHeroY+10,g_bmpHeroHit.bmWidth/6,g_bmpHeroHit.bmHeight,g_bufdc,g_iHeroFileNum*g_bmpHeroHit.bmWidth/6,0,g_bmpHeroHit.bmWidth/6,g_bmpHeroHit.bmHeight,RGB(255,255,255));//Robot站着SelectObject(g_bufdc,g_hRobotDirection[g_iRobotDirection]);TransparentBlt(g_hdcMem,g_iRobotX,g_iRobotY,bmpRobot.bmWidth/6,bmpRobot.bmHeight,g_bufdc,g_iRobotFileNum*bmpRobot.bmWidth/6,0,bmpRobot.bmWidth/6,bmpRobot.bmHeight,RGB(255,255,255));}else if(g_iRobotStrength<=0)//Robot阵亡{//robot倒地SelectObject(g_bufdc,g_hRobotDie);TransparentBlt(g_hdcMem,g_iRobotX,g_iRobotY+10,g_bmpRobotHit.bmWidth/6,g_bmpRobotHit.bmHeight,g_bufdc,g_iRobotFileNum*g_bmpRobotHit.bmWidth/6,0,g_bmpRobotHit.bmWidth/6,g_bmpRobotHit.bmHeight,RGB(255,255,255));//加载Hero Win的图片SelectObject(g_bufdc,g_hHeroWin);TransparentBlt(g_hdcMem,g_iHeroX,g_iHeroY,bmpHeroWin.bmWidth/6,bmpHeroWin.bmHeight,g_bufdc,g_iHeroFileNum*bmpHeroWin.bmWidth/6,0,bmpHeroWin.bmWidth/6,bmpHeroWin.bmHeight,RGB(255,255,255));}//游戏结束画面加载SelectObject(g_bufdc,g_hTryAgain);BitBlt(g_hdcMem,wndRect.right/4,0,bmpTryAgain.bmWidth/2,bmpTryAgain.bmHeight,g_bufdc,bmpTryAgain.bmWidth/2,0,SRCAND);BitBlt(g_hdcMem,wndRect.right/4,0,bmpTryAgain.bmWidth/2,bmpTryAgain.bmHeight,g_bufdc,0,0,SRCPAINT);//Hero的攻击状态标识全部置为falseg_heroActionFlag=false;g_HeroActionType=ACTION_TYPE_NULL;}//最终的显示BitBlt(g_hdc,0,0,wndRect.right,wndRect.bottom,g_hdcMem,0,0,SRCCOPY);//记录此次绘图时间,供下次游戏循环中判断是否已经达到画面更新操作设定的时间间隔g_tHeroPre = GetTickCount();g_iFireFrameNum++;g_iHeroFileNum++;g_iRobotFileNum++;if (g_iFireFrameNum==6){g_iFireFrameNum=0;}if (g_iRobotFileNum==6)g_iRobotFileNum=0;if(g_iHeroFileNum==6)g_iHeroFileNum=0;}

游戏资源句柄释放函数:

BOOL Game_CleanUp(HWND hwnd ){//释放各种资源句柄for(int i=0;i<3;i++){DeleteObject(g_hHeroDirection[i]);DeleteObject(g_hHeroHit[i]);DeleteObject(g_hRobotHit[i]);DeleteObject(g_hRobotDirection[i]);}DeleteObject(g_hTryAgain);DeleteObject(g_hHeroWin);DeleteObject(g_hHeroDie);DeleteObject(g_hRobotDie);DeleteObject(g_hFire);DeleteObject(g_hSnow);DeleteObject(g_hStart);DeleteObject(g_hLoading);DeleteObject(g_hLogoKO);DeleteObject(g_hHeroBlood);DeleteObject(g_hRobotBlood);DeleteObject(g_hHeroBeAttactedHeavy);DeleteObject(g_hHeroBeAttactedLight);DeleteObject(g_hRobotBeAttactedLight);DeleteObject(g_hRobotBeAttactedHeavy);DeleteObject(g_hHeroWave);DeleteObject(g_hRobotWave);DeleteObject(g_bufdc);DeleteDC(g_hdcMem);ReleaseDC(hwnd,g_hdc);return TRUE;}
上面的代码写得比较烂,没有面向对象去写,全部是过程式的,渲染函数写得太长,编程习惯不太好。但是,沿着这个思路走下去,把一个完整的“街霸”开发出来还是没有问题的。

1 0