LoginSignup
4
3

UE4 列挙型Enumについてのメモ

Last updated at Posted at 2022-07-07

概要

UnrealEngine4のEnumについてのメモです。
主にC++での扱い方となります。

更新履歴

日付 内容
2023/05/15 UE5.1でANY_PACKAGEが非推奨となった事の追記

参考

以下の記事を参考にいたしました、ありがとうございます。

UE4 における enum class の「フラグ」化
UE4 C++とUnreal C++の列挙型の扱い
UE4 C++ BitFlags について

環境

Windows10
Visual Studio 2017
UnrealEngine 4.26

関連ソース

"Engine\Source\Runtime\Core\Public\Misc\EnumClassFlags.h"
"Engine\Source\Runtime\Core\Public\Misc\EnumRange.h"

Enumのフラグ化

以下のようにビットの値を入れて、ENUM_CLASS_FLAGSマクロを指定します。

.h
#include "Misc/EnumClassFlags.h"

UENUM( BlueprintType, meta=( Bitflags ) )
enum class ETestDamageFlag : uint8
{
	None				= 0			UMETA(ToolTip="なし"),
	Magic				= (1<<0)	UMETA(ToolTip="魔法"),
	Physics				= (1<<1)	UMETA(ToolTip="物理"),
	Poison				= (1<<2)	UMETA(ToolTip="毒"),
	Fainting			= (1<<3)	UMETA(ToolTip="気絶"),
	NoDefended			= (1<<4)	UMETA(ToolTip="防御不可"),
};
ENUM_CLASS_FLAGS( ETestDamageFlag )

フラグのチェックは指定フラグのどれかが立っているとtrueを返す EnumHasAnyFlags と、指定フラグ全てが立っているとtrueを返す EnumHasAllFlags があります。
以下コード例。

.cpp
// チェック対象のフラグ
auto _Flag = ETestDamageFlag::Magic | ETestDamageFlag::Poison;

// Anyでチェック
if( EnumHasAnyFlags(_Flag, ETestDamageFlag::Magic|ETestDamageFlag::Physics)){
	// Magic か Physics のいずれかのフラグが立っている
	UE_LOG(LogTemp, Log, TEXT("Any"));
}

// Allでチェック
if( EnumHasAllFlags(_Flag, ETestDamageFlag::Magic|ETestDamageFlag::Physics)){
	// Magic か Physics のどちらのフラグも立っている
	UE_LOG(LogTemp, Log, TEXT("All"));
}

Enumのループ

範囲ループ1

enum classの定義の後、 ENUM_RANGE_BY_COUNTマクロを定義しておくことで範囲ループが可能です。
以下コード例。

.h
#include "Misc/EnumRange.h"

UENUM(BlueprintType)
enum class EZodiacSignId : uint8 {
	ZID_NONE		UMETA(DisplayName = "None",			ToolTip = "00:未定義"),
	ZID_ARIES		UMETA(DisplayName = "Aries",		ToolTip = "01:牡羊座"),
	ZID_TAURUS		UMETA(DisplayName = "Taurus",		ToolTip = "02:牡牛座"),
	ZID_GEMINI		UMETA(DisplayName = "Gemini",		ToolTip = "03:双子座"),
	ZID_CANCER		UMETA(DisplayName = "Cancer",		ToolTip = "04:蟹座"),
	ZID_LEO			UMETA(DisplayName = "Leo",			ToolTip = "05:獅子座"),
	ZID_VIRGO		UMETA(DisplayName = "Virgo",		ToolTip = "06:乙女座"),
	ZID_LIBRA		UMETA(DisplayName = "Libra",		ToolTip = "07:天秤座"),
	ZID_SCORPIO		UMETA(DisplayName = "Scorpio",		ToolTip = "08:蠍座"),
	ZID_SAGITTARIUS	UMETA(DisplayName = "Sagittarius",	ToolTip = "09:射手座"), 
	ZID_CAPPRICORN	UMETA(DisplayName = "Capricorn",	ToolTip = "10:山羊座"),
	ZID_AQUARIUS	UMETA(DisplayName = "Aquarius",		ToolTip = "11:水瓶座"),
	ZID_PISCES		UMETA(DisplayName = "Pisces",		ToolTip = "12:魚座"),

	Num				UMETA(Hidden)
};
ENUM_RANGE_BY_COUNT(EZodiacSignId, EZodiacSignId::Num);

