GitHub仓库地址: m969/EGamePlay

Status

buff本质上是一种短暂的 状态效果 ,我们称之为 Status ,它们有一定的生命周期。这种状态效果或者说这一系列状态效果会最终确定角色这一时刻的状态,以此影响角色的行为和战斗的结果,它们甚至还可以按一定的机制触发某种逻辑。
通常我们将增益的状态效果称作Buff,将减益的状态效果称作Debuff。
我们初步总结出三种基本状态效果类型: 属性修饰 、 行为禁制 和 逻辑触发 。
属性修饰 :诸如提升10%的攻击力、增加30点护甲值这些状态效果我们称之为属性修饰。
行为禁制 :行为禁止与控制,像眩晕、沉默、恐惧等就属于行为禁制类型的状态效果。比如眩晕包含移动禁止、攻击禁止、施法禁止,沉默包含施法禁止,恐惧包含施法禁止、攻击禁止、移动控制。
逻辑触发 :buff的逻辑触发机制基本分为3种,按时间触发、按条件触发、按行为动作触发。比如按时间触发有几秒后触发伤害、每间隔多少秒造成伤害等,按行为动作触发有受伤时怎么怎么样、攻击时怎么怎么样等,按条件触发有当生命值低于多少时怎么样、当受到的伤害大于多少时怎么样等。(条件机制比较复杂些,基本每种类型的条件都需要单独写代码判断,这个最好是通过抽象分离和事件解耦来减少代码的侵入性。)
其他 :以上3种虽然囊括了大部分的buff效果,但依然还是会有一些非常特殊的效果可能需要单独写逻辑代码。

状态配置

状态配置可通过在资产目录右键然后选择 Create/技能|状态/状态配置 命令来创建。
这里的Status配置都属于状态类型,而具体的参数可以固定,亦可以由战斗实体或是技能传参数进来。

Buff和被动

buff和被动本质上是一种东西,只是叫法不同,唯一区别就是被动一开始就被装载且一直存在,而buff的装载时机任意,且可以一直存在也可以短暂存在。

StatusAbility

