Skip to content

Defining a C++ Enum

Enum classes can be registered with the reflection system via UENUM, allowing for runtime interaction and Blueprints integration. While plain enums are supported, Unreal Engine highly recommends using enum class instead1.

Similar to structs, enums are typically declared in the header file of a related class. You can manually create a header file for the enum, but there is no option in the editor to create one.

Note

UENUMs cannot be defined inside the body of a class. The UENUM must be defined at the global scope.

Example

The following is a simple example of an enum that defines weapon types in a shooter game:

WeaponType.h
#pragma once

#include "WeaponType.generated.h"/* (1)! */

UENUM(BlueprintType)
enum class /* (2)! */ EWeaponType/* (3)! */ : uint8 /* (4)! */ {
    Common,
    Uncommon,
    Rare,
    Epic,
    Legendary,
    Mythic,
    Exotic
};
  1. Contains all the boilerplate code generated by UHT. This file must be included as the last #include in the header file. Note that we do not need GENERATED_BODY() in an enum.
  2. External linkage macros do not apply to enums.
  3. Per recommended conventions, the E prefix is used to denote an enum.
  4. Explicitly specify the underlying type of the enum. uint8 is the only allowed type if we intend to use the BlueprintType specifier.Even if you don't intend to use Blueprints, it's a good idea to use the smallest possible type to save memory and bandwidth.

UENUM

UENUM only accepts a handful of specifiers and meta specifiers.

UENUM(BlueprintType)

Exposes this struct as a type that can be used for variables in Blueprints.

In order to use this specifier, the underlying type of the enum must be uint8. This means that the enum will be limited to 256 values, but that is usually more than enough.

UENUM(Category="Category")

Associates a category with the enum. Categories can be nested by separating them with a pipe (|) without spaces, e.g., Main Category|Subcategory.

UENUM(ScriptName="Name")

Specifies an alternate name for the enum to be displayed in the editor.

UENUM(ToolTip="Some Description")

Used to provide a description of the enum that will be displayed in the editor when the enum is hovered over.

UMETA

In a UENUM, we can use the UMETA specifier to add metadata to the enum values. There are a few specifiers that can be used with UMETA. Note that these specifiers are all considered to be meta data, but they do not have to be declared under meta=().

UMETA(DisplayName="Name")

Specifies an alternate name for the enum value to be displayed in the editor. For example:

UENUM(BlueprintType)
enum class EMovementDirection : uint8 {
    Fwd UMETA(DisplayName = "Forward"),
    Bwd UMETA(DisplayName = "Backward")
};

UMETA(ToolTip="Some Description")

Used to provide a description of the enum value that will be displayed in the editor when the value is hovered over.

UMETA(Hidden)

Hides the enum value in the dropdown selector in the editor and in Blueprints.

Bitmasks

Enumerations are frequently used to define bitmask flags. To do this, we'll need a couple of meta specifiers, a macro call and a few additional steps.

UENUM(..., meta=(Bitflags/* (1)! */, UseEnumValuesAsMaskValuesInEditor=true/* (2)! */))
enum class EWeaponType : uint8 {
    None        = 0 UMETA(Hidden)/* (3)! */,
    Common      = 1 << 0/* (4)! */,
    Uncommon    = 1 << 1,
    Rare        = 1 << 2,
    Epic        = 1 << 3,
    Legendary   = 1 << 4,
    Mythic      = 1 << 5,
    Exotic      = 1 << 6,
};
ENUM_CLASS_FLAGS(EWeaponType);/* (5)! */
  1. Indicates that the enum contains bit flags.
  2. Indicates that the enum can be used as a UPROPERTY(meta=(Bitmask)). See the example below.
  3. We'll need to define a None value to represent the absence of any flags. No point in showing it in the editor, though.
  4. To avoid overlapping bits, we use the left shift operator to assign each flag a unique value.
  5. This extra macro call is required to register the enum values as bit flags globally.

Next, we can define a UPROPERTY in a class to use the EWeaponType enum as a bitmask:

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(Bitmask, BitmaskEnum=EWeaponType))
  int32 WeaponType;

The property will now show up in the editor as a bitmask, allowing us to select multiple flags.

Bitmask Property

You can pass the exact same specifiers to UPARAM to get the same behavior in function nodes:

UFUNCTION(BlueprintCallable)
  bool SpawnWeapon(UPARAM(meta=(Bitmask, BitmaskEnum=EWeaponType)) int32 AllowedTypes);

The resulting node will look like this:

Bitmask Function

Iteration

UENUMs can be iterated over using range-based for loops:

for (EWeaponType WeaponType : TEnumRange<EWeaponType>()) {
    // Do something with WeaponType
}

Before we can use this syntax, however, we need to inform the engine about the range of values the enum can take. There are three ways to do this:

  • ENUM_RANGE_BY_COUNT can be used with enums that have a contiguous range of values. We'll need to add an extra value at the end to represent the count. This is the legacy way of defining the range, and you'll find it in many places in the engine.

    UENUM()
    enum class ECanvasColor : uint8 {
        Red,
        Green,
        Blue,
        Count UMETA(Hidden)
    };
    ENUM_RANGE_BY_COUNT(ECanvasColor, ECanvasColor::Count);
    
  • ENUM_RANGE_BY_FIRST_AND_LAST can also be used with enums that have a contiguous range of values, but we won't have to add an extra value at the end.

    UENUM()
    enum class ECanvasColor : uint8 {
        Red,
        Green,
        Blue
    };
    ENUM_RANGE_BY_FIRST_AND_LAST(ECanvasColor, ECanvasColor::Red, ECanvasColor::Blue);
    
  • ENUM_RANGE_BY_VALUES can be used with enums that have a non-contiguous range and can even point to a subset of the values.

    UENUM()
    enum class ECanvasColor : uint8 {
        Red = 1,
        Green = 7,
        Blue = 42
    };
    ENUM_RANGE_BY_VALUES(ECanvasColor, ECanvasColor::Red, ECanvasColor::Blue, ECanvasColor::Green);
    

  1. This is simply due to the fact that enum class encloses the enumeration values in a namespace, which helps avoid naming conflicts. We could resolve such conflicts by using namespaces, but Epic is not a fan of them.