Явное определение комбинаций флагов в перечислении

Я думал о внедрении перечисления, которое определяет состояние игрового объекта, и я хотел знать, могу ли я напрямую использовать флаги в определении перечисления, вместо того, чтобы определять состояние объекта как набор флагов без простых, заранее определенных, глобальное имя для состояний, используемых в конечной машине.

Например, предположим, что существует 5 состояний: PreActivation (Создано, но не запущено, т.е. Враг в будущей волне), Active (в настоящее время используется, т.е. Противник на экране, атакует вас), приостановлено (больше не активно, но может активироваться, т.е. враг, если игрок использует силу замораживания времени), DeActivated (Объект, законченное использование которого все еще находится в игровом мире, т.е. враг, тело которого осталось после смерти, как в Doom 1 и 2), и ToRemove (объект, предназначенный для удаления из игры, т.е. Противник после того, как вы очистите уровень и перейдете к следующему).

То, что я хочу сделать, - это определить перечисление, чтобы в штатах сохранялись все применимые флаги; например, DeActivated противник: 1. Был ранее активирован и 2. В настоящий момент не активен. Мое нынешнее мышление делает что-то вроде этого:

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 been active
    Paused          = 0b0000101, // Not currently active, but it been active before
    DeActivated     = 0b0000001, // Not currently active, but it been active before, and it shouldn't get reactivated, but don't remove yet
    ToRemove        = 0b0001001  // Not currently active, but it been active before, and it shouldn't get reactivated, it should be removed
}

Насколько я знаю, это должно работать правильно, но у меня есть несколько основных проблем:

  1. Есть ли какие-то проблемы, которые могут возникнуть из-за этого?
  2. Это плохая практика?
  3. Это плохая практика? И если это так;
    • А. Что с ним не так?
    • B. Что мне делать вместо этого? Я бы просто сделал объект state коллекцией этих флагов, но мне бы хотелось, чтобы перечисление конкретных состояний было выполнено, так как это позволяет сложность для конкретных экземпляров и простоту, когда это необходимо. Есть ли более приемлемый способ достичь этого?

Извините, если это повторение или я нарушил какое-то другое правило, но я только что создал учетную запись сегодня; это мой первый пост. Кроме того, я не уверен, что вы бы назвали это при поиске, и у меня не было никаких подобных хитов здесь или Google.

Ответ 1

Вы можете это сделать. Это сама точка перечисления флагов. Если enum предназначено для работы в качестве флагов, отметьте его атрибутом [Flags].

Я бы предложил объединить существующие флаги с поразрядным или (|). Это более читаемо и менее подвержено ошибкам.

[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 been active
    PausedState          = BeenActivated | IsSuspended,     // Not currently active, but it been active before
    DeActivatedState     = BeenActivated,                   // Not currently active, but it been active before, and it shouldn't get reactivated, but don't remove yet
    ToRemoveState        = BeenActivated | ShouldRemove     // Not currently active, but it been active before, and it shouldn't get reactivated, it should be removed
}

Я также добавил суффикс "State" для состояний, чтобы лучше отличать их от флагов. Или поверните его и добавьте вместо флагов суффиксы "Флаги".

Ответ 2

Считаете ли вы использование [Флаги] -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 been active
var active = ObjectState.BeenActivated | ObjectState.CurrentlyActive;

Ответ 3

Трудно понять действительно без контекста, но вы можете захотеть изучить State Pattern для более расширяемого и OO-способа делать что-то.

Способ, которым он работает, заключается в том, что вы делаете свой игровой объект пустой оболочкой, реализуя тот же интерфейс, что и абстрактный класс Game State. Игровой объект будет вызывать состояние, чтобы выполнить то, что указывает интерфейс, в то время как состояние имеет ссылку на его содержащий игровой объект. Когда состояние изменяется, экземпляр состояния сообщает игровому объекту.

Так, например, предположим, что ваш игровой объект имеет интерфейс только с одним методом "Атака". Класс абстрактного состояния имеет два конкретных подкласса: "Живые и мертвые". Живые и мертвые тоже реализуют атаку. Когда что-то атакует ваш игровой объект, object.Attack(); , объект внутренне просто вызывает state.Attack(). Состояние Alive решит, была ли атака успешной и родитель.State = new DeadState() ;. Когда атака вызывается снова, DeadState ничего не делает.

Таким образом, вы избегаете перечислений, переключателей, ifs, все, и вы просто добавляете игры без дальнейшего программирования. Ваша "активированная" проверка будет также методом интерфейса.