程序实现上,我们将状态效果命名为 StatusAbility ,也即 状态能力 ,属于能力的一种类型,继承于AbilityEntity。
    
    
public class StatusAbility : AbilityEntity { //投放者、施术者 public CombatEntity Caster { get ; set ; } public StatusConfigObject StatusConfigObject { get ; set ; } public FloatModifier NumericModifier { get ; set ; } //激活 public override void ActivateAbility ( ) { } //结束 public override void EndAbility ( ) { } //应用能力效果 public override void ApplyAbilityEffect ( CombatEntity targetEntity ) { base . ApplyAbilityEffect ( targetEntity ) ; } }

StatusAbilityExecution

状态能力执行体StatusAbilityExecution ,这个用来实现一些外显的表现上的效果。比如我装载了一个被动,每隔几秒钟会释放一个逐渐向外扩散的光环对敌人造成伤害或是对己方进行治疗,就可以用状态能力执行体来实现。一般的buff效果应该用不到这个。

ConditionEntity

在一些比较复杂的状态效果里,按条件触发的效果会比较多,所以我们抽象出一个 条件实体ConditionEntity ,并统一由 条件管理组件ConditionManageComponent 管理。条件管理组件和行动点管理组件一样,每个战斗实体都会有一个。
    
    
/// <summary> /// 条件管理组件,在这里管理一个战斗实体所有条件达成事件的添加监听、移除监听、触发流程 /// </summary> public sealed class ConditionManageComponent : Component { private Dictionary < Action , ConditionEntity > Conditions { get ; set ; } = new Dictionary < Action , ConditionEntity > ( ) ; public override void Setup ( ) { base . Setup ( ) ; } public void AddListener ( ConditionType conditionType , Action action , object paramObj = null ) { switch ( conditionType ) { case ConditionType . WhenInTimeNoDamage : var time = ( float ) paramObj ; var condition = EntityFactory . CreateWithParent < WhenInTimeNoDamageCondition > ( Entity , time ) ; Conditions . Add ( action , condition ) ; condition . StartListen ( action ) ; break ; case ConditionType . WhenHPLower : break ; case ConditionType . WhenHPPctLower : break ; default : break ; } } public void RemoveListener ( ConditionType conditionType , Action action ) { if ( Conditions . ContainsKey ( action ) ) { Entity . Destroy ( Conditions [ action ] ) ; Conditions . Remove ( action ) ; } } }
这个是多少时间内没有受伤条件的实现:
    
    
public sealed class WhenInTimeNoDamageCondition : ConditionEntity { private GameTimer NoDamageTimer { get ; set ; } public override void Awake ( object initData ) { var time = ( float ) initData ; NoDamageTimer = new GameTimer ( time ) ; GetParent < CombatEntity > ( ) . ListenActionPoint ( ActionPointType . PostReceiveDamage , WhenReceiveDamage ) ; } public async void StartListen ( Action whenNoDamageInTimeCallback ) { while ( true ) { if ( IsDisposed ) { break ; } await ET . TimerComponent . Instance . WaitAsync ( 100 ) ; NoDamageTimer . UpdateAsFinish ( 0.1f , whenNoDamageInTimeCallback ) ; } } private void WhenReceiveDamage ( CombatAction combatAction ) { Log . Debug ( $" { GetType ( ) . Name } ->WhenReceiveDamage" ) ; NoDamageTimer . Reset ( ) ; } }

示例:坚韧(被动)

这里贴一个稍微复杂一点的被动效果的实现。 坚韧:如果英雄在4秒内没有受到任何伤害,就会每两秒回复2%的最大生命值。
    
    
public class StatusTenacity : StatusAbility { private GameTimer HealthReplyTimer { get ; set ; } = new GameTimer ( 2f ) ; private bool CanReplyHealth { get ; set ; } public override void ActivateAbility ( ) { base . ActivateAbility ( ) ; CanReplyHealth = true ; AbilityOwner . ListenActionPoint ( ActionPointType . PostReceiveDamage , EndReplyHealth ) ; AbilityOwner . ListenerCondition ( ConditionType . WhenInTimeNoDamage , StartReplyHealth , 4f ) ; Coroutine ( ) ; } //协程 private async void Coroutine ( ) { while ( true ) { if ( IsDisposed ) { break ; } await ET . TimerComponent . Instance . WaitAsync ( 100 ) ; if ( CanReplyHealth ) { if ( AbilityOwner . CurrentHealth . Percent ( ) < 1f ) { HealthReplyTimer . UpdateAsRepeat ( 0.1f , ReplyHealth ) ; } } } } //结束生命回复 private void EndReplyHealth ( CombatAction combatAction ) { CanReplyHealth = false ; } //开始生命回复 private void StartReplyHealth ( ) { CanReplyHealth = true ; } //生命回复 private void ReplyHealth ( ) { var action = CombatActionManager . CreateAction < CureAction > ( AbilityOwner ) ; action . Target = AbilityOwner ; action . CureValue = AbilityOwner . CurrentHealth . PercentHealth ( 2 ) ; action . ApplyCure ( ) ; } }
最新完整的demo和代码在github仓库可取得。
相关文章:
  • 0、如何实现一个灵活、通用的战斗(技能)系统——序章
  • 1、如何实现一个灵活、通用的战斗(技能)系统——数值系统
  • 2、如何实现一个灵活、通用的战斗(技能)系统——数值系统(升级版)
  • 3、如何实现一个灵活、通用的战斗(技能)系统——战斗行动机制
  • 4、如何实现一个灵活、通用的战斗(技能)系统——战斗实体
  • 5、如何实现一个灵活、通用的战斗(技能)系统——能力Ability
  • 6、如何实现一个灵活、通用的战斗(技能)系统——Status状态效果
  • 7、如何实现一个灵活、通用的战斗(技能)系统——Skill技能
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