游戏开发技术总结(经典之作)第十二集 游戏角色的生命、经验值等进行详说

来源:互联网 发布:sql server 2016 编辑:程序博客网 时间:2024/05/06 00:59

本集源代码下载地址------->游戏开发技术总结(经典之作)第十二集

 

12-1 任务

       在本章中我们的任务是赋予游戏中对抗对象的生命值,以便游戏中的打斗更有真

实性。具体实现方法是在上一章的程序中修改加入对应功能。

12-2 加入生命值后相关函数的改变

       我们规定,在人与动物相遇打斗时,生命值man[q].smz随对方的攻击力而减少。当

对象生命值减少到一定程度时,我们就认为这个对象生命终结了。对比动画的表现有

一个对象倒地的过程,倒地后的残体还应该慢慢的化解,到最后消失。下面我们在程

序中加入这种过程的算法。这些算法多数是在原来的程序中加入的,所以我们就按修

改程序的顺序来讲解。

12-2-1 对象显示函数中的修改

根据游戏角色动作序列有;

JCDZ zjdz[6]={ 0,5, 40,10, 120,10, 200,10, 280,10, 360,5};//

//0 站, 1 走, 2 刺, 3 劈, 4 倒地 5 残体化解

JCDZ zjdw[6]={ 0,5, 40,10, 120,10, 200,10, 280,10, 360,5};//

//0 站, 1 走, 2 跑, 3 斗, 4 倒地, 5 残体化解

man[q].zs=4为对象倒地;man[q].zs=5为对象残体化解;我们设man[q].zs>5为对象残

体化解完毕。

现在我们假定某个对象man[q].smz的生命值完了,从对象已倒地man[q].zs>4开始看

相关程序的修改。

对象显示函数中有对象的动作变换计数,我们在这里加上对象倒地后残体化解的

动作变换过程(见加黑部分)。

void game::Alphasetobj(int q,int a)//对象显示{ if(a==1&&man[q].lb==2) return;1 if(man[q].zs>5){Delete(q);return;} //删除对象返回⋯⋯//-----------------------------------生命值,对象倒地后残体化解的动作变换过程2 if(man[q].zd==1) setxtx(q,x,y); //对象正在打斗时,调显示生命值函数3 if(man[q].zs>4)//对象倒地4 {man[q].smz=man[q].smz-1; //对象倒地后,smz 用于残体化解计数5 if(man[q].smz<-200){man[q].smz=0;man[q].p++;}//残体化解过程}else//------------------------------------man[q].p++; //下一动作if(man[q].p>=man[q].m1) {bianfw(q);} //本动作完成,进行方位转换}


对象显示函数加入部分注释:

1行对象残体化解完毕man[q].zs>5,删除q对象返回。

2行对象正在打时,调显示生命值函数setxtx(q,x,y),显示对象的生命值。

3行对象已倒地执行第45行,否则执行正常的动作变换。

4行对象倒地后,smz用于残体化解计数,减缓残体化解过程。

5行,残体化解过程。

在这里我们新加入了两个函数 :删除对象、显示生命值。

1.新加删除对象函数

这是一个典型的在数组中删除一个结点的算法。

//**************************************************// Delete(int q) //删除// 删除选 中的q 对象。//**************************************************void gmedit::Delete(int q) //删除1{ if(gno==0||rs<2) return;2 int n=man[q].jisu3 for(int i=0;i<rs;i++)4 if(man[i].jisu==n)5 {for (int j=i;j<rs-1;j++) man[j]=man[j+1];//删除n 对象6 rs--; //对象数减17 return;}}


34行通过循环查找选中的对象。

5行删除对象数组man[]中第j个单元。

这是一个典型的数组单元删除算法, 被删单元后面的数据都前移一个单元。原理

见以下图示。

                                                          

                                                               图12-2

6rs--;对象数减一。

7行删除完毕后返回。

2新加显示生命值函数

       这个函数用于动物在打斗时显示其生命值和攻击力 。采用的是字符显示的方法。

字符串“*” 的多少表示攻击力,字符底色表示生命值。man[q].smz为对象q的生命值,

Font 为字体, 在前面已创建为4 impact体。

