ASC学习笔记0010:效果被应用时的委托
中文注释:UrealEngine-5.2.1源码-AbilitySystemComponent.h
学习内容:
/** 效果被应用时的委托 */DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);
一. 这是一个 Unreal Engine 中 Gameplay Ability System (GAS) 的委托声明。让我为您详细解释这个 FOnGameplayEffectAppliedDelegate:
委托结构分析
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, // 委托类型名称UAbilitySystemComponent*, // 第一个参数类型const FGameplayEffectSpec&, // 第二个参数类型 FActiveGameplayEffectHandle // 第三个参数类型
);
参数说明
-
UAbilitySystemComponent*-
指向应用该 Gameplay Effect 的 Ability System Component
-
通常是目标角色的 ASC
-
-
const FGameplayEffectSpec&-
Gameplay Effect 的规格说明,包含所有配置数据
-
包括等级、持续时间、修饰符等
-
-
FActiveGameplayEffectHandle-
激活的 Gameplay Effect 的句柄
-
用于后续引用和操作该效果
-
使用示例
绑定委托
// 在某个类中绑定到委托
AbilitySystemComponent->OnGameplayEffectAppliedDelegate.AddUObject(this, &AMyClass::OnGameplayEffectApplied
);
处理函数实现
void AMyClass::OnGameplayEffectApplied(UAbilitySystemComponent* ASC, const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{// 获取来源和目标信息AActor* SourceActor = Spec.GetEffectContext().GetInstigator();AActor* TargetActor = ASC->GetOwner();// 检查特定的 Gameplay Effectif (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 系统 - 状态效果显示
状态栏更新
// 在UI控制器中
void UStatusUIWidgetController::BindCallbacks()
{if (AbilitySystemComponent){AbilitySystemComponent->OnGameplayEffectAppliedDelegateToSelf.AddUObject(this, &UStatusUIWidgetController::OnEffectApplied);}
}void UStatusUIWidgetController::OnEffectApplied(UAbilitySystemComponent* ASC, const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle)
{// 检查效果类型并更新UIif (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. 音效和视觉效果系统
效果触发管理器
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. 成就和统计系统
成就追踪
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. 战斗日志系统
战斗记录
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. 调试和开发工具
效果监控面板
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. 复杂的游戏机制
元素反应系统(类似原神)
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. 性能考虑
// 使用标签检查而不是字符串比较
bool UMySystem::ShouldProcessEffect(const FGameplayTagContainer& AssetTags)
{return AssetTags.HasTag(FGameplayTag::RequestGameplayTag("Effect.Important"));
}// 避免在频繁触发的事件中进行复杂操作
2. 内存管理
// 记得在销毁时解绑委托
void UMyComponent::BeginDestroy()
{if (AbilitySystemComponent){AbilitySystemComponent->OnGameplayEffectAppliedDelegateToSelf.RemoveAll(this);}Super::BeginDestroy();
}
这些实际应用展示了 FOnGameplayEffectAppliedDelegate 在游戏开发中的强大功能,从基础UI更新到复杂的游戏机制都能发挥重要作用。
