そんなときに、ブループリントの変数(ブループリント側で作成した変数)にアクセスする方法を紹介します。
以下の環境で動作確認しています。
Unreal Engine 5.1.1
Visual Studio 2022
Windows 11 Home
以下の状況では、UUserWidget を使って、ウィジェットブループリントの変数にアクセスする必要があります。
- エディタで作成したウィジェットブループリント (UMG) を C++ 側で生成するときに、CreateWidgetInstance() や UWidgetBlueprintLibrary::Create() を使っている。
- その UMG は親が UUserWidget なので、ブループリントの変数を UPROPERTY でバインドしていない。
- いろんな事情で、その UMG の親クラスを自前のクラスに置き換えられない。
必要になる状況は稀だと思います。
初心者向けの内容ではないので、C++ とウィジェットブループリントの扱いが分かっている人向けに説明します。
動作確認のために作成した UE プロジェクトの設定はこんな感じです。
小さくてみづらい場合は、クリックすることで拡大表示できます。
親クラスを UUserWidget にして、ウィジェットブループリントを二種類、新規作成します。
※ひとつは参照用です。
作成した UMG
- WB_VariableTest1 … C++ で生成する UMG
- WB_VariableTest2 … WB_VariableTest1 に持たせる UMG
WB_VariableTest2(1. で新規作成した参照用のウィジェットブループリント)はキャンバス・パネルの下に追加しておきます。
C++ で生成するのが面倒なので。
1. で新規作成したウィジェットブループリントに変数を追加します。
Integer, Float, String, Boolean, 参照用のウィジェットブループリント (UserWidget) の5つ。
公開したり編集可能にしたりする必要はありません。
変数名と型と初期値だけ設定します。
VariableUserWidget だけ、初期値をブループリントで設定します(後述)。
C++ で設定するのが面倒なので。
参照用のウィジェットブループリント (WB_VariableTest2) には、int 型の変数だけ追加。
WB_VariableTest1 に追加した参照用のウィジェットブループリント変数に、初期値をセットする処理を追加します。
コンパイルして保存したら、ブループリント側の編集はこれで終わりです。
以降は C++ 側と動作確認だけです。
ウィジェットブループリントを読み込んで生成し、ブループリントの変数にアクセスする処理を C++ 側に追加します。
新規でソースコードを作るのが面倒だったので、UE が自動生成した GameMode に処理を追加しています。
プロジェクト名が UMGVariableTest なので、AUMGVariableTestGameMode クラスがあります。
ヘッダファイルはいじらず、cpp 側に処理を追加しています。
全コードは以下になります。
// Copyright Epic Games, Inc. All Rights Reserved.
#include "UMGVariableTestGameMode.h"
#include "UMGVariableTestCharacter.h"
#include "UObject/ConstructorHelpers.h"
//以下、追加したインクルード
#include "Editor.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Blueprint/UserWidget.h"
#include "Blueprint/WidgetBlueprintLibrary.h"
#include "UObject/UnrealType.h"
#include "UObject/UnrealTypePrivate.h"
//ここまで
//以下、追加したコード
namespace UE { namespace GameMode {
const float DISPLAY_SECONDS = 30.f;
const FLinearColor DISPLAY_COLOR = FLinearColor(1.f, 0.f, 0.5f, 1.f);
bool IsInGame()
{
return !GIsEditor;
}
bool IsInPIE()
{
return GEditor && GEditor->PlayWorld && !GEditor->bIsSimulatingInEditor;
}
UClass* LoadWidgetClass()
{
const FString WidgetClassPath = TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/WP_VariableTest1.WP_VariableTest1_C'");
FSoftClassPath WidgeClasstPath(WidgetClassPath);
return WidgeClasstPath.TryLoadClass<UUserWidget>();
}
UUserWidget* CreateWidget(UWorld* World)
{
UClass* WidgetClass = UE::GameMode::LoadWidgetClass();
if (!IsValid(WidgetClass))
{
return nullptr;
}
APlayerController* Player = UGameplayStatics::GetPlayerController(World, 0);
UUserWidget* Widget = UWidgetBlueprintLibrary::Create(World, WidgetClass, Player);
if (!IsValid(Widget))
{
return nullptr;
}
return Widget;
}
template <class PropertyType, typename ValueType>
ValueType* GetWidgetVariableValuePtr(UUserWidget* InWidget, const TCHAR* const InVariableName)
{
UClass* WidgetClass = InWidget->GetClass();
if (!IsValid(WidgetClass))
{
return nullptr;
}
PropertyType* Property = FindFProperty<PropertyType>(WidgetClass, InVariableName);
if (Property)
{
ValueType* Value = Property->ContainerPtrToValuePtr<ValueType>(InWidget, 0);
return Value;
}
return nullptr;
}
bool GetWidgetIntValue(UUserWidget* InWidget, const TCHAR* const InVariableName, int32& OutValue)
{
int* Value = UE::GameMode::GetWidgetVariableValuePtr<FIntProperty, int>(InWidget, InVariableName);
if (Value)
{
OutValue = *Value;
return true;
}
int64* Value1 = UE::GameMode::GetWidgetVariableValuePtr<FInt64Property, int64>(InWidget, InVariableName);
if (Value1)
{
OutValue = static_cast<int32>(*Value1);
return true;
}
int16* Value2 = UE::GameMode::GetWidgetVariableValuePtr<FInt16Property, int16>(InWidget, InVariableName);
if (Value2)
{
OutValue = *Value2;
return true;
}
int8* Value3 = UE::GameMode::GetWidgetVariableValuePtr<FInt8Property, int8>(InWidget, InVariableName);
if (Value3)
{
OutValue = *Value3;
return true;
}
return false;
}
bool GetWidgetFloatValue(UUserWidget* InWidget, const TCHAR* const InVariableName, float& OutValue)
{
float* Value = UE::GameMode::GetWidgetVariableValuePtr<FFloatProperty, float>(InWidget, InVariableName);
if (Value)
{
OutValue = *Value;
return true;
}
double* Value2 = UE::GameMode::GetWidgetVariableValuePtr<FDoubleProperty, double>(InWidget, InVariableName);
if (Value2)
{
OutValue = static_cast<float>(*Value2);
return true;
}
return false;
}
void ShowAllProperty(UUserWidget* InWidget)
{
UClass* Class = InWidget->GetClass();
for (TFieldIterator<FProperty> It(Class, EFieldIteratorFlags::IncludeSuper); It; ++It)
{
FProperty* Property = *It;
if (Property->HasAnyPropertyFlags(CPF_BlueprintVisible))
{
FString PrintString = FString::Printf(TEXT("Property=%s"), *Property->GetName());
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, FLinearColor(0.f, 1.f, 0.f, 1.f), DISPLAY_SECONDS * 10.f);
}
}
}
void PrintTextIntValue(UUserWidget* InWidget)
{
const TCHAR* const VariableName = TEXT("VariableInt");
int32 IntValue = 0;
if (UE::GameMode::GetWidgetIntValue(InWidget, VariableName, IntValue))
{
FString PrintString = FString::Printf(TEXT("%s=%d"), VariableName, IntValue);
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, DISPLAY_COLOR, DISPLAY_SECONDS);
}
}
void PrintTextFloatValue(UUserWidget* InWidget)
{
const TCHAR* const VariableName = TEXT("VariableFloat");
float FloatValue = 0.0f;
if (UE::GameMode::GetWidgetFloatValue(InWidget, VariableName, FloatValue))
{
FString PrintString = FString::Printf(TEXT("%s=%f"), VariableName, FloatValue);
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, DISPLAY_COLOR, DISPLAY_SECONDS);
}
}
void PrintTextStringValue(UUserWidget* InWidget)
{
const TCHAR* const VariableName = TEXT("VariableString");
if (FString* StringValue = UE::GameMode::GetWidgetVariableValuePtr<FStrProperty, FString>(InWidget, VariableName))
{
FString PrintString = FString::Printf(TEXT("%s=%s"), VariableName, **StringValue);
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, DISPLAY_COLOR, DISPLAY_SECONDS);
}
}
void PrintTextBoolValue(UUserWidget* InWidget)
{
const TCHAR* const VariableName = TEXT("bVariableBool");
if (bool* BoolValue = UE::GameMode::GetWidgetVariableValuePtr<FBoolProperty, bool>(InWidget, VariableName))
{
FString PrintString = FString::Printf(TEXT("%s=%s"), VariableName, *BoolValue ? TEXT("true") : TEXT("false"));
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, DISPLAY_COLOR, DISPLAY_SECONDS);
}
}
void PrintTextUserWidgetIntValue(UUserWidget* InWidget)
{
const TCHAR* const VariableName = TEXT("VariableUserWidget");
if (TObjectPtr<UObject>* ObjectValue = UE::GameMode::GetWidgetVariableValuePtr<FObjectProperty, TObjectPtr<UObject>>(InWidget, VariableName))
{
if (UUserWidget* ChildWidget = CastChecked<UUserWidget>(ObjectValue->Get()))
{
const TCHAR* const WidgetVariableName = TEXT("NewVar");
int32 IntValue = 0;
if (UE::GameMode::GetWidgetIntValue(ChildWidget, WidgetVariableName, IntValue))
{
FString PrintString = FString::Printf(TEXT("%s %s=%d"), VariableName, WidgetVariableName, IntValue);
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, DISPLAY_COLOR, DISPLAY_SECONDS);
}
}
}
}
}}
//ここまで
AUMGVariableTestGameMode::AUMGVariableTestGameMode()
{
// set default pawn class to our Blueprinted character
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter"));
if (PlayerPawnBPClass.Class != NULL)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
//以下、ブループリントの変数を取得するサンプルコード
const bool bIsInGame = UE::GameMode::IsInGame();
const bool bIsInPIE = UE::GameMode::IsInPIE();
if (!bIsInGame && !bIsInPIE)
{
// ゲーム中でも PIE 中でもないときは、何もしない
return;
}
UWorld* World = GetWorld();
if (!IsValid(World))
{
// World が無効なときは、何もしない
return;
}
UUserWidget* Widget = UE::GameMode::CreateWidget(World);
if (!IsValid(Widget))
{
// Widget が無効なときは、何もしない
return;
}
Widget->AddToViewport();
// ブループリントの変数を取得して、ゲーム画面の左上に表示する
UE::GameMode::PrintTextIntValue(Widget);
UE::GameMode::PrintTextFloatValue(Widget);
UE::GameMode::PrintTextStringValue(Widget);
UE::GameMode::PrintTextBoolValue(Widget);
UE::GameMode::PrintTextUserWidgetIntValue(Widget);
// 値をうまく取得できないときには、以下の関数を使ってみると良い
// ステップ実行してオブジェクトのパラメータを確認すると解決するヒントになる
//UE::GameMode::ShowAllProperty(Widget);
//ここまで
}
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class UMGVariableTest : ModuleRules
{
public UMGVariableTest(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "EnhancedInput" });
//以下を追加
PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd", "UMG" });
//ここまで
}
}
デフォルトの設定だと読み込むモジュールが足りないので、追加します。
AUMGVariableTestCharacter.cpp でエラーが出るかも知れません。
1>F:\UMGVariableTest\Source\UMGVariableTest\UMGVariableTestCharacter.cpp(66): error C2027: 認識できない型 'ULocalPlayer' が使われています。 1>F:\UE_5.1\Engine\Source\Runtime\Engine\Public\Subsystems\LocalPlayerSubsystem.h(9): note: 'ULocalPlayer' の宣言を確認してください 1>F:\UMGVariableTest\Source\UMGVariableTest\UMGVariableTestCharacter.cpp(66): error C2065: 'GetSubsystem': 定義されていない識別子です。 1>F:\UMGVariableTest\Source\UMGVariableTest\UMGVariableTestCharacter.cpp(66): error C2275: 'UEnhancedInputLocalPlayerSubsystem': 型の代わりに式が必要です 1>F:\UMGVariableTest\Source\UMGVariableTest\UMGVariableTestCharacter.cpp(66): error C2027: 認識できない型 'APlayerController' が使われています。 1>F:\UE_5.1\Engine\Plugins\EnhancedInput\Source\EnhancedInput\Public\EnhancedInputSubsystemInterface.h(14): note: 'APlayerController' の宣言を確認してください
出た場合は、以下のインクルードを追加します。
#include “Engine/LocalPlayer.h”
#include “GameFramework/PlayerController.h”
ビルドして UE5 エディタを起動し、PIE で確認します。
const FString WidgetClassPath = TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/WP_VariableTest1.WP_VariableTest1_C'");
FSoftClassPath WidgeClasstPath(WidgetClassPath);
return WidgeClasstPath.TryLoadClass<UUserWidget>();
WidgetClassPath に指定しているアセットリファレンスの末尾に _C を付ける必要があります。
UE5 エディタのコンテンツブラウザでアセットを右クリックすると、アセットリファレンスをクリップボードにコピーできますが、そちらには _C が付いていません。
付け忘れてロードに失敗しがちなので注意が必要です。
UUserWidget* Widget = UWidgetBlueprintLibrary::Create(World, WidgetClass, Player);
Player が nullptr でも生成できます。
template <class PropertyType, typename ValueType>
ValueType* GetWidgetVariableValuePtr(UUserWidget* InWidget, const TCHAR* const InVariableName)
{
UClass* WidgetClass = InWidget->GetClass();
if (!IsValid(WidgetClass))
{
return nullptr;
}
PropertyType* Property = FindFProperty<PropertyType>(WidgetClass, InVariableName);
if (Property)
{
ValueType* Value = Property->ContainerPtrToValuePtr<ValueType>(InWidget, 0);
return Value;
}
return nullptr;
}
ブループリントが持つ変数の値がポインタで返って来るので、このテンプレート関数を作っておくと使いまわせて便利です。
ポインタで返って来るので、代入すれば値の変更もできます。
UE4 では FindProperty や GetPropertyValue_InContainer が使えましたが、UE5 では非推奨になっています。
bool GetWidgetFloatValue(UUserWidget* InWidget, const TCHAR* const InVariableName, float& OutValue)
{
float* Value = UE::GameMode::GetWidgetVariableValuePtr<FFloatProperty, float>(InWidget, InVariableName);
if (Value)
{
OutValue = *Value;
return true;
}
double* Value2 = UE::GameMode::GetWidgetVariableValuePtr<FDoubleProperty, double>(InWidget, InVariableName);
if (Value2)
{
OutValue = static_cast<float>(*Value2);
return true;
}
return false;
}
ブループリントには float 型しかありませんが、内部では double で持っているときと、float で持っているときの2パターンあります。
どういうときに double になったり、float になるのかは不明です。
float で取得できなかったら、double で取得する汎用関数を作ることで対応しています。
bool GetWidgetIntValue(UUserWidget* InWidget, const TCHAR* const InVariableName, int32& OutValue)
{
int* Value = UE::GameMode::GetWidgetVariableValuePtr<FIntProperty, int>(InWidget, InVariableName);
if (Value)
{
OutValue = *Value;
return true;
}
int64* Value1 = UE::GameMode::GetWidgetVariableValuePtr<FInt64Property, int64>(InWidget, InVariableName);
if (Value1)
{
OutValue = static_cast<int32>(*Value1);
return true;
}
int16* Value2 = UE::GameMode::GetWidgetVariableValuePtr<FInt16Property, int16>(InWidget, InVariableName);
if (Value2)
{
OutValue = *Value2;
return true;
}
int8* Value3 = UE::GameMode::GetWidgetVariableValuePtr<FInt8Property, int8>(InWidget, InVariableName);
if (Value3)
{
OutValue = *Value3;
return true;
}
return false;
}
int 型の変数はブループリントだと Integer と Ingeter64 の2種類ありますが、内部でどう扱われるのか分からないので、どの型で扱われていても取得できるようにしています。
int128 は FInt128Property が存在しないので、対応する必要はありません。
void PrintTextUserWidgetIntValue(UUserWidget* InWidget)
{
const TCHAR* const VariableName = TEXT("VariableUserWidget");
if (TObjectPtr<UObject>* ObjectValue = UE::GameMode::GetWidgetVariableValuePtr<FObjectProperty, TObjectPtr<UObject>>(InWidget, VariableName))
{
if (UUserWidget* ChildWidget = CastChecked<UUserWidget>(ObjectValue->Get()))
{
const TCHAR* const WidgetVariableName = TEXT("NewVar");
int32 IntValue = 0;
if (UE::GameMode::GetWidgetIntValue(ChildWidget, WidgetVariableName, IntValue))
{
FString PrintString = FString::Printf(TEXT("%s %s=%d"), VariableName, WidgetVariableName, IntValue);
UKismetSystemLibrary::PrintString(GEngine->GetWorld(), PrintString, true, true, DISPLAY_COLOR, DISPLAY_SECONDS);
}
}
}
}
UObject しか対応していないので、UObject で取得します(UObject は UUserWidget の親クラスです)。
TObjectPtr<UObject>* ObjectValue = UE::GameMode::GetWidgetVariableValuePtr<FObjectProperty, TObjectPtr<UObject>>(InWidget, VariableName)
返って来るのは TObjectPtr<UObject>* です。
TObjectPtr は、ほぼ生ポインタと同じものですが、生ポインタでは取得できません。
UUserWidget* ChildWidget = CastChecked<UUserWidget>(ObjectValue->Get())
取得できたら、UUserWidget にダウンキャストします。
この ChildWidget が WB_VariableTest2 の情報を持っています。
あとは WB_VariableTest1 と同様に変数の値を取得しています。