/////////////////////////////////////////////setxtx(int q,int x,int y)显示生命值函数// 在[x,y]位置显示当前对象q 的生命值和攻击力// 生命值用颜色表示;按赤、橙、黄、绿、蓝、青、紫、从低到高表示。// 攻击力用星表示;星越多攻击力越高。///////////////////////////////////////////void game::setxtx(int q,int x,int y)//显示生命值函数{ if(man[q].lb==0) return; //是人返回CString cc;COLORREF col; //颜色if(smz[1][man[q].js]>8) {cc="●●●●●";goto aa;} //攻击力最大if(smz[1][man[q].js]>6) {cc="●●●●";goto aa;}if(smz[1][man[q].js]>4) {cc="●●●";goto aa;}if(smz[1][man[q].js]>2) {cc="●●";goto aa;}cc="● "; //攻击力最小aa: if(man[q].smz>=60) {col=RGB(0x0,0xff,0xff);gotobb;} //紫生命值大if(man[q].smz>=50) {col=RGB(0x0,0x88,0xff);goto bb;}if(man[q].smz>=40) {col=RGB(0x0,0x00,0xff);goto bb;}if(man[q].smz>=30) {col=RGB(0x0,0xff,0x88);goto bb;}if(man[q].smz>=20) {col=RGB(0xff,0xff,0x0);goto bb;}if(man[q].smz>=10) {col=RGB(0xff,0x88,0x0);goto bb;}col=RGB(0xff,0x0,0x0); //赤生命值小bb: HDC hdc; //设备环境句柄if (DXSBack1→GetDC(&hdc) != DD_OK) return; //打开DXSBack1 面CDC* dc = CDC::FromHandle (hdc);CFont *pOldFont;pOldFont=dc→SelectObject(&Font); //设置字体4 号impact体dc→SetBkColor(col); //字底色dc→SetTextColor(RGB(0x0,0x0,0x0)); //字色dc→TextOut(x+SCRW+w/2,y+SCRH-10,cc,lstrlen(cc));//写攻击力字串dc→SelectObject(pOldFont); //恢复原来的字体CDC::DeleteTempMap( );DXSBack1→ReleaseDC(hdc); //关闭DXSBack1 面}


 

12-2-2 有关对象倒地和残体化解的其它函数的修改

对象生命值结束后的倒地和残体化解都是动画的变换过程; 所以在设置活动对象

初值函数中,对倒地和残体化解应该有单独的处理。

void game::setman(int q)//设置活动对象初值1{ if(man[q].zs>3){man[q].zs++;goto aa;} //倒地后,转为下一动作。2 if(man[q].smz>-1&&man[q].smz<1){man[q].zs=4;} //生命值以完,转为倒地。aa: int a=400;⋯⋯⋯}


所加的第 2 行是生命值小于1时,对象的动作转为倒地。加上大于-1的判断是

在对象倒地后,man[q].smz用于它用。

在活动对象的移动函数中,应该把倒地和残体化解的过程屏蔽,死了的对象不能

再动了嘛。

void game::manmove(int i)//活动对象的移动{ if(man[i].smz<1) return; //生命值小于正常返回,不要移动。⋯⋯}


同理,对象倒地和残体化解也不做方位转换了,所以也应该在方位转换函数中将

它屏蔽。但是倒地和残体化解的动画过程还有,所以设置活动对象初值setman(q)还是

要的。

void game::bianfw(int q)//方位转换{ if(man[q].smz<1){setman(q);return;} //对象倒地以后不做方位转换⋯⋯}


12-3 调地图函数中加生命值和添加道具

12-3-1 生命值攻击力定义

不同的动物其生命值、攻击力也不同。我们用一个全局定义的二维数组来表示。

unsigned short smz[2][7]={ 60, 50, 30, 20,40, 10, 15, //生命值

10, 8, 6, 3, 5, 1, 2 };//攻击力

//兽[ 0豹 1 狼 2 猪 3 鹿 4 马 5 雀 6 羊 ]

siz[n][m] n=0为生命值,n=1为攻击力

m 为不同的动物m{0123鹿456}

例如:siz[0][2]表示猪的生命值;siz[1][2]表示猪的攻击力。

人有一个初始的生命值,我们允许人的生命值可以用其它方式增加。人还有一个

初始的攻击力我们称为经验值,人的经验值可以在猎物中增加。我们还可以设置人的

金钱、猎物拥有量等。

game.h中我们定义一维数组来表示人的生命值等的拥有量

short int mansmz[5]; //0生命1经验2金钱3猎物4

并在 game.cpp init()程序初始化时赋与初值。

mansmz[0]=200; //生命

mansmz[1]=5; //经验

mansmz[2]=10; //金钱

mansmz[3]=20; //猎物

mansmz[4]=0; //其它

12-3-2 调入地图函数的修改

        我们是在调入地图中对人和兽设置生命值初值的。每个对象调入后分别对人或动

物设定生命值,生命值的大小取至以上定义。

另外我们还要在场景中添加一些道具(如钱、药瓶等等),添加的数量和位置是随

机的。

