2010-12-15 155 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