Help us understand the problem. What is going on with this article?

UE4 c++を用いたデータテーブルを使った独自データアセットの作成

More than 1 year has passed since last update.

ユーザー定義オブジェクトのアセット化で楽をするというhistoriaさんの記事を見て…
データテーブルからデータアセットを作ってみようと思ったら意外と躓いたことがあったのでまとめてみます。

参考記事にも記載されていますが、複数のテーブルから一つのアセットを作成したり、IDを使ってリソースの検索を行う処理を書く際に有益かもです。

環境

UE4 4.21.0
Visual Studio Community 2017

参考記事

ユーザー定義オブジェクトのアセット化で楽をする

下準備

検証するためのプロジェクト作成。
新規プロジェクトタブから基本コードのプロジェクトを作成します。

c++クラス作成

上にリンクを貼ったhistoriaさんの記事に倣ってDataAssetを継承したc++クラスを作成します。
名前はMyDataAssetで簡単な敵データっぽいアセットを仮作成してみます。
img0001.png

MyDataAsset.h

#pragma once

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

// ================================
//  データテーブル型の構造体
//      Engine/DataTable.hをincludeしておくこと。
// ================================
USTRUCT()
struct FMyData : public FTableRowBase
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere)
    int ID;
    UPROPERTY(EditAnywhere)
    int Attack;
    UPROPERTY(EditAnywhere)
    int HP;
};


// ================================
//  インゲームで使用するデータアセットの1レコードにあたる構造体
// ================================
USTRUCT(BlueprintType)
struct FMyDataAssetRecord
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int ID;
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int Attack;
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int HP;
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    FText Name;
};


// ================================
//  インゲームで使用するデータアセットクラス
// ================================
UCLASS()
class MYPROJECT_API UMyDataAsset : public UDataAsset
{
    GENERATED_BODY()
public:
#if WITH_EDITORONLY_DATA
    UPROPERTY(EditAnywhere, Category = "Test")
    UDataTable* DataTable;
#endif
    UFUNCTION(meta = (CallInEditor = "true"))
    void Import();

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    TArray<FMyDataAssetRecord> Data;

};

データテーブルを作る際の元となるクラスの作成を行う際は、継承元にFTableRowBaseを設定します。
今回インゲームで使用する想定のアセットとして作ったのはその下の構造体からです。

UDataAssetを継承したUMyDataAssetが一つのデータアセットとして作成できます。
今回はほぼデータテーブルと同じ型の
#if WITH_EDITORONLY_DATA
#endif
で囲まれている部分に読み込み元として設定するデータテーブルのポインタを定義することで、読み込みに使用するデータテーブルはエディタ上でしか使用しないよう設定できます。

MyDataAsset.cpp

#include "MyDataAsset.h"

void UMyDataAsset::Import()
{
#if WITH_EDITORONLY_DATA
    if (! DataTable  || 
        ! DataTable->GetRowStruct()->IsChildOf(FMyData::StaticStruct()))
    {
        return;
    }

    Data.Empty();
    auto Names = DataTable->GetRowNames();

    for (int i = 0; i < Names.Num(); i ++)
    {
        auto record = DataTable->FindRow<FMyData>(Names[i], FString());

        FMyDataAssetRecord asset;
        asset.ID = record->ID;
        asset.Attack = record->Attack;
        asset.HP = record->HP;
        asset.Name = FText::FromName(Names[i]);

        Data.Add(asset);
    }

#endif
}

実装しているのはImportメソッドのみで、この中にDataTableからDataAssetへの変換を記載しています。
変換の際にはもちろんDataTableを使用するためヘッダの時と同様に
#if WITH_EDITORONLY_DATA
#endif
この記載をしておきます。
そしてこの後が躓いた点

躓き1:DataTableの型チェック

historiaさんの記事を見ていて、ヘッダ側にはUDataTableのポインタが記載されています。
今回もそれに倣っているのですが、当然UDataTableのポインタには今回自分が作った形式以外のDataTableを設定できてしまいます。
さてどうチェックしたものか。というのが躓きポイント。

解決方法

DataTable->GetRowStruct()->IsChildOf(FMyData::StaticStruct())
これでDataTableに使用されている構造体が適切であるかの確認ができます。

躓き2:DataTableの1レコードを取得する方法

UDataTableの定義の中を見るに、データは
TMap型で保持されているようです。実際にデータを取得してくる際はuint8*を拾ってキャストか…などと思っていましたが便利な関数がありました。

解決方法

auto Names = DataTable->GetRowNames();
for (int i = 0; i < Names.Num(); i ++)
{
auto record = DataTable->FindRow(Names[i], FString());
}
これで、FMyDataに変換された状態のレコードが取得できます。

テスト

上記のコードを記述した後、エディタ上でコンパイルを行いコードの内容を反映します。
その後コンテンツブラウザから今回作成したデータテーブルとデータアセットを作成します。

データテーブル
img0002.png
img0003.png

適当なデータを入れておきます。
スケルトンとかワイバーンとか。簡単な敵のデータ。
今更ながらEnemyDataとか名前を付けておけばよかった。画像取り直すのがめんdなんでもないです。

データアセット
img0004.png

開くと作成したImportメソッドがボタンを押すことで呼び出せること、DataTableを設定する枠があることを確認できると思います。
枠にDataTableを設定し、Importボタンを押すことでデータの読み込みが行えるはずです。

まとめ

データの管理を行う上でIDを割り振りたいシーンは多いと思いますし、IDから検索したいと思うことも多いと思います。
そんな拡張をする際の助けになればと。
でもこの方法だと、インゲームで使用しないデータテーブルが量産されるというデメリットもありますね。
作る手間はありますが、factoryを作成してエディタ上にドラッグアンドドロップでアセットを作る方法の方が良いのかも。
その方法についても今度記事書こうかと思っていたり。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away