特定のコンポーネントの変数及びUProperty系の変数を取得する方法

デバッグ用として自作のコンポーネントの変数などをJsonとして外部ファイルに書き出す機能のフィジビリティ検証を行っております。

現在、テスト用として以下のようなUTestComponentを作っています。

`UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class TEST_API UTestComponent : public UActorComponent
{
GENERATED_BODY()

public:
// Sets default values for this component’s properties
UTestComponent();

protected:
// Called when the game starts
virtual void BeginPlay() override;

public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

public:
UPROPERTY(EditInstanceOnly)
bool IsTestComponent = true;

int TestNum = 0;

FString TestStr = “Test”;

};`<br/>

このフィジビリティ検証の最終目的はこのUTestComponentがついているActorをレベルからすべて取得し、Actor名とUTestComponentのUPROPERTYの変数の値をJsonに変換後、ファイルに書き込みます。(可能であれば、UPROPERTY以外のメンバー変数も書き出したい)

そのため以下のようなUPROPERTYの値を取得する、関数を作っています。(現在、boolのUpropertyをbool型に変換しています。)

<br/>

`void MyTestClass::CreateJsonObject(UTestComponent* simComponennt)
{

UClass* _Class = simComponennt->GetClass();
for (TFieldIterator PropIt(_Class); PropIt; ++PropIt)
{
FProperty* _Property = PropIt;
FString PropertyName = PropIt->GetName();
void
CurrentPropAddr = PropIt->ContainerPtrToValuePtr(simComponennt);
UE_LOG(LogTemp, Log, TEXT(“%s”), *PropertyName);

if (FBoolProperty* BoolProp = CastField(_Property))
{
bool BoolValue = BoolProp->GetPropertyValue(CurrentPropAddr);
FString PropertyValue = BoolValue ? TEXT(“true”) : TEXT(“false”);
UE_LOG(LogTemp, Log, TEXT(“Boolean variable %s : %s”), *PropertyName, *PropertyValue);

}
}
}`<br/>

上記のコードを動かすと、UTestComponentの親クラスのUActorComponentの変数なども取得が出来てしまします。

実際きデバッグで確認したい変数はUTestComponentの変数のみなので、UActorComponentの変数は除外したいです。

上記の要求を満たす、実装やAPIなどありますでしょうか?

<br/>

よろしくお願いいたします

お世話になっております。

stringify.hにJSON文字列化用の関数があります。これは通常のリフレクションで取れるプロパティに加えて、ストリームシリアライズされたデータも含めて出力します。

ストリームシリアライズされたデータは扱いづらいこともありますが、UClassやULevelのような型を正確に把握するには重要です。

Stringify関数では標準のデルタエンコードも行っているので、オブジェクトの状態を簡潔に把握しやすくなっています。また、TFieldIteratorを使う場合はEFieldIteratorFlags::ExcludeSuperを渡すことで親クラスのプロパティを除外できます。

お手数ですが、

よろしくお願いします。

[mention removed]

ご回答ありがとうございます。

TFiledIteratorを使うより、stringify.hの方が便利でしょうか?

もしそうであれば、stringifyを使った変換の検討をしたいと思います。

(もし参考になるurlなどありましたら、教えて頂けると幸いです。)

また教えていただいた情報に加えて、次に独自で定義したUStructに対してUFieldを用いて変数名の取得の関数は見つけましたが変数の値を取得する関数などが分からなかったです。(CreateJsonObject関数の34行目)

ここも含めて教えていただけると幸いです。もしこの辺りもstringify.hを使った方が良いのであれば、教えていただけると幸いです。

よろしくお願いいたします

独自で定義した構造体↓

`USTRUCT(BlueprintType)
struct FTestProperty
{

GENERATED_BODY()

UPROPERTY(EditAnywhere)
int32 SomeInt;

UPROPERTY(EditAnywhere)
FString SomeString;

};`

`void MyTestClass::CreateJsonObject(UTestComponent* simComponennt)
{
UClass* _Class = simComponennt->GetClass();
for (TFieldIterator PropIt(_Class, EFieldIteratorFlags::ExcludeSuper); PropIt; ++PropIt)
{
FProperty* _Property = PropIt;
FString PropertyName = PropIt->GetName();
void
CurrentPropAddr = PropIt->ContainerPtrToValuePtr(simComponennt);
UE_LOG(LogTemp, Log, TEXT(“%s”), *PropertyName);

if (FBoolProperty* BoolProp = CastField(_Property))
{
bool BoolValue = BoolProp->GetPropertyValue(CurrentPropAddr);
FString PropertyValue = BoolValue ? TEXT(“true”) : TEXT(“false”);
UE_LOG(LogTemp, Log, TEXT(“Boolean variable %s : %s”), *PropertyName, *PropertyValue);

}

// 自作の構造体のプロパティを取得
else if (FStructProperty* StructProp = CastField(_Property))
{
if (StructProp->Struct == FTestProperty::StaticStruct())
{
FTestProperty* TestPropertyValue = StructProp->ContainerPtrToValuePtr(simComponennt);
UE_LOG(LogTemp, Log, TEXT(“FTestProperty: SomeInt: %d, SomeString: %s”), TestPropertyValue->SomeInt, *TestPropertyValue->SomeString);

UField* Field = StructProp->Struct->Children;
while (Field != nullptr)
{
// 各メンバー変数の名前を取得
FString MemberName = Field->GetName();

//TODO : FieldやUpropertyを用いて値を取得したい

Field = Field->Next;
}
}
}
}
}`

