虚幻回合制游戏战斗框架-伤害计算-伤害动态调整

本文介绍一个基于GAS的伤害计算的扩展,是一套对伤害计算中各个属性计算进行调整和修饰的方案。

一般的伤害计算流程为,获取包括攻击力,防御力,伤害百分比等伤害属性,然后计算出伤害。

尽管伤害属性可以做到大部分的事情,但是仍然有一些更复杂的情况,很难全部都通过属性的方式去实现。比如对眩晕的敌人造成额外的伤害,不太可能单独作为一个属性存在。否则还会有对击飞的敌人,对防御提升的敌人,对无法移动的敌人造成更多的伤害。每一种新的效果都需要增加新的属性,使用起来也过于繁琐和麻烦。

而如果把这些可能的伤害条件放到伤害计算类中,通过一个个if去判断,有感觉有些不太合理,也不方便进行管理。

因此我考虑在GE的基础上扩展一些功能,用于一些更加灵活和琐碎的伤害情况。

这里还是贴一下写的文章,有兴趣的可以关注一下

回合制游戏战斗框架​jfpejv68no.feishu.cn/docx/YFATdbdQfouMtAxYqCCcacVPnkd

伤害的动态修饰实现

新增类GameplayEffectAdjustment ,主要有两个函数组成。CanApplyEffectAdjustment 用于判断属性调整是否可以生效。如果生效的话,TryApplyEffectAdjustment 函数将需要调整的数值写入AdjustmentData

scss 复制代码
UCLASS(DefaultToInstanced, EditInlineNew, Blueprintable, BlueprintType)
class LYRAGAME_API UGameplayEffectAdjustment : public UObject
{
   GENERATED_BODY()

public:
   UFUNCTION(BlueprintNativeEvent)
   bool CanApplyEffectAdjustment(const FGameplayEffectSpecHandle& EffectSpecHandle, UAbilitySystemComponent* SourceASC,
      UAbilitySystemComponent* TargetASC);

   UFUNCTION(BlueprintNativeEvent)
   void TryApplyEffectAdjustment(TMap<FGameplayTag, float>& AdjustmentData);

protected:
   UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Adjustment)
   TArray<FGameplayEffectAdjustmentData> Adjustment;
};

然后对GameplayEffect的代码进行一定的修改,新增两个数组,分别储存在施放伤害GE时,自己作为source和target时的伤害修饰。

举个例子,如果有一个效果,对眩晕的敌人伤害增加50%,这个效果就会放SourceGrantEffectAdjustments 。而对于效果,眩晕时承受伤害减少50%,那么这个效果就存储在TargetGrantEffectAdjustments

ini 复制代码
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Instanced, Category=EffectAdjustment)
TArray<UGameplayEffectAdjustment*> SourceGrantEffectAdjustments;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Instanced, Category=EffectAdjustment)
TArray<UGameplayEffectAdjustment*> TargetGrantEffectAdjustments;

修改完GE后,我们需要让GE生效时,角色拥有GE的伤害修饰效果,在GE失效后,移除对应的伤害修饰效果。

创建一个存储伤害修饰器object的结构体。使用结构体而不是直接用object,我是考虑到未来可能会希望有一些动态数据的存储,而adjustment object希望是一个class default object,不希望在runtime环境下修改数据

scss 复制代码
USTRUCT()
struct FActiveEffectAdjustmentHandle
{
   GENERATED_USTRUCT_BODY()

   UPROPERTY()
   TObjectPtr<UGameplayEffectAdjustment> EffectAdjustment;
};

然后修改ASC,新增两个TMap

scss 复制代码
   UPROPERTY()
   TMap<FActiveGameplayEffectHandle, FActiveEffectAdjustmentHandle> SourceActiveEffectAdjustmentContainer;
   UPROPERTY()
   TMap<FActiveGameplayEffectHandle, FActiveEffectAdjustmentHandle> TargetActiveEffectAdjustmentContainer;

为了监听GE的添加和移除,需要绑定两个delegate,OnActiveGameplayEffectAddedDelegateToSelfOnAnyGameplayEffectRemovedDelegate

同时创建两个函数去处理GE添加和移除。

