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

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

といっても…ほぼ以下の記事のUserWidget版だけだったりします（素晴らしい記事をありがとうございます！）
[[UE4] C++で動的にアクターを生成（スポーン）する方法で一番実用的だった方法](https://qiita.com/Kuroyanagi96/items/7849dfaed83fc8df5741)
[【UE4】同期的なアセットのロード方法とデメリット](
https://qiita.com/4_mio_11/items/00de94dbb6b6ed59176f)

検証環境：UE4.25.3

# C++でアセットパスからUserWidgetClassを生成する方法
Widgetを作成する`Create Widget`を使うためには下図のノードにおける`Class`ピン、具体的には生成したいWidgetの`UserWidgetClass`を用意する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/dfe74014-6145-d1e3-9117-88fb13ae2630.png)

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

## LoadSynchronousを使う方法

```cpp
#include "Blueprint/UserWidget.h"

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

## LoadObjectを使う方法

```cpp
 #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++で実行する場合は

```cpp: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;
}
```
または、

```cpp: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`のいずれかを渡す必要があるという点です。ですので、

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

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

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