做个RPG游戏主人翁 第五篇 英雄是怎么炼成的——制作属性系统

软件世界

  阿志学会了在冒险世界中说话之后,开始了与精灵阿比的对话。阿志告诉精灵阿比他要去和妖怪战斗了,精灵阿比忙说要给阿志一些道具,可是阿志怎么找也找不到身上有什么道具。

  这就是英雄修养成长的摇篮,本期打造的人物属性系统

  阿志:“嘿!你在骗人呀,你哪有什么道具给我啊?我怎么找不到。”

  精灵阿比:“那是因为你现在还没有属性系统,我们先来认识和创建它吧。”

  一、RPG游戏精髓──属性系统

  当你在游戏中捡到名字听起来很酷的一把剑时,你第一时间会怎样呢?当然是进入属性系统里看看这把剑的属性,如果利害的话你还会将它装配上身;在游戏中,你升级后可能会到属性系统里查看你现在的攻击力和防御力等;战斗中当你失“血”过多的时候,你会进入属性系统里寻找物品箱的药物……

  你在玩RPG游戏时是不是时常遇到以上的情况呢?一般来讲,在属性系统里可以查看我方队伍当前各人的状态,可以使用物品箱,可以装配武器,有的还可以调节部分游戏选项(如音量大小)等等。属性系统在游戏中使用得很频繁,相信大家也不会觉得它陌生。我们这次就来一起打造属于自己的游戏属性系统。

  1.进入属性系统方法的实现

  进入属性系统的方法通常是按下Esc键或用鼠标点击属性系统的图标进入。我们这里选择后者。首先需要一个属性系统的图标,不用太大,比鼠标图标大一点就可以了。具体实现的效果是:平时在游戏中是见不到属性系统的图标的,当鼠标移到屏幕右上角时才把属性系统图标显示出来,然后单击鼠标便进入属性系统。

  我们现在要给游戏的状态机添加一个状态GAMESTATE_ATTRIBUTE,表明游戏处于属性系统状态。我们先添加一个属性系统类Cattribute,再添加一个函数Check()。此函数的工作就是检查鼠标是否移到屏幕右上角,它还是进入属性系统的桥梁。代码如下:

  POINT p;

  GetCursorPos(&p);//得到鼠标坐标

  if(p.x>740&&p.y<50)//检查鼠标是否在右上角

  {

  //显示属性系统图标

  //如果按下鼠标左键就把游戏状态改为GAMESTATE_ATTRIBUTE

  }

  2.认识属性系统的状态机

  在前面的叙述中状态机又和大家见面了,状态机在游戏中还真是无处不在呢!我们先看看我们的属性系统界面(如图1):

  在我们的属性系统界面中,左边的是选项栏,右边有一个列表框,列表框下面是说明信息栏。可以看到,属性系统中有六个选项。那么,属性系统的状态机就要有七个状态,如下:

  enum A_FSM{

  A_FSM_NONE, //没选中任何选项

  A_FSM_STATE, //处于显示人物当前状态的状态

  A_FSM_RES, //处于物品箱状态

  A_FSM_LOAD, //处于读取游戏进度状态

  A_FSM_SAVE, //处于储存游戏进度状态

  A_FSM_EXIT, //处于退出游戏状态

  A_FSM_RETURN //处于返回游戏场景状态

  };

  为属性系统类Cattribute添加一个运行函数Run()。当按下属性系统的图标,游戏状态变成GAMESTATE_ATTRIBUTE时,调用的就是Cattribute的Run()函数。此函数首先执行属性系统的菜单函数,然后便启动属性系统的状态机,如下:

  Menu();

  switch(m_A_FSM)

  {

  case A_FSM_STATE:

  //显示人物当前状态

  break;

  case A_FSM_RES:

  //进入物品箱

  break;

  case A_FSM_LOAD:

  //读取游戏进度

  break;

  case A_FSM_SAVE:

  //储存游戏进度

  break;

  case A_FSM_EXIT:

  //把游戏当前状态设为GAMESTATE_EXIT

  break;

  case A_FSM_RETURN:

  //把游戏当前状态设为GAMESTATE_RUN

  break;

  }

  二、游戏中的物品怎么实现的

  阿志听了精灵阿比对属性系统的介绍,非常开心,就准备开始使用强大的属性系统。精灵阿比忙阻止了他,因为属性系统都还没建立。在创建属性系统前,精灵阿比决定先带领阿志来认识一下各类物品。

  一般在RPG游戏中的物品可分成三类:可使用的、可装配的、用于激发剧情的。可使用的就是一些药物或在战斗中使用的辅助物品。可装配的则是武器和防具等。而用于激发剧情的物品在游戏中也是很常见的,只有当你拥有这类物品的时候,才能完成某项任务或去某个地点。

  1.实现物品箱类

  我们的游戏比较简单,所以只要新建一个物品箱类就可以,因为物品少,物品属性也是直接在程序中设定的。

  首先我们来完成添加物品箱类CResBox的工作。在这之前我们先来看看物品的结构:

  enum ResType{ //物品类型

  RESTYPE_MEDICAMENT, //药物

  RESTYPE_ARMS //武器

  };

  struct Res{

  char m_chName[64]; //物品的名字

  ResType m_ResType; //物品的类型

  char m_chScript[64]; //物品的脚本文件

  char m_chInfo[101]; //物品的说明信息

  };

  在物品箱类里还需要一个物品列表结构,记载物品结构和物品的数量,如下:

  struct reslist{

  Res m_res; //物品

  int m_count; //物品数量

  };

  2.添加物品的方法

  我们现在为角色类添加一个物品箱对象,然后就可以给主角添加物品了。添加什么物品?现在是你发挥想象力的时候了。

  如何来描述物品有什么用呢?还记得我们有一个“聪明”的方法吗?对了,就是用脚本了,物品的脚本记录了物品的作用。在我们的游戏目录下新建一个名为“Res”的文件夹,把物品脚本文件放在此文件夹之下。

  3.物品列表的展现

  首先把物品箱里的物品以列表形式显示出来,为物品箱类添加一个显示物品列表的函数ShowResList()。显示出来的物品列表如图2,物品列表左边显示的是物品的名称,右边显示的是物品的数量。

  显示物品列表的程序代码下载地址:http://www.cpcw.com/32/game52.rar。

  显示物品列表对大家来说应该不陌生吧?程序主要是一个循环,循环的次数就是角色拥有的物品的种数。用TextOut()函数显示出物品的名称和数量,列表中每一列的高度是40像素。

  现在我们来看看如何在物品列表里选中一种物品。在物品箱类里定义一个成员变量m_nSl,这个变量就是被选中物品在物品列表里的索引值,没选中任何物品时它的值为-1。选择物品的程序代码下载地址:http://www.cpcw.com/32/game53.rar。

  因为我们知道列表中每一列的高度是40像素,所以可以根据鼠标当前的坐标计算出鼠标是否指向某一物品列表。被选中的物品以蓝色显示出来,并且显示出该物品的说明信息,如图3。

  显示物品说明信息的程序代码下载地址:http://www.cpcw.com/32/game54.rar。

  4.使用物品的实现

  进入了物品箱后,在物品列表的下面出现一个“使用”按钮,选中物品后按下此按钮能使用被选中的物品。现在我们就一起来实现这个功能。

  还记得每样物品都有一个脚本文件吗?使用物品其实就是要运行物品的脚本文件。在物品箱类CResBox里添加一个函数Use()。此函数实现如下:如果当前选中一样物品(即m_nSl不等于-1),并且该物品的数量不等于0,就运行该物品的脚本文件。

  Use()函数的程序代码下载地址:http://www.cpcw.com/32/game55.rar。

  5.实现物品效果的脚本

  我们先来看看两个脚本:

  在“止血草.scr”里的脚本如下:

  ADDHP(“200”);

  RETURN();

  在“灵草.scr”里的脚本如下:

  ADDMP(“200”);

  RETURN();

  我们看到有两个新的脚本命令:ADDHP和ADDMP。分别用于添加生命值和添加魔法值。在脚本类添加这两个脚本命令。ADDHP的执行函数为AddHp();ADDMP的执行函数为AppMp()。

  (1)使用类物品的实现

  ADDHP这个脚本命令很容易实现,只要把主角的生命值加上要增加的生命值就可以了。增加后如果主角当前的生命值大于主角的最大生命值,便要把主角的最大生命值赋给主角的当前生命值。最后当然要把被选中的物品的数量减去一。AddHp()函数的实现程序代码下载地址:http://www.cpcw.com/32/game57.rar。ADDMP的实现也类似ADDHP。

  现在在物品箱里选中一个物品然后按“使用”按钮就可以使用该物品了。没反应?那当然,主角当前的生命值和魔法值都是满的。可以在函数AddHp()和函数AddMp()里把增加改成减少,这样就可以立刻看到效果了。

  (2)装配类物品的实现

  使用类的物品可以简单地用上面的方法实现,但装配类的物品呢?武器增加角色的攻击力,防具增加角色的防御力。这里的增加不能像使用类的物品那样直接加上去,因为这样加上去即使武器和防具拿下后,角色的攻击力和防御力也不会减少(这样的话只要不停地装配再卸下,我们的主角就会变成无敌了)。下面以武器这类物品为例来说明这个问题。

  给角色类定义一个结构,记录的是该角色是否装配有武器,武器的信息,该武器对角色各属性的影响。如下:

  struct Arms{

  bool m_bh; //是否装配有武器

  Res m_Arms; //武器

  int m_nAp; //该武器对攻击力的影响

  int m_nDp; //该武器对防御力的影响

  }

  当我们给角色装配武器时就把结构Arms里的m_bh设为true,即当前装配有武器;m_Arms得到的就是武器的信息;至于m_nAp和m_nDp就要从武器的脚本文件里得到了。

  角色类还要定义两个成员变量,分别为:辅助攻击力,辅助防御力。如下:

  int m_nAAp; //辅助攻击力

  int m_nADp; //辅助防御力

  程序刚开始当然要把它们都设成0。每次装配武器时都要进行以下计算:

  m_nAAp+=Arms.m_nAp; //计算出装配武器后的辅助攻击力

  m_nADp+=Arms.m_nDp; //计算出装配武器后的辅助防御力

  而在卸下武器时就要先进行以下计算:

  m_nAAp-=Arms.m_nAp; //计算出卸下武器后的辅助攻击力

  m_nADp-=Arms.m_nDp; //计算出卸下武器后的辅助防御力

  然后把结构Arms里的m_bh设为false就可以了。

  在战斗中角色的实际攻击力就等于角色的原始攻击力再加上辅助攻击力m_nAAp;角色的实际防御力就等于角色的原始防御力再加上辅助防御力。

  如果想让自己角色的能力更加丰富,可以给角色类多加几项属性,如敏捷度、速度等等,当然也要在结构Arms里加上对这些属性的影响值。其他装配如防具、饰物等等的实现方法也和武器的实现方法一样。

  (3)激发剧情类物品的实现

  这类物品通常主要用于激发新的剧情,同时有些也是可以装配的。比如师父叫你上山找一块奇怪的石头,如果找不到那石头就下不了山。

  要实现这个情节可以在实现山的出口处的代码里放一个脚本文件,该脚本的作用就是检查你的物品箱里有没有你师父要你找的奇怪的石头。如果有就放你下山,否则你就只好一直在山上找了,同时走到出口的地方还会自言自语地说一句话:“师父要的那块怪石头还没有找到,现在不能下山,否则就会挨骂了。”

  三、游戏存储与读取之谜

  阿志:“现在我可以进入我的属性系统了,还可以在里面查看我当前的状态,找到我的物品箱和使用物品。但当我累了的时候,真的可以把现在的冒险进度储存起来吗?”

  精灵阿比:“当然可以!当你累了的时候就可以进入属性系统里找‘客栈’,‘掌柜’会帮你记录你现在的冒险进度,然后你就可以去休息了。当你休息好要再次进入冒险世界的时候,‘掌柜’又会带你回到上次冒险的地方。”

  阿志:“真的?那我很想知道‘掌柜’是怎样做到的。”

  精灵阿比:“没问题!我现就带你去‘客栈’找‘掌柜’。”

  1.储存和读取游戏进度

  一个RPG游戏的冒险旅程是很长的,想一次就完成是会累死人的啊!所以在完成了某一阶段旅程时,便要储存当前的游戏进度,以后就可以读取这个进度继续进行你的冒险之旅了。要储存游戏当前进度就要把现在游戏相关的信息全部都以文件形式保存起来。这些游戏相关信息就是:地图当前数据、主角当前数据、脚本相关的变量值等。记得你玩的游戏的游戏目录下有时会有一个名为“data”或“save”的目录吗?游戏的储存文件通常就是保存在此目录下。

  在我们的游戏目录下新建一个名为“save”的文件夹。我们的游戏储存文件就是以后缀名“.dat”保存在此目录下。读取游戏进度就是把save目录下的储存文件读取出来。

  2.退出游戏和返回游戏

  退出游戏和返回游戏的设置也很简单。只需要把游戏状态设为GAMESTATE_EXIT即可,游戏引擎类接收到这个状态时就会调用QuitGame()进行一些扫尾工作,然后把ExitGame设为true,便可退出游戏。要想在属性系统中返回游戏,只要把游戏状态设为GAMESTATE_RUN,就可以实现。

  本篇完整程序下载地址:http://www.cpcw.com/32/gamr5.rar。

  阿志在掌柜的安排下好好地休息了一晚,现在可真是精神抖擞!正当阿志准备出发到妖怪的老巢的时候却被掌柜叫住了。

  掌柜:“小伙子,这么早你要去哪里?”

  阿志:“我要去降妖除魔!”

  掌柜:“哦!原来你就是勇士阿志啊。”

  阿志:“嘿嘿。没错,准备了这么久就为了这一天!”

  最后一期,阿志终于要和妖怪们开打了。精彩内容大家千万不要错过!

  交流:电脑报论坛支持(bbs.cpcw.com)。在疑难问题解答区有置顶的帖子供大家交流。