概要
UnrealEngine4のEnumについてのメモです。
主にC++での扱い方となります。
更新履歴
日付 | 内容 |
---|---|
2023/05/15 | UE5.1でANY_PACKAGEが非推奨となった事の追記 |
2024/09/11 | Enumの文字列化について追記 |
参考
以下の記事を参考にいたしました、ありがとうございます。
UE4 における enum class の「フラグ」化
UE4 C++とUnreal C++の列挙型の扱い
UE4 C++ BitFlags について
環境
Windows10
Visual Studio 2017
UnrealEngine 4.26, 5.3
関連ソース
"Engine\Source\Runtime\Core\Public\Misc\EnumClassFlags.h"
"Engine\Source\Runtime\Core\Public\Misc\EnumRange.h"
Enumのフラグ化
以下のようにビットの値を入れて、ENUM_CLASS_FLAGS
マクロを指定します。
#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
があります。
以下コード例。
// チェック対象のフラグ
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
マクロを定義しておくことで範囲ループが可能です。
以下コード例。
#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
にてテキスト型を取得し表示します。
for (EZodiacSignId _EnumId : TEnumRange<EZodiacSignId>()){
// Enum定義を文字列として出力
UE_LOG(LogTemp, Log, TEXT("%s"), *UEnum::GetDisplayValueAsText(_EnumId).ToString() );
}
以下出力結果です。
enum class
定義に 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
LogTemp: ZID NONE
LogTemp: ZID ARIES
LogTemp: ZID TAURUS
...以下省略...
範囲ループ2
ENUM_RANGE_BY_COUNT
は頭から指定数分ループしていますが、ENUM_RANGE_BY_FIRST_AND_LAST
は最初と最後を指定することでこの範囲をループできます。
// Aries から Virgoまで指定
ENUM_RANGE_BY_FIRST_AND_LAST(EZodiacSignId, EZodiacSignId::ZID_ARIES, EZodiacSignId::ZID_VIRGO);
範囲ループ3
ENUM_RANGE_BY_VALUES
はループするリストを指定するようです。
連番になっていない場合に使えると思われます。
#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定義を取得してループする方法です。
以下コード例。
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()
にてテキスト型を取得するか、以下のようなマクロを使うのが良いみたいです。
#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?") )
FString GetEnumValueAsString(const EMyEnum _EnumValue)
{
// StaticEnumで列挙型の情報を取得し、その値を文字列に変換
const UEnum* _EnumPtr = StaticEnum<EMyEnum>();
if (!_EnumPtr)
{
return( FString("Invalid") );
}
// EnumValueから対応する文字列を取得
return( _EnumPtr->GetNameStringByValue(static_cast<int64>(_EnumValue)) );
}
// 表示する
void Print()
{
EMyEnum _EnumValue = /*定義したEnum値*/;
FString _ENumStr = GetEnumValueAsString(_EnumValue);
UE_LOG(LogTemp, Warning, TEXT("%s"), *_EnumStr);
}
Enumのリダイレクタ
Enum定義毎に旧名と新名を指定します。
以下コード例。
[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"));
まとめ
使いたいときによく忘れているのでメモ書きとしてまとめています。