UE4,5 C++でData Assetを使う

# 1. データモデルの作成

### データ型の宣言（オプション）
```c++:DataManager.h
USTRUCT(BlueprintType)
struct FKawaiiStruct: public FTableRowBase
{
	GENERATED_BODY()

    // コンストラクタ
	FKawaiiStruct(){}
	FKawaiiStruct(const FName Name, const FSlateBrush Image): IconName(Name), IconImage(Image){}
	
    // 構造体構成値１：名前(FName)
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	FName IconName;

    // 構造体構成値２：画像(FSlateBrush)
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	FSlateBrush IconImage;
};
```

### データアセットのデータ型の宣言
#### UPrimaryDataAssetを継承
```c++:DataManager.h
UCLASS(BlueprintType)
class UKawaiiAssetDataModel : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
    // 実際のデータ
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Data Setting")
	TArray<FKawaiiStruct> KawaiiData;
    
    // データ読み込み元（画像）のディレクトリ指定用
	UPROPERTY(EditAnywhere, Category="Data Setting", meta=(RelativeToGameContentDir))
	FDirectoryPath ImagePath;

    // データ取り込み用ファンクション
	UFUNCTION(CallInEditor)
	void LoadData();
};
```

### データ読み込み用ファンクションの実装 (オプション）
```c++:DataManager.h
inline void UKawaiiAssetDataModel::LoadData()
{
    // 初期化
	KawaiiData.Empty();
    // 画像参照先パスの生成
	const FString Path = FPaths::Combine("/Game/", ImagePath.Path);
    // 指定ディレクトリ内アセットのリスト（パス名）の取得
	TArray<FString> Assets = UEditorAssetLibrary::ListAssets(Path, false);
	uint32 counter = 0;
	for(FString &Asset:Assets)
	{
        // パス名からアセットデータの取得
		FAssetData AssetData = UEditorAssetLibrary::FindAssetData(Asset);
		if(AssetData.AssetClass.ToString() == "Texture2D") // アセットがテクスチャの場合のみ処理
		{
			FSlateBrush IconImage;
			IconImage.ImageSize = FVector2d(100.f, 100.f); // サイズの指定
			IconImage.SetResourceObject(AssetData.GetAsset()); // 画像の指定
			KawaiiData.Add(FKawaiiStruct(AssetData.AssetName, IconImage)); // 配列に追加
			++counter;
		}
	}
    
	if(counter == 0) // 一つも処理できなかった場合（ディレクトリの指定エラー）
	{
		const FText Title = FText::FromString("Warning");
        // ダイアログの表示
		FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Select a Directory that has Images"), &Title);
	} else // 成功
	{
		FNotificationInfo NotifyInfo(FText::FromString(FString::FromInt(counter) + " Assets were Stored."));
		NotifyInfo.bUseLargeFont = true;
		NotifyInfo.FadeOutDuration = 7.f;
        // 処理数をNotificationに表示
		FSlateNotificationManager::Get().AddNotification(NotifyInfo);
	}
}

```
:::note info
自動でデータを読み込むのは超便利
:::

# 2. データアセットの作成
### Content Browser右クリック > Miscellaneous > Data Asset
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/7e7779ca-768d-963c-ae08-a284cc5575fe.png)

### 先ほど作ったデータアセットのデータ型（KawaiiAssetDataModel)を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/d4c11eb6-9d47-f4f5-577b-7a794649b64c.png)

### データアセットが生成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/ca33a023-b1f7-794b-1d25-63179c446b56.png)

## データセットにデータを読み込む
### 画像のあるディレクトリを指定して(1)、Load Data(2)でデータの読み込み　‐-今回の実装の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/6bb1ec53-9b5e-e737-5b98-3aaed3085c38.png)

### 一瞬で読み込み完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/138f0cfb-d073-827b-2488-f124058be3aa.png)


# 3. データアセットのデータを使う
### データアセットへの参照
```c++:UseDataAsset.h

protected:
	// Asset Data(BP_KawaiiData_CPP)へのファイルパス
	UPROPERTY( EditAnywhere, meta = (AllowedClasses = "KawaiiAssetDataModel") )
	FSoftObjectPath KawaiiDataAssetPath;

private:

	UPROPERTY()
	TSoftObjectPtr<class UKawaiiAssetDataModel> KawaiiAssetData;
```
### BP_ListView->Class Defaults->Details->Kawaii List View->Kawaii Data Asset Path
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24726/a246905e-8d9e-5ab4-5f31-698360c09ca9.png)

### データアセットのデータを使う。今回の例だとアセットの配列からランダムに１データ取り出し返す。
```c++:UseDataAsset.cpp
	FKawaiiStruct KData;
	if(KawaiiAssetData == nullptr)
	{
		UE_LOG(LogTemp, Warning, TEXT("KawaiiDataAssetPath: %s"), *KawaiiDataAssetPath.ToString());
		if(KawaiiDataAssetPath.IsNull()) return KData;
		KawaiiAssetData = Cast<UKawaiiAssetDataModel>(StaticLoadObject(UKawaiiAssetDataModel::StaticClass(), nullptr, *KawaiiDataAssetPath.ToString()));
		if(KawaiiAssetData == nullptr) return KData;
	}
	const int32 Max = KawaiiAssetData->KawaiiData.Num() - 1;
	KData = KawaiiAssetData->KawaiiData[FMath::RandRange(0, Max)];
	return  KData;
```

