Here is a simple example of a finite state machine engine - mainly aimed at beginners with a decent amount of comments. This came from a request / comment on one of the videos of the fruit ninja ghost game I’m working on - http://youtu.be/hSvaEGpdxjA
Hopefully the commenter is lurking here and finds it useful
--Simple Finite State Machine plus rising and fading sprite menu effect
-- by West
function setup()
displayMode(FULLSCREEN)
--set up the states for the Finite State Machine.
READY=1 --the state for the main menu
PLAYING=2 --the state for the actual game play
LEVELCOMPLETE=3 --the state for the level complete screen
GAMEOVER=4 --the state for the game over screen
gamestate=READY --set a variable to control the current state
-- set up a variable to allow a short delay between moving from different states
scrdelay=0
--set up a variable to store the current level
currentlevel=1
--set up a table to handle the touches
touches={}
spacemen={} --set up a table to hold each of the spacemen objects
--create a loop to add 30 new instances of the spaceman to the table
for i=1,30 do
-- insert a new instance with the following variables
-- x - the horizontal screen position of the spaceman.
-- y - the vertical screen position of the spaceman.
-- size - the size of the spaceman - initial value a random value between 0.3 and 1
-- fade - the transparency of the spaceman
-- dir - the direction of the fade with 1 meaning getting more solid and -1 getting more transparent
-- spd - the speed at which the spaceman rises up the screen
table.insert(spacemen,{x=math.random(WIDTH),y=math.random(HEIGHT),size=(2+math.random(8))/10,fade=math.random(255),dir=1,spd=math.random(10)})
end
end
-- This function gets called once every frame. This is the main program loop
function draw()
--the controller for the Finite State Machine. All this does is check to see what state the game is currently in and calls the appropriate function for that state
-- the scrdelay variable is a counter which is used to add a delay between moving between screens
if gamestate==PLAYING then
play()
elseif gamestate==READY then
scrdelay = scrdelay + 1
menu()
elseif gamestate==LEVELCOMPLETE then
scrdelay = scrdelay + 1
levelover()
elseif gamestate==GAMEOVER then
scrdelay = scrdelay + 1
gameoverscreen()
end
end
-- the function to drawthe main menu screen. Consists of 3 parts:
-- 1) the rising and fading spacemen
-- 2) the title
-- 3) the "tap to start" message which appears after the delay
-- handling the user interaction through touches is done through the touch function and is the same regardless of which gamestate we are in
function menu()
--set a background colour
background(33, 71, 148, 255)
-- 1) the rising and fading spacemen
--set up a loop where each instance of the spacemen will be dealt with in turn
for i,s in pairs(spacemen) do
--inside this loop, the current spaceman is represented by the variable s and each of the properties is accessed using the .property form
--For example, to get the x position of the current spaceman use s.x
-- set the transparency of the spaceman based on its fade value
tint(255,255,255,s.fade)
-- draw the spaceman on the screen based on its position and size. 65 and 92 are the width and height of the spaceman spriteand these vales are multiplied by the scaling factor (or size) variable
sprite("Platformer Art:Guy Standing",s.x,s.y,65*s.size,92*s.size)
--change the transparency based on the dir property which can be either 1 or -1
s.fade = s.fade + s.dir
--run a check on the fade - if it is outside the maximum value (255 / solid) or minimum value (0 / fully transparent) then reverse the direction of fade
-- so if dir was 1 then the spaceman is getting more solid by adding 1 to the fade property each loop of the main game
-- When it reaches the maximum value of 255 then set dir to be -1 and the fade value will now start to decraese and the spaceman will fade
if s.fade>255 or s.fade<1 then s.dir = s.dir * -1 end
--[[
--optional extra - move the spaceman to a new random position when it fades away completely
if s.fade<1 then
s.x=math.random(WIDTH)
s.y=math.random(HEIGHT)
end
]]--
--move the spaceman up the screen based on its speed. Here the raw speed has been slowed by a factor of 10
s.y = s.y + s.spd/10
--detect if the spaceman has gone past the top of the screen and if so then reset its height to the bottom of the screen
-- the + and - offsets are to take into account the height of the sprite. the y position is the centre of the sprite so there would be an untidy jump as the sprite would disappear when the middle of the sprite crosses the top of the screen
if s.y>HEIGHT+50 then s.y=-50 end
end
--housekeeping - reset the transparency to ensure that anything else drawn isn't faded
noTint()
-- 2) the title
--set up the font and print out the title
strokeWidth(2)
fill(171, 199, 221, 255)
font("AmericanTypewriter-Light")
fontSize(96)
text("FSM Demo",WIDTH/2,5*HEIGHT/8)
-- 3) the "tap to start" message which appears after the delay
font("AmericanTypewriter-Light")
fontSize(32)
if scrdelay>100 then
text("Tap to start",WIDTH/2,3*HEIGHT/8)
end
end
-- these next two functions are basically repeats of points 2) and 3) from the menu function above
function levelover()
background(33, 71, 148, 255)
strokeWidth(2)
fill(141, 127, 33, 255)
fontSize(64)
-- use .. to join strings together - here the current level is inserted into the message
text("Level "..currentlevel.." complete",WIDTH/2,HEIGHT/2)
fontSize(32)
if scrdelay>100 then
text("Tap for next level",WIDTH/2,3*HEIGHT/8)
end
end
function gameoverscreen()
background(33, 71, 148, 255)
strokeWidth(2)
fill(141, 127, 33, 255)
fontSize(64)
text("Game over",WIDTH/2,HEIGHT/2)
fontSize(32)
if scrdelay>100 then
text("Tap for menu",WIDTH/2,3*HEIGHT/8)
end
end
function play()
--very simple game - ifyou tap on the left hand side of the screen you win
-- this is just to demonstrate the finite state machine - obviously you would substitue your actual game function here
background(198, 18, 18, 255)
fill(117, 255, 0, 255)
rect(0,0,WIDTH/2,HEIGHT)
fill(63, 28, 144, 255)
font("AmericanTypewriter-CondensedLight")
fontSize(72)
text("The Game",WIDTH/2,3*HEIGHT/4)
text("Win",WIDTH/4,HEIGHT/2)
text("Lose",3*WIDTH/4,HEIGHT/2)
end
function touched(touch)
--this function is called each time the screen is touched regardless of what gamestate we are in.
--therefore we need to do a check on which state we are in and respond accordingly
-- in the menu, level complete and game over screens we are simply responding to a tap anywhere
-- in the actual gameplay we need to check where in the screen the tap has occured so a separate function is needed
if touch.state==ENDED or touch.state==CANCELLED then
if gamestate==PLAYING then
--separate function to check position of tap
processTouch(touch)
elseif gamestate==READY and scrdelay>100 then
--if the gamestate is on the main menu, change it to the game play state and reset the scrdelay counter
gamestate=PLAYING
scrdelay=0
elseif gamestate==LEVELCOMPLETE and scrdelay>100 then
--if the gamestate is on the level complete, change it to the game play state and reset the scrdelay counter
gamestate=PLAYING
scrdelay=0
-- increase the current level
currentlevel = currentlevel + 1
elseif gamestate==GAMEOVER and scrdelay>100 then
--if the gamestate is on the gameover, change it to the main menu state and reset the scrdelay counter
gamestate=READY
--reset the current level
currentlevel=1
end
touches[touch.id] = nil
else
touches[touch.id] = touch
end
end
function processTouch(touch)
--check the x position of the tap - if it is greater than half the width then change the game state to gameover and reset the scrdelay counter
if touch.x>WIDTH/2 then
scrdelay=0
gamestate=GAMEOVER
else
--otherwise the tap was on the left and so change the game state to level complete and reset the scrdelay counter
scrdelay=0
gamestate=LEVELCOMPLETE
end
end