**Executive summary** Are there any good tutorials on Game State FSMs, particularly OOP/OOD-based and not switch-statement-based? **Summary** Is there a practical process for creating a game state engine other than using a switch statement, preferably with examples? [Wikipedia's event-driven FSM article][1] only shows a switch statement and the article on [FSMs][2] is strictly theory-based with no practical examples. **Background** Currently all my games use a very simplistic game state "engine" that is just a switch statement. It's very messy, violates DRY horribly, and does not scale very well. Each state is treated as a level and must be checked at all three points throughout the game loop, i.e. Input, Processing, and Render. If a level or screen is added it must be added to all the switch statements. Practical example of recreation of pac-man for educational purposes (seriously, this was a school project a few years ago): enum STATE { LOADING, TITLE, LEVEL_ONE, LEVEL_TWO, LEVEL_THREE, LEVEL_FOUR, LEVEL_FIVE, LEVEL_SIX, LEVEL_SEVEN, WIN, GAME_OVER, CREDITS, }GAME_STATE; void Input() { keyboard->Update(); //Check if the user wants to quit, if so, jump out. if(keyboard->KeyDown(KEY_ESC)) quit = true; if(quit) return; switch(GAME_STATE) { case LOADING: break; case TITLE: if(keyboard->isKeyPressed()) NEXT_LEVEL = true; break; case LEVEL_ONE: case LEVEL_TWO: case LEVEL_THREE: case LEVEL_FOUR: case LEVEL_FIVE: case LEVEL_SIX: case LEVEL_SEVEN: if(keyboard->KeyPress(KEY_PAUSE)) return; InputCommon(); break; case WIN: case GAME_OVER: if(keyboard->KeyDown(KEY_ENTER)) GAME_STATE = CREDITS; break; case CREDITS: if(keyboard->isKeyPressed()) quit = true; break; } } void Processing(int dead) { //Do nothing if user wants to quit. if(quit) return; if(keyboard->KeyPress(KEY_PAUSE)) return; deathTimer = dead; if(NEXT_LEVEL) { createFood(); LoadLevel(); } switch(GAME_STATE) { case LOADING: break; case TITLE: break; case LEVEL_ONE: case LEVEL_TWO: case LEVEL_THREE: case LEVEL_FOUR: case LEVEL_FIVE: case LEVEL_SIX: case LEVEL_SEVEN: ProcessLevel(); if(GAME_STATE == LEVEL_SEVEN) { showWalls = true; sh->Index(BLEVELSEVEN)->SetAlpha(0); clear_bitmap(FOG_OF_WAR->GetImage()); rectfill(FOG_OF_WAR->GetImage(), 0, 0, 226, _gw->GetHeight(), TRANSPARENT); rectfill(FOG_OF_WAR->GetImage(), 798, 0, _gw->GetWidth(), _gw->GetHeight(), TRANSPARENT); for(int i = 0; i < MAX_PLAYERS; i++) { circlefill(FOG_OF_WAR->GetImage(), player[i]->GetX() + player[i]->GetSprite()->GetCenterX(), player[i]->GetY() + player[i]->GetSprite()->GetCenterY(), 90, TRANSPARENT); } } break; case WIN: break; case GAME_OVER: break; case CREDITS: break; } } void Render() { //Do nothing if the user wants to quit. if(quit) return; //Start render process. _gw->StartRender(); switch(GAME_STATE) { case LOADING: break; case TITLE: sh->Draw(_gw->GetBackBuffer(), sh->Index(0)); break; case LEVEL_ONE: case LEVEL_TWO: case LEVEL_THREE: case LEVEL_FOUR: case LEVEL_FIVE: case LEVEL_SIX: case LEVEL_SEVEN: //BACKGROUND RenderLevel(); //OBJECTS //PILLS for(int i = 0; i < MAX_PILLS; i++) { if(pills[i]->IsAlive()) sh->Draw(_gw->GetBackBuffer(), pills[i]->GetSprite()); } //FOOD for(int i = 0; i < MAX_FOOD; i ++) { if(food[i]->IsAlive()) sh->Draw(_gw->GetBackBuffer(), food[i]->GetSprite()); if(hasFood[i] == true) sh->Draw(_gw->GetBackBuffer(), dynamic_cast<AnimatedSprite*>(food[i]->GetSprite())->GetFrames()[i], 800, 100 + (i * 32), WHITE, 0, 255); } if(showWalls) { //WALLS for(int i = 0; i < MAX_WALLS; i++) { rectfill(_gw->GetBackBuffer(), walls[i]->GetX(), walls[i]->GetY(), walls[i]->GetX() + walls[i]->GetWidth(), walls[i]->GetY() + walls[i]->GetHeight(), walls[i]->GetColor()); } for(int i = 0; i < MAX_HIDDEN; i++) { rectfill(_gw->GetBackBuffer(), hidden[i]->GetX(), hidden[i]->GetY(), hidden[i]->GetX() + hidden[i]->GetWidth(), hidden[i]->GetY() + hidden[i]->GetHeight(), hidden[i]->GetColor()); } } if(showWaypoints) { for(int i = 0; i < MAX_WAYPOINTS; i ++) { rectfill(_gw->GetBackBuffer(), waypoints[i]->GetX(), waypoints[i]->GetY(), waypoints[i]->GetX() + 7,waypoints[i]->GetY() + 7, BLACK); } } //Copy area for teleports copyTeleports(); for(int i = 0; i < MAX_PLAYERS; i++) { if(player[i]->IsAlive()) { switch(player[i]->GetFacing()) { case Actor::RIGHT: sh->Draw(_gw->GetBackBuffer(), player[i]->GetSprite()); break; case Actor::LEFT: sh->Draw(_gw->GetBackBuffer(), player[i]->GetSprite()); break; case Actor::UP: sh->Draw(_gw->GetBackBuffer(), player[i]->GetSprite()); break; case Actor::DOWN: sh->Draw(_gw->GetBackBuffer(), player[i]->GetSprite()); break; } } else { sh->Draw(_gw->GetBackBuffer(), player[i]->GetSprite()); } } //ENEMIES for(int i = 0; i < MAX_ENEMIES; i++) { if(enemies[i]->IsAlive()) { switch(enemies[i]->GetFacing()) { case Actor::RIGHT: sh->Draw(_gw->GetBackBuffer(), enemies[i]->GetSprite()); break; case Actor::LEFT: sh->DrawFlip(_gw->GetBackBuffer(), enemies[i]->GetSprite(), SpriteHandler::VERTICAL); break; case Actor::UP: enemies[i]->GetSprite()->SetRotation(-90.0f); sh->DrawRotate(_gw->GetBackBuffer(), enemies[i]->GetSprite()); break; case Actor::DOWN: enemies[i]->GetSprite()->SetRotation(90.0f); sh->DrawRotateFlip(_gw->GetBackBuffer(), enemies[i]->GetSprite(), SpriteHandler::HORIZONTAL); break; } } } for(int i = 0; i < MAX_PUPS; i ++) { if(powerups[i]->IsAlive()) sh->Draw(_gw->GetBackBuffer(), powerups[i]->GetSprite()); } //MOVED FOOD TO BEFORE TELEPORT COPY SO FOOD DOESN'T GET COVERED. for(int i = 0; i < MAX_PLAYERS; i++) { drawUI(i); } //Draw area for teleports. drawTeleports(); //Draw fog of war for level seven. if(GAME_STATE == LEVEL_SEVEN) { sh->Draw(_gw->GetBackBuffer(), FOG_OF_WAR); } //Show level number textprintf_ex(_gw->GetBackBuffer(), UI_font->GetFont(), 20, 0, WHITE, -1, "LEVEL %d", GAME_STATE - 1); //Show PAUSE. if(keyboard->KeyPress(KEY_PAUSE)) { textout_ex(_gw->GetBackBuffer(), UI_font->GetFont(), "PAUSED", (_gw->GetWidth() / 2) - (UI_font->GetLength("PAUSED") / 2), (_gw->GetHeight() / 2) - (UI_font->GetHeight() / 2), WHITE, -1); } break; case WIN: sh->Draw(_gw->GetBackBuffer(), sh->Index(3)); break; case GAME_OVER: sh->Draw(_gw->GetBackBuffer(), sh->Index(1)); break; case CREDITS: sh->Draw(_gw->GetBackBuffer(), sh->Index(2)); break; } //End render process, display to screen. _gw->EndRender(); } [1]: http://en.wikipedia.org/wiki/Event_driven_finite_state_machine [2]: http://en.wikipedia.org/wiki/Finite_state_machine