main() blog

プログラムやゲーム、旅、愛する家族について綴っていきます。

【UE5】DataTableを使ってみよう!(C++編)

概要

データテーブルは、構造化データを整理するためのテーブル形式のデータ構造です。
行と列からなり、各行は一つのレコードを表し、各列はそのレコードの属性を表します。
データテーブルは様々な用途で使用できます。
例えば:

  • キャラクターのステータス情報: HP、攻撃力、防御力など
  • アイテムの属性情報: アイテム名、値段、効果など
  • レベルデザイン: 各レベルの設定、難易度、報酬など

などのデータを扱うことができます。

データテーブルは、CSVファイルやJSONファイルからインポートすることができます。

※本記事はC++での開発を前提としています。

環境

UnrealEngine 5.4.2

実装

構造体定義

DataTable用の構造体を作成します。
ツール → 新規C++クラスの追加を選択します。

親クラスは[None]を選択します。

エネミーデータの構造体を想定して作成してみます。
ファイル名を EnemyData としてクラスを作成します。

VisualStudioで実行している場合はソリューションにファイルが追加されます。
VisualStudioから実行していない場合はソリューションファイルを更新してください。
ヘッダーファイル、ソースファイルが追加されるがデフォルトで以下のコードが追加されているので削除します。
必要であればソースファイルはファイルごと削除してください。

#pragma once

#include "CoreMinimal.h"

/**
 * 
 */
class HOGE_API EnemyData
{
public:
    EnemyData();
    ~EnemyData();
};
#include "EnemyData.h"

EnemyData::EnemyData()
{
}

EnemyData::~EnemyData()
{
}

ヘッダーファイルに構造体を定義します。
UEのコーディング規約では構造体は"F"のプリフィクスを付けます。
FTableRowBaseを継承します。
UEが自動生成するヘッダーファイルのインクルードも追加してください。
パラメータをUEのエディタ上で編集するためプロパティはEditAnywhereに設定しておきます。

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataTable.h"

#include "EnemyData.generated.h"

USTRUCT(BlueprintType)
struct FEnemyData : public FTableRowBase
{
    GENERATED_BODY()

    // 攻撃力.
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 Attack;

    // 防御力.
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 Defence;

    // HP.
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 HP;
};

DataTableの作成

コンテンツ以下であれば場所は任意の場所で構いません。
※今回はコンテ追加にDataTablesというフォルダを作成しています
コンテンツブラウザのフォルダで右クリックからその他 → データテーブルを選択します。

今回作成したEnemyDataを選択します。

データテーブルのアセットが作成されるのでファイル名を変更します。
命名規則に沿って"DT_"のプレフィックスを付けます。

データ入力

作成したDT_EnemyDataを開きます。 メニューの行の追加マークを押して新しい行を追加します。

空のデータが追加されます。

行の名前を編集して任意の名前に変更します。
これが今後参照する際に検索するためのキーとなります。

パラメータを任意の値が入力できることを確認します。

C++でデータの参照

本来はデータテーブルを常駐化や非同期読み込みなどの対応が必要となります。
今回は簡易的な対応のため、参照時に存在しない場合に同期読みで対応しています。

hoge()
{
    FString strPath = TEXT("/Game/DataTables/DT_EnemyData.DT_EnemyData");
    TSoftObjectPtr<UDataTable> enemyDataTable(strPath);
    if (enemyDataTable == nullptr)
    {
        // 参照できない場合は同期読み.
        enemyDataTable.LoadSynchronous();
    }

    if (enemyDataTable)
    {
        // データテーブルから全データを取得する.
        TArray<FName> RowNames = enemyDataTable->GetRowNames();
        for (auto RowName : RowNames)
        {
            const FEnemyData* data = enemyDataTable->FindRow<FEnemyData>(RowName, FString());
            if (!data)
            {
                continue;
            }

            UE_LOG(LogTemp, Log, TEXT("[%s]:[%d][%d][%d]"), *RowName.ToString(), data->Attack, data->Defence, data->HP);
        }
    }
}

参照できる状態で行のキーを取得して検索することですべてのデータが参照できていることが確認できます。

BPでデータの参照

念のためBPからも参照できることを確認します。

コンソールのログに表示されていることが確認できます。

CSVにエクスポート

アセットブラウザからDT_EnemyDataを右クリック CSVにエクスポートを選択します。

出力先を選択してエクスポートします。 出力されたcsvを確認すると以下の様な内容で出力されています。

---,Attack,Defence,HP
Enemy000,"5","5","10"
Enemy001,"10","5","15"

CSVからインポート

エクスポートしたcsvに以下の様にパラメータを追加してみます。

---,Attack,Defence,HP
Enemy000,5,5,10
Enemy001,10,5,15
Enemy002,10,10,20
Enemy003,20,15,50

DataTableを開いた状態で、再インポートから先ほどのcsvを選択してインポートします。
一度インポートすると次からはファイル選択は行わなくてもインポートされるようです。

実行してログに追加されたデータが確認できます。