UE4 AssetUserDataを試してみる

# 概要
UnrealEngine4 のAssetUserDataについて、テストをしてみたメモ書きです。

アセットユーザーデータの定義と設定、取得を試しています。

## 環境
Windows10
Visual Studio 2017
UnrealEngine 4.22

## 参考
以下を参考にさせて頂きました、ありがとうございます。
[[UE4] Asset User Data とは？](http://historia.co.jp/archives/8632/)
[[UE4]アニメーション系アセットの設定項目「MetaData」について](http://historia.co.jp/archives/9431/)


# アセットユーザーデータについて
IInterface_AssetUserData を経由してアクセスできる独自定義データのようです。
取得のためのメソッドは *GetAssetUserDataOfClass* と *GetAssetUserDataArray* 辺りの様です。

## IInterface_AssetUserData
IInterface_AssetUserData を継承しているのは現在、以下10クラスのようです。
- AWorldSettings
- UActorComponent
- UAnimationAsset
- UGeometryCache
- ULevel
- UMaterialInterface
- USkeletalMesh
- USkeleton
- UStaticMesh
- UTexture

## 実装
StaticMesh.cpp での実装は以下のような感じになっていました。

```cpp:StaticMesh.cpp
void UStaticMesh::AddAssetUserData(UAssetUserData* InUserData)
{
	if(InUserData != NULL)
	{
		UAssetUserData* ExistingData = GetAssetUserDataOfClass(InUserData->GetClass());
		if(ExistingData != NULL)
		{
			AssetUserData.Remove(ExistingData);
		}
		AssetUserData.Add(InUserData);
	}
}

UAssetUserData* UStaticMesh::GetAssetUserDataOfClass(TSubclassOf<UAssetUserData> InUserDataClass)
{
	for(int32 DataIdx=0; DataIdx<AssetUserData.Num(); DataIdx++)
	{
		UAssetUserData* Datum = AssetUserData[DataIdx];
		if(Datum != NULL && Datum->IsA(InUserDataClass))
		{
			return Datum;
		}
	}
	return NULL;
}

void UStaticMesh::RemoveUserDataOfClass(TSubclassOf<UAssetUserData> InUserDataClass)
{
	for(int32 DataIdx=0; DataIdx<AssetUserData.Num(); DataIdx++)
	{
		UAssetUserData* Datum = AssetUserData[DataIdx];
		if(Datum != NULL && Datum->IsA(InUserDataClass))
		{
			AssetUserData.RemoveAt(DataIdx);
			return;
		}
	}
}

const TArray<UAssetUserData*>* UStaticMesh::GetAssetUserDataArray() const 
{
	return &AssetUserData;
}
```


# 実装テスト
UnrealC++でアセットユーザーデータを定義して、static meshに持たせ、それを取得してみます。

## AssetUserDataの作成

1. コンテンツブラウザを右クリック、[新規C++クラスを作成]を選ぶ。
1. 親クラスを[AssetUserData]にして適当な位置へクラス作成。
1. 必要なパラメータをメンバ変数で追加。
1. ビルドする

ParamInt と ParamFloat という２つを持たせてコンストラクタで初期値を設定しています。

```cpp:MyAssetUserData.h
#pragma once

#include "CoreMinimal.h"
#include "Engine/AssetUserData.h"
#include "MyAssetUserData.generated.h"

UCLASS()
class TEST_API UMyAssetUserData : public UAssetUserData
{
	GENERATED_BODY()
	
public:
	// コンストラクタ
	UMyAssetUserData(const FObjectInitializer& ObjectInitializer);

public:
	// Int型パラメータ
	UPROPERTY(Category = "UMyAssetUserData", EditAnywhere, BlueprintReadWrite)
		int			ParamInt;
	// Float型パラメータ
	UPROPERTY(Category = "UMyAssetUserData", EditAnywhere, BlueprintReadWrite)
		float		ParamFloat;
};

```


```cpp:MyAssetUserData.cpp
#include "MyAssetUserData.h"


// コンストラクタ
UMyAssetUserData::UMyAssetUserData(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	ParamInt = 100;
	ParamFloat = 50.0f;

}

```

このクラス(UMyAssetUserData)を更に継承して継承先のコンストラクタでプリセットを作っておくという使い方もできます。（この場合はブループリントで継承したほうがプリセットのパラメータを変更しやすいかもです。）


## AssetUserDataの設定
データを持たせたい対象のStaticMeshを開く。
StaticMeshの場合、[アドバンスな詳細も表示]にチェックを入れないと見えないので注意。

![staticmesh_advance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/23c2cd55-a868-0eb9-3226-34be2e59318b.png)


[Asset User Data]を追加し、自分の追加したクラスを選択。

![MyAssetUserData.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/a505b6b8-1c7b-191d-d1c4-7fed6e487ec3.png)

パラメータがエディットできるので必要によって修正できます。


## AssetUserDataの取得

Actor -> StaticMeshComponent -> StaticMesh と辿って行って取得した例。

```cpp:
for (TActorIterator<ATest>ActItr(GEngine->GameViewport->GetWorld()); ActItr; ++ActItr)
{
	ATest* Test = *ActItr;
	// コンポーネントを取得
	auto _Component = Cast<UStaticMeshComponent>(Test->GetComponentByClass(UStaticMeshComponent::StaticClass()));
	// スタティックメッシュを取得
	auto _Mesh = _Component ? _Component->GetStaticMesh() : nullptr;
	// アセットユーザーデータインターフェイス取得
	IInterface_AssetUserData* Interface = Cast<IInterface_AssetUserData>(_Mesh);

	if (Interface != nullptr) {
		// アセットユーザーデータ
		auto _AssetUserData = Interface->GetAssetUserDataOfClass(UMyAssetUserData::StaticClass());
		UMyAssetUserData* _MyAUD = Cast<UMyAssetUserData>(_AssetUserData);
		if (_MyAUD) {
			// 取得結果
			UE_LOG(LogTemp, Log, TEXT("%d, %f"), _MyAUD->ParamInt, _MyAUD->ParamFloat);
		}
	}

}

```

取得に成功すれば、ParamInt と ParamFloat の数値がアウトプットログに表示されます。


# まとめ
割と汎用性が高いので、細かい動作はなしでとにかく拡張データが欲しい時は有用かもしれません。
似た機能で、アニメーション系に **MetaData** というものがあるようです、名前がとても紛らわしい。:sweat_smile: