// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #include "Feedback/GES_ContextEffectComponent.h" #include "GameplayTagAssetInterface.h" #include "Engine/World.h" #include "Feedback/GES_ContextEffectsSubsystem.h" #include "PhysicalMaterials/PhysicalMaterial.h" #include "GES_LogChannels.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GES_ContextEffectComponent) class UAnimSequenceBase; class USceneComponent; // Sets default values for this component's properties UGES_ContextEffectComponent::UGES_ContextEffectComponent() { // Disable component tick, enable Auto Activate PrimaryComponentTick.bCanEverTick = false; bAutoActivate = true; // ... } // Called when the game starts void UGES_ContextEffectComponent::BeginPlay() { Super::BeginPlay(); // ... CurrentContexts.AppendTags(DefaultEffectContexts); CurrentContextEffectsLibraries = DefaultContextEffectsLibraries; // On Begin Play, Load and Add Context Effects pairings if (const UWorld* World = GetWorld()) { if (UGES_ContextEffectsSubsystem* ContextEffectsSubsystem = World->GetSubsystem()) { ContextEffectsSubsystem->LoadAndAddContextEffectsLibraries(GetOwner(), CurrentContextEffectsLibraries); } } if (bAutoSetupTagsProvider) { SetupTagsProvider(); } } void UGES_ContextEffectComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { // On End PLay, remove unnecessary context effects pairings if (const UWorld* World = GetWorld()) { if (UGES_ContextEffectsSubsystem* ContextEffectsSubsystem = World->GetSubsystem()) { ContextEffectsSubsystem->UnloadAndRemoveContextEffectsLibraries(GetOwner()); } } Super::EndPlay(EndPlayReason); } void UGES_ContextEffectComponent::SetupTagsProvider() { if (GetOwner()->GetClass()->ImplementsInterface(UGameplayTagAssetInterface::StaticClass())) { SetGameplayTagsProvider(GetOwner()); } else { TArray Components = GetOwner()->GetComponentsByInterface(UGameplayTagAssetInterface::StaticClass()); if (Components.IsValidIndex(0)) { SetGameplayTagsProvider(Components[0]); } } } void UGES_ContextEffectComponent::PlayContextEffectsWithInput_Implementation(FGES_SpawnContextEffectsInput Input) { AggregateContexts(Input); InjectPhysicalSurfaceToContexts(Input.HitResult, Input.SourceContext); // Prep Components TArray AudioComponentsToAdd; TArray NiagaraComponentsToAdd; TArray ParticleComponentsToAdd; // Cycle through Active Audio Components and cache for (UAudioComponent* ActiveAudioComponent : ActiveAudioComponents) { if (ActiveAudioComponent) { AudioComponentsToAdd.Add(ActiveAudioComponent); } } // Cycle through Active Niagara Components and cache for (UNiagaraComponent* ActiveNiagaraComponent : ActiveNiagaraComponents) { if (ActiveNiagaraComponent) { NiagaraComponentsToAdd.Add(ActiveNiagaraComponent); } } // Cycle through Active Particle Components and cache for (UParticleSystemComponent* ActiveParticleComponent : ActiveParticleComponents) { if (ActiveParticleComponent) { ParticleComponentsToAdd.Add(ActiveParticleComponent); } } // Get World if (const UWorld* World = GetWorld()) { // Get Subsystem if (UGES_ContextEffectsSubsystem* ContextEffectsSubsystem = World->GetSubsystem()) { // Set up components FGES_SpawnContextEffectsOutput Output; // Spawn effects ContextEffectsSubsystem->SpawnContextEffectsExt(GetOwner(), Input, Output); // Append resultant effects AudioComponentsToAdd.Append(Output.AudioComponents); NiagaraComponentsToAdd.Append(Output.NiagaraComponents); ParticleComponentsToAdd.Append(Output.ParticlesComponents); } } // Append Active Audio Components ActiveAudioComponents.Empty(); ActiveAudioComponents.Append(AudioComponentsToAdd); // Append Active ActiveNiagaraComponents.Empty(); ActiveNiagaraComponents.Append(NiagaraComponentsToAdd); ActiveParticleComponents.Empty(); ActiveParticleComponents.Append(ParticleComponentsToAdd); } void UGES_ContextEffectComponent::AggregateContexts(FGES_SpawnContextEffectsInput& Input) const { if (Input.SourceContextType == EGES_EffectsContextType::Merge) { FGameplayTagContainer TotalContexts; // Aggregate contexts TotalContexts.AppendTags(Input.SourceContext); TotalContexts.AppendTags(CurrentContexts); if (IGameplayTagAssetInterface* TagAssetInterface = Cast(GameplayTagsProvider)) { FGameplayTagContainer RetTags; TagAssetInterface->GetOwnedGameplayTags(RetTags); TotalContexts.AppendTags(RetTags); } Input.SourceContext = TotalContexts; } } void UGES_ContextEffectComponent::InjectPhysicalSurfaceToContexts(const FHitResult& InHitResult, FGameplayTagContainer& Contexts) { // Check if converting Physical Surface Type to Context if (!bConvertPhysicalSurfaceToContext) { return; } // Get Phys Mat Type Pointer TWeakObjectPtr PhysicalSurfaceTypePtr = InHitResult.PhysMaterial; // Check if pointer is okay if (PhysicalSurfaceTypePtr.IsValid()) { // Get the Surface Type Pointer TEnumAsByte PhysicalSurfaceType = PhysicalSurfaceTypePtr->SurfaceType; // If Settings are valid if (const UGES_ContextEffectsSettings* ContextEffectsSettings = GetDefault()) { if (ContextEffectsSettings->SurfaceTypeToContextMap.IsEmpty()) { GES_CLOG(Warning, "No surface type to context map, Please check ContextEffectsSetting in ProjectSettings!"); if (FallbackPhysicalSurface.IsValid()) { Contexts.AddTag(FallbackPhysicalSurface); } } else { // Convert Surface Type to known if (const FGameplayTag* SurfaceContextPtr = ContextEffectsSettings->SurfaceTypeToContextMap.Find(PhysicalSurfaceType)) { FGameplayTag SurfaceContext = *SurfaceContextPtr; Contexts.AddTag(SurfaceContext); } else { GES_CLOG(Warning, "No surface type(%d) to context map found, Please check ContextEffectsSetting in ProjectSettings!", PhysicalSurfaceType.GetValue()); if (FallbackPhysicalSurface.IsValid()) { Contexts.AddTag(FallbackPhysicalSurface); } } } } } else { if (FallbackPhysicalSurface.IsValid()) { Contexts.AddTag(FallbackPhysicalSurface); } } } void UGES_ContextEffectComponent::SetGameplayTagsProvider(UObject* Provider) { if (!IsValid(Provider)) { return; } if (IGameplayTagAssetInterface* TagAssetInterface = Cast(Provider)) { GameplayTagsProvider = Provider; } else { GES_CLOG(Warning, "Passed in GameplayTagsProvider(%s) Doesn't implement GameplayTagAssetInterface, it can't provide gameplay tags.", *GetNameSafe(Provider->GetClass())); } } void UGES_ContextEffectComponent::UpdateEffectContexts(FGameplayTagContainer NewEffectContexts) { // Reset and update CurrentContexts.Reset(NewEffectContexts.Num()); CurrentContexts.AppendTags(NewEffectContexts); } void UGES_ContextEffectComponent::UpdateLibraries( TSet> NewContextEffectsLibraries) { // Clear out existing Effects CurrentContextEffectsLibraries = NewContextEffectsLibraries; // Get World if (const UWorld* World = GetWorld()) { // Get Subsystem if (UGES_ContextEffectsSubsystem* ContextEffectsSubsystem = World->GetSubsystem()) { // Load and Add Libraries to Subsystem ContextEffectsSubsystem->LoadAndAddContextEffectsLibraries(GetOwner(), CurrentContextEffectsLibraries); } } }