見出し画像

UE5 独自UIプラグインを作成する 04~プロパティを更新するUIを作成する~

【今回のポジティブワード】
やれば分かる‼
やらなければ一生わからん‼
~吼えろペン 炎尾燃~

昨日は折り畳みのUIを作ろうとエンジンのソースコードを読んでいたのですが、ついにVerticalBoxのスロットの追加の底に行きついてしまいました。
VerticalBoxは底に行きついたけど、まだ下にはUIがある。
なんでだろう?
どうやらデータクラスを作成して、データクラスの変数からUIを作成することが出来るようです。
Detailパネルの仕組みが少し分かりました。
プラグインを作り始めなければ一生分からず使ってました。
やれば分かる!!

前回は「入力した2次元座標でViewportのFloorを動かす」方法について調査しました。
今回は「プロパティを更新するUIを作成する」方法について調査していきます。

プロパティを更新するUIを作成する

目標とする動作

FloorのプロパティをUIで変更できるようにします。
Rotation、StaticMesh、Materialを変更してみます。

今回学習できること

  • 詳細ウィンドウを少し再現

  • 回転を変更できる

  • マテリアルとスタティックメッシュを置き換える

新しくプラグインを作成する

ソースコードが多くなってきたので、別のプラグインとして作成します。
Editor Standalone Windowテンプレートを選択して、「CustomDetailsExample」に設定します。

Editor Standalone Windowテンプレートを選択 >「CustomDetailsExample」に設定 > Create Plugin

プロパティ表示をするためのデータクラスを作成する

publicフォルダにCustomDetailsSettings.hを作成します。

publicフォルダにCustomDetailsSettings.hを作成する

CustomDetailsSettings.h
StaticMeshActorのプロパティはもっと多いですが、今回プロパティを更新するためのプロパティのみ定義します。
関数は持たないのでヘッダーファイルだけに処理を書きます。

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "CustomDetailsSettings.generated.h"
/*
*
*/
UCLASS(BlueprintType, config = EditorPerProjectUserSettings)
class UCustomDetailsSettings : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY(EditAnywhere, category = "Transform")
	FRotator Rotation;

	UPROPERTY(EditAnywhere, category = "Static Mesh")
	UStaticMesh* StaticMesh;

	UPROPERTY(EditAnywhere, category = "Materials")
	UMaterial* Element0;
};

プロパティ表示をするためのWidgetクラスを作成します

プロパティ表示するためのWidgetクラスを作成します。
Publicフォルダ:CustomDetailsSettingsWidget.h
Privateフォルダ:CustomDetailsSettingsWidget.cpp

CustomDetailsSettingsWidgetクラスを作成する

CustomDetailsSettingsWidget.h

#pragma once

#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "CustomDetailsSettings.h"

class SCustomDetailsSettingsWidget : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SCustomDetailsSettingsWidget) {}
	SLATE_END_ARGS()

	/** コンストラクタ */
	void Construct(const FArguments& Args);

	// プロパティ
	UCustomDetailsSettings* CustomSettings;
private:

	TSharedPtr<class IDetailsView> PropertyWidget;
};

CustomDetailsSettingsWidget.cpp

データクラスの変数を元にUIを作成する処理を実装します。

#include "CustomDetailsSettingsWidget.h"


void SCustomDetailsSettingsWidget::Construct(const FArguments& Args)
{
	//Create our Details Panel object
	CustomSettings = NewObject<UCustomDetailsSettings>();

	//Load our Property Module
	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");

	//FDetailsViewArgs is a struct of settings to customize our Details View Widget
	FDetailsViewArgs ViewArgs;
	ViewArgs.bHideSelectionTip = true;

	//Create the widget and store it in the PropertyWidget pointer
	PropertyWidget = PropertyModule.CreateDetailView(ViewArgs);

	//Important! We set our details view to use our Custom Settings object
	PropertyWidget->SetObject(CustomSettings);
	// Create a property Detail View
	ChildSlot
	[
		SNew(SBorder)
		.BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
		[
			SNew(SOverlay)

			// Overlay slot for the main HLOD window area
			+ SOverlay::Slot()
			[
				PropertyWidget.ToSharedRef()
			]
		]
	];
}

UIを表示する処理を実装する

CustomDetailsExampleクラスにUIを表示する処理を実装します。

CustomDetailsExampleクラスにUIを表示する処理を実装

