[UE4] AnimNotifiy의 Play Particle Effect 부하를 UE4 표준 Object Pool 기능으로 줄이자!

소개





UE4의 애니메이션 알림(AnimNotify) 기능 중 하나인 Play Particle Effect( UAnimNotify_PlayParticleEffect )를 사용하여 캐릭터의 움직임에 맞게 효과를 재생하도록 하는 것이 일반적일까 생각합니다. 예를 들어 달리고있는 캐릭터의 다리가 땅에 도착했을 때 모래 연기를 내거나 공격 모션 중에 불꽃과 궤적을 내거나 ...
htps : // / cs. 그래, 응. 코 m/쟈/엔기네/아니마치온/세쿠엔세 s/의 치후에에 s/그리고 x. html # p ぁ y ぱ rc ぇ 에후 ぇ ct


그러나, 대량의 캐릭터가 돌아다니는 콘텐츠의 경우, 대량의 이펙트가 동시에 생성·재생되는 것에 의한 처리 부하가 문제가 되는 경우가 많습니다…

따라서이 기사에서는 이전에 소개 한 Spawn Emitter~ 노드의 Pooling Method를 사용하여 부하를 개선하는 방법에 대해 설명합니다.
Spawn Emitter ~ 노드의 Pooling Method 란 무엇입니까?

Pooling Method 유효판의 Play Particle Effect를 Blueprint로 만들자!



주식회사 히스토리아님의 기술 블로그 [UE4] 독자적인 Animation Notify 구현 방법 에서 설명한 대로, Blueprint를 사용해 독자적인 AnimNotify/AnimNotifyState를 만들 수 있습니다. 이 기능을 사용하여 Pooling Method를 활성화한 UAnimNotify_PlayParticleEffect를 만들면 이번 목표를 달성할 수 있습니다! 이것만 들으면 어렵게 느낄지도 모릅니다만, UAnimNotify_PlayParticleEffect 는 내부에서 SpawnEmitter~ 처리를 호출하고 있을 뿐이므로 간단합니다!

AnimNotify_PlayPArticleEffect.cpp
UParticleSystemComponent* UAnimNotify_PlayParticleEffect::SpawnParticleSystem(class USkeletalMeshComponent* MeshComp, class UAnimSequenceBase* Animation)
{
    UParticleSystemComponent* ReturnComp = nullptr;

    if (PSTemplate)
    {
        if (PSTemplate->IsLooping())
        {
            UE_LOG(LogParticles, Warning, TEXT("Particle Notify: Anim '%s' tried to spawn infinitely looping particle system '%s'. Spawning suppressed."), *GetNameSafe(Animation), *GetNameSafe(PSTemplate));
            return ReturnComp;
        }

        if (Attached)
        {
            ReturnComp = UGameplayStatics::SpawnEmitterAttached(PSTemplate, MeshComp, SocketName, LocationOffset, RotationOffset, Scale);
        }
        else
        {
            const FTransform MeshTransform = MeshComp->GetSocketTransform(SocketName);
            FTransform SpawnTransform;
            SpawnTransform.SetLocation(MeshTransform.TransformPosition(LocationOffset));
            SpawnTransform.SetRotation(MeshTransform.GetRotation() * RotationOffsetQuat);
            SpawnTransform.SetScale3D(Scale);
            ReturnComp = UGameplayStatics::SpawnEmitterAtLocation(MeshComp->GetWorld(), PSTemplate, SpawnTransform);
        }
    }
    else
    {
        UE_LOG(LogParticles, Warning, TEXT("Particle Notify: Particle system is null for particle notify '%s' in anim: '%s'"), *GetNotifyName(), *GetPathNameSafe(Animation));
    }

    return ReturnComp;
}

라고 하는 것으로, 참고 기사로 해설되고 있는 대로 「Notify 클래스를 계승한 BP를 작성」하고, 「Received_Notify」에 상기의 코드와 같은 구현을 BP로 써 갑니다.

갸리코리… 카리카리…

… 할 수 있었습니다!


Before

After