void game::loadmap(CString name)//调地图{ //打开地图文件for (i=0;i<rs;i++){//调各对象if(man[i].lb==0) man[i].smz=mansmz[0]; //设人生命值if(man[i].lb==1)man[i].smz=smz[0][man[i].js]; //设兽生命值if(man[i].lb<2) setman(man[i].jisu); //设置活动对象初值if(getpic(i,0)==FALSE) return;man[i].w=w; man[i].h=h; //对象的尺寸}fclose(f);Insert() //添加物品}


 

12-3-3 新加的添加物品函数

//**************************************************// Insert() //添加物品// 在man[]中顺序加入物品,加入后对象数rs 加1。//**************************************************void game::Insert() //添加物品{ int a=rand()%100+5; //随机产生物品数for (int i=0;i<a;i++){if(rs>SU_LIANG-2) return;//AfxMessageBox("对象数超出!");int i=rs;man[i].jisu=i+1; //序号man[i].lb=3; //类别:0 人1 兽2 景3 物man[i].p=rand()%4; //静物图形号man[i].xix=man[i].x0=rand()%(WIDTH*SCRP0-10); //随机产生位置xman[i].xiy=man[i].y0=rand()%(HEIGHT*SCRP0-10); //随机产生位置yif(getpic(i,0)==FALSE) return;man[i].w=w; man[i].h=h; //对象的尺寸rs++; //对象数加1}}


12-4 打斗时生命值的改变

         在我们这个游戏中,对象打斗时生命值的减少和人在打斗中经验值的增加,还有

人拾物的过程,都是在类文件“game_寻路.cpp” 的角色碰撞函数中实现的。以下我们

来看这些功能的实现方法。

在角色碰撞函数中我们加入了4个功能。

A.屏蔽死了的动物

1行动物生命值完返回。

B.屏蔽人已倒地

2行人已倒地返回。

C.人拾物的过程。

3行人见物,注意人见物的程序表述方式(man[i].lb==0&&man[q].lb==3)。

49行根据物品man[q].p 分别改变人的拥有物mansmz[]大小。

10行删除物品。东西被检了,当然应该不见了,所以应该删除它。

D.打斗中生命值的改变。

11行在一个打斗回合时改变对象生命值。

12行人的生命值按相斗的动物攻击力减少。

13行人的生命值校正。

14行动物的生命值按人的攻击力减少。

15行动物的生命值校正。

1617行根据相斗的动物攻击力增加人的经验值。

void gamepro::lookit(int i)//角色碰撞{ if(man[i].lb>=2) return;1 if(man[i].smz<1) return; //动物生命值完{for(int q=0;q<rs;q++){⋯⋯2 if(man[q].lb==0&&man[q].zs>3)return; // 人已倒if(abs(x)<man[q].w*4/3 &&abs(y)<man[q].h*4/3) //相遇{⋯⋯3 if(man[i].lb==0&&man[q].lb==3) //人见物4 {if(man[q].p==0) man[i].smz=200; //生命5 if(man[q].p==1) mansmz[1]=mansmz[1]+10; //经验6 if(man[q].p==2) mansmz[2]=mansmz[2]+10; //金钱17 if(man[q].p==3) mansmz[2]=mansmz[2]+20; //金钱28 if(man[q].p==4) mansmz[2]=mansmz[2]+50; //金钱59 if(man[q].p==5)mansmz[3]=mansmz[3]+smz[0][man[q].js];//食物10 Delete(q); //删除物品11 return;}if(man[q].lb>=1) continue; //是兽看见人if(man[i].lb!=man[q].lb) //不同类{⋯⋯//兽停下来//双方面对面//================================生命值11 if(man[q].p==man[q].m1-5)12{man[q].smz=man[q].smz-smz[1][man[i].js];//兽对人攻击力13 if(man[q].smz<0) man[q].smz=0; //生命值校正14 man[i].smz=man[i].smz-mansmz[1]; //人对兽攻击力15 if(man[i].smz<0) man[i].smz=0; //生命值校正16 da=da+smz[1][man[i].js]; //长经验值计数17 if(da>50) {mansmz[1]=mansmz[1]+1;da=0;}//长经验值}//==================================if(inscreer(i)) [发声] //在显示区?break;} //end if 不同类} //end if 相遇} //end for} //end if 动物生命值完}


12-5 调入、读取物品资源压缩包

       还有一项重要的修改,就是在原来调入、读取资源压缩包的地方加入物品压缩包。

常数定义.h中加入

