Есть ли хороший способ использовать логические операции на System.Enum в C #? - PullRequest
1 голос
/ 03 февраля 2012

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

Например, у меня есть что-то вроде, схематично:

[Flags]
enum Actions { None=0, MoveUp=1, MoveDown=2, MoveLeft=4, MoveRight=8 }

Actions currentActions;

private void SetActions()
{
 currentActions = Actions.None; 
 if (UpPressed)
     currentAction |= Actions.MoveUp;
 (etc)

}

public void Update()
{
 SetActions();
 if (currentActions & Actions.MoveUp) == Actions.MoveUp)
     MoveUp();
 (etc)
}

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

То, что я хотел бы сделать, - это создать новый класс "ActionManager", который содержит данные и методы, относящиеся к действию;

public Enum currentActions;
public void SetActions();
public void UpdateActions();

и другие классы, например, класс, соответствующий меню, могут добавлять свои собственные перечисления к этому глобальному Enum, так что каждый набор возможных действий определяется внутри каждого класса, отвечающего за обработку ввода, вместо того, чтобы быть предопределенным в глобальном перечислении.

Но я не могу этого сделать, потому что я не могу добавлять или удалять из перечисления и не могу наследовать от System.Enum. Единственный способ сделать это - списки перечислений:

// In class ActionManager
public List<Enum> allowedActions = new List<Enum>();
public List<Enum> currentActions = new List<Enum>();

public void SetActions(Enum action)
{
 currentActions.Clear();
 foreach (Enum e in allowedActions)
     if (IsKeyPressed(e))             
         currentActions.Add(e);
}

// In, say, GameMenu class
[Flags]
enum MenuActions { None=0, MenuUp = 1, ... }

private actionManager ActionManager = new ActionManager();

public void DefineActions()
{
 foreach (MenuActions m in Enum.GetValues(typeof(MenuActions)))
     actionManager.allowedActions.Add(m);
     //also define the key corresponding to the action 
     //here, from e.g., a config file
     //and put this into a Dictionary<buttons, actions>
}

public Update()
{
 actionManager.Update();

 if (actionManager.currentActions.Contains(MenuActions.MenuUp))
 (etc)
}

Но это кажется глупым, я заменил дешевые логические операции списком Enums, что кажется неудобным. Это также вынуждает меня использовать .Contains и т. Д. Вместо арифметики и использовать ссылочные типы, где типы значений будут иметь больше смысла (и универсальные списки в любом случае не должны отображаться снаружи).

Я понимаю, что мог бы также заменить свой Список на int и привести все перечисления моего другого класса к целым, чтобы я мог или действовать вместе, но это не обеспечивает безопасность типов перечислений.

Итак, мои вопросы: Есть ли способ сделать это с помощью перечислений? Есть ли более умный способ делать подобные вещи вообще (с помощью перечислений или другим способом полностью)?

Я делаю это, чтобы выучить язык, поэтому я был бы признателен за комментарии, если есть намного лучший способ сделать что-то подобное!

1 Ответ

3 голосов
/ 03 февраля 2012

Да; Вы можете использовать битовые операторы:

Actions allowedActions = Actions.Up | Actions.Right;
allowedActions |= Actions.Down; // to show building at runtime
Actions currentActions = Actions.Right | Actions.Down; // union

, а затем используйте различные & / | / ~ для выполнения тестов и изменений:

var limitedToAllowed = currentActions & allowedActions; // intersect

// disallow "up" for some reason
allowedActions &= ~Actions.Up;

Вы можете проверить перекрытия с помощью:

// test if **any** of the currentActions is in the allowedActions
bool anyAllowed = currentActions & allowedActions != 0;
// test if **all** of the currentActions are in the allowedActions
bool allAllowed = currentActions & allowedActions == currentActions;

Примечание о том, «какие значения допустимы», обычно можно сделать, добавив перечисление .All (в вашем случае 15), или во время выполнения:

Actions actions = 0;
foreach(Actions action in Enum.GetValues(typeof(Actions))) {
    actions |= action;
}
// actions now has all the flags that are defined
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...