Files
UnrealEngine/Engine/Plugins/FX/Niagara/Source/NiagaraAnimNotifies/Private/AnimNotifyState_TimedNiagaraEffect.cpp
2025-05-18 13:04:45 +08:00

268 lines
8.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNotifyState_TimedNiagaraEffect.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimSequenceBase.h"
#include "Components/SkeletalMeshComponent.h"
#include "NiagaraComponent.h"
#include "NiagaraSystem.h"
#include "NiagaraFunctionLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNotifyState_TimedNiagaraEffect)
UAnimNotifyState_TimedNiagaraEffect::UAnimNotifyState_TimedNiagaraEffect(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
Template = nullptr;
LocationOffset.Set(0.0f, 0.0f, 0.0f);
RotationOffset = FRotator(0.0f, 0.0f, 0.0f);
}
UFXSystemComponent* UAnimNotifyState_TimedNiagaraEffect::SpawnEffect(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) const
{
// Only spawn if we've got valid params
if (ValidateParameters(MeshComp))
{
FFXSystemSpawnParameters SpawnParams;
SpawnParams.SystemTemplate = Template;
SpawnParams.AttachToComponent = MeshComp;
SpawnParams.AttachPointName = SocketName;
SpawnParams.Location = LocationOffset;
SpawnParams.Rotation = RotationOffset;
SpawnParams.Scale = Scale;
SpawnParams.LocationType = EAttachLocation::KeepRelativeOffset;
SpawnParams.bAutoDestroy = !bDestroyAtEnd;
if (UNiagaraComponent* NewComponent = UNiagaraFunctionLibrary::SpawnSystemAttachedWithParams(SpawnParams))
{
if (bApplyRateScaleAsTimeDilation)
{
NewComponent->SetCustomTimeDilation(Animation->RateScale);
}
return NewComponent;
}
}
return nullptr;
}
UFXSystemComponent* UAnimNotifyState_TimedNiagaraEffect::GetSpawnedEffect(UMeshComponent* MeshComp)
{
if (MeshComp)
{
TArray<USceneComponent*> Children;
MeshComp->GetChildrenComponents(false, Children);
if (Children.Num())
{
for (USceneComponent* Component : Children)
{
if (Component && Component->ComponentHasTag(GetSpawnedComponentTag()))
{
if (UFXSystemComponent* FXComponent = CastChecked<UFXSystemComponent>(Component))
{
return FXComponent;
}
}
}
}
}
return nullptr;
}
void UAnimNotifyState_TimedNiagaraEffect::NotifyBegin(USkeletalMeshComponent * MeshComp, class UAnimSequenceBase * Animation, float TotalDuration)
{
}
void UAnimNotifyState_TimedNiagaraEffect::NotifyBegin(USkeletalMeshComponent * MeshComp, class UAnimSequenceBase * Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
{
if (UFXSystemComponent* Component = SpawnEffect(MeshComp, Animation))
{
// tag the component with the AnimNotify that is triggering the animation so that we can properly clean it up
Component->ComponentTags.AddUnique(GetSpawnedComponentTag());
}
Super::NotifyBegin(MeshComp, Animation, TotalDuration, EventReference);
}
void UAnimNotifyState_TimedNiagaraEffect::NotifyEnd(USkeletalMeshComponent * MeshComp, class UAnimSequenceBase * Animation)
{
}
void UAnimNotifyState_TimedNiagaraEffect::NotifyEnd(USkeletalMeshComponent * MeshComp, class UAnimSequenceBase * Animation, const FAnimNotifyEventReference& EventReference)
{
if (UFXSystemComponent* FXComponent = GetSpawnedEffect(MeshComp))
{
// untag the component
FXComponent->ComponentTags.Remove(GetSpawnedComponentTag());
// Either destroy the component or deactivate it to have it's active FXSystems finish.
// The component will auto destroy once all FXSystem are gone.
if (bDestroyAtEnd)
{
FXComponent->DestroyComponent();
}
else
{
FXComponent->Deactivate();
}
}
Super::NotifyEnd(MeshComp, Animation, EventReference);
}
bool UAnimNotifyState_TimedNiagaraEffect::ValidateParameters(USkeletalMeshComponent* MeshComp) const
{
bool bValid = true;
if (!Template)
{
bValid = false;
}
else if (!MeshComp->DoesSocketExist(SocketName) && MeshComp->GetBoneIndex(SocketName) == INDEX_NONE)
{
bValid = false;
}
return bValid;
}
FName UAnimNotifyState_TimedNiagaraEffect::GetSpawnedComponentTag() const
{
// we generate a unique tag to associate with our spawned components so that we can clean things up upon completion
FName NotifyName = GetFName();
NotifyName.SetNumber(GetUniqueID());
return NotifyName;
}
FString UAnimNotifyState_TimedNiagaraEffect::GetNotifyName_Implementation() const
{
if (Template)
{
return Template->GetName();
}
return UAnimNotifyState::GetNotifyName_Implementation();
}
//////////////////////////////////////////////////////////////////////////
UAnimNotifyState_TimedNiagaraEffectAdvanced::UAnimNotifyState_TimedNiagaraEffectAdvanced(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NotifyProgressUserParameter = FName("NormalizedNotifyProgress");
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::Serialize(FArchive& Ar)
{
Ar.UsingCustomVersion(FFortniteMainBranchObjectVersion::GUID);
Super::Serialize(Ar);
if (Ar.IsLoading())
{
if (Ar.CustomVer(FFortniteMainBranchObjectVersion::GUID) < FFortniteMainBranchObjectVersion::AnimNotifyAddRateScale)
{
bApplyRateScaleToProgress = false;
}
}
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::NotifyBegin(USkeletalMeshComponent* MeshComp, class UAnimSequenceBase* Animation, float TotalDuration)
{
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::NotifyBegin(USkeletalMeshComponent* MeshComp, class UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
{
Super::NotifyBegin(MeshComp, Animation, TotalDuration, EventReference);
FInstanceProgressInfo& NewInfo = ProgressInfoMap.Add(MeshComp);
NewInfo.Duration = TotalDuration;
NewInfo.Elapsed = 0.0f;
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::NotifyEnd(USkeletalMeshComponent* MeshComp, class UAnimSequenceBase* Animation)
{
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::NotifyEnd(USkeletalMeshComponent* MeshComp, class UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
{
Super::NotifyEnd(MeshComp, Animation, EventReference);
ProgressInfoMap.Remove(MeshComp);
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime)
{
}
void UAnimNotifyState_TimedNiagaraEffectAdvanced::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime, const FAnimNotifyEventReference& EventReference)
{
Super::NotifyTick(MeshComp, Animation, FrameDeltaTime, EventReference);
//Advance the progress.
//TODO: There must be some way to avoid this map and lookup. The information about the current elapsed time and a mapping onto the notify range should be available somewhere in the mesh comp and notify.
if (FInstanceProgressInfo* ProgressInfo = ProgressInfoMap.Find(MeshComp))
{
const float RateScale = bApplyRateScaleToProgress ? Animation->RateScale : 1.0f;
ProgressInfo->Elapsed += FrameDeltaTime * RateScale;
}
if (UFXSystemComponent* FXComponent = GetSpawnedEffect(MeshComp))
{
//send the notify progress to the FX Component
if (bEnableNormalizedNotifyProgress && !NotifyProgressUserParameter.IsNone())
{
FXComponent->SetFloatParameter(NotifyProgressUserParameter, GetNotifyProgress(MeshComp));
}
//Send anim curve data to the FX Component
if (AnimCurves.Num() != 0)
{
if (UAnimInstance* AnimInst = MeshComp->GetAnimInstance())
{
for (int32 Index = 0; Index != AnimCurves.Num(); ++Index)
{
FName CurveName = AnimCurves[Index].AnimCurveName;
FName NiagaraUserVariableName = AnimCurves[Index].UserVariableName;
if ((!CurveName.IsNone()) && (!NiagaraUserVariableName.IsNone()))
{
float CurveValue = 0.0f;
if (AnimInst->GetCurveValue(CurveName, CurveValue))
{
FXComponent->SetFloatParameter(NiagaraUserVariableName, CurveValue);
}
}
}
}
else
{
// No anim instance, defer to the mesh component's curves
for (int32 Index = 0; Index != AnimCurves.Num(); ++Index)
{
FName CurveName = AnimCurves[Index].AnimCurveName;
FName NiagaraUserVariableName = AnimCurves[Index].UserVariableName;
if ((!CurveName.IsNone()) && (!NiagaraUserVariableName.IsNone()))
{
float CurveValue = 0.0f;
if (MeshComp->GetCurveValue(CurveName, 0.0f, CurveValue))
{
FXComponent->SetFloatParameter(NiagaraUserVariableName, CurveValue);
}
}
}
}
}
}
}
float UAnimNotifyState_TimedNiagaraEffectAdvanced::GetNotifyProgress(UMeshComponent* MeshComp)
{
if (FInstanceProgressInfo* ProgressInfo = ProgressInfoMap.Find(MeshComp))
{
return FMath::Clamp(ProgressInfo->Elapsed / FMath::Max(ProgressInfo->Duration, SMALL_NUMBER), 0.0f, 1.0f);
}
return 0.0f;
}