CustomDetailsExample.h
SCustomDetailsSettingsWidgetを参照する変数を追加します。

private:
	// Add Varable
	TSharedPtr<SCustomDetailsSettingsWidget> CustomDetailsSettingsWidget;

CustomDetailsExample.cpp OnSpawnPluginTab関数
UIを表示する処理を実装します。

TSharedRef<SDockTab> FCustomDetailsExampleModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
	return SNew(SDockTab)
		.TabRole(ETabRole::NomadTab)
		[
			// Put your tab content here!
			SNew(SBox)
			[
				SNew(SVerticalBox)
				+ SVerticalBox::Slot()
				.AutoHeight()
				[
					SNew(SHorizontalBox)
					+ SHorizontalBox::Slot()
					.Padding(10.0f, 3.0f, 10.0f, 3.0f)
					.AutoWidth()
					[
						SNew(SButton)
						.Text(LOCTEXT("PropertyUpdate", "PropertyUpdate"))
					]
				]
				+ SVerticalBox::Slot()
				.Padding(0.0f, 12.0f, 0.0f, 3.0f)
				.FillHeight(1.0f)
				[
					SAssignNew(CustomDetailsSettingsWidget, SCustomDetailsSettingsWidget)
				]
			]
		];
}

CustomDetailsSettingsデータクラスを表示することが出来ました。

CustomDetailsSettingsデータクラスを表示することが出来た

ボタンをクリックしたときにFloorのプロパティを更新する処理を実装する

CustomDetailsExampleクラスにボタンをクリックした時の処理を実装します。

CustomDetailsExampleクラスにボタンをクリックした時の処理を実装

CustomDetailsExample.h
ボタンをクリックした時の関数を追加します。

public:
	// PropertyUpdateButtonをクリックした時の処理
	FReply PropertyUpdateButtonClicked();

CustomDetailsExample.cpp  PropertyUpdateButtonClicked関数
ボタンをクリックした時の処理を実装します。
FloorはStaticMeshComponentを変更するということを意識すると変更できます。

// includeを追加します
#include "EngineUtils.h"
#include "Runtime/Engine/Classes/Engine/StaticMeshActor.h"

FReply FCustomDetailsExampleModule::PropertyUpdateButtonClicked()
{
	UWorld* world = GEditor->GetEditorWorldContext().World();
	for (TActorIterator<AStaticMeshActor> actorItr(world); actorItr; ++actorItr)
	{
		FString staticMeshName = actorItr->GetActorLabel();
		if (staticMeshName.Equals(TEXT("Floor")))
		{
			// Rotationを更新する
			actorItr->SetActorRotation(CustomDetailsSettingsWidget->CustomSettings->Rotation);

			// StaticMeshComponentのStaticMeshを更新する
			actorItr->GetStaticMeshComponent()->SetStaticMesh(CustomDetailsSettingsWidget->CustomSettings->StaticMesh);

			// StaticMeshComponentのMaterialを更新する
			actorItr->GetStaticMeshComponent()->SetMaterial(0, CustomDetailsSettingsWidget->CustomSettings->Element0);
		}
	}
	return FReply::Handled();
}

CustomDetailsExample.cpp  OnSpawnPluginTab関数
ボタンにクリック関数を設定します。

SNew(SButton)
.Text(LOCTEXT("PropertyUpdate", "PropertyUpdate"))
.OnClicked_Raw(this, &FCustomDetailsExampleModule::PropertyUpdateButtonClicked)

プラグインをコンパイルしてPropertyUpdateボタンをクリックします。
FloorのRotation,StaticMesh,Materialが更新されます。
UIを1つ1つ部品を追加するより、データクラスを作って読み込ませた方が簡単にUIを作成することが出来ました。

PropertyUpdateボタンをクリック > FloorのRotation,StaticMesh,Materialが更新

まとめ

普段何気なく使用している詳細パネルですが、「こうやって作っていたのか」ということが知れたので面白かったです。
OnSpawnPluginTab関数でUIを作成するサンプルがメジャーではないので貴重な情報が公開されていて助かりました。ありがとうございます。

作りたいプラグインのアイディアが少しずつ出てきたので、目標設定して役に立つプラグインを提供できるように頑張ります!

【ソースコード】

note記事ではコードを全部貼ることも出来ないので、GitHubにリポジトリを用意しました。

【参照URL】


この記事が気に入ったらサポートをしてみませんか?