scss 复制代码
UFUNCTION()
void HandleGameplayEffectAppliedToSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle);

UFUNCTION()
void HandleGameplayEffectRemovedFromSelf(const FActiveGameplayEffect& ActiveEffect);

重写InitAbilityActorInfoEndPlay函数

scss 复制代码
void ULyraAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor)
{
      // 监听Effect Applied,用于注册Effect Adjustment
      if(!EffectAppliedToSelfHandle.IsValid())
      {
         EffectAppliedToSelfHandle = OnActiveGameplayEffectAddedDelegateToSelf.AddUObject(this, &ULyraAbilitySystemComponent::HandleGameplayEffectAppliedToSelf);
      }
      if(!EffectRemovedFromSelfHandle.IsValid())
      {
         EffectRemovedFromSelfHandle = OnAnyGameplayEffectRemovedDelegate().AddUObject(this, &ULyraAbilitySystemComponent::HandleGameplayEffectRemovedFromSelf);
      }
}

void ULyraAbilitySystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
   // 解除监听Effect Applied和removed
   if(EffectAppliedToSelfHandle.IsValid())
   {
      OnGameplayEffectAppliedDelegateToSelf.Remove(EffectAppliedToSelfHandle);
   }
   if(EffectRemovedFromSelfHandle.IsValid())
   {
      OnAnyGameplayEffectRemovedDelegate().Remove(EffectRemovedFromSelfHandle);
   }
}

HandleGameplayEffectAppliedToSelf实现

scss 复制代码
void ULyraAbilitySystemComponent::HandleGameplayEffectAppliedToSelf(UAbilitySystemComponent* Source,
   const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle)
{
   if(!IsValid(SpecApplied.Def)) return;

   ULyraGameplayEffect* EffectCDO = Cast<ULyraGameplayEffect>(SpecApplied.Def);
   if(!IsValid(EffectCDO)) return;

   if(!EffectCDO->SourceGrantEffectAdjustments.IsEmpty())
   {
      for(UGameplayEffectAdjustment* Adjustment: EffectCDO->SourceGrantEffectAdjustments)
      {
         FActiveEffectAdjustmentHandle AdjustmentHandle;
         AdjustmentHandle.EffectAdjustment = Adjustment;
         SourceActiveEffectAdjustmentContainer.Add(ActiveHandle, AdjustmentHandle);
      }
   }

   if(!EffectCDO->TargetGrantEffectAdjustments.IsEmpty())
   {
      for(UGameplayEffectAdjustment* Adjustment: EffectCDO->TargetGrantEffectAdjustments)
      {
         FActiveEffectAdjustmentHandle AdjustmentHandle;
         AdjustmentHandle.EffectAdjustment = Adjustment;
         TargetActiveEffectAdjustmentContainer.Add(ActiveHandle, AdjustmentHandle);
      }
   }
}

HandleGameplayEffectRemovedFromSelf实现

ini 复制代码
void ULyraAbilitySystemComponent::HandleGameplayEffectRemovedFromSelf(const FActiveGameplayEffect& ActiveEffect)
{
   const FActiveGameplayEffectHandle& Handle = ActiveEffect.Handle;
   SourceActiveEffectAdjustmentContainer.Remove(Handle);
   TargetActiveEffectAdjustmentContainer.Remove(Handle);
}

到这里通过GE给角色添加Effect Adjustment已经完成了,接下来需要实现的是在伤害中获取对伤害的修饰

首先在ASC中添加静态函数TryApplyGameplayEffectAdjustment

arduino 复制代码
 static void TryApplyGameplayEffectAdjustment(TMap<FGameplayTag, float>& AdjustmentData, const FGameplayEffectSpecHandle& EffectSpecHandle,
ULyraAbilitySystemComponent* SourceASC, ULyraAbilitySystemComponent* TargetASC);

实现,逻辑为遍历角色身上的Effect Adjustments,然后将修改的属性写入字典AdjustmentData中

