C语言打地鼠游戏

第一章  项目描述

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个元素,对应的就是出现的僵尸。当bActiveFlgtrue时,显示出来这个僵尸。

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];

地图初始化,把二维数组的iXmapiYmap坐标与具体的行列号对应起来。

这里的坐标是左上角为原点的屏幕坐标。后面只要知道MAP的行列号就知道对应的坐标。

typedef struct {
	char bExisteFlg;
	int iXmap;                                                           // λÖÃX
	int iYmap;   	
}__MAP;
__MAP MAP[5][9];

地图初始化,把二维数组的iXmapiYmap坐标与具体的行列号对应起来。

这里的坐标是左上角为原点的屏幕坐标。后面只要知道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随机产生出现坐标

随机产生僵尸的出现坐标。主要是随机产生iAZomXiAZomY数组的每个元素。其中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僵尸慢慢冒出头效果

使用全局变量cSumFlgcSumFlgrandomCreate 函数中是会自增的,每次贴僵尸图与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属性置零。这样在绘图中因为某僵尸的bActiveFlg0就不会继续绘制那个僵尸了,实现打死僵尸效果。


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;
    }
}

第四章  运行结果


设计报告下载

点击打开链接
源码,素材实现效果下载

点击打开链接


你可能感兴趣的:(打地鼠,C游戏)