Hi!
I've created this simple state machine for creating simple menus and game states during my LD48.
[attachment deleted by admin]
Here is how it's work:
PROTOTYPE STATEPROTO: state AS TSTATE
TYPE TSTATE
id
typ$ //const enum
pos AS TPOINT
_paused //bool
_visible //bool
_created //bool
_inited //bool
create AS STATEPROTO //first create, load everything
destroy AS STATEPROTO //destroy, unload everything
init AS STATEPROTO //init after create or reset
reset AS STATEPROTO //reset all the stuff then init
update AS STATEPROTO //update
render AS STATEPROTO //draw
FUNCTION _superinit:
self.id=-1
self._paused = TRUE
self._created = FALSE
self._inited = FALSE
self._visible = FALSE
self.pos.set(0,0)
ENDFUNCTION
FUNCTION _isready:
IF (self._created AND self._inited)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNCTION
FUNCTION _pause:
self._paused=TRUE
ENDFUNCTION
FUNCTION _start:
IF (self._isready())
self._paused=FALSE
ENDIF
ENDFUNCTION
ENDTYPE
// call these super TSTATE functions from your custom functions
FUNCTION state_create: stateid //first create, load everything
ALIAS state AS STATES[stateid]
state.create(state)
state._created=TRUE
state_init(stateid)
ENDFUNCTION
FUNCTION state_destroy: stateid //destroy, unload everything
ALIAS state AS STATES[stateid]
state_destroy(stateid)
state._paused=TRUE
state._inited=FALSE
state._created=FALSE
ENDFUNCTION
FUNCTION state_init: stateid //init after create or reset
ALIAS state AS STATES[stateid]
DBG("state._created:"+state._created,"state_init")
IF (state._created)
DBG("state.init()","state_init")
state.init(state)
state._inited=TRUE
ENDIF
ENDFUNCTION
FUNCTION state_reset: stateid //reset all the stuff then init
ALIAS state AS STATES[stateid]
state.reset(state)
state._inited = FALSE
state.pos.set(0,0)
state_init(stateid)
ENDFUNCTION
FUNCTION state_update: stateid //update
ALIAS state AS STATES[stateid]
IF (NOT state._paused)
state.update(state)
ENDIF
ENDFUNCTION
FUNCTION state_render: stateid //draw
ALIAS state AS STATES[stateid]
state.render(state)
ENDFUNCTION
FUNCTION state_emptyfunction: state AS TSTATE //dummy empty function
ENDFUNCTION
GLOBAL STATES[] AS TSTATE
GLOBAL currStateID
GLOBAL nextStateID
GLOBAL switchingState
FUNCTION newState AS TSTATE: typ$
LOCAL o AS TSTATE
o.typ$ = typ$
o.create = state_emptyfunction
o.destroy = state_emptyfunction
o.reset = state_emptyfunction
o.update = state_emptyfunction
o.render = state_emptyfunction
o._superinit()
RETURN o
ENDFUNCTION
FUNCTION addState: o AS TSTATE
o.id = BOUNDS(STATES[], 0)
DIMPUSH STATES[], o
RETURN o.id
ENDFUNCTION
FUNCTION changeState: stateid
STATES[currStateID]._pause()
currStateID = stateid
STATES[currStateID]._start()
ENDFUNCTION
//an empty template, copy this for a new State, and rename all the words with 'Template' in it.
CONSTANT STATETYPE_TEMPLATE$ = "template"
FUNCTION state_Template_create: state AS TSTATE //first create, load everything
ENDFUNCTION
FUNCTION state_Template_destroy: state AS TSTATE //destroy, unload everything
ENDFUNCTION
FUNCTION state_Template_init: state AS TSTATE //init after create or reset
IF (state._created)
ENDIF
ENDFUNCTION
FUNCTION state_Template_reset: state AS TSTATE //reset all the stuff then init
ENDFUNCTION
FUNCTION state_Template_update: state AS TSTATE //update
ENDFUNCTION
FUNCTION state_Template_render: state AS TSTATE //draw
ENDFUNCTION
FUNCTION state_Template_register:
LOCAL state AS TSTATE
state = newState(STATETYPE_TEMPLATE$)
state.create = state_Template_create
state.destroy = state_Template_destroy
state.init = state_Template_init
state.reset = state_Template_reset
state.update = state_Template_update
state.render = state_Template_render
RETURN addState(state)
ENDFUNCTION
So we can create as many sates as we want, we can start/pause them, or reset/destroy.
Each state implements these functions (or use the default empty one):
create: load all the resources (Sprites, images, sounds, music, other resources), call only once
destroy: unload all the resources loaded on create, free all the memory used by state, If you want to use this state again, you have to create again before.
init: can be called only after already created the state, init all the variables, init timers, health points and so on
reset: reset the state, free all the variables initialized during init, then call the init to reinit them
update: process state, make all the calc inside update or ignore it if the state paused
render: render the state, make all your draw inside render
The main loop using state machine:
//gameloop
WHILE NOT KEY(0)
//init
CLEARSCREEN RGB(128,128,128)
IF (switchingState)
IF (transitionStates())
changeState(nextStateID)
ENDIF
//render
state_render(currStateID)
state_render(nextStateID)
ELSE
//process
state_update(currStateID)
//render
state_render(currStateID)
ENDIF
//finish
SHOWSCREEN
WEND
Hi Albert. Really great work, beautifully done! It clearly illustrates working GUI techniques in GLBasic. Thank you for sharing it with us.
Interesting stuff Albert, summit for me to play with later.
Thanx
Great!
A bit over my possibility of understanding but, I guess a template for LD work always comes in handy to speed us up on such and many more occasions.
Hi guys! Does anyone know why the code fails on some earlier versions of GLBasic? I wasn't able to run the project on v.9.006, but it worked fine on v.10.202.
If you think this stuff is cool, then check out my LD48 entry. Unfinished, but there are some cool features in it, like the simple platform game physics http://www.ludumdare.com/compo/ludum-dare-23/?action=preview&uid=11574
Hi Albert thanks for the Code, I use in my game Machine States, and goes very well for make a lot of things into in a Class
Thank you.
Very useful. :good:
I've compiled to HTML5 and uploaded to Kongregate just to see if this is possible. http://www.kongregate.com/games/Alberton/glbasic-test-game
Not the perfect game to showcase GLBasic true performance but I wanted smthing playable fast.
Super interesting! :good:
I gave a try, works fine, hard to play.