本项目同步上传于github和coding上,国内读者可以通过在coding下载项目,注意本套开发日志同步的是Tutorial分支,其他分支内容可能与日志内容不符。请注意
github地址:https://github.com/Liweimin0512/uRPG
coding地址:https://git.dev.tencent.com/JeremyBrett/uRPG.git
今天我们来搞定角色的生命值和魔法值,在此之前我们要解释一个重要的概念——组件。
基于组件的游戏开发
也许你听过“组合大于继承”(没听过就先去听一下),简单来讲就是在游戏开发中描述一个游戏物体有什么比描述它是什么更合理。
那么这里我想把生命值和魔法值做成组件,因为游戏中除了玩家角色,游戏中的NPC、怪物甚至是一些“可交互物”都是有生命值的概念的。
起先,我想将生命值、魔法值作为两个组件。但更深入的分析,我将这两个数值抽象成一种叫做“状态”的组件,这个组件本身负责维护角色的各种状态数值,比如这里讲的生命值和魔法值。如果我们要开发一款类《饥荒》游戏,则这里的状态就可以是生命值、饥饿值和san值。
闲话少说,我们开始操作。
准备工作
首先我们先新建一个枚举蓝图,取名为E_Stats,有三个值,分别如下图所示:
然后我们创建一个结构体,保存最大、最小值、当前值和回复时间、恢复速度等,取名为s_StateData。如下图所示:
组件:BpC_StateManager
然后我们创建一个组件,也就是今天的主角:BpC_StateManager。这个组件负责储存、管理其所在Actor上的所有State,并在需要的时候通过事件调度器完成回调,这个我们会在之后讲到。
正如上文所说,我们需要储存状态,所以这里我们新建一个Map类型的变量,取名为StatDatas:
这个组件需要几个函数,我们一个一个新建
首先是GetStateData,很简单,获取状态数据:
其次就是设置状态数据,SetStateData:
这里调用了UpdateStat这个事件调度器,这个我们在之后讲。此外,为了方便设置当前值,这里还写了一个函数ModifyStat:
这个函数需要传入状态类型、更改的值。先将这两个值作为局部变量存储起来(主要为了代码的整洁)。然后判断当前Animated?是否为真。为真则直接返回。否则:
通过SetStatData设置状态数据,设置的方式就是先获取当前值,然后加上传入的值,并且保证在最大、最小值之间(Clamp)。
部分状态可能需要自动回复,这部分我的做法是创建一个自定义事件,Actor调用这个事件,就会通过SetTimerByFuncitonName节点来周期性调用状态恢复函数,具体代码如下图所示:
首先便利StatDatas,其中的项。先判断RateValue和RegenValue都不为零。然后:
RateValue就是SetTimerByFuncitonName调用的时间,具体的函数名依据StatType确认,然后调用上文中编写的SetStatData函数即可。
Actor
首先就是给Bp_CharacterBase这个Actor添加BpC_StateManager组件,并设置组件的值如下图所示:
如果你跟着一步步操作到现在,应该已经意识到StatType中“/”的作用,这里就不再多说了。
还记得上文中所说的事件调度器么?这里我们要在Bp_CharacterBase中调用它。就在BeginPlay事件中,添加如下节点:
上图代码的含义,就是其BpC_StateManager组件调用UpdateStat状态时候,也会调用下方的SetupStaBar函数,这里之所以如此实现,是因为不同的Actor对于UpdateState事件的处理方式是不同的,比如主角是要显示在主界面UI上,而怪物则是显示在头顶版血条上。
并且需要注意的就是,在最后调用BpC_StateManager的SetupStatRegeneration事件。
现在我们需要让状态变化的结果显示在UI上,创建一个Weiget,并取名W_Main。添加两个ProgressBar。就可以关掉了。
在Bp_CharacterBase中创建W_Main并添加到ViewPort上:
虽然UE4为我们提供了关于ProgessBar中很多参数很方便的绑定,但这并不是我们学习的重点。仔细思考发现,我们并不需要每帧检测State数值的变化,而只需要在UpdateState时候更改ProgessBar上的显示即可,所以函数SetupStatBar的实现如下图所示:
Debug
又到了愉快的Debug时间,这次的Debug方式依然简单,在Bp_CharacterBase的事件图表中添加如下事件:
然后运行游戏,试着改变Mana,然后将StatType修改为Health试试。好的,搞定,完美!
下集预告
之后的几期,我们将陆续开发一个可扩展的技能系统。这个技能系统尽可能地由数据驱动,但我并不会直接创造一个这样的系统,而是由最“笨”的方法不断改进、重构,最终得到的,这个过程也将记录下来,可以更好地和同学们分享。那我们下期再见!