做个RPG游戏主人翁
软件世界
在上一回,勇士阿志去了盘足山找盘古,希望他能帮自己打造冒险世界。谁知盘古因沉睡太久,手脚一时不能动弹。万般无奈之下,盘古只好教阿志使用地图编辑器创造了一个冒险世界。当阿志终于完成了任务的时候,盘古却说道:“唉,把冒险世界创造出来后,却会面临大问题。”
“会有什么问题?”
“当冒险世界展现出来的时候,也就是妖怪醒来的时候。到时是福是祸还是未定之数。”
“那些妖怪很厉害吗?”
“它们沉睡了那么久,全都是一些老妖,当然厉害。你现在只是在地图编辑器把冒险世界打造好了,还未在众人面前将它呈现出来,现在后悔还来得及。”
阿志说道:“我不会后悔,只用这地图编辑器怎么能显示我的本领?我就是要到自己打造的冒险世界里去闯荡!”
“好,既然你那么有勇气,我就教你如何把地图编辑器里的世界呈现出来吧!”
第三篇 在冒险世界中闯荡
上一期为大家简单介绍了地图编辑器的相关知识和使用方法,同时还为大家提供了一个简单的地图编辑器。你有没有试过自己亲手编辑一张地图呢?如果有的话记住要保存哦,因为这一期我们就要在游戏中把自己编辑好的地图调入进游戏中。然后,我们还要尝试着让我们的英雄在这冒险世界中走动、闯荡。
在学习如何调入地图场景之前,让我们先来认识一下程序的游戏引擎和在程序中多处用到的一个重要概念:状态机。
一、动起来──游戏程序的核心
所谓“麻雀虽小、五脏俱全”,我们的冒险游戏虽然只是最简单的RPG游戏,但同样拥有游戏引擎、状态机等开发的要素。正是因为具备了这些要素,我们的游戏程序才有了强大的运行动力,才能“动起来”。下面,就让我们先来认识一下它们。
1.游戏运行的动力──游戏引擎
我们这期主要学习场景的调入和人物的走动,它们在游戏程序中的哪部分呢?请看如图1所示的游戏程序的流程图。
游戏的主要工作都在运行场景这部分完成,所以像游戏中场景的调入和人物的走动等都在这部分工作。而游戏的核心,游戏引擎工作的地方也在这里。那什么是游戏引擎呢?
游戏引擎:游戏引擎是游戏开发中又一个有趣的概念。大家知道汽车有引擎、摩托有引擎。所以简单地讲,游戏的引擎和它们类似,也是为了让游戏跑起来和动起来的东西。而且,游戏引擎还应该是一个可以复用的事物,就像汽车引擎可以装在不同的汽车上使用一样。我们的RPG游戏虽然简单,但同样也会有一个简单的游戏引擎。
我们游戏引擎CEngine类的结构如图2所示。
2.模仿人的特性──认识状态机
在前面的图2中已经出现了“有限状态机”的字眼了,那什么是状态机呢?它在游戏开发中有什么用呢?
“状态机”(也叫有限状态机,有限是指状态有限)在这里简单地讲就是参照人的特性,使游戏在不同的情况下表现出不同的行为。好比人在累的时候就会睡觉,肚子饿的时候就会吃东西。这也是实现人工智能最简单的原理。
状态机用于游戏中,就是把一个游戏当成一台状态机,根据游戏中不同的状态来调用一个或多个不同的函数,从而实现不同的功能。在图1中,游戏有三个状态:GAMESTATE_BEGIN、GAMESTATE_RUN、GAMESTATE_EXIT,分别调用游戏的开始界面、运行场景(这是游戏的核心)、结束游戏三个函数。一个简单的状态机结构如图3所示。
那我们如何在程序中实现状态机呢?请看下面简单的示例:
enum GAMESTATE{ GAMESTATE_B
EGIN, GAMESTATE_RUN, GAMESTATE_
EXIT };
GAMESTATE GameState;
Switch(GameState)
{
case GAMESTATE_BEGIN: //处于游戏开始界面状态
开始界面;
break;
case GAMESTATE_RUN: //处于运行场景状态
运行场景;
break;
case GAMESTATE_EXIT: //处于结束游戏状态
结束游戏;
break;
}
上面的三个case语句就对应了状态机的三个状态。那么如何去改变游戏的状态呢?这很简单,一开始把游戏状态设为GAMESTATE_BEGIN,那样就会进入游戏的开始界面;在游戏的开始界面点击“开始游戏”时就把游戏状态改为GAMESTATE_RUN,这时就进入游戏的正题了;点击“退出游戏”时游戏状态设为GAMESTATE_EXIT。
小提示:编码前的准备工作
首先当然要在地图编辑器把地图编辑好。保存好地图文件后用VC.net新建一个空的Win32项目,项目名为“Game”。把地图编辑目录下的“Floor”、“Role”、“Object”三个文件夹复制到我们新建的游戏Game目录下,然后在Game目录下新建一个“Map”文件夹,并把编辑好的地图文件放在里面。
二、展现冒险世界──在游戏中调入场景
阿志在盘古大哥的教导下认识了游戏程序的基本结构之后,接着阿志就想向盘古讨教如何在游戏中展现前面编制好的地图了,只有将地图场景展现出来,阿志才有地方闯荡。盘古笑呵呵地说:“好,不要急。我们就来看看如何展现你打造的冒险世界。”
1.初识场景类
在游戏中调入完整的场景就是要依次调入地块、物体和人物,但在这之前,先要初始化场景类。
初始化场景的工作就是读取地图文件,然后根据读取出来的地图信息进行一些初始化工作。
读取的地图文件就是我们在地图编辑器所编辑好的地图文件,要成功调入场景,就要了解地图结构。地图结构如下:
const int MAP_WIDTH_MAX=64;
const int MAP_HEIGHT_MAX=64;
typedef struct MapStruct{
char mapname[32]; //地图名
int mapwidth; //地图宽度(单位:单元格)
int mapheight; //地图高度(单位:单元格)
int rolecount; //地图中的角色数目
RoleStruct role[64]; //角色结构,一个地图最多可以有64个角色
//场景单元格结构,最多可以有64×64个单元格
TitleStruct titlestruct[MAP_WIDTH_M
AX][MAP_HEIGHT_MAX];
}MapStruct;
(1)读取地图中的所有资源
在上一篇里我们知道,场景是由地块,物体,角色所组成的,要把场景调出来首先就要有这些资源。为CScene添加一个成员函数LoadSceneRes(),用来读取场景中所要用到的所有资源。如何读取地图资源,下面会有详细讲解。(调入场景的流程图见图7)
(2)调出场景
这就是场景类的最重要的部分了,要调入场景就是要先铺上地块,再放入物体,最后放入角色,实现遮蔽。利用场景类的实例代码下载地址:http://www.cpcw.com/30/game35.txt。
2.无中生有──显示大地
如果初始化了场景后,那在调出场景中的第一步就是要铺上地块了。
要铺上地块首先就要认识地块的结构。添加一个地块CFloor类,它的工作是读取游戏中的所有地块资源和信息。地块的结构如下:
typedef struct FloorStruct{
char chFloorPic[32]; //地块的图像名
int nFloorID; //地块的ID号
}FloorStruct;
在Floor目录下有一个文件floorinfo.flr,它保存了Floor目录下的地块图像名和ID号,CFloor类的工作就是读取这文件,然后调入地块图像。添加一个成员函数LoadFloor(),负责把文件floorinfo.flr的地块信息读取到数组m_FloorStruct中。
下面重点看看读取地图文件的原理。原理很简单,扫描每一个场景单元格,如果该单元格应该铺上地块(ID不等于-1),检查铺在该单元格上的地块ID号,然后把相应ID号的地块铺在该单元格上。
相应地调入地块的示例程序代码下载地址:http://www.cpcw.com/30/game 36.txt。如图4,我们将整个地块都展现在屏幕上了。
3.有屋又有田──调入物体
成功地调入了游戏中的地块后,大家是不是更有信心了呢。要调入完整的场景,还要调入相应的物体和人物。现在我们就先来看看调入物体的原理。
和调入地块相比,调入物体的不同主要是在调入的同时要实现遮蔽。首先要扫描每一个单元格,如果该单元格是某物体的关键坐标,就要得到该物体的ID号,然后在物体资源中查找该ID号的物体,找到后将它放到场景页面中。因为放入的时候要求是从Y值最小的开始放入,所以物体在放入后就实现了遮蔽。
相关程序下载地址:http://www.cpcw.com/30/game39.rar,运行后已把场景中的物体合理地调出来了(如图5)。
4.英雄何处不现身──角色的展现
阿志大嘘了一口气,盘古一口气讲了这么多,让他也感到要全部展现场景已经快成功了。于是,他抖擞精神,又继续开始听讲了。
在调入了地块和物体之后,最后当然是要调出角色了,这也是最难的一步。因为在调出每一个角色时都要检查该角色有没有被某些物体遮挡住(也就是角色有没有“踩”到某些物体的遮蔽坐标),如果有,就在放入角色后重新放入遮挡住角色的物体 ,从而实现遮蔽效果。
调出角色的原理如下:在地图文件里保存了地图上有多少个角色,还有地图上各个角色的结构信息(角色的坐标、方向等)。有了这些信息就可以正确地在场景中调出角色了,在场景上放上角色的时候要利用前面讲的方法实现遮蔽。
好了,经过我们逐步的努力,现在终于可以把整个场景都调出来了,而且还实现了角色的遮蔽效果(如图6)。调入场景的完整程序下载地址:http://www.cpcw.com/30/game311.rar。
三、冒险世界任我闯──角色的走动
阿志在盘古的带领下,终于将冒险世界呈现出来了。但当他一脚踏入其中的时候,却发现自己不能动了,这可怎么闯荡呀?盘古笑道:“不要急,我还没有教你怎么走动呢,咱们一步步来 。”
1.向左走、向右走──角色走动方向
要实现人物的走动,先要确定人物往哪个方向走。如何确定呢?我们先来看看一幅角色图(如图8):
这一幅图一共有八行人物,从上到下的八行我们分别编号为0到7,对应的角色走动的方向分别是:上方,右上方,右方,右下方,下方,左下方,左方,左上方。
知道了应该走向哪个方向,如何在角色图像中把正确的单个角色图像调出来呢?只要用角色的方向值乘以单个角色图像的高度即可,如角色向上方的图像的Y值就是0×Role.CellHeight。
2.我动、我静──控制角色的状态
前面为大家讲解了状态机的概念,状态机用在角色状态上就更直观了。角色可以有很多种状态,如处于“被敌人攻击”状态就会进行反攻或逃走,处于“肚子饿”状态就会去找食物或饿死,处于“生气”状态就会摆出生气的样子等等。这些都可以利用状态机设置合理的多种状态来实现。
原理:当然这里不需要那么多的状态,只要简单的“站立”和“走动”两个状态就行了,示例如下:
enum ROLESTATE{ ROLESTATE_ STAND, ROLESTATE_MOVE };
ROLESTATE RoleState;
检测:我们可以添加一个CheckRoleState( )函数用于检测角色现在处于的状态,它的实现方法很简单:当鼠标的右键按下时角色便处于“走动”状态,否则角色处于“站立”状态。
实现:添加一个RoleFSM()函数,它用于实现角色的状态机控制。在RoleFSM刚开始运行时要调用CheckRoleState()函数来确定角色初始的状态,然后就根据角色每个时刻的状态执行相应的行为。
3.脚长、脚短──如何走
知道了角色要走动的方向和角色的状态后,要让角色走动就要考虑角色走哪步和每步走多远了,甚至还要考虑多长时间换一次脚步等。看起来是不是很麻烦?放心吧,当你弄清楚以下内容时,要让角色走动起来并不比你自己走动更困难。
(1)该走哪一个脚步
角色应该往哪个方向走我们已经知道了,现在来看怎么确定角色应该踏哪一个脚步。回到图8,这个问题也就是要确定对于每行横向应该显示图8的哪一个角色图像。在图8中,我们知道,横向的八行代表的是角色的方向,而纵向的四列则代表了角色在各个方向走动时的四个脚步了。
定义一个变量step用于标志角色当前踏的是哪一个脚步,踏下一个脚步时就把step加1,如果加1后超出角色的脚步数目(图8的脚步数目为4),就再把step设为0。如此循环下去,从画面上我们就能看到角色在不停地走动了。
(2)走动算法
接下来,就要设置角色一步应该走多远,多长时间换一次脚步了。这两项的设置没有绝对的标准,更多地要靠自己去多多尝试,直到觉得角色走得“很合理”。
小提示:为了使角色走起来逼真,当角色往斜方向走的时候可以把每一步走动的值设小一点,否则玩家在玩的时候就会觉得角色往斜方向走的时候走得特别快。
4.人走不如屏走──实现滚屏
除非一个游戏场景的大小刚好和你的屏幕一般大,否则在人物走动中必然要用到滚屏技术。那滚屏是什么?又如何利用呢?
大家在玩游戏的时候有没有发现游戏的主角一直都在屏幕的中间交换脚步,而场景不停地在动,看上去就好象是主角在场景中走到一样。其实游戏的主角完全没有走动过,动的只是场景,这也可以说是相对论的应用吧。我们可以想象成摄影师拿着摄影机一直跟着主角跑。这种滚动屏幕从而相对地实现人物走动的技术就是滚屏技术,它是根据主角的位置来决定应在屏幕上显示场景中的某个部分(如图9)。
图9就是一个例子,假设图中黑色方框才是屏幕能显示的大小,那如何让人物从场景的左上角走到场景的中央呢?请看下面的示例:
设屏幕的宽度和高度分别为:
const int SCREEN_WIDTH = 800; //屏幕的宽度
const int SCREEN_HEIGHT = 600; //屏幕的高度
主角的坐标为(RoleX , RoleY)。
RECT rt;
rt.left = RoleX – SCREEN_WIDTH/2; //让角色的X坐标在屏幕中间
rt.top = RoleY – SCREEN_HEIGHT/2; //让角色的Y坐标在屏幕中间
rt.right = rt.left + SCREEN_WIDTH;
rt.bottom = rt.top + SCREEN_HEIGHT;
上面的代码就是实现了屏幕相对角色的移动。我们只要在角色走动的时候进行以上的计算就可以方便地实现滚屏了。
阿志在盘古的指导下,感觉到进步越来越大了。现在当他再次踏入冒险世界的时候,已经能四处走动了,虽然脚步还不太稳,虽然景色也不太丰富,但阿志还是感到十二分的满意。正当阿志准备从出口走向下一个场景的时候,发现完全走不到。开口想问盘古,也说不了话。盘古忙将他从冒险世界中抓了出来,又不徐不急地告诉他:“不要急,场景的切换和你说话的问题我们下次解决,好吗?”
“嗯”,阿志开心地答道。
人物走动的相关程序下载地址:http://www.cpcw.com/30/game3.rar
交流方式:
1.E-mail:bluefox72@21cn.com
2.论坛交流:电脑报读者论坛http://bbs.cpcw.com在疑难问题解答区有专门交流的贴子。








