回合制的游戏中,涉及到属性计算,最重要的一块就是伤害,也可以说是一个战斗的核心灵魂。这个部分希望分享一下游戏中伤害的计算的实现思路。
如果还需要考虑额外的伤害数值思路,可以参考我的这一篇文章,相当于对这一篇的扩展。
高贵纯合子:虚幻回合制游戏战斗框架-伤害计算-伤害动态调整9 赞同 · 2 评论文章
如果对回合制游戏的框架感兴趣,则可以看这一篇
高贵纯合子:虚幻回合制游戏战斗框架52 赞同 · 13 评论文章
最近在玩1999,那么我就参照这个游戏来写伤害的计算方式。
攻击力防御力技能伤害倍率技能增伤率防御减伤率暴击百分比(攻击力−防御力)×技能伤害倍率×(1+技能增伤率)×(1−防御减伤率)×(1+暴击百分比)
创建伤害相关属性
首先创建AttributeSet,为伤害计算提供必要的一些属性。
先只考虑最基础的情况,创建属性攻击,防御,暴击率和暴击倍率
然后创建一个HealthAttributeSet,里面为角色的生命值等属性,重点是这里面应该创建一些中间属性,方便伤害的计算。伤害计算后并不会直接修改生命值,而修改的属性是FinalDamage,然后在PostGameplayEffectExecute函数中对生命和护盾值就行修改。
为什么把属性拆成多个属性集而不是放到一起呢,我认为有两个好处,第一是对于一些可破坏物体,建筑物之类的东西,并不需要冗余的属性;第二是GAS里涉及到属性的get,set都是通过在属性集里进行遍历属性进行的,将高频的属性拆分出来,或者将一个庞大的属性集拆分成多个小的属性集,都能减少游戏的消耗
创建伤害计算类
通过ExecutionCalculation实现伤害的计算,因此伤害是不可预测的
这里为了方便,我创建了一个结构体,存储伤害计算中需要用到的一些数据
然后创建四个函数,分别用于计算基础攻击,技能倍率,伤害百分比,减伤百分比,暴击伤害倍率。这里选择把伤害拆分成多个地方进行计算是为了方便后续的派生和代码的管理,一股脑地写在一起也是木有问题的w
下一步重写Execute函数,在这里实现伤害计算的公式
实现如下,其实就是调用上面的几个计算伤害的公式,分别算出基础伤害,技能倍率,增伤百分比,减伤百分比,暴击倍率,然后通过伤害计算公式获取伤害,然后把伤害写入中间属性FinalDamage中。
伤害计算实现
考虑到我们目前实现的属性只有攻击力,防御力,暴击率和暴击倍率,那么需要实现的函数只涉及到计算基础伤害,暴击伤害和技能倍率,其余的计算直接返回1.0即可。
基础伤害,直接攻击力-防御力
伤害倍率,如果伤害是由技能触发的,则读取技能的伤害倍率
计算暴击倍率,如果暴击,则增伤1+暴击倍率
对计算出的伤害进行处理
在HealthSet中,重写函数,会在GE执行后触发。我们在伤害计算,修改属性FinalDamage后,就会触发这个函数。
依据伤害,对生命和护盾进行扣减。
解释一下逻辑,获取生命值+护盾。如果护盾值>=伤害,则说明护盾正好可以抵消伤害,只需要扣护盾的数值即可。而如果护盾的数值无法抵消伤害,则将护盾数值清零,然后生命值扣取剩余的伤害。如果伤害后的角色血量小于0,则广播角色死亡的消息。
伤害的扩展
接下来考虑伤害计算中更多的情况,比如有的装备可以让角色对中毒或者燃烧状态的敌人造成更多的伤害。我认为这种较为通用的情况将这些做成一个属性会更好一点。类似的效果只需要通过一个ge修改对应的属性值即可
新增两个属性,对燃耗状态的增伤,以及对中毒状态的增伤。
然后重写计算上海百分比的函数。首先判断目标是否带有中毒或者火焰的tag,如果有,则增加对应属性作为增伤的伤害百分比。
闪避和命中
考虑到有些情况下,需要有命中和闪避的属性,可以通过GameplayEffectCustomApplicationRequirement
来实现,GE只有满足条件时才会Apply。当然,这个判断直接放到伤害计算里也是没有问题的,这里只是提供一种解法。
首先新增两个属性Accuracy和Dodging
然后创建GameplayEffectCustomApplicationRequirement
的派生类,用于判断伤害是否命中
代码实现如下,命中率=(命中-闪避)