ブロック崩しを作りながら学ぶ Unreal Engine #8 マスターデータ

# 概要
マスターデータとはそのゲームにおいて不変のデータの事です。
マスターデータは、ブロック崩しで言えば、「あるステージでのブロック配置」、「ブロックを破壊した時に加算されるスコア」などがそれにあたります。逆に「現在のスコア」などプレー中に変化するデータはマスターデータではありません。この記事では Unreal Engine 上でマスターデータを扱う方法を、ブロック崩し作成を通して解説します。

## 環境
Windows 11
Unreal Engine 5.03
エディタ言語 英語

# シリーズ記事
[[UE4] ブロック崩しを作る #1 壁とボール](https://qiita.com/jamaican/items/9dae2dc581589f50a921)
[[UE4] ブロック崩しを作る #2 バーとブロック](https://qiita.com/jamaican/items/9730ef4c97ffed2783e4)
[[UE4] ブロック崩しを作る #3 C++](https://qiita.com/jamaican/items/6303fe259bbf92a7c4ed)
[[UE4] ブロック崩しを作る #4 PlayerController (C++)](https://qiita.com/jamaican/items/1e968afd31825249c70b)
[[UE4] ブロック崩しを作る #5 Debug (C++)](https://qiita.com/jamaican/items/3eed3698942f6944d305)
[[UE4] ブロック崩しを作る #6 スコアUI (C++)](https://qiita.com/jamaican/items/034db7b561b2fe23ea22)
[[UE4] ブロック崩しを作る #7 デリゲート(C++)](https://qiita.com/jamaican/items/6fcdb90c5659195db5fe)

# 参考リンク
こちらを参考にさせて頂きました。ありがとうございます。

[【UE4】DataTableの使い方-その①　～基本編～【★～★★】](https://kinnaji.com/2018/12/04/datatable-01/)
[UE4 DataTableからのパラメータ取得とJSONインポートについて](https://qiita.com/unknown_ds/items/4da34cccb3181e633efe)
[UE4 レベルブループリントのメモ](https://qiita.com/unknown_ds/items/ce784a6a9660700799eb)
[[UE4] C++で動的にアクターを生成（スポーン）する方法で一番実用的だった方法](https://qiita.com/Kuroyanagi96/items/7849dfaed83fc8df5741)

# BP編

## 構造体を作成
Content Drawer で右クリック
Blueprints → Structure を選択
作成された Structure を LevelDefinedStruct にリネーム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/ffa47abc-3adf-5084-7109-2ff213f5313a.png)

## 構造体を編集
LevelDefinedStruct をダブルクリックして編集画面を開く
以下の操作で SS にあるような構造体を設定する
- 新規変数を追加 … + Add Variable ボタンをクリック
- 変数名を変更 … テキストボックスを編集する
- 変数の型を変更 … コンボボックスから選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/86a8f2c2-3192-428a-1ade-fc66f98bae46.png)

## Data Table を作成
Content Drawer で右クリック
Miscellaneous → Data Table を選択
(作成時に構造体を指定する必要があるので LevelDefinedStruct を選択)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/fe763f9b-ce44-f391-9f00-63276bfa5542.png)

## Data Table を編集
作成した DataTable をダブルクリックして編集画面を開く
以下の操作で SS にあるような Data Table を設定する
- 行追加 … 上部の Add ボタン
- 選択行を削除 … 上部の Remove ボタン
- 選択行を編集 ... 下部の Row Editor タブ画面から
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/afa6a73f-177c-c507-9661-dbf42fafbc50.png)

## Data Tabel をゲームで読み込み

以下をゲームに実装します。これらは **レベルブループリント**上に実装する事にします。
1. Data Tabel から初期ブロック配置を読み込み
2. 読み込んだブロック配置通りにブロックを配置

### レベルブループリントを開く
上部メニューから Open Level Blueprint を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/44e277d9-a4b3-02d2-25d4-d6e5c7eddfdf.png)

### レベルブループリントに実装

以下のノードを作成し、それぞれ SS にあるよう接続し BP を実装します。

* Get Data Table Row Names ノード (DataTable の RowName をすべて取得する)
  * Table には作成した DataTabel を指定
* For Each Loop ノード
* Get Data Table Row LevelDataTable ノード (RowName の Rowデータを取得)
  * Table には作成した DataTabel を指定
* Break LevelDefinedStruct ノード (Rowデータを LevelDefinedStruct に変換)
* Make Vector ノード (Vector を生成)
* Spawn Actor BP Block ノード (BP_Block 配置)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/d7072669-c846-57f0-c1b6-62a5b6161d8b.png)

## ゲーム実行

前回まで作成していたブロック崩しから配置したブロックを全部削除します。
レベルBP をコンパイルして実行すると DataTable の座標通りにブロックが配置されるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/f9bfa89e-c367-5376-481d-4031e9be0752.png)

# C++編
同じことを BP ではなく C++ から行ってみます。

## DataTabel 用の構造体(C++)を作成
上部メニュー Tools → New C++ Class..
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/bd96e79c-e167-655d-3c14-2af613aa8e55.png)
親クラスに None を指定して Next
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/457434a8-c39b-8f6c-75fb-dcb9b3ea4124.png)
Name を Levelnfo に
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/95f9da22-86c0-e9bc-c430-559dc0b62c31.png)

Visual Studio が起動
* LevelInfo.cpp は削除します
* LevelInfo.h を以下のようにします
* ビルド (ライブコーディングが有効な時は UEエディタ上で Ctrl + Alt + F11 します)

```cpp:LevelInfo.h
#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "Engine/DataTable.h"
#include "LevelInfo.generated.h"

USTRUCT(BlueprintType)
struct FLevelInfo : public FTableRowBase
{
    GENERATED_USTRUCT_BODY()

public:

    FLevelInfo()
        : y(0.0f)
        , z(0.0f)
    {}

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float y;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float z;
};
```

## DataTable を作成

Content Drawer で右クリック
Miscellaneous → Data Table を選択
(作成時に構造体は FLevelInfo を選択)

## DataTable を編集

BP編と同じようにブロック座標データを編集

## Data Tabel をゲームで読み込み

### レベルブループリントに実装
上部メニュー Tools → New C++ Class..
LevelScriptActor を選択して Next
MyLevelScriptActor が作成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/eb974220-523f-7dea-1998-4786e36e6923.png)

