2010-12-15 151 views
2

我在写C一个蛇程序++(可视化JNI)和有效移动按钮要么(移动90°逆时针)或(顺时针移动90°)。嵌套if语句C++

在游戏圈我检索一个GUI界面的关键事件,并移动根据该关键事件我蛇的每一个循环,这是怎么做到这一点:

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语句。编辑:
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

基本上这是walkingTarget和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中那样,它们基本上是整数。 将与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来完成此操作。这取决于你想要放置代码的位置以及它需要扩展的程度。子类化更灵活,但在这种情况下可能会矫枉过正。

这应该不太容易出错,因为您可以比大的if-else-else或switch语句更容易地单元测试MoveState的每个实例。

编辑:嘿。这基本上是@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

尽管这也是我的方法,但我无法实现,因为这是作业的一部分。在这个任务中,我们给出了固定的头文件和限制(IMO除去了我们不得不通过解决这个问题的路径和逻辑),我们必须实现这些。我的蛇的作品,如果我想在我的空闲时间优化这个,我会给这个方法一个镜头:)。 – Aerus 2010-12-15 19:52:16