ASC学习笔记0010:效果被应用时的委托

中文注释:UrealEngine-5.2.1源码-AbilitySystemComponent.h

学习内容:

cpp 复制代码
/** 效果被应用时的委托 */
	DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);

一. 这是一个 Unreal Engine 中 Gameplay Ability System (GAS) 的委托声明。让我为您详细解释这个 FOnGameplayEffectAppliedDelegate

委托结构分析

cpp 复制代码
DECLARE_MULTICAST_DELEGATE_ThreeParams(
    FOnGameplayEffectAppliedDelegate,       // 委托类型名称
    UAbilitySystemComponent*,               // 第一个参数类型
    const FGameplayEffectSpec&,             // 第二个参数类型  
    FActiveGameplayEffectHandle             // 第三个参数类型
);

参数说明

  1. UAbilitySystemComponent*

    • 指向应用该 Gameplay Effect 的 Ability System Component

    • 通常是目标角色的 ASC

  2. const FGameplayEffectSpec&

    • Gameplay Effect 的规格说明,包含所有配置数据

    • 包括等级、持续时间、修饰符等

  3. FActiveGameplayEffectHandle

    • 激活的 Gameplay Effect 的句柄

    • 用于后续引用和操作该效果

使用示例

绑定委托

cpp 复制代码
// 在某个类中绑定到委托
AbilitySystemComponent->OnGameplayEffectAppliedDelegate.AddUObject(
    this, 
    &AMyClass::OnGameplayEffectApplied
);

处理函数实现

cpp 复制代码
void AMyClass::OnGameplayEffectApplied(
    UAbilitySystemComponent* ASC, 
    const FGameplayEffectSpec& Spec, 
    FActiveGameplayEffectHandle Handle)
{
    // 获取来源和目标信息
    AActor* SourceActor = Spec.GetEffectContext().GetInstigator();
    AActor* TargetActor = ASC->GetOwner();
    
    // 检查特定的 Gameplay Effect
    if (Spec.Def->GetFName() == FName("MyEffect"))
    {
        // 处理特定的效果应用逻辑
        UE_LOG(LogTemp, Warning, TEXT("MyEffect was applied!"));
    }
    
    // 可以访问效果的其他属性
    float EffectLevel = Spec.GetLevel();
    float Duration = Spec.GetDuration();
}

实际应用场景

  • 状态效果触发:当角色获得燃烧、中毒等状态时执行逻辑

  • UI 更新:更新 HUD 显示新获得的效果

  • 音效/特效:播放相应的视觉和听觉反馈

  • 成就系统:追踪特定效果的获得次数

  • 调试工具:监控效果应用情况

这个委托是 GAS 中非常重要的回调机制,让您能够响应游戏中各种状态变化。

二. 在实际项目中,FOnGameplayEffectAppliedDelegate 有广泛的应用场景。以下是一些具体的实际用例:

1. UI 系统 - 状态效果显示

状态栏更新

cpp 复制代码
// 在UI控制器中
void UStatusUIWidgetController::BindCallbacks()
{
    if (AbilitySystemComponent)
    {
        AbilitySystemComponent->OnGameplayEffectAppliedDelegateToSelf.AddUObject(
            this, &UStatusUIWidgetController::OnEffectApplied);
    }
}

