第一章 项目描述
1.1功能描述
打僵尸采用win32API编写,具体实现功能:
A.仿照打地鼠游戏编写,只不过显示的是僵尸
B.僵尸出现是慢慢冒出来的
C.能统计得分,同时出现僵尸数量随分数上升
D.打击僵尸有爆炸特效
1.2所需技术
透明贴图,爆炸动画处理
第二章 总体设计
2.1打僵尸运行流程
太简单了,就像打地鼠。
第三章 详细设计
3.1背景地图
游戏背景地图是一张植物大战僵尸草地的图片,其中每个格子对应着将要出现的僵尸的位置。
hBmpBackground=(HBITMAP)LoadImage(hinstance,MAKEINTRESOURCE(IDB_BITMAPground), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
void backgroudDraw()//游戏进程背景绘制
{
SelectObject(hdcImageStore, hBmpBackground );
BitBlt( hdcImageDraw, 0,0, C_TILE_W*C_MAP_W,C_TILE_H*C_MAP_H, hdcImageStore,0, 0 , SRCCOPY);
}
hBmpBackground是一个HBITMAP全局变量,从资源中获得背景地图的句柄,然后载入到内存位图hdcImageDraw中。
3.2僵尸出现
这里最多同时出现3个僵尸。定义一个僵尸结构体数组,这里只有3个元素,对应的就是出现的僵尸。当bActiveFlg为true时,显示出来这个僵尸。
typedef struct
{
bool bActiveFlg;
int iXpos; // λÖÃX
int iYpos; // λÖÃY
}__ZOMBIE;
__ZOMBIE ZOMBIE[3];
<span style="font-family: 宋体; font-size: 12pt; text-indent: 21pt; background-color: rgb(255, 255, 255);">地图也同时定义一个结构体。地图是绿色与浅绿色相间的就是每个格子,一共有</span><span style="font-size: 12pt; background-color: rgb(255, 255, 255); font-family: 'Times New Roman';">5</span><span style="font-family: 宋体; font-size: 12pt; text-indent: 21pt; background-color: rgb(255, 255, 255);">行,</span><span style="font-size: 12pt; text-indent: 21pt; background-color: rgb(255, 255, 255); font-family: 'Times New Roman';">9</span><span style="font-family: 宋体; font-size: 12pt; text-indent: 21pt; background-color: rgb(255, 255, 255);">列格子,这里用一个</span><span style="font-size: 12pt; text-indent: 21pt; background-color: rgb(255, 255, 255); font-family: 'Times New Roman';">5*9</span><span style="font-family: 宋体; font-size: 12pt; text-indent: 21pt; background-color: rgb(255, 255, 255);">二维数组表示。</span>
typedef struct {
char bExisteFlg;
int iXmap; // λÖÃX
int iYmap;
}__MAP;
__MAP MAP[5][9];
地图初始化,把二维数组的iXmap,iYmap坐标与具体的行列号对应起来。
这里的坐标是左上角为原点的屏幕坐标。后面只要知道MAP的行列号就知道对应的坐标。
typedef struct {
char bExisteFlg;
int iXmap; // λÖÃX
int iYmap;
}__MAP;
__MAP MAP[5][9];
地图初始化,把二维数组的iXmap,iYmap坐标与具体的行列号对应起来。
这里的坐标是左上角为原点的屏幕坐标。后面只要知道MAP的行列号就知道对应的坐标。
void mapInit ()
{
int i,j;
for ( i = 0; i < 5; i++) {
for (j = 0; j < 9; j++) {
MAP[i][j].iXmap=30+80*j;
MAP[i][j].iYmap=80+100*i;
}
}
}
3.2.1随机产生出现坐标
随机产生僵尸的出现坐标。主要是随机产生iAZomX,iAZomY数组的每个元素。其中iAZomX数组每个元素随机为0~4的值,iAZomY数组每个元素随机为0~8的值,对应的三个僵尸的初始位置就置为地图二维数组对应iAZomX行,iAZomY列的元素位置。同时地图二维数组上相应位置标记有僵尸,置1对应1号僵尸,置2对应这个格子是2号僵尸。
void coordChange ()
{
int i,j,k;
static int iAZomX[3]={0,0,0};
static int iAZomY[3]={0,0,0};
for ( i = 0; i < 3; i++) {
iAZomX[i]=rand()%5;
iAZomY[i]=rand()%9;
}
for ( i = 0;i < 3;i++ ) {
ZOMBIE[i].iXpos=MAP[iAZomX[i]][iAZomY[i]].iXmap;
ZOMBIE[i].iYpos=MAP[iAZomX[i]][iAZomY[i]].iYmap;
}
for ( i = 0; i < 5; i++) {
for (j = 0; j < 9; j++) {
if ( i== iAZomX[0] && j == iAZomY[0])
MAP[i][j].bExisteFlg=1;
else if ( i== iAZomX[1] && j == iAZomY[1])
MAP[i][j].bExisteFlg=2;
else if ( i== iAZomX[2] && j == iAZomY[2])
MAP[i][j].bExisteFlg=3;
else
MAP[i][j].bExisteFlg=0;
}
}
}
3.2.2随分数多少产生僵尸
随着分数iScore的增加,依次激活相应数量的僵尸,即使僵尸的bActiveFlg标志置为1,激活了的僵尸就会显示出来。
bool randomCreate ( DWORD dwTime )
{
static long int iLastUpdate=0;
if( ( dwTime - iLastUpdate )>100 ) //µ±Ê±¼ä´óÓÚ100ʱ²¥·ÅÏÂÒ»¸ö¶¯»
{
iLastUpdate = dwTime;
cSumFlg++;
if(cSumFlg >= 12) {
if(iScore < 100)
ZOMBIE[0].bActiveFlg=1;
if( 100 <= iScore &&iScore < 200) {
ZOMBIE[1].bActiveFlg=1;
ZOMBIE[0].bActiveFlg=1;
}
if( 200 <= iScore) {
ZOMBIE[1].bActiveFlg=1;
ZOMBIE[2].bActiveFlg=1;
ZOMBIE[0].bActiveFlg=1;
}
coordChange();
cSumFlg = 0;
}
}
return true;
}
3.2.3僵尸慢慢冒出头效果
使用全局变量cSumFlg,cSumFlg在randomCreate 函数中是会自增的,每次贴僵尸图与cSumFlg结合起来。僵尸显示的纵坐标ZOMBIE[i].iYpos+100-cSumFlg*10,每次显示在y方向递增cSumFlg*10。
<pre name="code" class="cpp">void zombieDraw(DWORD dwTime )
{
int i = 0;
SelectObject(hdcImageStore, hBmpZombie);
for (i = 0; i < 3; i++) {
if ( ZOMBIE[i].bActiveFlg == 1) {
transparentPaint( hdcImageDraw,ZOMBIE[i].iXpos, ZOMBIE[i].iYpos+100-cSumFlg*10, 80, cSumFlg*10, 0,0, RGB(255,255,255) );
}
}
}
3.3打击僵尸使其消失
响应鼠标左键点击消息,扫描地图二维数组,当鼠标点击坐标落在对应的地图某个格子中,判断那个格子中是否有僵尸标记,有的话清空地图格子的僵尸标记,并且把对应僵尸数组中那个元素的bActiveFlg属性置零。这样在绘图中因为某僵尸的bActiveFlg为0就不会继续绘制那个僵尸了,实现打死僵尸效果。
for ( row = 0; row < 5; row++) {
for (col = 0; col < 9; col++) {
if ( MAP[row][col].bExisteFlg!=0&&LOWORD(lParam)<=MAP[row][col].iXmap+80&&LOWORD(lParam)>=MAP[row][col].iXmap&&HIWORD(lParam)<=MAP[row][col].iYmap+100&&HIWORD(lParam)>=MAP[row][col].iYmap) {
if(MAP[row][col].bExisteFlg == 1)
ZOMBIE[0].bActiveFlg=0;
else if(MAP[row][col].bExisteFlg == 2)
ZOMBIE[1].bActiveFlg=0;
else if(MAP[row][col].bExisteFlg == 3)
ZOMBIE[2].bActiveFlg=0;
cHitFlg=1;
MAP[row][col].bExisteFlg=0;
3.4爆炸效果
首先定义一个爆炸结构体。
struct Explosion
{
int p_nX; //爆炸位置x
int p_nY; // 爆炸位置y
int m_nCurFrame; // 当前帧
int m_nType; // 爆炸类型
int m_oldUpdate; // 播放下一张连续图更新时间
struct Explosion *next;
};
struct Explosion *Explosionhead ; //全局爆炸链表
3.4.1初始化爆炸链表
爆炸链表里面能存十个元素,说明同时能进行十个爆炸。把当前帧置为-1,这样每次从爆炸链表里取某个还没播放的元素进行爆炸的播放。
void bombInit()
{
int i;
struct Explosion *e;
//´´½¨±¬Õ¨Á´±í
if(Explosionhead==NULL)
{
Explosionhead=(struct Explosion*)malloc(sizeof(struct Explosion));
Explosionhead->next=NULL;
}
e=Explosionhead;
for( i=0; i<10; i++ )
{
e->next=(struct Explosion*)malloc(sizeof(struct Explosion));
e->m_nCurFrame=-1;
e->m_oldUpdate=0;
e->m_nType=0;
e->next->next=NULL;
e=e->next;
}
}
3.4.2激活爆炸链表某元素
遍历爆炸链表,找到还没播放帧为-1的元素,即那个元素没处于播放状态,把它播放帧置零,表示激活了,并把相应的打击的坐标传入。
void PlayExplosion( int nX, int nY, int nType )
{
int i;
struct Explosion *e;
e=Explosionhead;
// ±¬Õ¨´¦Àí
for( i=0; i<10; i++ )
{
// if( !((e->m_nCurFrame==-1) ? false : true) ) //ÅжÏÕâ¸ö±¬Õ¨³ÉÔ±ÊÇ·ñÒѾ±»¼¤»îÁË Èç¹ûû±»¼¤»î
if( e->m_nCurFrame==-1 )
{
e->p_nX= nX;
e->p_nY= nY;
e->m_nType= nType;
e->m_nCurFrame = (nType==0)?0:0;
break;//Õâ¸öbreak ÊÇʲôÒâ˼
}
e=e->next;
}
}
3.4.3进入爆炸播放帧
当某爆炸元素处在激活状态,即播放帧不为-1,进行播放帧随着时间递增。
bool ExplosionEnterFrame(int *nCurFrame , int *nType , int *oldUpdate , DWORD dwTime )
{
if( *nCurFrame == -1 )
return false;
if( ( dwTime - *oldUpdate )>=100 ) //µ±Ê±¼ä´óÓÚ100ʱ²¥·ÅÏÂÒ»¸ö¶¯»
{
*oldUpdate = dwTime;
(*nCurFrame)++;
if( *nType == 0 && *nCurFrame>2 ) //±¬Õ¨ÀàÐÍnTypeΪ0£¬²¥·ÅͼƬÉÙ£¬Ê±¼äÒ²¶Ì£¬Ò»°ãÊÇ»÷ÖÐÕÏ°Îï
{
*nCurFrame = -1;
}
if( *nType == 1 && *nCurFrame>7 ) //±¬Õ¨ÀàÐÍnTypeΪ1£¬½«²¥·ÅÍê7¸öͼƬ£¬Ê±¼ä³¤£¬Ò»°ãÊÇ»÷ÖÐÄ¿±ê
{
*nCurFrame = -1;
}
}
return true;
}
3.4.4绘制爆炸效果
遍历爆炸链表,当某元素播放帧不为-1时,每次从爆炸的横向长图片中取出与播放帧nCurFrame对应的位置的一小块进行贴图,结合时间播放帧递增,实现换图功能,爆炸的特效得以实现。
void ExplosionDraw(int nX , int nY , int m_nCurFrame)
{
if( m_nCurFrame != -1 ) //µÈÓÚ-1²»»æÖƱ¬Õ¨
{
SelectObject(hdcImageStore , hBmpBomb );
transparentPaint( hdcImageDraw, nX-16, nY-16, 32,32, m_nCurFrame*32, 0, RGB(0x0,0x0,0x0) );
}
}
// »æÖƱ¬Õ¨³¡Ãæ
void DrawExplosion( )
{
int i=0;
struct Explosion *e;
e=Explosionhead;
for( i=0; i<10; i++ )
{
ExplosionDraw(e->p_nX , e->p_nY , e->m_nCurFrame);
e=e->next;
}
}
第四章 运行结果
设计报告下载
点击打开链接
源码,素材实现效果下载