UE4 C++ BitFlags について
この記事はUnreal Engine 4 (UE4) Advent Calendar 2020 その1、8日目の記事です。
7日目は 0xUMAさんの『命名規則に従ったアセットの自動リネームとアセットバリデーション』でした。
- この記事について
- 今回のサンプルコード
- Bitflags, UseEnumValuesAsMaskValuesInEditor
- ENUM_CLASS_FLAGS(EMyFlag)
- BitmaskEnum
- ビット判定方法
この記事について
UE4 C++のBitflags がいろんなオプションがあって、何が何を意味しているかわからなかったので調査してみました。
今回のサンプルコード
まずはシンプルな実装コードを提示します。
今回説明するのはこのコードの内容に関することになります。
// 定義 UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) enum class EMyFlag : uint8 { None = 0 UMETA(hidden), Flag1 = 0x01, Flag2 = 0x02, Flag3 = 0x04, }; ENUM_CLASS_FLAGS(EMyFlag)
// フラグをメンバ変数にする UCLASS() class MYPROJECT_API AMyActor : public AActor { GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (Bitmask, BitmaskEnum = "EMyFlag")) uint8 MyFlags; };
// 判定部分 const EMyFlag Flags = (EMyFlag)MyFlags; if (EnumHasAnyFlags(Flags, EMyFlag::Flag1 | EMyFlag::Flag2)) { // bit on // EMyFlag::Flag1 か EMyFlag::Flag2 のbitが立っている } else { // bit off // EMyFlag::Flag1 か EMyFlag::Flag2 のbitが立っていない }
Bitflags, UseEnumValuesAsMaskValuesInEditor
UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
UENUMのmetaに Bitflags
を付加すると、その列挙型の変数がビットフラグかどうか判断できるようになります。ですが、今回の調査ではこれを付けることで、どのような変化が起こるかよくわかりませんでした。
列挙に対してこちらで値を代入していなければ(例: Flag1 = 0x01
ではなく Flag1,
にした場合)、勝手にビット毎の値を順番に入れてくれるかと思いましたが、通常の列挙と変わりませんでした。
また、 UseEnumValuesAsMaskValuesInEditor
が true
だと、ビットフラグに合っていない列挙値がBlueprint上で表示されないようになります。
たとえば値が 0x01
と 0x02
の場合はBlueprintのリストボックスの選択肢に表示されるのですが、 0x03
は複数のビットフラグが立ってしまう値のためBlueprintのリストボックスで表示されなくなります。
コードに補足を加えると、次のようになります。
enum class EMyFlag : uint8 { None = 0 UMETA(hidden), // 初期値とかはhiddenにして隠しておくとよい Flag1 = 0x01, Flag2 = 0x02, Flag3 = 0x04, // 下記は説明用に追加 TestValue = 0x0e, // UseEnumValuesAsMaskValuesInEditor=trueだと、これはエディタ上で表示されない TestValue2 = 0x08, // こちらのようなビットになっている部分はリストボックスに表示される };
ENUM_CLASS_FLAGS(EMyFlag)
ENUM_CLASS_FLAGS(EMyFlag)
を宣言すると、 EMyFlag
型同士は、 &
や |
などのビットフラグでおなじみのOperator演算子系が使えるようになります。
そのため、とりあえず書いておきましょう。
BitmaskEnum
次に、ビットフラグを保持するメンバ変数部分についてです。
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, meta = (Bitmask, BitmaskEnum = "EMyFlag"))
uint8 MyFlags;
フラグを複数立てたい変数に対しては、列挙型ではなく、通常の整数型を使用します。今回だと uint8
です。 値を uint8
よりも大きく取りたければ int32
で定義しましょう。 uint32
だと Blueprintで使えないのでエラーになります。また、型を EMyFlag
にすると、複数のビットを立てることができません。( UseEnumValuesAsMaskValuesInEditor
が false
で、列挙値に複数のビットを立てているものがあれば代用は可能)
それに対して、整数値の型(今回だと uint8
)の UPROPERTY
の meta
に Bitmask
を付けると、Blueprint編集上でビットマスク用のリストボックスで編集できます。
また、このビットマスク用のリストボックスは BitmaskEnum
で列挙型(今回だと EMyFlag
)を指定するとリストボックスでそのUEnum型の値から選択できるようになります。 BitmaskEnum
を付けていないと、Flag 1~32から選択という形になりますが、ビット選択できなくなるわけではありません。
ビット判定方法
最後にビットの判定方法についてです。
ビットが立っているか判定
ビットが立っているか判定するときには、一旦列挙型に変換して、 EnumHasAnyFlags
関数で判定します。
const EMyFlag Flags = (EMyFlag)MyFlags; // 一旦列挙型にキャストする(|や&演算子を使えるようにするため) if (EnumHasAnyFlags(Flags, EMyFlag::Flag1 | EMyFlag::Flag2)) { // bit on // EMyFlag::Flag1 か EMyFlag::Flag2 のbitが立っている } else { // bit off // EMyFlag::Flag1 か EMyFlag::Flag2 のbitが立っていない }
すべてのフラグが立っているか判定
また、すべてのフラグが立っているときにtrueを返したい場合には EnumHasAllFlags
で判定します。
if (EnumHasAllFlags(Flags, EMyFlag::Flag1 | EMyFlag::Flag2)) { // bit on // EMyFlag::Flag1 と EMyFlag::Flag2 両方のbitが立っている // 両方のbitが立っていれば、他のフラグが立っていてもよい } else { // bit off // EMyFlag::Flag1 か EMyFlag::Flag2 両方のbitが立っていない(どちらかだけではだめ) }