#define WBUF 10 //物最大数Game.h 中加入CFile sfile,rfile,jfile,wfile; //压缩资源包的文件指针BYTE * stmp,*rtmp,*jtmp,*wtmp; //压缩的内存变量int wbufadd[10]; //物的资源指针Game.cpp 的调压缩资源包中加入void gamepro::loaddata()//调压缩资源包{ ⋯⋯// A.cc=dir+"物.dar"; //读物品的数据指针f=fopen(cc,"r");if(f==NULL) return;fscanf(f,"%d",&len);for(i=0;i<len;i++)fscanf(f,"%d,%d,%d",&wbufadd[i],&j,&j);//读图形的位置fclose(f);⋯⋯// B.cc=dir+"物.gam"; //读物品的压缩包if( !wfile.Open(cc, CFile::modeRead, NULL ) )return;⋯⋯}Game.cpp 的调图片中加入BOOL game::getpic(int q,int a)//调图片{ ⋯⋯if(man[q].lb==3) //是调物品{if(p>WBUF-1) return FALSE;len=wbufadd[p+1]-wbufadd[p]; //取数据块长度tmp=(BYTE *)new BYTE[len];Memcpy0(tmp,wtmp+wbufadd[p],len);}⋯⋯}


12-6 在主界面显示人的生命值

        加入生命值后,在主程序中还要做一点修改。在“真的打呀Dlg.cpp”的OnTimer(UINT

nIDEvent)时钟函数中,加入人牺牲后的结束游戏的处理,还要新增加一个在主界面显示

人的生命值、经验值的函数。

12-6-1 时钟函数的修改

        在时钟函数的显示信息段加入人牺牲后的结束游戏的处理和在主界面显示生命

值、经验值,金钱、食物等信息的代码。

void CMyDlg::OnTimer(UINT nIDEvent) //时钟函数{ if(nIDEvent==1) //动画刷屏{ ⋯⋯}if(nIDEvent==2) //显示信息{char cc[255],c1[255];int q=m_game.mann;1 if(m_game.man[q].smz<-5) //人倒,开始计时。2 {KillTimer(1);KillTimer(2);3 MCIWndStop(hMCI);4 AfxMessageBox("你牺牲了! 游戏结束。");5 OnOK();}6 setsmz(m_game.man[q].smz,0);7 setsmz(m_game.mansmz[1],1);8 sprintf(cc,"金钱:%d",m_game.mansmz[2]);9 SetDlgItemText(IDC_STATIC9,cc);10 sprintf(cc,"食物:%d",m_game.mansmz[3]);11 SetDlgItemText(IDC_STATIC10,cc);⋯⋯}⋯⋯}


15行是人牺牲后的结束游戏的处理。

6行显示人的生命值。

7行显示人的经验值。

811行显示人的金钱、食物等信息。

12-6-2 新加主界面上显示生命值函数

这是一个最简单的生命值图形显示方式。读者可以在此基础上改成更生动好看的

方式。

void CMyDlg::setsmz(int h,int a){ CClientDC dc(this); //取客户区设备环境1 int x,y;2 int s=m_game.mansmz[0];3 char cc[255];4 CBrush Brush(RGB(120,120,130));5 dc.SelectObject(Brush);6 if(a==0) // 在主界面左下方显示生命值图示7 {x=3;y=500;8 dc.PatBlt(x,y,40,50,PATCOPY);9 CBrush Brush(RGB(200,0,0));10 dc.SelectObject(Brush);11 dc.PatBlt(x,y+51-h*50/s,40,h*50/s,PATCOPY);12 sprintf(cc,"生命:%d",h);13 SetDlgItemText(IDC_STATIC7,cc);}14 if(a==1) // 在主界面右下方显示经验值图示15 {x=600;y=500;16 dc.PatBlt(x,y,40,50,PATCOPY);17 CBrush Brush(RGB(0,0,200));18 dc.SelectObject(Brush);19 dc.PatBlt(x,y+50-h*50/20,40,h*50/20,PATCOPY);20 sprintf(cc,"经验:%d",h);21 SetDlgItemText(IDC_STATIC8,cc);}


 

1行 定义显示的x,y坐标。

2行 取人的生命值。

4行 定义灰色刷子。

5行 置当前显示设备环境为灰色

6a=0时第713行在主界面左下方显示生命值图示

7行 设显示坐标。

8行 在当前显示设备显一个40×50的灰色矩形。

9行 定义红色刷子

10行置当前显示设备环境为红色

11行在当前显示设备显一个表示当前生命值的红色矩形。

1213行在IDC_STATIC7上显示生命数值。

1421行在主界面右下方显示经验值图示。

到此,我们的游戏中生命值和有关的修改基本完成。现在再进入游戏中可要小心

了,这下可是“要命” 的了;当然你可以拾红色的药瓶为你加生命值⋯⋯。有关游戏

情节设计和玩法设计就留给读者自己去发挥了。

12-7 小结

在这一章里,我们学习了以下知识和方法。

1.对象的新属性:生命值、攻击力。

2.对象生命值的显示。

3.打斗时生命值的改变。

4.在场景中添加物品。

原创粉丝点击