お世話になっております。

UFieldを利用して関数の名前を取ることができても、関数の価値を取得できていないということですね。

Struct全体てきにの話ではなく、フィールドだけであれば、stringifyのExportText_Directがピッタリかと思います。

お手数ですが、よろしくお願いします。

お世話になっております。

軽く確認したところ、今の使い方が特に問題ないと思います!!

引き続きよろしくお願いします。

ご確認ありがとうございます!

こちらクローズにして頂けると幸いです

回答ありがとうございます。

ExportText_Directのテストコードを書いてみました。

とても便利だと思いますが、例えば構造体のメンバー変数が追加になった場合、修正が必要ということに気がつきました。

これだと、修正漏れが発生する可能性があります。

`void MyTestClass::LogTestPropertyValues(FTestProperty& TestProperty)

{

// メンバー変数の値を文字列化するための変数

FString IntValueString;

FString StringValueString;

FString FloatValueString;

FString DoubleValueString;

// SomeIntの値を文字列化

FIntProperty* IntProperty = FindFProperty(FTestProperty::StaticStruct(), TEXT(“SomeInt”));

if (IntProperty)

{

IntProperty->ExportText_Direct(IntValueString, &TestProperty.SomeInt, nullptr, nullptr, PPF_None);

UE_LOG(LogTemp, Log, TEXT(“SomeInt: %s”), *IntValueString);

}

// SomeStringの値を文字列化

FStrProperty* StrProperty = FindFProperty(FTestProperty::StaticStruct(), TEXT(“SomeString”));

if (StrProperty)

{

StrProperty->ExportText_Direct(StringValueString, &TestProperty.SomeString, nullptr, nullptr, PPF_None);

UE_LOG(LogTemp, Log, TEXT(“SomeString: %s”), *StringValueString);

}

// SomeFloatの値を文字列化

FFloatProperty* FloatProperty = FindFProperty(FTestProperty::StaticStruct(), TEXT(“SomeFloat”));

if (StrProperty)

{

FloatProperty->ExportText_Direct(FloatValueString, &TestProperty.SomeFloat, nullptr, nullptr, PPF_None);

UE_LOG(LogTemp, Log, TEXT(“SomeFloat: %s”), *FloatValueString);

}

// SomeDoubleの値を文字列化

FDoubleProperty* DoubleProperty = FindFProperty(FTestProperty::StaticStruct(), TEXT(“SomeDouble”));

if (StrProperty)

{

DoubleProperty->ExportText_Direct(DoubleValueString, &TestProperty.SomeDouble, nullptr, nullptr, PPF_None);

UE_LOG(LogTemp, Log, TEXT(“SomeDouble: %s”), *DoubleValueString);

}

}`上記の問題も考え、以下のようなコードが自分のプロジェクトだとベストだということが分かりました。

以下のコードはFTestProperty内のすべてのPropertyを取得し適した型への変換を行うコードになっています。

以下のコードを使った場合、FTestPropertyにintやfloatの変数を追加しても変換コードに修正がいりません。

(対応していない型変換(例えばFVectorなど)の追加実装は必要ですが。。。)

一応、解決ではありますが、もし他のbest practiceがあれば、ご教授お願い致します。

`else if (FStructProperty* StructProp = CastField(_Property))
{
FTestProperty* TestPropertyValue = StructProp->ContainerPtrToValuePtr(simComponennt);

if (StructProp->Struct == FTestProperty::StaticStruct())
{
auto staticStruct = FTestProperty::StaticStruct();
auto currentProperty = staticStruct->PropertyLink;

while (currentProperty != nullptr)
{
FString propertyName = currentProperty->GetName();
void* propertyAddr = currentProperty->ContainerPtrToValuePtr(TestPropertyValue);

if (FIntProperty* IntProp = CastField(currentProperty))
{
int value = IntProp->GetPropertyValue(propertyAddr);
UE_LOG(LogTemp, Log, TEXT(“Int variable %s : %i”), propertyName, value);
}
else if (FFloatProperty
FloatProp = CastField(currentProperty))
{
float value = FloatProp->GetPropertyValue(propertyAddr);
UE_LOG(LogTemp, Log, TEXT(“Float variable %s : %f”), propertyName, value);
}
else if (FDoubleProperty
DoubleProp = CastField(currentProperty))
{
double value = DoubleProp->GetPropertyValue(propertyAddr);
UE_LOG(LogTemp, Log, TEXT(“Double variable %s : %f”), propertyName, value);
}
else if (FStrProperty
StrProp = CastField(currentProperty))
{
FString value = StrProp->GetPropertyValue(propertyAddr);
UE_LOG(LogTemp, Log, TEXT(“String variable %s : %s”), *propertyName, *value);
}
else
{

}
currentProperty = currentProperty->PropertyLinkNext;
}
}
}`