**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