Code archives/Miscellaneous/Game state engine

This code has been declared by its author to be Public Domain code.

Download source code

Game state engine by deps2005
You can use this code to split the game into different states.
Extend GenericState into things like "StatePlay", "StateMenu", "StateGamePaused", etc and just call "GameState.Switch( new StatePlay )" when you want to jump from one state to another.

Here is a quick example:
Strict 

' Include the goodies
Include "gamestate.bmx"


Graphics 800,600,0

' Switch to the menu state
GameState.Switch( New StateMenu )

' Go!
GameState.Run()

End ' We're done now.


'------------------------------------------------------------
'-------------------- Our extended states
'------------------------------------------------------------

' Create a new kind of state
Type StateMenu Extends GenericState
	Method startup()
		Print "Loading menu..."
	EndMethod
	
	Method update()
	
		If KeyHit( KEY_1 ) Then 
			GameState.switch( New StatePlay ) ' Player wants to play a game
		EndIf
		
		If KeyHit( KEY_2 ) Then GameState.ExitState() ' Exit this state.
	
	EndMethod
		
	Method draw()
		Cls
		
		DrawText "Game menu 2000 ultra power", 50,50
		
		DrawText "1.  Play the game", 50,80
		DrawText "2.  Quit", 50,100
		
		Flip ; FlushMem
	EndMethod
		
	Method shutdown()
		Print "Removing menu stuff..."
	EndMethod

	Method on_hold()
		Print "Menu is paused while another state is running."
	EndMethod
	
	Method resume()
		Print "The menu is back in bussiness!"
	EndMethod	
EndType


Type StatePlay Extends GenericState

	Field x,y, xv,yv
	Field paused_time

	Method startup()
		Print "Loading game..."
		
		x = Rand(0,800)
		y = Rand(0,600)
		xv = Rand(0,1)
		yv = Rand(0,1)
		
	EndMethod
	
	Method update()
	
		If KeyHit( KEY_ESCAPE ) Then GameState.ExitState() ' Player doesn't want to play anymore...
		If KeyHit( KEY_P ) Then GameState.Switch( New StatePause ) ' Pause the game
	
		If xv Then
			x:+1
		Else
			x:-1
		EndIf
		
		If yv Then
			y:+1
		Else
			y:-1
		EndIf
		
		If x < 0 Or x > 800 Then xv = Not xv
		If y < 0 Or y > 600 Then yv = Not yv
	
	EndMethod
		
	Method draw()
		Cls
		
		DrawRect( x-5,y-5,11,11 )
		
		DrawText "Press Escape to quit, P to pause the game", 0,0
		
		Flip ; FlushMem
	EndMethod
		
	Method shutdown()
		Print "Game is no more..."
	EndMethod

	Method on_hold()
		Print "Game paused while another state is running."
		' Just for fun, save the millisecs()
		paused_time = MilliSecs()
	EndMethod
	
	Method resume()
		Print "Game resumed"
		' For how long was we on hold?
		Print "Your little intermission lasted for "+(MilliSecs()-paused_time)+" milliseconds."
	EndMethod	
EndType



Type StatePause Extends GenericState
	Method startup()
		Print "Game paused."
		
		SetBlend ALPHABLEND
		SetColor 0,0,0
		SetAlpha 0.5
		DrawRect( 0,0, 800,600 )
		SetAlpha 1
		SetColor 255,255,255
		
		' Draw the paused message here, only need to draw it once.
		DrawText "Tea time, eh? Press P to resume.", 100,100
		Flip ; FlushMem

		FlushKeys()
		
	EndMethod
	
	Method update()
		' Just wait for the right key to be pressed
	
		If KeyHit( KEY_P ) Then GameState.ExitState() ' Exit the pause state and go back to the play state
	
	EndMethod

				
	Method draw()
		' No need to draw anything. It was done during startup
	EndMethod
		
	Method shutdown()
		Print "Unpaused."
	EndMethod

	Method on_hold()
		Print "Pause state paused? Funny that..."
		' Not that crazy after all. Many games have a game menu that can be reached during a paused game
	EndMethod
	
	Method resume()
		Print "The pause state is resumed."
	EndMethod	
EndType


And here you have gamestate.bmx:
' make a new type that extends this one. Overload the methods.
Type GenericState

	' Overload this one and put startup code here (loading images, sounds, etc)
	Method startup()
	EndMethod
	
	
	' No drawing here! Only user input and other behind-the-scene stuff
	Method update()
	EndMethod
		
	
	' Draw things in this one. But no user input or logic code!
	Method draw()
	EndMethod
		
	
	' Save highscore/nullify variables/scream like a banshee
	Method shutdown()
	EndMethod


	' Called when the engine switches to a new state.
	Method on_hold()
	EndMethod
	
	
	' Called when the engine is done with the other state and switches back t this one
	Method resume()
	EndMethod


EndType



Type GameState

	Global state_list:TList
	Global current_state:GenericState

	Function Switch( new_state:GenericState )
		If Not state_list Then state_list = CreateList()
		
		state_list.addlast( new_state )
		If current_state Then current_state.on_hold()
		new_state.startup()
		current_state = new_state

	EndFunction
	
	' Called by the running state when it doesn't want to play anymore
	Function ExitState()
	
		If Not state_list Then RuntimeError("ExitState should only be called by running states!")
	
		current_state.shutdown()
		
		state_list.remove( current_state )
		
		If state_list.Count() > 0 Then
			current_state = GenericState( state_list.last() )
		
			current_state.resume()		
		EndIf
	
	EndFunction
	
	
	Function Run()
	
		If Not state_list Then RuntimeError("Switch to a new game state before calling GameState.Run()")
	
		While state_list.Count() > 0
	
			current_state.update()
			current_state.draw()
	
		Wend
	
	EndFunction
	
	

EndType

Comments

deps2005
The logic and drawing code is separated in the states so that you could implement frame skipping in GameState.Run().

If you want to use delta time then just do it in the update() method of your states.

Hope you will find this useful.


deps2005
Oops, double post. deleted.


Code Archives Forum