第一次提交
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class GenericEffectsSystem : ModuleRules
|
||||
{
|
||||
public GenericEffectsSystem(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"PhysicsCore",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"GameplayTags",
|
||||
"Niagara",
|
||||
"DeveloperSettings"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Feedback/GES_AnimNotify_ContextEffects.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NiagaraFunctionLibrary.h"
|
||||
#include "NiagaraSystem.h"
|
||||
#include "Feedback/GES_ContextEffectsInterface.h"
|
||||
#include "Feedback/GES_ContextEffectsLibrary.h"
|
||||
#include "Feedback/GES_ContextEffectsPreviewSetting.h"
|
||||
#include "Feedback/GES_ContextEffectsSubsystem.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GES_AnimNotify_ContextEffects)
|
||||
|
||||
|
||||
bool UGES_ContextEffectsSpawnParametersProvider::ProvideParameters_Implementation(USkeletalMeshComponent* InMeshComp, const UGES_AnimNotify_ContextEffects* InNotifyNotify,
|
||||
UAnimSequenceBase* InAnimation, FVector& OutSpawnLocation,
|
||||
FRotator& OutSpawnRotation) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UGES_AnimNotify_ContextEffects::UGES_AnimNotify_ContextEffects(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
NotifyColor = FColor::Blue;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UGES_AnimNotify_ContextEffects::PostLoad()
|
||||
{
|
||||
Super::PostLoad();
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UGES_AnimNotify_ContextEffects::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
}
|
||||
#endif
|
||||
|
||||
FString UGES_AnimNotify_ContextEffects::GetNotifyName_Implementation() const
|
||||
{
|
||||
// If the Effect Tag is valid, pass the string name to the notify name
|
||||
if (Effect.IsValid())
|
||||
{
|
||||
return Effect.ToString();
|
||||
}
|
||||
|
||||
return Super::GetNotifyName_Implementation();
|
||||
}
|
||||
|
||||
void UGES_AnimNotify_ContextEffects::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
|
||||
const FAnimNotifyEventReference& EventReference)
|
||||
{
|
||||
Super::Notify(MeshComp, Animation, EventReference);
|
||||
|
||||
if (!MeshComp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FVector SpawnLocation = FVector::ZeroVector;
|
||||
FRotator SpawnRotation = FRotator::ZeroRotator;
|
||||
|
||||
bool bValidProvider = !bAttached && IsValid(SpawnParametersProvider) && SpawnParametersProvider->ProvideParameters(MeshComp, this, Animation, SpawnLocation, SpawnRotation);
|
||||
|
||||
if (!bValidProvider && MeshComp->GetOwner())
|
||||
{
|
||||
SpawnRotation = MeshComp->GetOwner()->GetActorTransform().TransformRotation(RotationOffset.Quaternion()).Rotator();
|
||||
SpawnLocation = MeshComp->GetOwner()->GetActorTransform().TransformPosition(LocationOffset);
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// SpawnRotation = UKismetMathLibrary::ComposeRotators(SpawnRotation, RotationOffset);
|
||||
// SpawnLocation = SpawnLocation + UKismetMathLibrary::Quat_RotateVector(SpawnRotation.Quaternion(), LocationOffset);
|
||||
// }
|
||||
|
||||
// Make sure both MeshComp and Owning Actor is valid
|
||||
if (AActor* OwningActor = MeshComp->GetOwner())
|
||||
{
|
||||
// Prepare Trace Data
|
||||
bool bHitSuccess = false;
|
||||
FHitResult HitResult;
|
||||
FCollisionQueryParams QueryParams;
|
||||
|
||||
if (TraceProperties.bIgnoreActor)
|
||||
{
|
||||
QueryParams.AddIgnoredActor(OwningActor);
|
||||
}
|
||||
|
||||
QueryParams.bReturnPhysicalMaterial = true;
|
||||
|
||||
if (bPerformTrace)
|
||||
{
|
||||
// If trace is needed, set up Start Location to Attached
|
||||
FVector TraceStart = bAttached ? MeshComp->GetSocketLocation(SocketName) + LocationOffset : SpawnLocation;
|
||||
|
||||
// Make sure World is valid
|
||||
if (UWorld* World = OwningActor->GetWorld())
|
||||
{
|
||||
// Call Line Trace, Pass in relevant properties
|
||||
bHitSuccess = World->LineTraceSingleByChannel(HitResult, TraceStart, (TraceStart + TraceProperties.EndTraceLocationOffset),
|
||||
TraceProperties.TraceChannel, QueryParams, FCollisionResponseParams::DefaultResponseParam);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare Contexts in advance
|
||||
FGameplayTagContainer SourceContext;
|
||||
|
||||
FGameplayTagContainer TargetContext;
|
||||
|
||||
// Set up Array of Objects that implement the Context Effects Interface
|
||||
TArray<UObject*> Implementers;
|
||||
|
||||
// Determine if the Owning Actor is one of the Objects that implements the Context Effects Interface
|
||||
if (OwningActor->Implements<UGES_ContextEffectsInterface>())
|
||||
{
|
||||
// If so, add it to the Array
|
||||
Implementers.Add(OwningActor);
|
||||
}
|
||||
|
||||
// Cycle through Owning Actor's Components and determine if any of them is a Component implementing the Context Effect Interface
|
||||
for (const auto Component : OwningActor->GetComponents())
|
||||
{
|
||||
if (Component)
|
||||
{
|
||||
// If the Component implements the Context Effects Interface, add it to the list
|
||||
if (Component->Implements<UGES_ContextEffectsInterface>())
|
||||
{
|
||||
Implementers.Add(Component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FGES_SpawnContextEffectsInput Input;
|
||||
Input.EffectName = Effect;
|
||||
Input.bAttached = bAttached;
|
||||
Input.Bone = SocketName;
|
||||
Input.ComponentToAttach = MeshComp;
|
||||
Input.Location = bHitSuccess?HitResult.Location:SpawnLocation;
|
||||
Input.Rotation = SpawnRotation;
|
||||
Input.LocationOffset = LocationOffset;
|
||||
Input.RotationOffset = RotationOffset;
|
||||
Input.AnimationSequence = Animation;
|
||||
Input.bHitSuccess = bHitSuccess;
|
||||
Input.HitResult = HitResult;
|
||||
Input.SourceContext = SourceContext;
|
||||
Input.TargetContext = TargetContext;
|
||||
Input.VFXScale = VFXProperties.Scale;
|
||||
Input.AudioVolume = AudioProperties.VolumeMultiplier;
|
||||
Input.AudioPitch = AudioProperties.PitchMultiplier;
|
||||
|
||||
// Cycle through all objects implementing the Context Effect Interface
|
||||
for (UObject* Implementer : Implementers)
|
||||
{
|
||||
// If the object is still valid, Execute the AnimMotionEffect Event on it, passing in relevant data
|
||||
if (Implementer)
|
||||
{
|
||||
IGES_ContextEffectsInterface::Execute_PlayContextEffectsWithInput(Implementer, Input);
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
PerformEditorPreview(OwningActor, SourceContext, TargetContext, MeshComp);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UGES_AnimNotify_ContextEffects::ValidateAssociatedAssets()
|
||||
{
|
||||
Super::ValidateAssociatedAssets();
|
||||
}
|
||||
|
||||
void UGES_AnimNotify_ContextEffects::SetParameters(FGameplayTag EffectIn, FVector LocationOffsetIn, FRotator RotationOffsetIn,
|
||||
FGES_ContextEffectAnimNotifyVFXSettings VFXPropertiesIn, FGES_ContextEffectAnimNotifyAudioSettings AudioPropertiesIn,
|
||||
bool bAttachedIn, FName SocketNameIn, bool bPerformTraceIn, FGES_ContextEffectAnimNotifyTraceSettings TracePropertiesIn)
|
||||
{
|
||||
Effect = EffectIn;
|
||||
LocationOffset = LocationOffsetIn;
|
||||
RotationOffset = RotationOffsetIn;
|
||||
VFXProperties.Scale = VFXPropertiesIn.Scale;
|
||||
AudioProperties.PitchMultiplier = AudioPropertiesIn.PitchMultiplier;
|
||||
AudioProperties.VolumeMultiplier = AudioPropertiesIn.VolumeMultiplier;
|
||||
bAttached = bAttachedIn;
|
||||
SocketName = SocketNameIn;
|
||||
bPerformTrace = bPerformTraceIn;
|
||||
TraceProperties.EndTraceLocationOffset = TracePropertiesIn.EndTraceLocationOffset;
|
||||
TraceProperties.TraceChannel = TracePropertiesIn.TraceChannel;
|
||||
TraceProperties.bIgnoreActor = TracePropertiesIn.bIgnoreActor;
|
||||
}
|
||||
|
||||
void UGES_AnimNotify_ContextEffects::PerformEditorPreview(AActor* OwningActor, FGameplayTagContainer& SourceContext, FGameplayTagContainer& TargetContext, USkeletalMeshComponent* MeshComp)
|
||||
{
|
||||
if (!bAttached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const UGES_ContextEffectsSettings* ContextEffectsSettings = GetDefault<UGES_ContextEffectsSettings>();
|
||||
if (ContextEffectsSettings == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This is for Anim Editor previewing, it is a deconstruction of the calls made by the Interface and the Subsystem
|
||||
if (!ContextEffectsSettings->bPreviewInEditor || ContextEffectsSettings->PreviewSetting.IsNull())
|
||||
return;
|
||||
|
||||
UWorld* World = OwningActor->GetWorld();
|
||||
|
||||
// Get the world, make sure it's an Editor Preview World
|
||||
if (!World || World->WorldType != EWorldType::EditorPreview)
|
||||
return;
|
||||
|
||||
// const auto& PreviewProperties = ContextEffectsSettings->PreviewProperties;
|
||||
const UGES_ContextEffectsPreviewSetting* PreviewSetting = ContextEffectsSettings->PreviewSetting.LoadSynchronous();
|
||||
|
||||
if (PreviewSetting == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Add Preview contexts if necessary
|
||||
SourceContext.AppendTags(PreviewSetting->PreviewSourceContext);
|
||||
TargetContext.AppendTags(PreviewSetting->PreviewTargetContext);
|
||||
|
||||
// Convert given Surface Type to Context and Add it to the Contexts for this Preview
|
||||
if (PreviewSetting->bPreviewPhysicalSurfaceAsContext)
|
||||
{
|
||||
TEnumAsByte<EPhysicalSurface> PhysicalSurfaceType = PreviewSetting->PreviewPhysicalSurface;
|
||||
|
||||
if (const FGameplayTag* SurfaceContextPtr = ContextEffectsSettings->SurfaceTypeToContextMap.Find(PhysicalSurfaceType))
|
||||
{
|
||||
FGameplayTag SurfaceContext = *SurfaceContextPtr;
|
||||
|
||||
SourceContext.AddTag(SurfaceContext);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PreviewSetting->PreviewContextEffectsLibraries.Num(); ++i)
|
||||
{
|
||||
// Libraries are soft referenced, so you will want to try to load them now
|
||||
if (UObject* EffectsLibrariesObj = PreviewSetting->PreviewContextEffectsLibraries[i].TryLoad())
|
||||
{
|
||||
// Check if it is in fact a UGES_ContextEffectLibrary type
|
||||
if (UGES_ContextEffectsLibrary* EffectLibrary = Cast<UGES_ContextEffectsLibrary>(EffectsLibrariesObj))
|
||||
{
|
||||
// Prepare Sounds and Niagara System Arrays
|
||||
TArray<USoundBase*> TotalSounds;
|
||||
TArray<UNiagaraSystem*> TotalNiagaraSystems;
|
||||
TArray<UParticleSystem*> TotalParticleSystems;
|
||||
|
||||
|
||||
// Attempt to load the Effect Library content (will cache in Transient data on the Effect Library Asset)
|
||||
EffectLibrary->LoadEffects();
|
||||
|
||||
// If the Effect Library is valid and marked as Loaded, Get Effects from it
|
||||
if (EffectLibrary && EffectLibrary->GetContextEffectsLibraryLoadState() == EGES_ContextEffectsLibraryLoadState::Loaded)
|
||||
{
|
||||
// Prepare local arrays
|
||||
TArray<USoundBase*> Sounds;
|
||||
TArray<UNiagaraSystem*> NiagaraSystems;
|
||||
TArray<UParticleSystem*> ParticleSystems;
|
||||
|
||||
|
||||
// Get the Effects
|
||||
EffectLibrary->GetEffects(Effect, SourceContext, TargetContext, Sounds, NiagaraSystems, ParticleSystems);
|
||||
|
||||
// Append to the accumulating arrays
|
||||
TotalSounds.Append(Sounds);
|
||||
TotalNiagaraSystems.Append(NiagaraSystems);
|
||||
TotalParticleSystems.Append(ParticleSystems);
|
||||
}
|
||||
|
||||
// Cycle through Sounds and call Spawn Sound Attached, passing in relevant data
|
||||
for (USoundBase* Sound : TotalSounds)
|
||||
{
|
||||
UGameplayStatics::SpawnSoundAttached(Sound, MeshComp, SocketName, LocationOffset, RotationOffset, EAttachLocation::KeepRelativeOffset,
|
||||
false, AudioProperties.VolumeMultiplier, AudioProperties.PitchMultiplier, 0.0f, nullptr, nullptr, true);
|
||||
}
|
||||
|
||||
// Cycle through Niagara Systems and call Spawn System Attached, passing in relevant data
|
||||
for (UNiagaraSystem* NiagaraSystem : TotalNiagaraSystems)
|
||||
{
|
||||
UNiagaraFunctionLibrary::SpawnSystemAttached(NiagaraSystem, MeshComp, SocketName, LocationOffset,
|
||||
RotationOffset, VFXProperties.Scale, EAttachLocation::KeepRelativeOffset, true, ENCPoolMethod::None, true, true);
|
||||
}
|
||||
|
||||
// Cycle through Particle Systems and call Spawn System Attached, passing in relevant data
|
||||
for (UParticleSystem* ParticleSystem : TotalParticleSystems)
|
||||
{
|
||||
UGameplayStatics::SpawnEmitterAttached(ParticleSystem, MeshComp, SocketName, LocationOffset,
|
||||
RotationOffset, VFXProperties.Scale, EAttachLocation::KeepRelativeOffset, true, EPSCPoolMethod::None, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,266 @@
|
||||
// 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<UGES_ContextEffectsSubsystem>())
|
||||
{
|
||||
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<UGES_ContextEffectsSubsystem>())
|
||||
{
|
||||
ContextEffectsSubsystem->UnloadAndRemoveContextEffectsLibraries(GetOwner());
|
||||
}
|
||||
}
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void UGES_ContextEffectComponent::SetupTagsProvider()
|
||||
{
|
||||
if (GetOwner()->GetClass()->ImplementsInterface(UGameplayTagAssetInterface::StaticClass()))
|
||||
{
|
||||
SetGameplayTagsProvider(GetOwner());
|
||||
}
|
||||
else
|
||||
{
|
||||
TArray<UActorComponent*> 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<UAudioComponent*> AudioComponentsToAdd;
|
||||
TArray<UNiagaraComponent*> NiagaraComponentsToAdd;
|
||||
TArray<UParticleSystemComponent*> 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<UGES_ContextEffectsSubsystem>())
|
||||
{
|
||||
// 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<IGameplayTagAssetInterface>(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<UPhysicalMaterial> PhysicalSurfaceTypePtr = InHitResult.PhysMaterial;
|
||||
|
||||
// Check if pointer is okay
|
||||
if (PhysicalSurfaceTypePtr.IsValid())
|
||||
{
|
||||
// Get the Surface Type Pointer
|
||||
TEnumAsByte<EPhysicalSurface> PhysicalSurfaceType = PhysicalSurfaceTypePtr->SurfaceType;
|
||||
|
||||
// If Settings are valid
|
||||
if (const UGES_ContextEffectsSettings* ContextEffectsSettings = GetDefault<UGES_ContextEffectsSettings>())
|
||||
{
|
||||
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<IGameplayTagAssetInterface>(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<TSoftObjectPtr<UGES_ContextEffectsLibrary>> NewContextEffectsLibraries)
|
||||
{
|
||||
// Clear out existing Effects
|
||||
CurrentContextEffectsLibraries = NewContextEffectsLibraries;
|
||||
|
||||
// Get World
|
||||
if (const UWorld* World = GetWorld())
|
||||
{
|
||||
// Get Subsystem
|
||||
if (UGES_ContextEffectsSubsystem* ContextEffectsSubsystem = World->GetSubsystem<UGES_ContextEffectsSubsystem>())
|
||||
{
|
||||
// Load and Add Libraries to Subsystem
|
||||
ContextEffectsSubsystem->LoadAndAddContextEffectsLibraries(GetOwner(), CurrentContextEffectsLibraries);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Feedback/GES_ContextEffectsEnumLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GES_ContextEffectsEnumLibrary)
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Feedback/GES_ContextEffectsLibrary.h"
|
||||
#include "NiagaraSystem.h"
|
||||
#include "Sound/SoundBase.h"
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GES_ContextEffectsLibrary)
|
||||
|
||||
|
||||
void UGES_ContextEffectsLibrary::GetEffects(const FGameplayTag Effect, const FGameplayTagContainer& SourceContext, const FGameplayTagContainer& TargetContext,
|
||||
TArray<USoundBase*>& Sounds, TArray<UNiagaraSystem*>& NiagaraSystems, TArray<UParticleSystem*>& ParticleSystems)
|
||||
{
|
||||
// Make sure Effect is valid and Library is loaded
|
||||
if (Effect.IsValid() && SourceContext.IsValid() && EffectsLoadState == EGES_ContextEffectsLibraryLoadState::Loaded)
|
||||
{
|
||||
// Loop through Context Effects
|
||||
for (const auto& ActiveContextEffect : ActiveContextEffects)
|
||||
{
|
||||
bool bMatchesEffectTag = Effect.MatchesTagExact(ActiveContextEffect->EffectTag);
|
||||
bool bMatchesSourceContext = ActiveContextEffect->SourceTagQuery.Matches(SourceContext) && (ActiveContextEffect->SourceTagQuery.IsEmpty() == SourceContext.IsEmpty());
|
||||
|
||||
// Target context is optional
|
||||
bool bMatchesTargetContext = ActiveContextEffect->TargetTagQuery.IsEmpty() || ActiveContextEffect->TargetTagQuery.Matches(TargetContext);
|
||||
|
||||
// Make sure the Effect is an exact Tag Match and ensure the Context has all tags in the Effect (and neither or both are empty)
|
||||
if (bMatchesEffectTag && bMatchesSourceContext && bMatchesTargetContext)
|
||||
{
|
||||
// Get all Matching Sounds and Niagara Systems
|
||||
Sounds.Append(ActiveContextEffect->Sounds);
|
||||
NiagaraSystems.Append(ActiveContextEffect->NiagaraSystems);
|
||||
ParticleSystems.Append(ActiveContextEffect->ParticleSystems);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGES_ContextEffectsLibrary::LoadEffects()
|
||||
{
|
||||
// Load Effects into Library if not currently loading
|
||||
if (EffectsLoadState != EGES_ContextEffectsLibraryLoadState::Loading)
|
||||
{
|
||||
// Set load state to loading
|
||||
EffectsLoadState = EGES_ContextEffectsLibraryLoadState::Loading;
|
||||
|
||||
// Clear out any old Active Effects
|
||||
ActiveContextEffects.Empty();
|
||||
|
||||
// Call internal loading function
|
||||
LoadEffectsInternal();
|
||||
}
|
||||
}
|
||||
|
||||
EGES_ContextEffectsLibraryLoadState UGES_ContextEffectsLibrary::GetContextEffectsLibraryLoadState()
|
||||
{
|
||||
// Return current Load State
|
||||
return EffectsLoadState;
|
||||
}
|
||||
|
||||
void UGES_ContextEffectsLibrary::LoadEffectsInternal()
|
||||
{
|
||||
// TODO Add Async Loading for Libraries
|
||||
|
||||
// Copy data for async load
|
||||
TArray<FGES_ContextEffects> LocalContextEffects = ContextEffects;
|
||||
|
||||
// Prepare Active Context Effects Array
|
||||
TArray<UGES_ActiveContextEffects*> ActiveContextEffectsArray;
|
||||
|
||||
// Loop through Context Effects
|
||||
for (const FGES_ContextEffects& ContextEffect : LocalContextEffects)
|
||||
{
|
||||
// Make sure Tags are Valid
|
||||
if (ContextEffect.EffectTag.IsValid() && !ContextEffect.SourceTagQuery.IsEmpty())
|
||||
{
|
||||
// Create new Active Context Effect
|
||||
UGES_ActiveContextEffects* NewActiveContextEffects = NewObject<UGES_ActiveContextEffects>(this);
|
||||
|
||||
// Pass relevant tag data
|
||||
NewActiveContextEffects->EffectTag = ContextEffect.EffectTag;
|
||||
NewActiveContextEffects->SourceTagQuery = ContextEffect.SourceTagQuery;
|
||||
NewActiveContextEffects->TargetTagQuery = ContextEffect.TargetTagQuery;
|
||||
|
||||
|
||||
// Try to load and add Effects to New Active Context Effects
|
||||
for (const FSoftObjectPath& Effect : ContextEffect.Effects)
|
||||
{
|
||||
if (UObject* Object = Effect.TryLoad())
|
||||
{
|
||||
if (Object->IsA(USoundBase::StaticClass()))
|
||||
{
|
||||
if (USoundBase* SoundBase = Cast<USoundBase>(Object))
|
||||
{
|
||||
NewActiveContextEffects->Sounds.Add(SoundBase);
|
||||
}
|
||||
}
|
||||
else if (Object->IsA(UNiagaraSystem::StaticClass()))
|
||||
{
|
||||
if (UNiagaraSystem* NiagaraSystem = Cast<UNiagaraSystem>(Object))
|
||||
{
|
||||
NewActiveContextEffects->NiagaraSystems.Add(NiagaraSystem);
|
||||
}
|
||||
}
|
||||
else if (Object->IsA(UParticleSystem::StaticClass()))
|
||||
{
|
||||
if (UParticleSystem* ParticleSystem = Cast<UParticleSystem>(Object))
|
||||
{
|
||||
NewActiveContextEffects->ParticleSystems.Add(ParticleSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add New Active Context to the Active Context Effects Array
|
||||
ActiveContextEffectsArray.Add(NewActiveContextEffects);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Call Load Complete after Async Load
|
||||
// Mark loading complete
|
||||
this->OnContextEffectLibraryLoadingComplete(ActiveContextEffectsArray);
|
||||
}
|
||||
|
||||
void UGES_ContextEffectsLibrary::OnContextEffectLibraryLoadingComplete(
|
||||
TArray<UGES_ActiveContextEffects*> InActiveContextEffects)
|
||||
{
|
||||
// Flag data as loaded
|
||||
EffectsLoadState = EGES_ContextEffectsLibraryLoadState::Loaded;
|
||||
|
||||
// Append incoming Context Effects Array to current list of Active Context Effects
|
||||
ActiveContextEffects.Append(InActiveContextEffects);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UGES_ContextEffectsLibrary::PreSave(FObjectPreSaveContext SaveContext)
|
||||
{
|
||||
for (FGES_ContextEffects& ContextEffect : ContextEffects)
|
||||
{
|
||||
if (!ContextEffect.Context.IsEmpty())
|
||||
{
|
||||
FGameplayTagQueryExpression Expression;
|
||||
Expression.AllTagsMatch().AddTags(ContextEffect.Context);
|
||||
ContextEffect.SourceTagQuery.Build(Expression, FString::Format(TEXT("Has all tags({0})"), {ContextEffect.Context.ToStringSimple()}));
|
||||
ContextEffect.Context = FGameplayTagContainer();
|
||||
}
|
||||
ContextEffect.EditorFriendlyName = ContextEffect.EffectTag.IsValid()
|
||||
? FString::Format(TEXT("Effect({0}) Source({1}) Target({2})"),
|
||||
{
|
||||
ContextEffect.EffectTag.GetTagName().ToString(), ContextEffect.SourceTagQuery.GetDescription(),
|
||||
ContextEffect.TargetTagQuery.GetDescription()
|
||||
})
|
||||
: TEXT("Invalid Effect");
|
||||
}
|
||||
Super::PreSave(SaveContext);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Feedback/GES_ContextEffectsPreviewSetting.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GES_ContextEffectsPreviewSetting)
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Feedback/GES_ContextEffectsStructLibrary.h"
|
||||
#include "Animation/AnimSequenceBase.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GES_ContextEffectsStructLibrary)
|
||||
@@ -0,0 +1,289 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Feedback/GES_ContextEffectsSubsystem.h"
|
||||
|
||||
#include "Feedback/GES_ContextEffectsLibrary.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NiagaraFunctionLibrary.h"
|
||||
#include "NiagaraSystem.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GES_ContextEffectsSubsystem)
|
||||
|
||||
class AActor;
|
||||
class UAudioComponent;
|
||||
class UNiagaraSystem;
|
||||
class USceneComponent;
|
||||
class USoundBase;
|
||||
|
||||
void UGES_ContextEffectsSubsystem::SpawnContextEffects(UObject* WorldContextObject, TSoftObjectPtr<UGES_ContextEffectsLibrary> EffectsLibrary, FGES_SpawnContextEffectsInput Input,
|
||||
FGES_SpawnContextEffectsOutput& Output)
|
||||
{
|
||||
if (WorldContextObject == nullptr || WorldContextObject->GetWorld() == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EffectsLibrary.IsNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare Arrays for Sounds and Niagara Systems
|
||||
TArray<USoundBase*> TotalSounds;
|
||||
TArray<UNiagaraSystem*> TotalNiagaraSystems;
|
||||
TArray<UParticleSystem*> TotalParticleSystems;
|
||||
|
||||
// Cycle through Effect Libraries
|
||||
if (UGES_ContextEffectsLibrary* EffectLibrary = EffectsLibrary.LoadSynchronous())
|
||||
{
|
||||
if (EffectLibrary && EffectLibrary->GetContextEffectsLibraryLoadState() == EGES_ContextEffectsLibraryLoadState::Unloaded)
|
||||
{
|
||||
// Sync load effects
|
||||
EffectLibrary->LoadEffects();
|
||||
}
|
||||
|
||||
// Check if the Effect Library is valid and data Loaded
|
||||
if (EffectLibrary->GetContextEffectsLibraryLoadState() == EGES_ContextEffectsLibraryLoadState::Loaded)
|
||||
{
|
||||
// Set up local list of Sounds and Niagara Systems
|
||||
TArray<USoundBase*> Sounds;
|
||||
TArray<UNiagaraSystem*> NiagaraSystems;
|
||||
TArray<UParticleSystem*> ParticleSystems;
|
||||
|
||||
// Get Sounds and Niagara Systems
|
||||
EffectLibrary->GetEffects(Input.EffectName, Input.SourceContext, Input.TargetContext, Sounds, NiagaraSystems, ParticleSystems);
|
||||
|
||||
// Append to accumulating array
|
||||
TotalSounds.Append(Sounds);
|
||||
TotalNiagaraSystems.Append(NiagaraSystems);
|
||||
TotalParticleSystems.Append(ParticleSystems);
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through found Sounds
|
||||
for (USoundBase* Sound : TotalSounds)
|
||||
{
|
||||
if (Input.bAttached)
|
||||
{
|
||||
// Spawn Sounds Attached, add Audio Component to List of ACs
|
||||
UAudioComponent* AudioComponent = UGameplayStatics::SpawnSoundAttached(Sound, Input.ComponentToAttach, Input.Bone, Input.LocationOffset, Input.RotationOffset,
|
||||
EAttachLocation::KeepRelativeOffset,
|
||||
false, Input.AudioVolume, Input.AudioPitch, 0.0f, nullptr, nullptr, true);
|
||||
Output.AudioComponents.Add(AudioComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UAudioComponent* AudioComponent = UGameplayStatics::SpawnSoundAtLocation(WorldContextObject, Sound, Input.Location, Input.Rotation, Input.AudioVolume, Input.AudioPitch, 0.0f, nullptr,
|
||||
nullptr, true);
|
||||
Output.AudioComponents.Add(AudioComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through found Niagara Systems
|
||||
for (UNiagaraSystem* NiagaraSystem : TotalNiagaraSystems)
|
||||
{
|
||||
if (Input.bAttached)
|
||||
{
|
||||
// Spawn Niagara Systems Attached, add Niagara Component to List of NCs
|
||||
UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAttached(NiagaraSystem, Input.ComponentToAttach, Input.Bone, Input.LocationOffset,
|
||||
Input.RotationOffset, Input.VFXScale, EAttachLocation::KeepRelativeOffset, true,
|
||||
ENCPoolMethod::None,
|
||||
true,
|
||||
true);
|
||||
Output.NiagaraComponents.Add(NiagaraComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(WorldContextObject, NiagaraSystem, Input.Location,
|
||||
Input.Rotation, Input.VFXScale, true, true,
|
||||
ENCPoolMethod::None, true);
|
||||
|
||||
Output.NiagaraComponents.Add(NiagaraComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through found Particle Systems
|
||||
for (UParticleSystem* ParticleSystem : TotalParticleSystems)
|
||||
{
|
||||
if (Input.bAttached)
|
||||
{
|
||||
// Spawn Particle Systems Attached, add Niagara Component to List of NCs
|
||||
UParticleSystemComponent* ParticleComponent = UGameplayStatics::SpawnEmitterAttached(ParticleSystem, Input.ComponentToAttach, Input.Bone, Input.LocationOffset,
|
||||
Input.RotationOffset, Input.VFXScale, EAttachLocation::KeepRelativeOffset, true,
|
||||
EPSCPoolMethod::None,
|
||||
true);
|
||||
Output.ParticlesComponents.Add(ParticleComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UParticleSystemComponent* ParticleComponent = UGameplayStatics::SpawnEmitterAtLocation(WorldContextObject, ParticleSystem, Input.Location, Input.Rotation, Input.VFXScale, true,
|
||||
EPSCPoolMethod::None, true);
|
||||
Output.ParticlesComponents.Add(ParticleComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGES_ContextEffectsSubsystem::SpawnContextEffectsExt(const AActor* SpawningActor, const FGES_SpawnContextEffectsInput& Input, FGES_SpawnContextEffectsOutput& Output)
|
||||
{
|
||||
// First determine if this Actor has a matching Set of Libraries
|
||||
if (TObjectPtr<UGES_ContextEffectsSet>* EffectsLibrariesSetPtr = ActiveActorEffectsMap.Find(SpawningActor))
|
||||
{
|
||||
// Validate the pointers from the Map Find
|
||||
if (UGES_ContextEffectsSet* EffectsLibraries = *EffectsLibrariesSetPtr)
|
||||
{
|
||||
// Prepare Arrays for Sounds and Niagara Systems
|
||||
TArray<USoundBase*> TotalSounds;
|
||||
TArray<UNiagaraSystem*> TotalNiagaraSystems;
|
||||
TArray<UParticleSystem*> TotalParticleSystems;
|
||||
|
||||
// Cycle through Effect Libraries
|
||||
for (UGES_ContextEffectsLibrary* EffectLibrary : EffectsLibraries->ContextEffectsLibraries)
|
||||
{
|
||||
// Check if the Effect Library is valid and data Loaded
|
||||
if (EffectLibrary && EffectLibrary->GetContextEffectsLibraryLoadState() == EGES_ContextEffectsLibraryLoadState::Loaded)
|
||||
{
|
||||
// Set up local list of Sounds and Niagara Systems
|
||||
TArray<USoundBase*> Sounds;
|
||||
TArray<UNiagaraSystem*> NiagaraSystems;
|
||||
TArray<UParticleSystem*> ParticleSystems;
|
||||
|
||||
// Get Sounds and Niagara Systems
|
||||
EffectLibrary->GetEffects(Input.EffectName, Input.SourceContext, Input.TargetContext, Sounds, NiagaraSystems, ParticleSystems);
|
||||
|
||||
// Append to accumulating array
|
||||
TotalSounds.Append(Sounds);
|
||||
TotalNiagaraSystems.Append(NiagaraSystems);
|
||||
TotalParticleSystems.Append(ParticleSystems);
|
||||
}
|
||||
else if (EffectLibrary && EffectLibrary->GetContextEffectsLibraryLoadState() == EGES_ContextEffectsLibraryLoadState::Unloaded)
|
||||
{
|
||||
// Else load effects
|
||||
EffectLibrary->LoadEffects();
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through found Sounds
|
||||
for (USoundBase* Sound : TotalSounds)
|
||||
{
|
||||
if (Input.bAttached)
|
||||
{
|
||||
// Spawn Sounds Attached, add Audio Component to List of ACs
|
||||
UAudioComponent* AudioComponent = UGameplayStatics::SpawnSoundAttached(Sound, Input.ComponentToAttach, Input.Bone, Input.LocationOffset, Input.RotationOffset,
|
||||
EAttachLocation::KeepRelativeOffset,
|
||||
false, Input.AudioVolume, Input.AudioPitch, 0.0f, nullptr, nullptr, true);
|
||||
Output.AudioComponents.Add(AudioComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UAudioComponent* AudioComponent = UGameplayStatics::SpawnSoundAtLocation(SpawningActor, Sound, Input.Location, Input.Rotation, Input.AudioVolume, Input.AudioPitch, 0.0f, nullptr,
|
||||
nullptr, true);
|
||||
Output.AudioComponents.Add(AudioComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through found Niagara Systems
|
||||
for (UNiagaraSystem* NiagaraSystem : TotalNiagaraSystems)
|
||||
{
|
||||
if (Input.bAttached)
|
||||
{
|
||||
// Spawn Niagara Systems Attached, add Niagara Component to List of NCs
|
||||
UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAttached(NiagaraSystem, Input.ComponentToAttach, Input.Bone, Input.LocationOffset,
|
||||
Input.RotationOffset, Input.VFXScale, EAttachLocation::KeepRelativeOffset, true,
|
||||
ENCPoolMethod::None,
|
||||
true,
|
||||
true);
|
||||
Output.NiagaraComponents.Add(NiagaraComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UNiagaraComponent* NiagaraComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(SpawningActor, NiagaraSystem, Input.Location,
|
||||
Input.Rotation, Input.VFXScale, true, true,
|
||||
ENCPoolMethod::None, true);
|
||||
|
||||
Output.NiagaraComponents.Add(NiagaraComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through found Particle Systems
|
||||
for (UParticleSystem* ParticleSystem : TotalParticleSystems)
|
||||
{
|
||||
if (Input.bAttached)
|
||||
{
|
||||
// Spawn Particle Systems Attached, add Niagara Component to List of NCs
|
||||
UParticleSystemComponent* ParticleComponent = UGameplayStatics::SpawnEmitterAttached(ParticleSystem, Input.ComponentToAttach, Input.Bone, Input.LocationOffset,
|
||||
Input.RotationOffset, Input.VFXScale, EAttachLocation::KeepRelativeOffset, true,
|
||||
EPSCPoolMethod::None,
|
||||
true);
|
||||
Output.ParticlesComponents.Add(ParticleComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
UParticleSystemComponent* ParticleComponent = UGameplayStatics::SpawnEmitterAtLocation(SpawningActor, ParticleSystem, Input.Location, Input.Rotation, Input.VFXScale, true,
|
||||
EPSCPoolMethod::None, true);
|
||||
Output.ParticlesComponents.Add(ParticleComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UGES_ContextEffectsSubsystem::GetContextFromSurfaceType(
|
||||
TEnumAsByte<EPhysicalSurface> PhysicalSurface, FGameplayTag& Context)
|
||||
{
|
||||
// Get Project Settings
|
||||
if (const UGES_ContextEffectsSettings* ProjectSettings = GetDefault<UGES_ContextEffectsSettings>())
|
||||
{
|
||||
// Find which Gameplay Tag the Surface Type is mapped to
|
||||
if (const FGameplayTag* GameplayTagPtr = ProjectSettings->SurfaceTypeToContextMap.Find(PhysicalSurface))
|
||||
{
|
||||
Context = *GameplayTagPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if Context is Valid
|
||||
return Context.IsValid();
|
||||
}
|
||||
|
||||
void UGES_ContextEffectsSubsystem::LoadAndAddContextEffectsLibraries(AActor* OwningActor,
|
||||
TSet<TSoftObjectPtr<UGES_ContextEffectsLibrary>> ContextEffectsLibraries)
|
||||
{
|
||||
// Early out if Owning Actor is invalid or if the associated Libraries is 0 (or less)
|
||||
if (OwningActor == nullptr || ContextEffectsLibraries.Num() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new Context Effect Set
|
||||
UGES_ContextEffectsSet* EffectsLibrariesSet = NewObject<UGES_ContextEffectsSet>(this);
|
||||
|
||||
// Cycle through Libraries getting Soft Obj Refs
|
||||
for (const TSoftObjectPtr<UGES_ContextEffectsLibrary>& ContextEffectSoftObj : ContextEffectsLibraries)
|
||||
{
|
||||
// Load Library Assets from Soft Obj refs
|
||||
// TODO Support Async Loading of Asset Data
|
||||
if (UGES_ContextEffectsLibrary* EffectsLibrary = ContextEffectSoftObj.LoadSynchronous())
|
||||
{
|
||||
// Call load on valid Libraries
|
||||
EffectsLibrary->LoadEffects();
|
||||
|
||||
// Add new library to Set
|
||||
EffectsLibrariesSet->ContextEffectsLibraries.Add(EffectsLibrary);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Active Actor Effects Map
|
||||
ActiveActorEffectsMap.Emplace(OwningActor, EffectsLibrariesSet);
|
||||
}
|
||||
|
||||
void UGES_ContextEffectsSubsystem::UnloadAndRemoveContextEffectsLibraries(AActor* OwningActor)
|
||||
{
|
||||
// Early out if Owning Actor is invalid
|
||||
if (OwningActor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove ref from Active Actor/Effects Set Map
|
||||
ActiveActorEffectsMap.Remove(OwningActor);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GES_LogChannels.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogGES)
|
||||
|
||||
|
||||
FString GetGESLogContextString(const UObject* ContextObject)
|
||||
{
|
||||
ENetRole Role = ROLE_None;
|
||||
FString RoleName = TEXT("None");
|
||||
FString Name = "None";
|
||||
|
||||
if (const AActor* Actor = Cast<AActor>(ContextObject))
|
||||
{
|
||||
Role = Actor->GetLocalRole();
|
||||
Name = Actor->GetName();
|
||||
}
|
||||
else if (const UActorComponent* Component = Cast<UActorComponent>(ContextObject))
|
||||
{
|
||||
if (AActor* ActorOwner = Cast<AActor>(Component->GetOuter()))
|
||||
{
|
||||
Role = ActorOwner->GetLocalRole();
|
||||
Name = ActorOwner->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
const AActor* Owner = Component->GetOwner();
|
||||
Role = IsValid(Owner) ? Owner->GetLocalRole() : ROLE_None;
|
||||
Name = IsValid(Owner) ? Owner->GetName() : TEXT("None");
|
||||
}
|
||||
}
|
||||
else if (IsValid(ContextObject))
|
||||
{
|
||||
Name = ContextObject->GetName();
|
||||
}
|
||||
|
||||
if (Role != ROLE_None)
|
||||
{
|
||||
RoleName = (Role == ROLE_Authority) ? TEXT("Server") : TEXT("Client");
|
||||
}
|
||||
return FString::Printf(TEXT("[%s] (%s)"), *RoleName, *Name);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GES_Tags.h"
|
||||
|
||||
namespace GMS_MovementModeTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Root, FName{TEXTVIEW("GES")},"Generic Effects System")
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GenericEffectsSystem.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FGenericEffectsSystemModule"
|
||||
|
||||
void FGenericEffectsSystemModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
|
||||
}
|
||||
|
||||
void FGenericEffectsSystemModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FGenericEffectsSystemModule, GenericEffectsSystem)
|
||||
@@ -0,0 +1,195 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Animation/AnimNotifies/AnimNotify.h"
|
||||
#include "Chaos/ChaosEngineInterface.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GES_ContextEffectsStructLibrary.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "GES_AnimNotify_ContextEffects.generated.h"
|
||||
|
||||
class UGES_AnimNotify_ContextEffects;
|
||||
|
||||
/**
|
||||
* Base class for customizing spawn behavior of context effects.
|
||||
* 自定义情景效果生成行为的基类。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable, EditInlineNew, DefaultToInstanced, CollapseCategories, Const)
|
||||
class UGES_ContextEffectsSpawnParametersProvider : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Provides spawn parameters for context effects.
|
||||
* 为情景效果提供生成参数。
|
||||
* @param InMeshComp The skeletal mesh component. 骨骼网格组件。
|
||||
* @param InNotifyNotify The context effects notify. 情景效果通知。
|
||||
* @param InAnimation The animation sequence. 动画序列。
|
||||
* @param OutSpawnLocation The spawn location (output). 生成位置(输出)。
|
||||
* @param OutSpawnRotation The spawn rotation (output). 生成旋转(输出)。
|
||||
* @return True if parameters were provided, false otherwise. 如果提供了参数则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GES|AnimNotify")
|
||||
bool ProvideParameters(USkeletalMeshComponent* InMeshComp, const UGES_AnimNotify_ContextEffects* InNotifyNotify, UAnimSequenceBase* InAnimation,
|
||||
FVector& OutSpawnLocation, FRotator& OutSpawnRotation) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Animation notify for playing context effects.
|
||||
* 用于播放情景效果的动画通知。
|
||||
*/
|
||||
UCLASS(const, hidecategories = Object, CollapseCategories, Config = Game, meta = (DisplayName = "Play Context Effects"))
|
||||
class GENERICEFFECTSSYSTEM_API UGES_AnimNotify_ContextEffects : public UAnimNotify
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor for the context effects animation notify.
|
||||
* 情景效果动画通知构造函数。
|
||||
*/
|
||||
UGES_AnimNotify_ContextEffects(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
|
||||
/**
|
||||
* Called after the object is loaded.
|
||||
* 对象加载后调用。
|
||||
*/
|
||||
virtual void PostLoad() override;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Handles property changes in the editor.
|
||||
* 处理编辑器中的属性更改。
|
||||
* @param PropertyChangedEvent The property change event. 属性更改事件。
|
||||
*/
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Retrieves the display name for the notify.
|
||||
* 获取通知的显示名称。
|
||||
* @return The notify name. 通知名称。
|
||||
*/
|
||||
virtual FString GetNotifyName_Implementation() const override;
|
||||
|
||||
/**
|
||||
* Called when the notify is triggered during animation.
|
||||
* 动画期间通知触发时调用。
|
||||
* @param MeshComp The skeletal mesh component. 骨骼网格组件。
|
||||
* @param Animation The animation sequence. 动画序列。
|
||||
* @param EventReference The animation notify event reference. 动画通知事件引用。
|
||||
*/
|
||||
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Validates associated assets in the editor.
|
||||
* 在编辑器中验证相关资产。
|
||||
*/
|
||||
virtual void ValidateAssociatedAssets() override;
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Sets parameters for the context effects notify in the editor.
|
||||
* 在编辑器中设置情景效果通知的参数。
|
||||
* @param EffectIn The effect tag. 效果标签。
|
||||
* @param LocationOffsetIn The location offset. 位置偏移。
|
||||
* @param RotationOffsetIn The rotation offset. 旋转偏移。
|
||||
* @param VFXPropertiesIn The VFX settings. VFX设置。
|
||||
* @param AudioPropertiesIn The audio settings. 音频设置。
|
||||
* @param bAttachedIn Whether to attach to mesh. 是否附加到网格。
|
||||
* @param SocketNameIn The socket name for attachment. 附加的插槽名称。
|
||||
* @param bPerformTraceIn Whether to perform a trace. 是否执行追踪。
|
||||
* @param TracePropertiesIn The trace settings. 追踪设置。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|AnimNotify")
|
||||
void SetParameters(FGameplayTag EffectIn, FVector LocationOffsetIn, FRotator RotationOffsetIn,
|
||||
FGES_ContextEffectAnimNotifyVFXSettings VFXPropertiesIn, FGES_ContextEffectAnimNotifyAudioSettings AudioPropertiesIn,
|
||||
bool bAttachedIn, FName SocketNameIn, bool bPerformTraceIn, FGES_ContextEffectAnimNotifyTraceSettings TracePropertiesIn);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The effect to play.
|
||||
* 要播放的效果。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (DisplayName = "Effect", ExposeOnSpawn = true))
|
||||
FGameplayTag Effect;
|
||||
|
||||
/**
|
||||
* Location offset for effect spawning (socket if attached, mesh if not).
|
||||
* 效果生成的位置偏移(附加时为插槽,否则为网格)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (ExposeOnSpawn = true))
|
||||
FVector LocationOffset = FVector::ZeroVector;
|
||||
|
||||
/**
|
||||
* Rotation offset for effect spawning.
|
||||
* 效果生成的旋转偏移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (ExposeOnSpawn = true))
|
||||
FRotator RotationOffset = FRotator::ZeroRotator;
|
||||
|
||||
/**
|
||||
* Visual effects settings for the notify.
|
||||
* 通知的视觉效果设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (ExposeOnSpawn = true))
|
||||
FGES_ContextEffectAnimNotifyVFXSettings VFXProperties;
|
||||
|
||||
/**
|
||||
* Audio settings for the notify.
|
||||
* 通知的音频设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (ExposeOnSpawn = true))
|
||||
FGES_ContextEffectAnimNotifyAudioSettings AudioProperties;
|
||||
|
||||
/**
|
||||
* Whether to attach the effect to the mesh component.
|
||||
* 是否将效果附加到网格组件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AttachmentProperties", meta = (DisplayName="Attach To Mesh", ExposeOnSpawn = true))
|
||||
uint32 bAttached : 1;
|
||||
|
||||
/**
|
||||
* Optional provider for custom spawn location/rotation if not attached.
|
||||
* 如果未附加,可选的自定义生成位置/旋转提供者。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="AttachmentProperties", meta=(EditCondition="!bAttached"))
|
||||
TObjectPtr<UGES_ContextEffectsSpawnParametersProvider> SpawnParametersProvider;
|
||||
|
||||
/**
|
||||
* Socket name to attach the effect to.
|
||||
* 附加效果的插槽名称。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AttachmentProperties", meta=(EditCondition="bAttached"))
|
||||
FName SocketName{NAME_None};
|
||||
|
||||
/**
|
||||
* Whether to perform a trace for surface type conversion.
|
||||
* 是否执行追踪以进行表面类型转换。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (ExposeOnSpawn = true))
|
||||
uint32 bPerformTrace : 1;
|
||||
|
||||
/**
|
||||
* Trace settings for the notify.
|
||||
* 通知的追踪设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="AnimNotify", meta = (ExposeOnSpawn = true, EditCondition = "bPerformTrace"))
|
||||
FGES_ContextEffectAnimNotifyTraceSettings TraceProperties;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Performs a preview of the context effects in the editor.
|
||||
* 在编辑器中执行情景效果预览。
|
||||
* @param InOwningActor The owning actor. 拥有演员。
|
||||
* @param InSourceContext The source context tags. 源情景标签。
|
||||
* @param InTargetContext The target context tags. 目标情景标签。
|
||||
* @param InMeshComp The skeletal mesh component. 骨骼网格组件。
|
||||
*/
|
||||
void PerformEditorPreview(AActor* InOwningActor, FGameplayTagContainer& InSourceContext, FGameplayTagContainer& InTargetContext, USkeletalMeshComponent* InMeshComp);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,184 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "GES_ContextEffectsInterface.h"
|
||||
#include "GES_ContextEffectComponent.generated.h"
|
||||
|
||||
namespace EEndPlayReason
|
||||
{
|
||||
enum Type : int;
|
||||
}
|
||||
|
||||
class UAnimSequenceBase;
|
||||
class UAudioComponent;
|
||||
class UGES_ContextEffectsLibrary;
|
||||
class UNiagaraComponent;
|
||||
class UObject;
|
||||
class USceneComponent;
|
||||
struct FFrame;
|
||||
struct FHitResult;
|
||||
|
||||
/**
|
||||
* Component that implements context effects interface for handling effects playback.
|
||||
* 实现情景效果接口的组件,用于处理效果播放。
|
||||
*/
|
||||
UCLASS(ClassGroup = (GES), hidecategories = (Variable, Tags, ComponentTick, ComponentReplication, Activation, Cooking, AssetUserData, Collision), CollapseCategories,
|
||||
meta = (BlueprintSpawnableComponent))
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectComponent : public UActorComponent, public IGES_ContextEffectsInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor for the context effect component.
|
||||
* 情景效果组件构造函数。
|
||||
*/
|
||||
UGES_ContextEffectComponent();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called when the game starts.
|
||||
* 游戏开始时调用。
|
||||
*/
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* Called when the game ends.
|
||||
* 游戏结束时调用。
|
||||
* @param EndPlayReason The reason for ending play. 结束播放的原因。
|
||||
*/
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/**
|
||||
* Sets up the gameplay tags provider.
|
||||
* 设置游戏标签提供者。
|
||||
*/
|
||||
virtual void SetupTagsProvider();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Plays context effects based on input parameters.
|
||||
* 根据输入参数播放情景效果。
|
||||
* @param Input The context effects input data. 情景效果输入数据。
|
||||
*/
|
||||
virtual void PlayContextEffectsWithInput_Implementation(FGES_SpawnContextEffectsInput Input) override;
|
||||
|
||||
/**
|
||||
* Aggregates context tags for effect playback.
|
||||
* 为效果播放聚合情景标签。
|
||||
* @param Input The context effects input data. 情景效果输入数据。
|
||||
*/
|
||||
void AggregateContexts(FGES_SpawnContextEffectsInput& Input) const;
|
||||
|
||||
/**
|
||||
* Injects physical surface information into context tags.
|
||||
* 将物理表面信息注入情景标签。
|
||||
* @param InHitResult The hit result. 命中结果。
|
||||
* @param Contexts The context tags (output). 情景标签(输出)。
|
||||
*/
|
||||
void InjectPhysicalSurfaceToContexts(const FHitResult& InHitResult, FGameplayTagContainer& Contexts);
|
||||
|
||||
/**
|
||||
* Sets the gameplay tags provider.
|
||||
* 设置游戏标签提供者。
|
||||
* @param Provider The object providing gameplay tags. 提供游戏标签的对象。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffect")
|
||||
void SetGameplayTagsProvider(UObject* Provider);
|
||||
|
||||
/**
|
||||
* Automatically converts physical surface to context tags.
|
||||
* 自动将物理表面转换为情景标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Settings")
|
||||
bool bConvertPhysicalSurfaceToContext = true;
|
||||
|
||||
/**
|
||||
* Fallback surface type when no mapping or valid physical material exists.
|
||||
* 当无映射或有效物理材料时使用的备用表面类型。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Settings", meta=(Categories="GES.SurfaceType"))
|
||||
FGameplayTag FallbackPhysicalSurface;
|
||||
|
||||
/**
|
||||
* Default context tags for effect playback.
|
||||
* 用于效果播放的默认情景标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="Settings")
|
||||
FGameplayTagContainer DefaultEffectContexts;
|
||||
|
||||
/**
|
||||
* Default context effects libraries for this actor.
|
||||
* 此演员的默认情景效果库。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="Settings")
|
||||
TSet<TSoftObjectPtr<UGES_ContextEffectsLibrary>> DefaultContextEffectsLibraries;
|
||||
|
||||
/**
|
||||
* Updates the current effect contexts, overriding default contexts.
|
||||
* 更新当前效果情景,覆盖默认情景。
|
||||
* @param NewEffectContexts The new context tags. 新情景标签。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffect")
|
||||
void UpdateEffectContexts(FGameplayTagContainer NewEffectContexts);
|
||||
|
||||
/**
|
||||
* Updates the context effects libraries.
|
||||
* 更新情景效果库。
|
||||
* @param NewContextEffectsLibraries The new effects libraries. 新效果库。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffect")
|
||||
void UpdateLibraries(TSet<TSoftObjectPtr<UGES_ContextEffectsLibrary>> NewContextEffectsLibraries);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Current context tags for effect playback.
|
||||
* 用于效果播放的当前情景标签。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Transient, Category="State")
|
||||
FGameplayTagContainer CurrentContexts;
|
||||
|
||||
/**
|
||||
* Automatically sets up the tags provider if enabled.
|
||||
* 如果启用,自动设置标签提供者。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, Category="Settings")
|
||||
bool bAutoSetupTagsProvider{true};
|
||||
|
||||
/**
|
||||
* Optional object implementing GameplayTagAssetInterface for tag provision.
|
||||
* 实现GameplayTagAssetInterface的可选对象,用于提供标签。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="State")
|
||||
TObjectPtr<UObject> GameplayTagsProvider{nullptr};
|
||||
|
||||
/**
|
||||
* Current context effects libraries in use.
|
||||
* 当前使用的情景效果库。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TSet<TSoftObjectPtr<UGES_ContextEffectsLibrary>> CurrentContextEffectsLibraries;
|
||||
|
||||
/**
|
||||
* Active audio components for effect playback.
|
||||
* 用于效果播放的活跃音频组件。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UAudioComponent>> ActiveAudioComponents;
|
||||
|
||||
/**
|
||||
* Active Niagara components for effect playback.
|
||||
* 用于效果播放的活跃Niagara组件。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UNiagaraComponent>> ActiveNiagaraComponents;
|
||||
|
||||
/**
|
||||
* Active particle system components for effect playback.
|
||||
* 用于效果播放的活跃粒子系统组件。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UParticleSystemComponent>> ActiveParticleComponents;
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GES_ContextEffectsEnumLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Enum defining the load state of a context effects library.
|
||||
* 定义情景效果库加载状态的枚举。
|
||||
*/
|
||||
UENUM()
|
||||
enum class EGES_ContextEffectsLibraryLoadState : uint8
|
||||
{
|
||||
/**
|
||||
* Library is not loaded.
|
||||
* 库未加载。
|
||||
*/
|
||||
Unloaded = 0,
|
||||
|
||||
/**
|
||||
* Library is currently loading.
|
||||
* 库正在加载。
|
||||
*/
|
||||
Loading = 1,
|
||||
|
||||
/**
|
||||
* Library is fully loaded.
|
||||
* 库已完全加载。
|
||||
*/
|
||||
Loaded = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum defining how source context tags are applied.
|
||||
* 定义如何应用源情景标签的枚举。
|
||||
*/
|
||||
UENUM()
|
||||
enum class EGES_EffectsContextType : uint8
|
||||
{
|
||||
/**
|
||||
* Merge source context with existing contexts.
|
||||
* 将源情景与现有情景合并。
|
||||
*/
|
||||
Merge,
|
||||
|
||||
/**
|
||||
* Override existing contexts with source context.
|
||||
* 使用源情景覆盖现有情景。
|
||||
*/
|
||||
Override
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/HitResult.h"
|
||||
#include "GES_ContextEffectsStructLibrary.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "GES_ContextEffectsInterface.generated.h"
|
||||
|
||||
class UAnimSequenceBase;
|
||||
class UObject;
|
||||
class USceneComponent;
|
||||
struct FFrame;
|
||||
|
||||
/**
|
||||
* Interface for objects that can respond to context effects.
|
||||
* 可响应情景效果的对象接口。
|
||||
*/
|
||||
UINTERFACE(Blueprintable)
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectsInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation class for context effects interface.
|
||||
* 情景效果接口的实现类。
|
||||
*/
|
||||
class GENERICEFFECTSSYSTEM_API IGES_ContextEffectsInterface : public IInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Plays context effects based on input parameters.
|
||||
* 根据输入参数播放情景效果。
|
||||
* @param Input The context effects input data. 情景效果输入数据。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GES|ContextEffect")
|
||||
void PlayContextEffectsWithInput(FGES_SpawnContextEffectsInput Input);
|
||||
virtual void PlayContextEffectsWithInput_Implementation(FGES_SpawnContextEffectsInput Input) = 0;
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GES_ContextEffectsEnumLibrary.h"
|
||||
#include "UObject/SoftObjectPath.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "UObject/WeakObjectPtr.h"
|
||||
#include "GES_ContextEffectsStructLibrary.h"
|
||||
#include "GES_ContextEffectsLibrary.generated.h"
|
||||
|
||||
class UNiagaraSystem;
|
||||
class USoundBase;
|
||||
struct FFrame;
|
||||
|
||||
/**
|
||||
* Instance of context effects for runtime use.
|
||||
* 用于运行时的情景效果实例。
|
||||
*/
|
||||
UCLASS(DisplayName="GES Context Effects Instance")
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ActiveContextEffects : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Tag identifying the effect.
|
||||
* 标识效果的标签。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES")
|
||||
FGameplayTag EffectTag;
|
||||
|
||||
/**
|
||||
* Query for source tags.
|
||||
* 源标签查询。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES")
|
||||
FGameplayTagQuery SourceTagQuery;
|
||||
|
||||
/**
|
||||
* Query for target tags.
|
||||
* 目标标签查询。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES")
|
||||
FGameplayTagQuery TargetTagQuery;
|
||||
|
||||
/**
|
||||
* Array of sound assets for the effect.
|
||||
* 效果的音效资产数组。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES")
|
||||
TArray<TObjectPtr<USoundBase>> Sounds;
|
||||
|
||||
/**
|
||||
* Array of Niagara systems for the effect.
|
||||
* 效果的Niagara系统数组。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES")
|
||||
TArray<TObjectPtr<UNiagaraSystem>> NiagaraSystems;
|
||||
|
||||
/**
|
||||
* Array of particle systems for the effect.
|
||||
* 效果的粒子系统数组。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES")
|
||||
TArray<TObjectPtr<UParticleSystem>> ParticleSystems;
|
||||
};
|
||||
|
||||
DECLARE_DYNAMIC_DELEGATE_OneParam(FGES_ContextEffectLibraryLoadingComplete, TArray<UGES_ActiveContextEffects *>, ActiveContextEffects);
|
||||
|
||||
/**
|
||||
* Data asset containing context effects definitions.
|
||||
* 包含情景效果定义的数据资产。
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectsLibrary : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Array of context effects definitions.
|
||||
* 情景效果定义数组。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "GES", meta = (TitleProperty="EditorFriendlyName"))
|
||||
TArray<FGES_ContextEffects> ContextEffects;
|
||||
|
||||
/**
|
||||
* Retrieves effects for a given tag and context.
|
||||
* 获取指定标签和情景的效果。
|
||||
* @param Effect The effect tag. 效果标签。
|
||||
* @param SourceContext The source context tags. 源情景标签。
|
||||
* @param TargetContext The target context tags. 目标情景标签。
|
||||
* @param Sounds The sound assets (output). 音效资产(输出)。
|
||||
* @param NiagaraSystems The Niagara systems (output). Niagara系统(输出)。
|
||||
* @param ParticleSystems The particle systems (output). 粒子系统(输出)。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffect")
|
||||
void GetEffects(const FGameplayTag Effect, const FGameplayTagContainer& SourceContext, const FGameplayTagContainer& TargetContext, TArray<USoundBase*>& Sounds,
|
||||
TArray<UNiagaraSystem*>& NiagaraSystems, TArray<UParticleSystem*>& ParticleSystems);
|
||||
|
||||
/**
|
||||
* Loads the effects in the library.
|
||||
* 加载库中的效果。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffect")
|
||||
void LoadEffects();
|
||||
|
||||
/**
|
||||
* Retrieves the load state of the effects library.
|
||||
* 获取效果库的加载状态。
|
||||
* @return The load state. 加载状态。
|
||||
*/
|
||||
EGES_ContextEffectsLibraryLoadState GetContextEffectsLibraryLoadState();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Internal method for loading effects.
|
||||
* 加载效果的内部方法。
|
||||
*/
|
||||
void LoadEffectsInternal();
|
||||
|
||||
/**
|
||||
* Called when effect library loading is complete.
|
||||
* 效果库加载完成时调用。
|
||||
* @param InActiveContextEffects The loaded context effects. 已加载的情景效果。
|
||||
*/
|
||||
void OnContextEffectLibraryLoadingComplete(TArray<UGES_ActiveContextEffects*> InActiveContextEffects);
|
||||
|
||||
/**
|
||||
* Array of active context effects.
|
||||
* 活跃情景效果数组。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UGES_ActiveContextEffects>> ActiveContextEffects;
|
||||
|
||||
/**
|
||||
* Current load state of the effects library.
|
||||
* 效果库的当前加载状态。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
EGES_ContextEffectsLibraryLoadState EffectsLoadState = EGES_ContextEffectsLibraryLoadState::Unloaded;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Pre-save processing for editor.
|
||||
* 编辑器预保存处理。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "Chaos/ChaosEngineInterface.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GES_ContextEffectsPreviewSetting.generated.h"
|
||||
|
||||
/**
|
||||
* Data asset for context effects preview settings.
|
||||
* 情景效果预览设置的数据资产。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectsPreviewSetting : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Whether to use physical surface as context for preview.
|
||||
* 是否使用物理表面作为预览情景。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Preview)
|
||||
bool bPreviewPhysicalSurfaceAsContext = true;
|
||||
|
||||
/**
|
||||
* Physical surface type for preview.
|
||||
* 预览的物理表面类型。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Preview, meta = (EditCondition = "bPreviewPhysicalSurfaceAsContext"))
|
||||
TEnumAsByte<EPhysicalSurface> PreviewPhysicalSurface = EPhysicalSurface::SurfaceType_Default;
|
||||
|
||||
/**
|
||||
* Context effects libraries for preview.
|
||||
* 预览的情景效果库。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Preview, meta = (AllowedClasses = "/Script/GenericEffectsSystem.GES_ContextEffectsLibrary"))
|
||||
TArray<FSoftObjectPath> PreviewContextEffectsLibraries;
|
||||
|
||||
/**
|
||||
* Source context tags for preview.
|
||||
* 预览的源情景标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Preview)
|
||||
FGameplayTagContainer PreviewSourceContext;
|
||||
|
||||
/**
|
||||
* Target context tags for preview.
|
||||
* 预览的目标情景标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Preview)
|
||||
FGameplayTagContainer PreviewTargetContext;
|
||||
};
|
||||
@@ -0,0 +1,303 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "Engine/HitResult.h"
|
||||
#include "GES_ContextEffectsEnumLibrary.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GES_ContextEffectsStructLibrary.generated.h"
|
||||
|
||||
class UNiagaraComponent;
|
||||
class UAudioComponent;
|
||||
class UAnimSequenceBase;
|
||||
class UParticleSystemComponent;
|
||||
|
||||
/**
|
||||
* Definition of a context effect.
|
||||
* 情景效果的定义。
|
||||
*/
|
||||
USTRUCT(BlueprintType, DisplayName="GES Context Effects Definition")
|
||||
struct GENERICEFFECTSSYSTEM_API FGES_ContextEffects
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Tag identifying the effect.
|
||||
* 标识效果的标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GES")
|
||||
FGameplayTag EffectTag;
|
||||
|
||||
/**
|
||||
* Query for source tags.
|
||||
* 源标签查询。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GES")
|
||||
FGameplayTagQuery SourceTagQuery;
|
||||
|
||||
/**
|
||||
* Query for target tags.
|
||||
* 目标标签查询。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GES")
|
||||
FGameplayTagQuery TargetTagQuery;
|
||||
|
||||
/**
|
||||
* Array of effect assets (sounds, Niagara systems, particle systems).
|
||||
* 效果资产数组(音效、Niagara系统、粒子系统)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GES", meta = (AllowedClasses = "/Script/Engine.SoundBase, /Script/Niagara.NiagaraSystem, /Script/Engine.ParticleSystem"))
|
||||
TArray<FSoftObjectPath> Effects;
|
||||
|
||||
/**
|
||||
* Deprecated context tags.
|
||||
* 已废弃的情景标签。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GES", meta=(DisplayName="Context(Deprecated)"))
|
||||
FGameplayTagContainer Context;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Editor-friendly name for the effect.
|
||||
* 效果的编辑器友好名称。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GES", meta=(EditCondition=false, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Visual effects settings for animation notify.
|
||||
* 动画通知的视觉效果设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICEFFECTSSYSTEM_API FGES_ContextEffectAnimNotifyVFXSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Scale for spawning visual effects.
|
||||
* 生成视觉效果的缩放。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=FX)
|
||||
FVector Scale = FVector(1.0f, 1.0f, 1.0f);
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio settings for animation notify.
|
||||
* 动画通知的音频设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICEFFECTSSYSTEM_API FGES_ContextEffectAnimNotifyAudioSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Volume multiplier for audio effects.
|
||||
* 音频效果的音量倍增器。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Sound)
|
||||
float VolumeMultiplier = 1.0f;
|
||||
|
||||
/**
|
||||
* Pitch multiplier for audio effects.
|
||||
* 音频效果的音高倍增器。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Sound)
|
||||
float PitchMultiplier = 1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trace settings for animation notify.
|
||||
* 动画通知的追踪设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICEFFECTSSYSTEM_API FGES_ContextEffectAnimNotifyTraceSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Trace channel for surface detection.
|
||||
* 用于表面检测的追踪通道。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Trace)
|
||||
TEnumAsByte<ECollisionChannel> TraceChannel = ECollisionChannel::ECC_Visibility;
|
||||
|
||||
/**
|
||||
* Vector offset for the end of the trace.
|
||||
* 追踪结束位置的向量偏移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Trace)
|
||||
FVector EndTraceLocationOffset = FVector::ZeroVector;
|
||||
|
||||
/**
|
||||
* Whether to ignore the actor during tracing.
|
||||
* 追踪时是否忽略演员。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Trace)
|
||||
bool bIgnoreActor = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Input structure for spawning context effects.
|
||||
* 生成情景效果的输入结构。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICEFFECTSSYSTEM_API FGES_SpawnContextEffectsInput
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Name of the effect to spawn.
|
||||
* 要生成的效果名称。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES")
|
||||
FGameplayTag EffectName;
|
||||
|
||||
/**
|
||||
* Whether the effect is attached to a component.
|
||||
* 效果是否附加到组件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES")
|
||||
bool bAttached{true};
|
||||
|
||||
/**
|
||||
* Location for spawning if not attached.
|
||||
* 如果未附加,生成位置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES", meta=(EditCondition="!bAttached", EditConditionHides))
|
||||
FVector Location{FVector::ZeroVector};
|
||||
|
||||
/**
|
||||
* Rotation for spawning if not attached.
|
||||
* 如果未附加,生成旋转。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES", meta=(EditCondition="!bAttached", EditConditionHides))
|
||||
FRotator Rotation{ForceInit};
|
||||
|
||||
/**
|
||||
* Determines how source context is applied (merge or override).
|
||||
* 确定源情景的应用方式(合并或覆盖)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Context")
|
||||
EGES_EffectsContextType SourceContextType{EGES_EffectsContextType::Merge};
|
||||
|
||||
/**
|
||||
* Optional source context tags.
|
||||
* 可选的源情景标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Context")
|
||||
FGameplayTagContainer SourceContext;
|
||||
|
||||
/**
|
||||
* Optional target context tags.
|
||||
* 可选的目标情景标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Context")
|
||||
FGameplayTagContainer TargetContext;
|
||||
|
||||
/**
|
||||
* Bone name for attachment if attached.
|
||||
* 如果附加,附加的骨骼名称。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Attachment", meta=(EditCondition="bAttached", EditConditionHides))
|
||||
FName Bone{NAME_None};
|
||||
|
||||
/**
|
||||
* Component to attach the effect to.
|
||||
* 附加效果的组件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Attachment", meta=(EditCondition="bAttached", EditConditionHides))
|
||||
TObjectPtr<USceneComponent> ComponentToAttach{nullptr};
|
||||
|
||||
/**
|
||||
* Location offset for attached effects.
|
||||
* 附加效果的位置偏移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Attachment", meta=(EditCondition="bAttached", EditConditionHides))
|
||||
FVector LocationOffset{FVector::ZeroVector};
|
||||
|
||||
/**
|
||||
* Rotation offset for attached effects.
|
||||
* 附加效果的旋转偏移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Attachment", meta=(EditCondition="bAttached", EditConditionHides))
|
||||
FRotator RotationOffset{ForceInit};
|
||||
|
||||
/**
|
||||
* Optional animation sequence triggering the effect.
|
||||
* 触发效果的可选动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES")
|
||||
TObjectPtr<const UAnimSequenceBase> AnimationSequence{nullptr};
|
||||
|
||||
/**
|
||||
* Scale for visual effects.
|
||||
* 视觉效果的缩放。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Vfx")
|
||||
FVector VFXScale = FVector(1);
|
||||
|
||||
/**
|
||||
* Volume multiplier for audio effects.
|
||||
* 音频效果的音量倍增器。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Sfx")
|
||||
float AudioVolume = 1;
|
||||
|
||||
/**
|
||||
* Pitch multiplier for audio effects.
|
||||
* 音频效果的音高倍增器。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Sfx")
|
||||
float AudioPitch = 1;
|
||||
|
||||
/**
|
||||
* Whether the effect was triggered by a successful hit.
|
||||
* 效果是否由成功命中触发。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Hit")
|
||||
bool bHitSuccess{false};
|
||||
|
||||
/**
|
||||
* Optional hit result for the effect.
|
||||
* 效果的可选命中结果。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES|Hit")
|
||||
FHitResult HitResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* Output structure for spawned context effects.
|
||||
* 生成情景效果的输出结构。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICEFFECTSSYSTEM_API FGES_SpawnContextEffectsOutput
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Array of spawned audio components.
|
||||
* 生成的音频组件数组。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES")
|
||||
TArray<TObjectPtr<UAudioComponent>> AudioComponents;
|
||||
|
||||
/**
|
||||
* Array of spawned Niagara components.
|
||||
* 生成的Niagara组件数组。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES")
|
||||
TArray<TObjectPtr<UNiagaraComponent>> NiagaraComponents;
|
||||
|
||||
/**
|
||||
* Array of spawned particle system components.
|
||||
* 生成的粒子系统组件数组。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GES")
|
||||
TArray<TObjectPtr<UParticleSystemComponent>> ParticlesComponents;
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/DeveloperSettings.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GES_ContextEffectsStructLibrary.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "GES_ContextEffectsSubsystem.generated.h"
|
||||
|
||||
class UGES_ContextEffectsPreviewSetting;
|
||||
enum EPhysicalSurface : int;
|
||||
|
||||
class AActor;
|
||||
class UAudioComponent;
|
||||
class UGES_ContextEffectsLibrary;
|
||||
class UNiagaraComponent;
|
||||
class USceneComponent;
|
||||
struct FFrame;
|
||||
struct FGameplayTag;
|
||||
struct FGameplayTagContainer;
|
||||
|
||||
/**
|
||||
* Developer settings for context effects system.
|
||||
* 情景效果系统的开发者设置。
|
||||
*/
|
||||
UCLASS(config = Game, defaultconfig)
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectsSettings : public UDeveloperSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Mapping of physical surface types to context tags.
|
||||
* 物理表面类型到情景标签的映射。
|
||||
*/
|
||||
UPROPERTY(config, EditAnywhere, Category="GES")
|
||||
TMap<TEnumAsByte<EPhysicalSurface>, FGameplayTag> SurfaceTypeToContextMap;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Enables preview in the editor.
|
||||
* 在编辑器中启用预览。
|
||||
*/
|
||||
UPROPERTY(Config, EditAnywhere, Category="PreviewProperties")
|
||||
uint32 bPreviewInEditor : 1;
|
||||
|
||||
/**
|
||||
* Preview settings for context effects in the editor.
|
||||
* 编辑器中情景效果的预览设置。
|
||||
*/
|
||||
UPROPERTY(config, EditAnywhere, Category="PreviewProperties", meta = (EditCondition = "bPreviewInEditor"))
|
||||
TSoftObjectPtr<UGES_ContextEffectsPreviewSetting> PreviewSetting;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Set of context effects libraries for an actor.
|
||||
* 演员的情景效果库集合。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectsSet : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Set of context effects libraries.
|
||||
* 情景效果库集合。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TSet<TObjectPtr<UGES_ContextEffectsLibrary>> ContextEffectsLibraries;
|
||||
};
|
||||
|
||||
/**
|
||||
* World subsystem for managing context effects.
|
||||
* 管理情景效果的世界子系统。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICEFFECTSSYSTEM_API UGES_ContextEffectsSubsystem : public UWorldSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Spawns context effects using a single effects library.
|
||||
* 使用单个效果库生成情景效果。
|
||||
* @param WorldContextObject The world context object. 世界上下文对象。
|
||||
* @param EffectsLibrary The effects library to use. 要使用的效果库。
|
||||
* @param Input The context effects input data. 情景效果输入数据。
|
||||
* @param Output The context effects output data (output). 情景效果输出数据(输出)。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffects", meta=(WorldContext = "WorldContextObject"))
|
||||
void SpawnContextEffects(UObject* WorldContextObject, TSoftObjectPtr<UGES_ContextEffectsLibrary> EffectsLibrary, FGES_SpawnContextEffectsInput Input, FGES_SpawnContextEffectsOutput& Output);
|
||||
|
||||
/**
|
||||
* Spawns context effects for an actor with extended input.
|
||||
* 为演员生成情景效果,使用扩展输入。
|
||||
* @param SpawningActor The actor spawning the effects. 生成效果的演员。
|
||||
* @param Input The context effects input data. 情景效果输入数据。
|
||||
* @param Output The context effects output data (output). 情景效果输出数据(输出)。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffects")
|
||||
void SpawnContextEffectsExt(const AActor* SpawningActor, const FGES_SpawnContextEffectsInput& Input, FGES_SpawnContextEffectsOutput& Output);
|
||||
|
||||
/**
|
||||
* Retrieves the context tag for a given physical surface.
|
||||
* 获取指定物理表面的情景标签。
|
||||
* @param PhysicalSurface The physical surface type. 物理表面类型。
|
||||
* @param Context The context tag (output). 情景标签(输出)。
|
||||
* @return True if a context tag was found, false otherwise. 如果找到情景标签则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffects")
|
||||
bool GetContextFromSurfaceType(TEnumAsByte<EPhysicalSurface> PhysicalSurface, FGameplayTag& Context);
|
||||
|
||||
/**
|
||||
* Loads and adds context effects libraries for an actor.
|
||||
* 为演员加载并添加情景效果库。
|
||||
* @param OwningActor The actor owning the libraries. 拥有库的演员。
|
||||
* @param ContextEffectsLibraries The libraries to load. 要加载的库。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffects")
|
||||
void LoadAndAddContextEffectsLibraries(AActor* OwningActor, TSet<TSoftObjectPtr<UGES_ContextEffectsLibrary>> ContextEffectsLibraries);
|
||||
|
||||
/**
|
||||
* Unloads and removes context effects libraries for an actor.
|
||||
* 为演员卸载并移除情景效果库。
|
||||
* @param OwningActor The actor owning the libraries. 拥有库的演员。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GES|ContextEffects")
|
||||
void UnloadAndRemoveContextEffectsLibraries(AActor* OwningActor);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Map of actors to their active context effects sets.
|
||||
* 演员到其活跃情景效果集合的映射。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TMap<TObjectPtr<AActor>, TObjectPtr<UGES_ContextEffectsSet>> ActiveActorEffectsMap;
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Logging/LogMacros.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
|
||||
/**
|
||||
* Gets the context string for logging purposes.
|
||||
* 获取用于日志记录的上下文字符串。
|
||||
* @param ContextObject The object providing the context (optional). 提供上下文的对象(可选)。
|
||||
* @return The context string. 上下文字符串。
|
||||
*/
|
||||
FString GetGESLogContextString(const UObject* ContextObject = nullptr);
|
||||
|
||||
/**
|
||||
* Log category for generic effects system messages.
|
||||
* 通用效果系统消息的日志类别。
|
||||
*/
|
||||
GENERICEFFECTSSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogGES, Log, All);
|
||||
|
||||
/**
|
||||
* Macro for logging effects system messages.
|
||||
* 用于记录效果系统消息的宏。
|
||||
* @details Logs messages with function name and formatted message. 记录包含函数名和格式化消息的日志。
|
||||
*/
|
||||
#define GES_LOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGES, Verbosity, TEXT("%S: %s"),__FUNCTION__, *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
/**
|
||||
* Macro for context-based logging for effects system.
|
||||
* 用于效果系统的基于上下文的日志记录宏。
|
||||
* @details Logs messages with function name, context, and formatted message. 记录包含函数名、上下文和格式化消息的日志。
|
||||
*/
|
||||
#define GES_CLOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGES, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGESLogContextString(this), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
/**
|
||||
* Macro for context-based logging with an explicit owner.
|
||||
* 使用显式拥有者进行基于上下文的日志记录宏。
|
||||
* @details Logs messages with function name, owner context, and formatted message. 记录包含函数名、拥有者上下文和格式化消息的日志。
|
||||
*/
|
||||
#define GES_OWNED_CLOG(LogOwner, Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGES, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGESLogContextString(LogOwner), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
10
Plugins/GGS/Source/GenericEffectsSystem/Public/GES_Tags.h
Normal file
10
Plugins/GGS/Source/GenericEffectsSystem/Public/GES_Tags.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
namespace GES_Tags
|
||||
{
|
||||
GENERICEFFECTSSYSTEM_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Root)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FGenericEffectsSystemModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
Reference in New Issue
Block a user