LoginSignup
13
7

More than 1 year has passed since last update.

enumをインデックスとした便利な配列の作り方

Last updated at Posted at 2022-04-10

はじめに

ゲームを作っていると難易度ごとで敵の体力や攻撃力などを設定できるようにしたいという状況に出くわすことがあると思います。
敵のデータテーブルを難易度ごとに作るなど色々と方法があると思いますが、今回はプロパティ上でできる方法をご紹介します。

例として難易度ごとに敵のデータを設定できるようにするため、適当に敵のデータ構造体と難易度の列挙型を作ります。

EnemyData.h
USTRUCT()
struct FEnemyData
{
	GENERATED_BODY()
	
public:
	UPROPERTY(EditAnywhere)
	int32 Health;

	UPROPERTY(EditAnywhere)
	int32 Attack;

	UPROPERTY(EditAnywhere)
	FName RewardId;
};
Difficulty.h
UENUM()
enum class EDifficulty : uint8
{
	Easy,
	Normal,
	Hard,

	Max
};
ENUM_RANGE_BY_COUNT(EDifficulty, EDifficulty::Max);

ENUM_RANGE_BY_COUNTマクロはUENUMforeachで簡単に回せるTEnumRangeというクラステンプレートを使うために定義しています。
Iterating over UENUM with TEnumRange - ben ui


まずすぐに思いつく方法としては以下の様に難易度の列挙型をkeyで敵データの構造体をvalueにしたTMapを作るというのがあるかと思います。

Something.h
UPROPERTY(EditAnywhere, EditFixedSize)
TMap<EDifficulty, FEnemyData> EnumMap;

image.png

Detailsパネルでの見た目は後で紹介するenumをインデックスとした配列とほとんど変わりませんが、普通のTMapだと難易度分の要素を自分で追加しないといけない上に、特定の難易度のデータがない状態などが起こり得ます。
そこで、TArrayTMapの要素数を編集できないようにするUPROPERTYのメタ指定子のEditFixedSizeを使い、Detailsパネルから要素数の編集をできないようにして、以下の様にコンストラクタかどこかで難易度の種類分の要素を追加しておく必要があります。

Something.h
ATest()
{
	if (EnumMap.Num() == 0)
	{
		for (const auto& ArrayIndex : TEnumRange<EDifficulty>())
		{
			EnumMap.Add(ArrayIndex);
		}
	}
}

色々と面倒くさいですね...

作り方

それでは本題に入ります。
enumをインデックスとした配列は以下の様にして定義できます。

Something.h
UPROPERTY(EditAnywhere)
FEnemyData EnumArray[(int32)EDifficulty::Max];

image.png

この方法で定義するとEditFixedSizeを設定しなくても要素数の変更ができないようになっています。
ちなみにC++で扱う際はTArrayではなくC++標準の固定長配列になっているため、要素を足したりはできません。
また、普通の固定長配列なのでC++で要素を取得する際は以下の様に整数にキャストしてインデックスを指定する必要があります。

Something.h
EnumArray[static_cast<int32>(EDifficulty::Easy)].Attack;

余談

EnumArrayの定義を見た時に「あれ?C++なのにCタイプのキャスト使ってない?」と思った方も多いと思います。
とりあえず、static_castに直してみてみましょう。

Something.h
UPROPERTY(EditAnywhere)
FEnemyData EnumArray[static_cast<int32>(EDifficulty::Max)];

image.png

難易度の表示名があるところがなぜか、Index[N]の表示になってしまっています。
エンジンコードでこの方法を使っているところを見るといずれもCタイプのキャストを使っていたため、私もstatic_castに直したのですがどうやらCタイプキャストでないといけない理由があるようでした。
おそらくUnrealHeaderToolstatic_castを使った構文に対応していないことが原因だと思います。


また、UnrealHeaderTool関連でもう一つ問題があります。
難易度ごとに何かの有効無効を設定するためにboolで同じように定義するとビルドできません。

Something.h
UPROPERTY(EditAnywhere)
bool EnumArray[static_cast<int32>(EDifficulty::Max)];

どうやらboolの固定長配列がUnrealHeaderToolでサポートされていないのが原因のようです。(なぜなのか...)
固定長配列ではサポートされていないだけで、TArray<bool>はサポートされています。
Bool arrays in c++? - Forums

おわりに

[(int32)で全体検索してみるとエンジンコードで結構使われているのが伺えます。(なのにこれに関する記事が全然ない不思議...)
余談で書いたこと以外にもUPROPERTYのメタ指定子のTitlePropertyが使えないなどの制約もありますが、便利なのでぜひTMapの代わりに使って頂ければと思います。

TitlePropertyについては以下の記事が参考になります。
エディター上で配列の要素の横に文字列を表示する

13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7