前言
Buff模块可以说是技能中最核心又最复杂的系统了。一个优秀的Buff系统能够让策划的创意得到最大限度的发挥,大幅增强游戏的战斗深度和可玩性,并且同时也能让开发者轻易的扩展维护,支持更多的效果和功能。本章将为你详细讲述一个强大的Buff系统是如何实现的。(长文预警)
正文
第一节:Buff定义
首先我们将Buff系统分为三个层次,具体继承关系如下:
Buff:所有Buff的基类,包含各类成员函数和基本接口。
Modifier:继承于Buff,代表这个Buff是一个修改器,它可以用来修改当前目标的各种属性,状态等等。抽象Modifier这个类的目的是出于性能优化的考虑。因为当Buff修改角色的属性或者状态时,会导致重新计算角色的动态属性, 而在游戏中我们很多的Buff并不需要修改角色的属性状态,仅仅用来提供一段逻辑。那么如果它是一个Buff不是Modifier,就不需要重新计算角色的动态属性。
MotionModifier:继承于Modifier,代表此类Buff提供修改玩家运动效果的功能。因为牵涉到与运动组件的交互,所以抽象出一个新的类。
Buff类层次结构划分了之后,那么Buff需要包含那些成员数据呢?
我们提供BuffTypeId(Buff类型Id), Caster(Buff施加者),Parent(Buff当前挂载的目标), Ability(Buff由哪个技能创建),BuffLayer(层数), BuffLevel(等级)BuffDuration(时长),BuffTag,BuffImmuneTag(免疫BuffTag)以及Context(Buff创建时的一些相关上下文数据)等等。
在这里,我将说明一下Caster,Ability以及Context这三个成员,这也可能是我们Buff系统中一些独特的点。
Caster代表Buff的施加者,它有可能为空,也有可能不为空,视具体构造时是否传Caster参数而定。但是Buff有一个配置项bNoCaster(是否强制设置Caster为空)。如果bNoCaster = true。则Buff的Caster一定为空。
为什么要有一个bNoCaster设置呢?那是因为我们的Caster不仅仅是一个成员项,它还关系到Buff合并问题。如果存在两个TypeId类型相同的Buff时候,当他们的Caster相同才可以走合并流程(Buff层数增加),如果Caster不同,则不能合并。当策划有一些玩法需求可以多人给BOSS叠Buff时就可以配置Buff的bNoCaster=true,这样就不需要开发者在写代码添加Buff的时候小心翼翼的设置Caster参数为空了。另外还有几种情况也需要设置bNoCaster=true,比如存在一个熔岩地图,或者冰雪地图,玩家每秒掉多少血量,这个时候也可以配置bNoCaster=true。再比如说一些活动buff,如双倍经验buff,红名惩罚buff,都可以由策划配置bNoCaster=true。类似于双倍经验,还有红名Buff这种所有需要存盘的Buff,我们都需要设置bNoCaster=true。也许会有人有疑问,这样能满足需求吗?完全可以,我会在最后的示例部分举出一个例子来解答这个疑问。
Ability代表Buff是由哪个技能创建,它有可能为空,也有可能不为空,视具体构造时是否传Ability参数而定。通过Ability这个成员类型,我们就将Buff与技能联系起来了,我们能在Buff中取得技能的各种数据,通过获取技能的数据,然后由Buff来实现各种各样的技能效果。
BuffTag,BuffImmuneTag由策划配置(基于标记位),标注这个Buff属于那些种类以及免疫哪些种类。策划可以定义一些Tag如下:Metal = 1 << 1 (金系)
Wood = 1 << 2(木系)
Water = 1 <<