UE5 EditorValidatorBaseクラスの5.4での変更メモ

# 概要
UnrealEngine の EditorValidatorBaseクラスのメソッドが UE5.4から変わったためそれに対するメモ書きです。


## 更新履歴
| 日付 | 内容 |
|:----|:-----|
|2025/02/05| 初版 |
|2025/03/31| Subsystemへの登録について追記 |


## 参考
以下を参考にさせて頂きました、ありがとうございます。
[UE公式：データ検証](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/data-validation-in-unreal-engine)
[UE公式：ゲームプレイモジュールを作成する](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/how-to-make-a-gameplay-module-in-unreal-engine?application_version=5.3)


### 関連過去記事
[UE4 プログラミングサブシステムを試してみる](https://qiita.com/unknown_ds/items/afcff802ab17db486822)


## 環境
Windows10
Visual Studio 2022
UnrealEngine 5.5.1

## 関連ソース
"Engine\Plugins\Editor\DataValidation\Source\DataValidation\Public\EditorValidatorBase.h"
"Engine\Plugins\Editor\DataValidation\Source\DataValidation\Public\EditorValidatorSubsystem.h"


# EditorValidatorBaseクラスについて
`EditorValidatorSubsystem`でのデータ検証を行う基本クラスです。これをC++かBPで継承してデータ検証用のコードを書きます。


## UE5.4からの変更点
C++にてデータ検証コードを書く場合、`CanValidateAsset_Implementation` と `ValidateLoadedAsset_Implementation` を継承してコードを書きますが、このメソッドの引数が変更になっています。


```C++:EditorValidatorBase.h
	// これをオーバーライドして、このコンテキストでこのバリデータを使用できるかどうかを決定します。
	// 何らかの問題により検証を実行できない場合は、コンテキストを使用してエラーを追加できます。
	virtual bool CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const

	// これをオーバーライドして、FDataValidationContext にアクセスして C++ で検証します。
	virtual EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context)


	// ＃＃＃非推奨になったコード＃＃＃
	UE_DEPRECATED("5.4", "CanValidate_Implementation(const EDataValidationUsecase InUsecase) is deprecated, override CanValidateAsset_Implementation(UObject* InObject, FDataValidationContext& InContext) instead")
	virtual bool CanValidate_Implementation(const EDataValidationUsecase InUsecase) const 
	{
		return false; 
	}
	UE_DEPRECATED("5.4", "CanValidateAsset_Implementation(UObject* InAsset) is deprecated, override CanValidateAsset_Implementation(UObject* InObject, FDataValidationContext& InContext) instead")
	virtual bool CanValidateAsset_Implementation(UObject* InAsset) const 
	{
		 return true; 
	}
	UE_DEPRECATED("5.4", "ValidateLoadedAsset_Implementation(UObject* InAsset, TArray<FText>& ValidationErrors) is deprecated, override ValidateLoadedAsset_Implementation(UObject* InAsset, FDataValidationContext& Context) instead")
	virtual EDataValidationResult ValidateLoadedAsset_Implementation(UObject* InAsset, TArray<FText>& ValidationErrors) 
	{
		return EDataValidationResult::NotValidated;
	}
```

上記の変更点を受けてUE5.4からは少し書き方が変わります。
以下にサンプルコード、単純にBPのアクターかどうかを調べるだけです。

```C++:MyEditorValidator.h
UCLASS()
class VALIDATORTEST_API UMyEditorValidatorBase : public UEditorValidatorBase
{
	GENERATED_BODY()

protected:
	virtual bool CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const override;
	virtual EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context) override;

};
```


```C++:MyEditorValidator.cpp
bool UMyEditorValidatorBase::CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const
{
	// チェック対象かどうかの条件を書く
    
	if (InObject) {
		if (!InObject->IsA(UBlueprint::StaticClass())) return(false);
	}

	return(true);
}

EDataValidationResult UMyEditorValidatorBase::ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context)
{
	// チェック内容を書く

	UBlueprint* _Blueprint = Cast<UBlueprint>(InAsset);
	auto _BPClass = Cast<UBlueprintGeneratedClass>(_Blueprint->GeneratedClass);
	if (::IsValid(_BPClass)) {
		auto _DefaultObject = Cast<AActor>(_BPClass->GetDefaultObject());
		if (_DefaultObject) {
			if (!_DefaultObject->ActorHasTag(FName("TestActor"))) {
				AssetFails(InAsset, FText::FromString(TEXT("エラー出力：タグが違う")));
			}
		}
	}else
	{
		AssetFails(InAsset, FText::FromString(TEXT("エラー出力：BPではない"))  );
	}

	// チェック結果
	if (GetValidationResult() != EDataValidationResult::Invalid)
	{
		AssetPasses(InAsset);
	}

	return(GetValidationResult());
}
```


# データ検証するための補足
`EditorValidator`クラスだけではデータ検証はできません。いくつか必要な設定がありますが詳しくは他の記事を参考にしてもらうとして、ここでは簡単に羅列します。

### モジュールの追加
C++ で扱うにはモジュール `UnrealEd`の追加が必要です。

```cs:Build.cs
	PrivateDependencyModuleNames.AddRange(new string[] {
		"UnrealEd",
	}
```

このモジュールはエディタ用なので、サンプルプロジェクトではない実際のプロジェクトだとエディタ専用モジュールを切り分ける必要があると思います。


### エディタユーティリティブループリント
エディタ用モジュールに切り分けた場合、自作したC++の`EditorValidator`クラスだけではうまく動いてくれません。（分けない場合はC++のみでも動作します）
その場合はエディタユーティリティブループリントで継承しておくとUEエディタ起動時に認識します。

![EditorUtiltyBP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/f0242a3c-b5fe-f7f6-cb64-bb71058addbd.png)


:::note info
C++のみで動作しないのは、別モジュール（エディタ用モジュールなど）にあるクラスがUEditorValidatorSubsystem::Initialize() の GetDerivedClasses()で取得されていないためで別途に登録が必要です。
:::


### C++のみのバリデータをサブシステムに登録する
BPでバリデータを用意する（継承する）場合は不要ですが、バリデータを別モジュールでC++のみで作成した場合別途登録処理が必要です。一番手っ取り早い方法はモジュールの開始処理にオブジェクトを生成することだと思います。
以下サンプルコード


```C++:MyEditorModule.cpp
void FMyEditorModule::StartupModule()
{

	if (UEditorValidatorSubsystem* _Subsystem = GEditor->GetEditorSubsystem<UEditorValidatorSubsystem>())
	{
		_Subsystem->AddValidator(NewObject<UMyValidator>());
	}
}

```


### データ検証の設定
[プロジェクト設定]->[エディタ]->[データ検証の設定]で検証コードが走るタイミングの設定ができます。
![Setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/562bdd16-1563-2124-dfcb-56c6a72f261a.png)

個別に検証する場合は、コンテンツブラウザにてアセットファイルの右クリックから[アセットアクション]->[アセットの検証]でできます。


# まとめ
過去に自作していた `EditorValidator` が動作していないことに気づいたので調べました。エンジンアップデートの際には要注意です。:sweat_smile: