2010-12-15 3 views
2
C++

Я пишу программу Снейк в C++ (визуализация с JNI) и действительные кнопки перемещения либо влево (перемещение на 90 ° против часовой стрелки) или вправо (перемещение на 90 ° по часовой стрелке).Вложенные если-заявления

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

if(key_event != NULL){ 
    if(key_event == LEFT){ 
     if(moveDirection == UP || moveDirection == DOWN){ 
      moveDirection = LEFT; 
      (moveDirection == UP) ? /*change turn after head*/ : /*change turn after head*/; 
     } //else if (moveDir == LEFT || RIGHT) 
    } //else if (key_event == RIGHT) 
    //... 
} 

КРП с: /*change turn after head*/ является потому что если змея движется вниз и идет влево есть другой график для поворота тогда, когда это будет до и идет влево.

Это приводит к множеству if-утверждений и не очень читаемо, поэтому мне интересно, существует ли общий способ решения вложенных if-утверждений, подобных этому.

EDIT:
key_event и moveDirection - перечисления.

ответ

5

У меня лично была бы другая функция, чтобы применить ход.

if(key_event != NULL){ 
    if(key_event == LEFT){ 
     moveLeft(); 
    } 
    ... 
} 

void moveLeft() 
{ 
    switch(moveDirection) 
    { 
    case UP: 
    case DOWN: 
     moveDirection = LEFT; 
     break; 
    case ... 
    } 
} 

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

Дело в том, когда у вас есть вложенные петли, посмотрите, можете ли вы разбить его на функцию, чтобы упростить задачу.

Надеюсь, это поможет.

+0

В основном это комбинация ответов walkTarget и Mark B, это, безусловно, должно улучшить читаемость, спасибо! – Aerus 2010-12-15 15:15:39

3

Скорее всего, код будет более читабельным, если вы учитываете разделы проверок if в функциях. Не видя полного контекста выполняемой работы, трудно сказать наверняка, но что-то вроде get_new_direction(event, direction), вероятно, станет хорошим началом. Когда компоненты проверок if являются хорошо названными функциями, это поможет как читабельности, так и, возможно, уровням вложенности.

+0

Я задаюсь вопросом, почему я не думал об этом! Будет определенно использовать это, в сочетании с ответом raRaRa, спасибо! – Aerus 2010-12-15 15:18:21

1

Если ваш key_event является int или char, вы можете использовать оператор switch, который может быть более читаемым.

if (moveDirection == UP || moveDirection == DOWN) 
{ 
    switch (key_event) 
    { 
     case LEFT: 
      //Turn snake left 
      break; 
     case RIGHT: 
      //Turn snake right 
      break; 
     default: 
      //Invalid turn, do nothing 
      break; 
    } 
} 
else if (moveDirection == LEFT || moveDirection == RIGHT) 
{ 
    switch (key_event) 
    { 
     case UP: 
      //Turn snake up 
      break; 
     case DOWN: 
      //Turn snake down 
      break; 
     default: 
      //Invalid turn, do nothing 
      break; 
    } 
} 
+0

Это действительно `int`, я удивлен, так как мой профессор сказал, что перечисления были автономными типами на C++, а не как C, они были в основном ints. Будет использовать это в сочетании с ответом raRaRa, спасибо! – Aerus 2010-12-15 15:17:00

6

Похоже, Finite State Machine - это то, что вам нужно здесь. Вы можете определять ключи как события, змеиные позиции как состояния и описывать переходы из одного состояния в другое в зависимости от события (какая клавиша нажата). Это позволит решить проблему и использовать таблицу перехода вместо вложенных операторов if и сделать вашу реализацию менее подверженной ошибкам. Boost Statechart Library может помочь вам быстро реализовать его.

+0

Я никогда не слышал об этом раньше, это выглядит очень интересно и определенно стоит попробовать, возможно, немного переборщить для этого проекта. Проблема в том, что это часть задания, и нам разрешено использовать стандартные библиотеки и уже заданные файлы заголовков. Я обязательно проверю это на другие проекты! – Aerus 2010-12-15 15:13:55

1

Ну, очевидно, вы могли бы устранить первое "если" что-то вроде этого:

if(keyEvent == null) 
return; 

Далее, использовать что-то вроде этого:

Action defaultAction = TurnLeftAction; 
if(keyEvent == Up) 
    defaultAction = TurnUpAction; 