MyLevelScriptActor を以下のようにしてコンパイル
```cpp:MyLevelScriptActor.h
#pragma once

#include "CoreMinimal.h"
#include "Engine/LevelScriptActor.h"
#include "Engine/DataTable.h"
#include "MyLevelScriptActor.generated.h"

/**
 * 
 */
UCLASS()
class BREAKBLOCKCPP_API AMyLevelScriptActor : public ALevelScriptActor
{
	GENERATED_BODY()
	
protected:
	virtual void BeginPlay() override;

	UPROPERTY(Category = "Test", EditAnywhere, BlueprintReadWrite)
	class UDataTable* MyDataTable;
};
```

```cpp:MyLevelScriptActor.cpp
#include "MyLevelScriptActor.h"
#include "LevelInfo.h"

void AMyLevelScriptActor::BeginPlay()
{
    Super::BeginPlay();
 
    if (MyDataTable != nullptr)
    {
        auto _Names = MyDataTable->GetRowNames();
        for (int _Lp = 0; _Lp < _Names.Num(); _Lp++)
        {
            auto _Record = MyDataTable->FindRow<FLevelRow>(_Names[_Lp], FString());

            FString path = "/Game/BP_Block.BP_Block_C";
            TSubclassOf<class AActor> sc = TSoftClassPtr<AActor>(FSoftObjectPath(*path)).LoadSynchronous();
            if (sc != nullptr)
            {
                AActor* a = GetWorld()->SpawnActor<AActor>(sc);
                a->SetActorLocation(FVector(0, _Record->y, _Record->z));
            }
        }
    }
}
```

### レベルブループリント の親クラスを MyLevelScriptActor に差し替え

上部メニューから Open Level Blueprint を選択してレベルブループリントを開く
上部メニューの Class Settings をクリック
表示された Details から Parent Class を MyLevelScriptActor に変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/889ec50f-f1a7-3867-10e1-f4ac3462e318.png)

上部メニューから Class Defaults をクリック
表示された Details から My Data Table を NewDataTable にセット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/23cc916e-54df-e01b-b46b-0427e9e00779.png)

## ゲーム実行

これで BP と同じようにプレイすると DataTable の座標通りにブロックが配置されるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39286/f9bfa89e-c367-5376-481d-4031e9be0752.png)