초엄밀한 비교라고 하는 것은 아니지만, Spawn Emitter ~ 노드의 Pooling Method 란 무엇입니까? 에서 설명한 대로, Pooling Method를 활용하는 것으로 일부 처리를 생략할 수 있었던 것으로 부하가 개선하고 있습니다. 이번 프로필 환경은 적당히 좋은 PC + 심플한 이펙트라고 하는 것으로 12 μs 라고 하는 약간의 개선이었습니다만 환경·조건에 따라서는 무시할 수 없을 정도의 개선이 되는 경우도 있습니다 (예:저스펙인 모바일 단말상에서 움직인다 , 수십체 이상의 캐릭터가 동시에 몇개의 이펙트를 자주 내는 컨텐츠).

프로파일링했을 때 UAnimNotify_PlayParticleEffect 의 부하가 눈에 띄는 때는 꼭 Pooling Method를 활용하는 방법으로의 전환을 검토해 보세요!

덤 : 이번에 설명한 자작 AnimNotify의 과제 「Socket Name의 예측이 나오지 않는다」에 대해



UAnimNotify_PlayParticleEffect 의 속성인 Socket Name , 문자를 입력하면 예측을 해줍니다. 편리합니다.

한편, 이번 자작한 AnimNotify는...내주지 않습니다! 「아이에에에! 같은 프로퍼티명・같은 Name형인데 난데!?」라고 생각했기 때문에 조사해 보았습니다.

AnimNotifyDetails.cpp
bool FAnimNotifyDetails::CustomizeProperty(IDetailCategoryBuilder& CategoryBuilder, UObject* Notify, TSharedPtr<IPropertyHandle> Property)
{
    if(Notify && Notify->GetClass() && Property->IsValidHandle())
    {
        FString ClassName = Notify->GetClass()->GetName();
        FString PropertyName = Property->GetProperty()->GetName();
        bool bIsBoneName = Property->GetBoolMetaData(TEXT("AnimNotifyBoneName"));

        if(ClassName.Find(TEXT("AnimNotify_PlayParticleEffect")) != INDEX_NONE && PropertyName == TEXT("SocketName"))
        {
            AddBoneNameProperty(CategoryBuilder, Notify, Property);
            return true;
        }
        else if(ClassName.Find(TEXT("AnimNotifyState_TimedParticleEffect")) != INDEX_NONE && PropertyName == TEXT("SocketName"))
        {
            AddBoneNameProperty(CategoryBuilder, Notify, Property);
            return true;
        }
        else if(ClassName.Find(TEXT("AnimNotify_PlaySound")) != INDEX_NONE && PropertyName == TEXT("AttachName"))
        {
            AddBoneNameProperty(CategoryBuilder, Notify, Property);
            return true;
        }
        else if (ClassName.Find(TEXT("AnimNotifyState_Trail")) != INDEX_NONE)
        {
            if(PropertyName == TEXT("FirstSocketName") || PropertyName == TEXT("SecondSocketName"))
            {
                AddBoneNameProperty(CategoryBuilder, Notify, Property);
                return true;
            }
            else if(PropertyName == TEXT("WidthScaleCurve"))
            {
                AddCurveNameProperty(CategoryBuilder, Notify, Property);
                return true;
            }
        }
        else if (bIsBoneName)
        {
            AddBoneNameProperty(CategoryBuilder, Notify, Property);
            return true;
        }
    }
    return false;
}

특정의 클래스가 특정의 이름의 프로퍼티를 가지거나 UPROPERTY의 meta로 AnimNotifyBoneName가 유효하게 되어 있는 경우는 본 소켓명의 예측이 나오게 된다고 하는 사양이었습니다. 따라서 위의 코드에 없는 클래스에서 예측을 실행하려면 다음과 같이 AnimNotifyBoneName을 활성화해야 합니다.
UPROPERTY(EditAnywhere,  meta = (AnimNotifyBoneName = "true"))

BP의 경우는 ... 죄송합니다 ... 할 수 없어야합니다 ....
라고 하는 것으로, Pooling Method 유효판의 UAnimNotify_PlayParticleEffect를 진심으로 만드는 경우는 UAnimNotify_PlayParticleEffect를 개조, 또는 계승한 클래스에서 Pooling Method를 AnimNotify의 상세 패널로부터 설정할 수 있는 형태로 하는 것이 좋을까 생각합니다.

오시마

좋은 웹페이지 즐겨찾기