J Mor
J Mor

Reputation: 185

Explicitly defining flag combinations in an enum

I was thinking of implementing an enum that defines the state of a game object, and I wanted to know if I could directly use flags within the enum's definition, instead of defining the object's state as a collection of flags with no easy, pre-defined, global name for the states used in the state machine.

For example, let's say there are 5 states: PreActivation (Created but not started; i.e. an enemy in a future wave), Active (Currently in use; i.e. an enemy on the screen, attacking you), Paused (No longer active, but may reactivate; i.e. an enemy if the player uses a time-freezing power), DeActivated (An object whose finished use but is still in the game world; i.e. an enemy whose body is left after death like in Doom 1 & 2), and ToRemove (An object slated for removal from the game; i.e. an enemy after you clear a level and move to the next one).

What I want to do is define the enum so the states hold all applicable flags; for instance, a DeActivated enemy: 1. Has been previously activated, and 2. Isn't currently active. My current thinking is doing something like this:

public enum ObjectState
{
    // The first section are the flags
    BeenActivated   = 0b0000001, // Previously activated
    CurrentlyActive = 0b0000010, // Currently activated
    IsSuspended     = 0b0000100, // It may be reactivated
    ShouldRemove    = 0b0001000, // It should be removed
    // These are the states
    PreActivation   = 0b0000100, // Mot currently active, nor has it ever been active, but it will get activated
    Active          = 0b0000011, // Currently active,     and it's been active
    Paused          = 0b0000101, // Not currently active, but it's been active before
    DeActivated     = 0b0000001, // Not currently active, but it's been active before, and it shouldn't get reactivated, but don't remove yet
    ToRemove        = 0b0001001  // Not currently active, but it's been active before, and it shouldn't get reactivated, it should be removed
}

As far as I know, this should work correctly, but I have a few main concerns:

  1. Are there any problems likely to come from this?
  2. Is this bad practice?
  3. Is this bad practice? And, if it is;
    • A. What's wrong with it?
    • B. What should I do instead? I'd just make the object's state a collection of these flags, but I'd like a shorthand enum for specific states, as this allows for complexity for specific instances and simplicity when it's needed. Is there a more acceptable way to achieve this?

Sorry if this is a repeat or I broke some other rule, but I just created an account today; this is my 1st post. Plus, I'm not sure what you would call this when searching, and I didn't get any similar hits from here or Google.

Upvotes: 16

Views: 3305

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112712

You can do so. It's the very point of flag enums. If an enum is intended to work as flags, mark it with the [Flags] attribute.

I would suggest to combine the existing flags with a bitwise or (|) instead. It's more readable and less error-prone.

[Flags]
public enum ObjectState
{
    // Flags
    BeenActivated   = 0b0000001, // Previously activated
    CurrentlyActive = 0b0000010, // Currently activated
    IsSuspended     = 0b0000100, // It may be reactivated
    ShouldRemove    = 0b0001000, // It should be removed

    // States as combination of flags.
    PreActivationState   = IsSuspended,                     // Mot currently active, nor has it ever been active, but it will get activated
    ActiveState          = BeenActivated | CurrentlyActive, // Currently active,     and it's been active
    PausedState          = BeenActivated | IsSuspended,     // Not currently active, but it's been active before
    DeActivatedState     = BeenActivated,                   // Not currently active, but it's been active before, and it shouldn't get reactivated, but don't remove yet
    ToRemoveState        = BeenActivated | ShouldRemove     // Not currently active, but it's been active before, and it shouldn't get reactivated, it should be removed
}

I also added a "State" suffix to the states to better differentiate them from flags. Or turn it around and add a "Flags" suffix to the flags instead.

Upvotes: 15

Jan Köhler
Jan Köhler

Reputation: 6030

Did you consider using a [Flags]-enum?

[Flags]
public enum ObjectState
{
    BeenActivated   = 1, // Previously activated
    CurrentlyActive = 2, // Currently activated
    IsSuspended     = 4, // It may be reactivated
    ShouldRemove    = 8, // It should be removed
}

// Currently active, and it's been active
var active = ObjectState.BeenActivated | ObjectState.CurrentlyActive;

Upvotes: 4

Sentinel
Sentinel

Reputation: 3697

It is hard to know really without more context, but you may wish to look into State Pattern for a more extensible and OO way of doing things.

The way it works is that you make your game object an empty shell, implementing the same interface an abstract Game State class does. The game object will call the state to do what the interface specifies, while the state has a reference to its containing game object. When state changes, the state instance tells the game object.

So for example, let us say your game object has an interface with one method only, "Attack". The abstract state class has two concrete subclasses, Alive and Dead. Alive and Dead both implement Attack too. When something attacks your game object, object.Attack(); , the object internally just calls state.Attack(). The Alive state will decide if the attack was successful and do parent.State=new DeadState();. When attack is called again, DeadState does nothing.

This way you avoid enums, switches, ifs,everything, and you just add gamestates without further programming. Your "been activated" check would be an interface method too.

Upvotes: 1

Related Questions