このチュートリアルでは、カスタム プロパティ タイプ (構造体) および Actor クラスの [Details (詳細)] パネルのカスタマイズ の作成方法を説明します。
必要な設定
このチュートリアルを開始する前に、ランタイム モジュールと対応するエディタ モジュールを設定してください。このチュートリアルでは、CustomGameplay モジュールと CustomGameplayEditor モジュールを使用します。どちらも エディタ モジュール チュートリアルで定義されています。フォルダ構造は、次のようになります。
- プロジェクトのソース フォルダ
- CustomGameplay
- CustomGameplay.Build.cs
- Target.cs
- Editor.Target.cs
- Private
- Public
- CustomGameplayEditor
- CustomGameplayEditor.Build.cs
- Private
- CustomGameplayEditorModule.cpp
- Public
- CustomGameplayEditorModule.h
- CustomGameplay
プロパティ タイプの詳細をカスタマイズする
構造体の [Details] パネルをカスタマイズするには、以下の手順に従います。
-
CustomGameplay モジュールの「
Public」フォルダに新しいヘッダ (.h) ファイルを作成し、「CustomDataProperty.h」という名前を付けます。 -
次の C++ コードを「
CustomDataProperty.h」に追加します。CustomDataProperty.h
#pragma once #include "CoreMinimal.h" #include "CustomDataType.generated.h" USTRUCT(BlueprintType, Category="Custom Data") struct CUSTOMGAMEPLAY_API FCustomDataProperty { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Custom Data") TSoftObjectPtr<UTexture> CustomTexture; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom Data") FName CustomName; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom Data") FString CustomString; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Custom Data") int32 CustomInt; };FCustomDataPropertyがBlueprintType指定子を含み、そのすべてのプロパティにEditAnywhere指定子を含むUPROPERTYマクロがあることを確認してください。これは、ブループリント クラスでFCustomDataPropertyにアクセスできるようにし、構造体のプロパティを表示するために必要です。 -
CustomGameplayEditor モジュールの「
Public」フォルダに新しいヘッダ (.h) ファイルを作成し、「CustomDataPropertyCustomization.h」という名前を付けます。 -
次の C++ コードを「
CustomDataPropertyCustomization.h」に追加します。CustomDataPropertyCustomization.h
#pragma once #include "CoreMinimal.h" #include "IPropertyTypeCustomization.h" class IPropertyHandle; //IPropertyHandle インターフェースの前方宣言 class FCustomDataPropertyCustomization : public IPropertyTypeCustomization { public: // IPropertyTypeCustomization から継承 virtual void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; virtual void CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; //プロパティのカスタマイズのインスタンスを作成するユーティリティ関数。 static TSharedRef<IPropertyTypeCustomization> MakeInstance(); }; -
CustomGameplayEditorModule の「
Private」フォルダに新しいクラス本体 (.cpp) ファイルを作成し、「CustomDataPropertyCustomization.cpp」という名前を付けます。 -
次の C++ コードを「
CustomDataPropertyCustomization.cpp」に追加します。CustomDataPropertyCustomization.cpp
#include "CustomDataDetailsCustomization.h" #include "Widgets/SWidget.h" #include "DetailLayoutBuilder.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" #include "IDetailChildrenBuilder.h" #include "Widgets/Input/SButton.h" #include "Widgets/Text/STextBlock.h" #include "HAL/PlatformApplicationMisc.h" #include "IPropertyUtilities.h" void FCustomDataDetailsCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) { } void FCustomDataDetailsCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) { //続行する前に、アクセスされるプロパティが有効であることを確認してください。 if (!PropertyHandle->IsValidHandle()) { return; } //「Hello World!」というテキストを表示するカスタム行を追加します。 ChildBuilder.AddCustomRow( FText::FromString("HelloWorldTest")) .NameContent() [ SNew(STextBlock) .Text(FText::FromString(TEXT("Hello, World!"))) .Font(IDetailLayoutBuilder::GetDetailFont()) ]; //構造体の子プロパティをループし、デフォルトのプロパティの詳細を追加できる for ループを設定します。 uint32 NumChildren = 0; PropertyHandle->GetNumChildren(NumChildren); //構造体の元の子プロパティを表示します。 for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { ChildBuilder.AddProperty(PropertyHandle->GetChildHandle(ChildIndex).ToSharedRef()); } } //詳細カスタマイズのスタティック インスタンスを作成します。 TSharedRef<IPropertyTypeCustomization> FCustomDataDetailsCustomization::MakeInstance() { return MakeShareable(new FCustomDataDetailsCustomization); }上記のコードは以下を実行します。
-
include ディレクティブは、詳細のカスタマイズに必要な以下を含むクラスへのアクセスを追加します。
-
ウィジェット (SButton、STextBlock) に関連するクラス。
-
詳細ウィジェットをモデリングするためのクラス (DetailLayoutBuilder、DetailWidgetRow、DetailCategoryBuilder、IDetailChildrenBuilder)。
-
[Details] パネルのカスタマイズを定義するためのクラス (IDetailChildrenBuilder、IPropertyUtilities)
-
-
IPropertyTypeCustomizationの実装要件を満たすためにFCustomDataDetailsCustomization::CustomizeHeaderを実装する必要がありますが、この例ではそれ以外は使用されていません。 -
FCustomDataDetailsCustomization::CustomizeChildrenは、詳細カスタマイズの本体を表示するためのロジックが格納されています。-
IDetailChildrenBuilder::AddCustomRowは新しいスレートの詳細ウィジェット行を定義します。その中で他のスレート ウィジェットを宣言できます。 -
その後、for ループで元の構造体の子要素ごとにデフォルトの表示を設定します。
-
-
FCustomDataDetailsCustomization::MakeInstanceでは、詳細カスタマイズのインスタンスを作成し、そのインスタンスをTSharedRefで返します。これは、次のステップで [Details] パネルにプロパティを表示するために使用されます。
-
-
CustomGameplayEditor モジュールの実装ファイル (
CustomGameplayEditor.cpp) を開きます。次のコードをOnModuleStartupに追加します。CustomGameplayEditor.cpp
#include "CustomGameplayEditorModule.h" #include "CustomDataDetailsCustomization.h" #include "PropertyEditorDelegates.h" #include "PropertyEditorModule.h" IMPLEMENT_GAME_MODULE(FCustomGameplayEditorModule, CustomGameplayEditor); //StartupModule 関数で詳細のカスタマイズを登録します。 void FCustomGameplayEditorModule::StartupModule() { //プロパティ モジュールへの参照を取得します。 FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"); /* カスタム プロパティ タイプのレイアウトを登録します。これにはプロパティ タイプの名前が必要です。文字列 (「CustomData」) として手動で提供することも、StaticStruct から FName を取得することもできます。 また、詳細カスタマイズのインスタンスを作成する関数にデリゲートを提供する必要があります。この場合、先程作成した MakeInstance 関数を使用します。 */ PropertyModule.RegisterCustomPropertyTypeLayout(FCustomDataProperty::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FCustomDataDetailsCustomization::MakeInstance)); } //ShutdownModule 関数で詳細カスタマイズを削除します。 void FCustomGameplayEditorModule::ShutdownModule() { PropertyModule.UnregisterCustomPropertyTypeLayout(FCustomDataProperty::StaticStruct()->GetFName()); }上記のコードは以下を実行します。
PropertyEditorDelegates.hおよびPropertyEditorModule.hの include ディレクティブで、プロパティ タイプのカスタマイズの登録に必要なクラスを追加します。CustomDataPropertyCustomization.hの include ディレクティブで、前のステップで作成したプロパティ タイプのカスタマイズを追加します。-
FCustomGameplayEditorModule::StartupModuleのコードは、Unreal Editor がモジュールをロードするときに以下を実行します。- プロパティ エディタ モジュールのインスタンスをロードまたは取得します。
-
FPropertyEditorModule::RegisterCustomPropertyTypeLayoutを使用して、作成したプロパティ タイプのカスタマイズを使用するようにエディタに指示します。- これには、構造体の名前 (「CustomDataType」) と、エディタがこの名前を表示する必要があるときにプロパティ タイプのカスタマイズのインスタンスを作成するメソッド (
FCustomDataPropertyCustomization::MakeInstance) が含まれます。
- これには、構造体の名前 (「CustomDataType」) と、エディタがこの名前を表示する必要があるときにプロパティ タイプのカスタマイズのインスタンスを作成するメソッド (
クラスの詳細をカスタマイズする
-
アクタ (
UActor) を親クラスとして新しい C++ クラスを作成します。このクラスに「CustomActor」という名前を付けます。 -
「
CustomActor.h」を開き、次のメンバーを追加します。CustomActor.h
UPROPERTY(EditAnywhere) TSoftObjectPtr<UStaticMesh> CustomMesh; UPROPERTY(EditAnywhere) float CustomFloat; UPROPERTY(EditAnywhere) bool CustomBool; UPROPERTY(EditAnywhere) FCustomData CustomData;[Details] パネルのカスタマイズのためのコードでカテゴリを指定するため、これらのどのプロパティにもカテゴリは含まれていません。
-
CustomGameplayEditor モジュールの「
Public」フォルダに新しいヘッダ (.h) ファイルを作成し、「CustomActorClassCustomization.h」という名前を付けます。 -
次の C++ コードを「
CustomActorClassCustomization.h」に追加します。CustomActorClassCustomization.h
#pragma once #include "CoreMinimal.h" #include "IDetailCustomization.h" //IPropertyHandle の前方宣言。 class IPropertyHandle; //カスタム クラスの詳細のカスタマイズ class FCustomClassDetailsCustomization : public IDetailCustomization { public: //[Details] パネルをカスタマイズする関数。 virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; //[Details] パネルのカスタマイズのスタティック インスタンスを返します。 static TSharedRef<IDetailCustomization> MakeInstance(); };
プロパティ タイプのカスタマイズ のように、このクラスには後でエディタ モジュールに登録する FCustomClassDetailsCustomization::MakeInstance 関数が含まれます。
-
CustomGameplayEditor モジュールの「
Private」フォルダに新しい本体 (.cpp) ファイルを作成し、「CustomActorClassCustomization.cpp」という名前を付けます。 -
次の C++ コードを「
CustomActorClassCustomization.cpp」に追加します。CustomActorClassCustomization.cpp
#include "Widgets/SWidget.h" #include "DetailLayoutBuilder.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" #include "IDetailChildrenBuilder.h" #include "Widgets/Input/SButton.h" #include "Widgets/Text/STextBlock.h" #include "HAL/PlatformApplicationMisc.h" #include "IPropertyUtilities.h" #include "CustomActor.h" #include "CustomClassDetailsCustomization.h" void FCustomClassDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { IDetailCategoryBuilder& CustomCategory = DetailBuilder.EditCategory("CustomSettings"); //CustomData と CustomBool をリストの先頭に置き、プロパティがヘッダに記載された順序ではなく、このコードの順序で追加されることを示します。 CustomCategory.AddProperty(GET_MEMBER_NAME_CHECKED(ACustomActor, CustomData)); CustomCategory.AddProperty(GET_MEMBER_NAME_CHECKED(ACustomActor, CustomBool)); CustomCategory.AddProperty(GET_MEMBER_NAME_CHECKED(ACustomActor, CustomMesh)); CustomCategory.AddProperty(GET_MEMBER_NAME_CHECKED(ACustomActor, CustomFloat)); } //モジュール起動時に登録するために必要なこの詳細のカスタマイズのスタティック インスタンスを作成します。 TSharedRef<IDetailCustomization> FCustomClassDetailsCustomization::MakeInstance() { return MakeShareable(new FCustomClassDetailsCustomization); }クラスの [Details] パネルのカスタマイズにフィールドを追加する最も簡単な方法は、詳細カテゴリを定義し、それにフィールドを追加することです。上記のコードでは、
CustomSettingsカテゴリを追加し、IDetailCategoryBuilder::AddProperty関数を使用して各プロパティの詳細のデフォルトの実装を追加しています。これは、これらのプロパティのUPROPERTY指定子で定義したカテゴリをオーバーライドします。 -
CustomGameplayEditor モジュールの実装ファイル (
CustomGameplayEditor.cpp) を開きます。StartupModule関数のPropertyModule.RegisterCustomClassLayout呼び出しとShutdownModule関数のPropertyModule.UnregisterCustomClassLayout呼び出しを追加します。コードは次のようになります。CustomGameplayEditorModule.cpp
IMPLEMENT_GAME_MODULE(FCustomGameplayEditorModule, CustomGameplayEditor); void FCustomGameplayEditorModule::StartupModule() { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"); PropertyModule.RegisterCustomPropertyTypeLayout(FCustomDataProperty::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FCustomDataDetailsCustomization::MakeInstance)); PropertyModule.RegisterCustomClassLayout(ACustomActor::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FCustomClassDetailsCustomization::MakeInstance)); } void FCustomGameplayEditorModule::ShutdownModule() { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"); PropertyModule.UnregisterCustomPropertyTypeLayout(FCustomDataProperty::StaticStruct()->GetFName()); PropertyModule.UnregisterCustomClassLayout(ACustomActor::StaticClass()->GetFName()); } -
コードをコンパイルし、プロジェクトを開きます。CustomActor のインスタンスをマップに配置し、[Details] パネルを確認します。パネルは、次のように表示されます。
想定どおり、カスタムのデータ型が最初に表示され、「Hello, World!」という文字列と構造体の各メンバー、カスタムのブール値、その他のプロパティの順に表示されます。
参考文献
次のページでは、カスタム [Details] パネルを作成する際の一般的な操作について説明します。