上記のenum定義を範囲ループさせます。UEnum::GetDisplayValueAsTextにてテキスト型を取得し表示します。

.cpp
for (EZodiacSignId _EnumId : TEnumRange<EZodiacSignId>()){
	// Enum定義を文字列として出力
	UE_LOG(LogTemp, Log, TEXT("%s"), *UEnum::GetDisplayValueAsText(_EnumId).ToString() );
}

以下出力結果です。
enum class定義に DisplayName のメタ情報の有無で結果が変わるようです。

出力結果1(DisplayNameがある場合)
LogTemp: None
LogTemp: Aries
LogTemp: Taurus
LogTemp: Gemini
LogTemp: Cancer
LogTemp: Leo
LogTemp: Virgo
LogTemp: Libra
LogTemp: Scorpio
LogTemp: Sagittarius
LogTemp: Capricorn
LogTemp: Aquarius
LogTemp: Pisces
出力結果2(DisplayNameがない場合)
LogTemp: ZID NONE
LogTemp: ZID ARIES
LogTemp: ZID TAURUS
...以下省略...

範囲ループ2

ENUM_RANGE_BY_COUNTは頭から指定数分ループしていますが、ENUM_RANGE_BY_FIRST_AND_LAST は最初と最後を指定することでこの範囲をループできます。

.h
// Aries から Virgoまで指定
ENUM_RANGE_BY_FIRST_AND_LAST(EZodiacSignId, EZodiacSignId::ZID_ARIES, EZodiacSignId::ZID_VIRGO);

範囲ループ3

ENUM_RANGE_BY_VALUES はループするリストを指定するようです。
連番になっていない場合に使えると思われます。

.h
#include "Misc/EnumRange.h"

UENUM()
enum class ETestNo : uint8 {
	APPLE = 10,
	STRAWBERRY = 20,
	GRAPE = 30,
};
ENUM_RANGE_BY_VALUES(ETestNo, ETestNo::APPLE, ETestNo::STRAWBERRY, ETestNo::GRAPE);

以下、出力結果。

出力結果
LogTemp: APPLE
LogTemp: STRAWBERRY
LogTemp: GRAPE

FindObjectを使ったループ

FindObject を使ってEnum定義を取得してループする方法です。
以下コード例。

.cpp
const UEnum* _EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EZodiacSignId"), true);
if (_EnumPtr != nullptr){
	for (int32 _Lp = 0; _Lp < _EnumPtr->NumEnums()-1; _Lp++){
		EZodiacSignId _EnumValue = (EZodiacSignId)(_EnumPtr->GetValueByIndex(_Lp));
			
		UE_LOG(LogTemp, Log, TEXT("%s"), *UEnum::GetDisplayValueAsText(_EnumValue).ToString() );
	}
}

この場合、定義されているものが全て出力されるため、最後のNumも出力されます。for文 でのループ数を変えるか、if文で不要な定義を抜くなどの対応が必要になるかもしれません。

その他

Enum定義のC++での文字列取得

UEnum::GetDisplayValueAsText() にてテキスト型を取得するか、以下のようなマクロを使うのが良いみたいです。

Enum文字列取得マクロ
#define GETENUMSTRING(etype, evalue) ( (FindObject<UEnum>(ANY_PACKAGE, TEXT(etype), true) != nullptr) ? FindObject<UEnum>(ANY_PACKAGE, TEXT(etype), true)->GetNameStringByIndex((int32)evalue) : FString("Invalid - are you sure enum uses UENUM() macro?") )

Enumのリダイレクタ

Enum定義毎に旧名と新名を指定します。
以下コード例。

Config/DefaultEngine.ini
[CoreRedirects]
+EnumRedirects=(OldName="EItemKind", ValueChanges=(("ITEM_KIND_RINGO","ITEM_KIND_APPLE"),("ITEM_KIND_ICHIGO","ITEM_KIND_STRAWBERRY"),("ITEM_KIND_BUDOU","ITEM_KIND_GRAPE")))

追記

UE5.1から ANY_PACKAGEが非推奨になったようです。
以下、変更コード例。

// 変更前
const UEnum* _EnumObj = FindObject(ANY_PACKAGE, TEXT("EMyEnum"));

// 変更後 
const UEnum* _EnumObj = FindObject(nullptr, TEXT("/Script/MyProject.EMyEnum"));

まとめ

使いたいときによく忘れているのでメモ書きとしてまとめています。

4
3
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
4
3