中文注释: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 // 第三个参数类型
);
参数说明
-
UAbilitySystemComponent*-
指向应用该 Gameplay Effect 的 Ability System Component
-
通常是目标角色的 ASC
-
-
const FGameplayEffectSpec&-
Gameplay Effect 的规格说明,包含所有配置数据
-
包括等级、持续时间、修饰符等
-
-
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更新到复杂的游戏机制都能发挥重要作用。