scss 复制代码
void ULyraAbilitySystemComponent::TryApplyGameplayEffectAdjustment(TMap<FGameplayTag, float>& AdjustmentData,
   const FGameplayEffectSpecHandle& EffectSpecHandle, ULyraAbilitySystemComponent* SourceASC,
   ULyraAbilitySystemComponent* TargetASC)
{
   for(auto& Pair: SourceASC->SourceActiveEffectAdjustmentContainer)
   {
      FActiveEffectAdjustmentHandle AdjustmentHandle = Pair.Value;
      if(AdjustmentHandle.EffectAdjustment)
      {
         if(AdjustmentHandle.EffectAdjustment->CanApplyEffectAdjustment(EffectSpecHandle, SourceASC, TargetASC))
         {
            AdjustmentHandle.EffectAdjustment->TryApplyEffectAdjustment(AdjustmentData);
         }
      }
   }
   for(auto& Pair: TargetASC->TargetActiveEffectAdjustmentContainer)
   {
      FActiveEffectAdjustmentHandle AdjustmentHandle = Pair.Value;
      if(AdjustmentHandle.EffectAdjustment)
      {
         if(AdjustmentHandle.EffectAdjustment->CanApplyEffectAdjustment(EffectSpecHandle, SourceASC, TargetASC))
         {
            AdjustmentHandle.EffectAdjustment->TryApplyEffectAdjustment(AdjustmentData);
         }
      }
   }
}

有了伤害修饰的数据,可以把伤害相关的数据划分,分配不同的tag,比如基础伤害,额外伤害,伤害百分比等

ini 复制代码
FGameplayTag SetByCaller_Damage = FGameplayTag::RequestGameplayTag("SetByCaller.Damage");
FGameplayTag SetByCaller_ExtraDamage = FGameplayTag::RequestGameplayTag("SetByCaller.ExtraDamage");
FGameplayTag SetByCaller_Rate = FGameplayTag::RequestGameplayTag("SetByCaller.Rate");

然后在伤害计算的Execution中,取出伤害修饰的数据,放入伤害计算中

css 复制代码
ULyraAbilitySystemComponent::TryApplyGameplayEffectAdjustment(ExecutionHelper.AdjustmentData, SpecHandle, SourceASC, TargetASC);
float DamageExtra = 0.f;
DamageExtra += ExecutionHelper.AdjustmentData.FindRef(FLyraGameplayTags::Get().SetByCaller_ExtraDamage);

伤害动态伤害实例

这里举一个简单的例子,假设有一个愤怒buff,角色生命值低于50%时造成的伤害增加额外值10点

首先创建EffectAdjustment的子类

重写CanApplyEffectAdjustment函数

当攻击者的生命值低于50%时满足条件,返回true

如果满足条件,需要增加额外伤害10点

最后把实现的Effect Adjustment配置到GameplayEffect中即可

相关推荐
幻狐boke12 小时前
【游戏模组】星际争霸1代模组燃烧之地,泰伦帝国对决UED。特效华丽兵种巨多特别好玩
游戏
科技资讯早知道18 小时前
java计算机毕设课设—坦克大战游戏
java·开发语言·游戏·毕业设计·课程设计·毕设
程序猿阿伟2 天前
《C++游戏人工智能开发:开启智能游戏新纪元》
c++·人工智能·游戏
Artistation Game2 天前
九、怪物行为逻辑
游戏·unity·游戏引擎
妙为2 天前
unreal engine5制作动作类游戏时,我们使用刀剑等武器攻击怪物或敌方单位时,发现攻击特效、伤害等没有触发
游戏·游戏引擎·虚幻·碰撞预设
网站领航2 天前
服装时尚与动漫游戏的跨界联动:创新运营与策划策略研究
游戏·流量运营·用户运营
龙智DevSecOps解决方案2 天前
Perforce演讲回顾(上):从UE项目Project Titan,看Helix Core在大型游戏开发中的版本控制与集成使用策略
游戏·ue5·源代码管理·perforce·helix core
dangoxiba2 天前
[Unity Demo]从零开始制作空洞骑士Hollow Knight第十三集:制作小骑士的接触地刺复活机制以及完善地图的可交互对象
游戏·unity·visualstudio·c#·游戏引擎
新手unity自用笔记2 天前
项目-坦克大战学习-游戏结束
学习·游戏