🌍️

【UE5】C++でLatentノードを自作し、UMGに対して簡単なフェードイン・フェードアウトアニメーションを実現させる

2024/12/06に公開

概要

この記事は一人アドベントカレンダー by ダリアの6日目の記事です。

今回はUMGのアニメーションをUMGに備えられてるアニメーション機能を使わずにそれっぽいものが出来ないか試したものをまとめた記事になります。
イメージは下記のようなものを想定してます。
https://x.com/daria_nicht/status/1856536824757588450

アルファ値をいじってアニメーションさせるくらいの簡単なアニメーションなら、UMGのアニメーション機能を作らなくても済むので管理コストも下がるのではないかと思い試した結果、
白フェードに対してアルファ値を更新するLatentノードを自作して、フェードイン・フェードアウトを実現することができたので、それについて解説しようと思います。

ノードはこんな感じです。

環境

UE5.4.4

フェード用のLatentノードの作成

まずノードはC++で作成してありますのでコードの全体図を記載します。

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "AsyncAction_Fade.generated.h"

//デリゲート定義
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAsyncFadeCompletedDelegate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAsyncFadeUpdateDelegate,float,Result);

UCLASS()
class UAsyncAction_Fade : public UBlueprintAsyncActionBase, public FTickableGameObject
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable,Category = "AsyncTest",meta = ( WorldContext = "WorldContextObject",BlueprintInternalUseOnly = "true" ))
    static UAsyncAction_Fade* AsyncFade(UObject* WorldContextObject, float fadeSeconds);

    void Activate() override;
    void Tick(float DeltaTime) override;
    virtual TStatId GetStatId() const override;
    bool IsTickable() const override;
    
    UPROPERTY(BlueprintAssignable)
    FAsyncFadeUpdateDelegate Ticked;
    
    UPROPERTY(BlueprintAssignable)
    FAsyncFadeCompletedDelegate Completed;
private:
    float FadeSeconds;
    float TickSeconds;
    bool CanTick = false;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "AsyncAction_Fade.h"

#include "CommonFadeSubsystem.h"

UAsyncAction_Fade* UAsyncAction_Fade::AsyncFade(UObject* WorldContextObject, float fadeSeconds)
{
    UAsyncAction_Fade* AsyncFade = NewObject<UAsyncAction_Fade>();
    AsyncFade->FadeSeconds = fadeSeconds;
    AsyncFade->TickSeconds = 0.0f;
    return AsyncFade;
}

void UAsyncAction_Fade::Activate()
{
    CanTick = true;
}

void UAsyncAction_Fade::Tick(float DeltaTime)
{
    UGameInstance* OldGameInstance = RegisteredWithGameInstance.Get();
    TickSeconds += DeltaTime;
    Ticked.Broadcast(TickSeconds / FadeSeconds);
    
    if(TickSeconds >= FadeSeconds)
    {
        Completed.Broadcast();
        SetReadyToDestroy();
        CanTick = false;
    }
}

TStatId UAsyncAction_Fade::GetStatId() const
{
    RETURN_QUICK_DECLARE_CYCLE_STAT(UAsyncAction_Fade,STATGROUP_Tickables);
}

bool UAsyncAction_Fade::IsTickable() const
{
    return CanTick;
}

やっていることは、Tick処理でDeltaTimeを加算していき、指定した時間になるまでTickedデリゲートを呼び出して現在の秒数に想定秒数を割って割合を出して出力させています。
指定した秒数になったらCompletedを呼んで終わらせます。
UBlueprintAsyncActionBaseSetReadyToDestroyという関数を使い終わるタイミングで呼ぶ必要があるので呼びます。

CanTickを最初falseにしてる理由はコンストラクタのタイミングでもTickが呼ばれるみたいなので、有効化したタイミングでTickを有効にするようにしてます。(詳しくは参考サイトを貼らせていただいたのでそちらをご参照ください)

Widgetの作成

Widgetのレイアウトはこのようになっています。
ボーダーだけを置いた状態にしています

冒頭でも話しましたがAnimation機能は作りません。

ここで準備は終わりです。
冒頭のノードのように繋げばフェードイン・フェードアウトが出来てれば成功です。

参考

Using async Blueprint nodes to automate smooth equipment slot transitions
UE FTickableGameObjectのTickが2回呼ばれる

Discussion