:::note info
データアセットのプロパティとしてアクセスするだけなので超簡単。
:::

# コード全体
```c++:DataManager.h
#pragma once

#include "CoreMinimal.h"
#include "EditorAssetLibrary.h"
#include "Misc/MessageDialog.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Framework/Notifications/NotificationManager.h"
#include "KawaiiDataManager.generated.h"


// Kawaii Structure
USTRUCT()	// BlueprintType
struct FKawaiiStruct //: public FTableRowBase <-for Datatable
{
	GENERATED_BODY()


public:
	FKawaiiStruct(){}
	FKawaiiStruct(const FName Name, const FSlateBrush Image): IconName(Name), IconImage(Image){}
	
	UPROPERTY(EditAnywhere)
	FName IconName;

	UPROPERTY(EditAnywhere)
	FSlateBrush IconImage;
};


// Kawaii Data Entry
UCLASS()	// Blueprintable
class LISTVIEW_CPP_API UKawaiiDataEntry : public UObject
{
	GENERATED_BODY()

public:

	UPROPERTY()	// BlueprintReadWrite
	FKawaiiStruct KawaiiData;
};

// Kawaii Data Asset Model
UCLASS()	// BlueprintType
class LISTVIEW_CPP_API UKawaiiAssetDataModel : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:

	UPROPERTY(EditAnywhere)	// EditAnywhere, BlueprintReadOnly, Category="Data Setting"
	TArray<FKawaiiStruct> KawaiiData;

	UPROPERTY(EditAnywhere, Category="Data Setting", meta=(RelativeToGameContentDir))
	FDirectoryPath ImagePath;

	UFUNCTION(CallInEditor)
	void LoadData();
};


// load asset into Asset Data 
inline void UKawaiiAssetDataModel::LoadData()
{
	KawaiiData.Empty();
	const FString Path = FPaths::Combine("/Game/", ImagePath.Path);
	TArray<FString> Assets = UEditorAssetLibrary::ListAssets(Path, false);
	uint32 counter = 0;
	for(FString &Asset:Assets)
	{
		FAssetData AssetData = UEditorAssetLibrary::FindAssetData(Asset);
		if(AssetData.AssetClass.ToString() == "Texture2D")
		{
			FSlateBrush IconImage;
			IconImage.ImageSize = FVector2d(100.f, 100.f);
			IconImage.SetResourceObject(AssetData.GetAsset());
			KawaiiData.Add(FKawaiiStruct(AssetData.AssetName, IconImage));
			++counter;
			UE_LOG(LogTemp, Warning, TEXT("Name: %s"), *AssetData.AssetClass.ToString());
		}
	}
	if(counter == 0)
	{
		const FText Title = FText::FromString("Warning");
		FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Select a Directory that has Images"), &Title);
	} else
	{
		FNotificationInfo NotifyInfo(FText::FromString(FString::FromInt(counter) + " Assets were Stored."));
		NotifyInfo.bUseLargeFont = true;
		NotifyInfo.FadeOutDuration = 7.f;
		FSlateNotificationManager::Get().AddNotification(NotifyInfo);
	}
}


```


```c++:KawaiiListView.h
#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "KawaiiListView.generated.h"


UCLASS(Abstract)
class LISTVIEW_CPP_API UKawaiiListView : public UUserWidget
{
	GENERATED_BODY()

protected:

	UPROPERTY(meta=(BindWidget))
	class UListView *ListView;

	virtual void NativeConstruct() override;
	
	UPROPERTY( EditAnywhere, meta = (AllowedClasses = "KawaiiAssetDataModel") )
	FSoftObjectPath KawaiiDataAssetPath;
	
	UPROPERTY(EditAnywhere)
	int32 NumOfImage = 30;

private:

	UPROPERTY()
	TSoftObjectPtr<class UKawaiiAssetDataModel> KawaiiAssetData;

	struct FKawaiiStruct GetRandomDataFromAsset(); 

};


```

```c++:KawaiiListView.cpp
#include "KawaiiListView.h"
#include "KawaiiDataManager.h"
#include "Components/ListView.h"

void UKawaiiListView::NativeConstruct()
{
	Super::NativeConstruct();
	if(KawaiiAssetData) UE_LOG(LogTemp, Warning, TEXT("KawaiiDataAsset is not null"));
	for(int i = 0; i < NumOfImage; i++)
	{
		UKawaiiDataEntry* Entry = NewObject<UKawaiiDataEntry>();
		Entry->KawaiiData = GetRandomDataFromAsset();
		ListView->AddItem(Entry);
	}
}


FKawaiiStruct UKawaiiListView::GetRandomDataFromAsset()
{
	FKawaiiStruct KData;
	if(KawaiiAssetData == nullptr)
	{
		UE_LOG(LogTemp, Warning, TEXT("KawaiiDataAssetPath: %s"), *KawaiiDataAssetPath.ToString());
		if(KawaiiDataAssetPath.IsNull()) return KData;
		KawaiiAssetData = Cast<UKawaiiAssetDataModel>(StaticLoadObject(UKawaiiAssetDataModel::StaticClass(), nullptr, *KawaiiDataAssetPath.ToString()));
		if(KawaiiAssetData == nullptr) return KData;
	}
	const int32 Max = KawaiiAssetData->KawaiiData.Num() - 1;
	KData = KawaiiAssetData->KawaiiData[FMath::RandRange(0, Max)];
	return  KData;
}

```