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更新到复杂的游戏机制都能发挥重要作用。

相关推荐
信奥胡老师24 分钟前
苹果电脑(mac系统)安装vscode与配置c++环境,并可以使用万能头文件全流程
c++·ide·vscode·macos·编辑器
妖灵翎幺25 分钟前
C++ 中的 :: 操作符详解(一切情况)
开发语言·c++·ide
小毅&Nora44 分钟前
【人工智能】【深度学习】 ⑦ 从零开始AI学习路径:从Python到大模型的实战指南
人工智能·深度学习·学习
Maxwell_li11 小时前
Pandas 描述分析和分组分析学习文档
学习·数据分析·numpy·pandas·matplotlib
star _chen1 小时前
C++实现完美洗牌算法
开发语言·c++·算法
雷工笔记1 小时前
MES学习笔记之SCADA采集的数据如何与MES中的任务关联起来?
笔记·学习
繁星星繁2 小时前
【C++】脚手架学习笔记 gflags与 gtest
c++·笔记·学习
路痴楷2 小时前
无法定位程序输入点问题
c++·qt·visual studio
2301_810746312 小时前
CKA冲刺40天笔记 - day20-day21 SSL/TLS详解
运维·笔记·网络协议·kubernetes·ssl
Source.Liu2 小时前
【LibreCAD】 RS_Units 类完整解析
c++·qt·rust