void UStatusUIWidgetController::OnEffectApplied(UAbilitySystemComponent* ASC, 
    const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{
    // 检查效果类型并更新UI
    if (UGameplayEffect* GE = Spec.Def)
    {
        FGameplayTagContainer AssetTags;
        GE->GetAssetTags(AssetTags);
        
        // 如果是增益效果
        if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Type.Buff")))
        {
            OnBuffApplied.Broadcast(GE, Spec.GetLevel());
        }
        // 如果是减益效果
        else if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Type.Debuff")))
        {
            OnDebuffApplied.Broadcast(GE, Spec.GetLevel());
        }
        
        // 更新状态图标
        UpdateStatusIcons();
    }
}
复制代码

2. 音效和视觉效果系统

效果触发管理器

cpp 复制代码
void UEffectResponseManager::OnGameplayEffectApplied(UAbilitySystemComponent* ASC, 
    const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{
    if (!ASC) return;
    
    AActor* TargetActor = ASC->GetAvatarActor();
    UGameplayEffect* GE = Spec.Def;
    
    if (!TargetActor || !GE) return;
    
    FGameplayTagContainer AssetTags;
    GE->GetAssetTags(AssetTags);
    
    // 根据标签播放对应效果
    if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Fire")))
    {
        // 播放燃烧音效和粒子
        UGameplayStatics::SpawnEmitterAtLocation(
            GetWorld(), FireEffectParticle, 
            TargetActor->GetActorLocation()
        );
        UGameplayStatics::PlaySoundAtLocation(
            GetWorld(), FireEffectSound, 
            TargetActor->GetActorLocation()
        );
    }
    else if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Heal")))
    {
        // 播放治疗效果
        SpawnHealingEffect(TargetActor);
    }
    else if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Stun")))
    {
        // 播放眩晕效果
        ApplyStunVisuals(TargetActor);
    }
}
复制代码

3. 成就和统计系统

成就追踪

cpp 复制代码
void UAchievementSystem::OnEffectApplied(UAbilitySystemComponent* ASC, 
    const FGameplayEffectSpec& Spec, FActiveGameplayHandle Handle)
{
    // 获取施放者信息
    UAbilitySystemComponent* SourceASC = Spec.GetContext().GetInstigatorAbilitySystemComponent();
    AActor* SourceActor = SourceASC ? SourceASC->GetAvatarActor() : nullptr;
    
    // 检查是否为玩家施加的效果
    if (SourceActor && SourceActor->IsA(APlayerCharacter::StaticClass()))
    {
        UGameplayEffect* GE = Spec.Def;
        FGameplayTagContainer AssetTags;
        GE->GetAssetTags(AssetTags);
        
        // 追踪特定类型的成就
        if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Damage.OverTime")))
        {
            // "施加第一个持续伤害效果"成就
            UnlockAchievement("FirstDoTApplied");
        }
        
        if (AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.CrowdControl")))
        {
            // 增加控制效果计数
            CrowdControlCount++;
            if (CrowdControlCount >= 100)
            {
                UnlockAchievement("MasterOfControl");
            }
        }
    }
}
复制代码

4. 战斗日志系统

战斗记录

cpp 复制代码
void UCombatLogger::OnEffectApplied(UAbilitySystemComponent* TargetASC, 
    const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{
    UAbilitySystemComponent* SourceASC = Spec.GetContext().GetInstigatorAbilitySystemComponent();
    AActor* SourceActor = SourceASC ? SourceASC->GetAvatarActor() : nullptr;
    AActor* TargetActor = TargetASC->GetAvatarActor();
    
    FCombatLogEntry Entry;
    Entry.Timestamp = GetWorld()->GetTimeSeconds();
    Entry.SourceActor = SourceActor;
    Entry.TargetActor = TargetActor;
    Entry.EffectClass = Spec.Def;
    Entry.EffectLevel = Spec.GetLevel();
    
    // 添加到战斗日志
    CombatLog.Add(Entry);
    
    // 通知UI更新
    OnCombatLogUpdated.Broadcast(Entry);
}
复制代码

5. 调试和开发工具

效果监控面板

cpp 复制代码
void UGameplayEffectDebugger::OnEffectApplied(UAbilitySystemComponent* ASC, 
    const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{
#if !UE_BUILD_SHIPPING
    // 只在开发版本中记录
    FString EffectName = Spec.Def->GetName();
    FString TargetName = ASC->GetAvatarActor()->GetName();
    FString SourceName = "Unknown";
    
    if (UAbilitySystemComponent* SourceASC = Spec.GetContext().GetInstigatorAbilitySystemComponent())
    {
        SourceName = SourceASC->GetAvatarActor()->GetName();
    }
    
    UE_LOG(LogGameplayEffects, Log, TEXT("Effect Applied: %s from %s to %s (Level: %.1f)"),
        *EffectName, *SourceName, *TargetName, Spec.GetLevel());
    
    // 在调试面板显示
    AddToDebugHistory(EffectName, SourceName, TargetName, Spec.GetLevel());
#endif
}
复制代码

6. 复杂的游戏机制

元素反应系统(类似原神)

cpp 复制代码
void UElementalReactionSystem::OnEffectApplied(UAbilitySystemComponent* ASC, 
    const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{
    AActor* Target = ASC->GetAvatarActor();
    FGameplayTagContainer AppliedTags;
    Spec.Def->GetAssetTags(AppliedTags);
    
    // 检查现有的元素状态
    TArray<FActiveGameplayEffectHandle> ActiveEffects;
    ASC->GetActiveEffectsWithAllTags(ElementalTags, ActiveEffects);
    
    // 检查元素反应
    for (const FActiveGameplayEffectHandle& ActiveHandle : ActiveEffects)
    {
        FActiveGameplayEffect* ActiveEffect = ASC->GetActiveGameplayEffect(ActiveHandle);
        if (ActiveEffect)
        {
            FGameplayTagContainer ExistingTags;
            ActiveEffect->Spec.Def->GetAssetTags(ExistingTags);
            
            // 检查元素组合
            CheckElementalReaction(AppliedTags, ExistingTags, Target, Spec.GetContext());
        }
    }
}
复制代码

实际项目中的最佳实践

1. 性能考虑

cpp 复制代码
// 使用标签检查而不是字符串比较
bool UMySystem::ShouldProcessEffect(const FGameplayTagContainer& AssetTags)
{
    return AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Important"));
}

// 避免在频繁触发的事件中进行复杂操作
复制代码

2. 内存管理

cpp 复制代码
// 记得在销毁时解绑委托
void UMyComponent::BeginDestroy()
{
    if (AbilitySystemComponent)
    {
        AbilitySystemComponent->OnGameplayEffectAppliedDelegateToSelf.RemoveAll(this);
    }
    
    Super::BeginDestroy();
}
复制代码

这些实际应用展示了 FOnGameplayEffectAppliedDelegate 在游戏开发中的强大功能,从基础UI更新到复杂的游戏机制都能发挥重要作用。

相关推荐
AA陈超2 小时前
ASC学习笔记0004:通知相关方能力规格已被修改
c++·笔记·学习·游戏·ue5·游戏引擎·虚幻
hadage2332 小时前
--- git 笔记 ---
笔记·git·elasticsearch
阿巴~阿巴~2 小时前
IPv4地址转换函数详解及C++容器安全删除操作指南
linux·服务器·c++·网络协议·算法·c++容器安全删除操作·ipv4地址转换函数
oioihoii2 小时前
C/C++混合项目中的头文件管理:.h与.hpp的分工与协作
java·c语言·c++
im_AMBER2 小时前
数据结构 11 图
数据结构·笔记·学习·图论
彷徨而立2 小时前
【C/C++】不能在派生类的构造函数初始化列表中直接初始化属于基类的成员变量
c语言·c++
一条破秋裤3 小时前
前端性能问题分析
学习
老虎06273 小时前
黑马点评学习笔记11(Redission)
笔记·学习
小呀小萝卜儿3 小时前
2025-11-14 学习记录--Python-特征归一化方法(Min-Max或StandardScaler)
开发语言·python·学习