[UE4]C++でアセットパスからWidget Blueprintを動的に生成(CreateWidget)する方法について


はじめに

お仕事で本件について調べたので、自分用メモも兼ねてご共有

といっても…ほぼ以下の記事のUserWidget版だけだったりします(素晴らしい記事をありがとうございます!)
[UE4] C++で動的にアクターを生成(スポーン)する方法で一番実用的だった方法
【UE4】同期的なアセットのロード方法とデメリット

検証環境:UE4.25.3

C++でアセットパスからUserWidgetClassを生成する方法

Widgetを作成するCreate Widgetを使うためには下図のノードにおけるClassピン、具体的には生成したいWidgetのUserWidgetClassを用意する必要があります。

いくつか方法がありますが、その中から使いやすい方法をご紹介。

LoadSynchronousを使う方法

#include "Blueprint/UserWidget.h"

FString path = "/Game/UMG_Test.UMG_Test_C";
TSubclassOf<class UUserWidget> WidgetClass = TSoftClassPtr<UUserWidget>(FSoftObjectPath(*Path)).LoadSynchronous();

LoadObjectを使う方法

 #include "Blueprint/UserWidget.h"

 FString path = "/Game/UMG_Test.UMG_Test";
 TSubclassOf<class UUserWidget> WidgetClass;
 UBlueprint* BP = LoadObject<UBlueprint>(NULL, *Path);
 if (BP != nullptr)
 {
    WidgetClass = BP->GeneratedClass;
 }

どちらも有効ですが、ソフトリファレンスであるTSoftClassPtrを使うLoadSynchronousの方が非同期ロード対応やその他の管理が楽になるのでオススメです。

C++で指定のUserWidgetをCreateWidgetする方法

CreateWidgetをC++で実行する場合は

UserWidget.h
template <typename WidgetT = UUserWidget, typename OwnerT = UObject>
WidgetT* CreateWidget(OwnerT* OwningObject, TSubclassOf<UUserWidget> UserWidgetClass = WidgetT::StaticClass(), FName WidgetName = NAME_None)
{
    static_assert(TIsDerivedFrom<WidgetT, UUserWidget>::IsDerived, "CreateWidget can only be used to create UserWidget instances. If creating a UWidget, use WidgetTree::ConstructWidget.");

    static_assert(TIsDerivedFrom<OwnerT, UWidget>::IsDerived
        || TIsDerivedFrom<OwnerT, UWidgetTree>::IsDerived
        || TIsDerivedFrom<OwnerT, APlayerController>::IsDerived
        || TIsDerivedFrom<OwnerT, UGameInstance>::IsDerived
        || TIsDerivedFrom<OwnerT, UWorld>::IsDerived, "The given OwningObject is not of a supported type for use with CreateWidget.");

    SCOPE_CYCLE_COUNTER(STAT_CreateWidget);

    if (OwningObject)
    {
        return Cast<WidgetT>(UUserWidget::CreateWidgetInstance(*OwningObject, UserWidgetClass, WidgetName));
    }
    return nullptr;
}

または、

WidgetBlueprint.h
UCLASS(meta=(ScriptName="WidgetLibrary"))
class UMG_API UWidgetBlueprintLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_UCLASS_BODY()

public:
    /** Creates a widget */
    UFUNCTION(BlueprintCallable, BlueprintCosmetic, meta=( WorldContext="WorldContextObject", DisplayName="Create Widget", BlueprintInternalUseOnly="true" ), Category="Widget")
    static class UUserWidget* Create(UObject* WorldContextObject, TSubclassOf<class UUserWidget> WidgetType, APlayerController* OwningPlayer);

を呼び出すことになります。

前者は少しややこしいコードに見えますが、実際に使う上で注意すべき点はOwningObjectにはUWidget, UWidgetTree, APlayerControllr, UGameInstance, UWorldのいずれかを渡す必要があるという点です。ですので、

TSubclassOf<class UUserWidget> WidgetClass;
WidgetClass = TSoftClassPtr<UUserWidget>(FSoftObjectPath(*Path)).LoadSynchronous();
UUserWidget* UserWidget = CreateWidget<UUserWidget>(GetWorld(), WidgetClass);

といった感じで使うことができます。

サクッとした内容でしたが少しでもご参考になれば幸いです。