else if(keyEvent == Down) 
defaultAction = TurnDownAction; 
else 
defaultAction = TurnRightAction; 

defaultAction.Execute(); 

Я надеюсь, что вы пойманной идею :)

+0

Мне нравится, как устранить первое, если, я не могу этого сделать, потому что есть кусок кода под ним, который будет пропущен. (я мог бы поместить все if-утверждения в новый метод, а затем я смог бы это сделать) Хорошая идея! – Aerus 2010-12-15 15:25:49

0

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

+0

Теперь я буду использовать коммутационный футляр, поскольку, как вы упомянули, это самый простой вариант, который дает мне достаточную читаемость. – Aerus 2010-12-15 15:28:23

1

Вы можете сделать это с объектами:

class MoveState { 
    public: 
     MoveState* getNextState(MoveDirection change); 
     GraphicsStuff* getDirectionChangeGraphics(MoveDirection change); 
     void setNextState(MoveDirection change, MoveState* nextState, GraphicsStuff* graphics); 
} 

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

MoveState STATES[4]; 

void init() { 
    STATES[UP].setNextState(LEFT, &STATES[LEFT], whatever); 
    STATES[UP].setNextState(RIGHT, &STATES[RIGHT], whatever); 
    STATES[DOWN].setNextState(LEFT, &STATES[RIGHT], whatever); 
    STATES[DOWN].setNextState(RIGHT, &STATES[LEFT], whatever); 
    ... 
} 

void handleInput() { 
    ... 
    if(key_event != NULL){ 
     MoveDirection change = convertEventToDirection(key_event); 
     MoveState* nextState = currentState->getNextState(change); 
     if (nextState != NULL) { 
      drawCoolGraphics(currentState->getDirectionChangeGraphics(change); 
      currentState = nextState; 
     } 
    } 
} 

Вы также можете сделать это путем подкласса MoveState для каждого направления движения. Это зависит от того, где вы хотите разместить свой код и как его можно продлить. Подкласс более гибкий, но в этом случае может быть излишним.

Это должно быть менее подвержено ошибкам, поскольку вы можете тестировать каждый экземпляр MoveState гораздо легче, чем большой оператор if-then-else или switch.

EDIT: Heh. Это в основном менее краткий вариант того, что предложил @Vlad.

+0

Я думаю, что это очень общий подход к такой проблеме, и мне нужно будет изучить его немного больше, но я, вероятно, попробую реализовать его так, как вы говорите, как упражнение для себя. Спасибо за вклад! – Aerus 2010-12-15 15:23:21

2

Простая таблица поиска может помочь:

enum dir { UP, RIGHT, DOWN, LEFT }; 

dir lturn[] = {LEFT, UP, RIGHT, DOWN}; 
dir rturn[] = {RIGHT, DOWN, LEFT, UP}; 

, так что вы можете написать

dir curr_dir; 
if (moveDirection == LEFT) 
    curr_dir = lturn[curr_dir]; 
if (moveDirection == RIGHT) 
    curr_dir = rturn[curr_dir]; 
1

Я бы рекомендовал, представляющий направление змеи как вектор, четыре направления бытия (1 0) - правый, (-1 0) - левый, (0 1) - вверх и (0 -1) вниз. Это позволит устранить большинство коммутаторов в вашей программе, поскольку перемещение змеи обрабатывается простой математикой, а не путем сопоставления произвольных значений перечисления для перемещения.

struct Vector2D 
{ 
    int x, y; 
    Vector2D(int x, int y); 
    ... 
}; 

Затем поворот влево и вправо становится:

Vector2D turn_left(Vector2D direction) 
{ 
    return Vector2D(-direction.y, direction.x); 
} 

Vector2D turn_right(Vector2D direction) 
{ 
    return Vector2D(direction.y, -direction.x); 
} 

И ключи будут обрабатываться:

if (key_event == LEFT) snake_direction = turn_left(snake_direction); 
else if (key_event == RIGHT) snake_direction = turn_right(snake_direction); 
+0

Хотя это тоже мой подход, я не могу реализовать это, потому что это часть задания.В этом задании нам даны фиксированные файлы заголовков и ограничения (которые ИМО убирает след и ошибки и логики, которые нам пришлось бы решить для решения этой проблемы), которые мы должны реализовать. Моя змея работает, и если мне когда-нибудь захочется оптимизировать это в свободное время, я дам этот подход выстрелом :). – Aerus 2010-12-15 19:52:16

Смежные вопросы