Text adventures & state machines (a newb approaches!)

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
trubblegum
Party member
Posts: 192
Joined: Wed Feb 22, 2012 10:40 pm

Re: Text adventures & state machines (a newb approaches!)

Post by trubblegum »

Very limited time today, but in short : Not worth it - try this : viewtopic.php?p=53634#p53634
Wojak
Party member
Posts: 134
Joined: Tue Jan 24, 2012 7:15 pm

Re: Text adventures & state machines (a newb approaches!)

Post by Wojak »

in My opinion this is the simplest (I use it):

Code: Select all

local state = 1
local x = 300
local y = 200


local drawfunc = {}
function dtawanim()
	love.graphics.print("state: "..state,x,y)
end

drawfunc[1] = function()
	love.graphics.print(":)",30,30)
	dtawanim()
end

drawfunc[2] = function()
	love.graphics.print(":|",30,30)
	dtawanim()
end

drawfunc[3] = function()
	love.graphics.print(":(",30,30)
	dtawanim()
end

function love.draw() 
	if drawfunc[state] then drawfunc[state]() end
end

local updatefunc = {}

updatefunc[1] = function(dt)
	x = x + 20*dt
	if x > 450 then x = 300 end
end

updatefunc[2] = function(dt)
	y = y + 20*dt
	if y > 350 then y = 200 end
end

updatefunc[3] = function(dt)
	updatefunc[1](dt)
	updatefunc[2](dt)
end

function love.update(dt)
	if updatefunc[state] then updatefunc[state](dt) end
end

local keys = {
{["a"] = function()
	state = 1
end,
["s"] = function()
	state = 2
end,
["d"] = function()
	state = 3
end},
{["a"] = function()
	state = 3
end,
["s"] = function()
	state = 2
end,
["d"] = function()
	state = 1
end},
{["a"] = function()
	state = 2
end,
["s"] = function()
	state = 1
end,
["d"] = function()
	state = 3
end}
}


function love.keypressed(key)
	if keys[state] and keys[state][key] then keys[state][key]() end	
end
User avatar
kraftman
Party member
Posts: 277
Joined: Sat May 14, 2011 10:18 am

Re:

Post by kraftman »

a neobum wrote:That much I gathered and I was told that it's far from a smooth implementation. But I was hoping somebody would walk me through the process; function by function and explain what's doing what to what, in what order, why things are where they are and so on. Yes, folks, I'm really that thick.
I've commented it fairly heavily, hopefully it will help:

Code: Select all

function love.load() --anything in here is called when the game first loads, and can be called manually later to reset the game
   love.graphics.setFont(12) --sets the size of the current font object
   
   StateManager = {} --creates a new table called StateManager
   
   function StateManager:processKey(key)
	--[[
		^^ this creates a new function inside the table StateManager. The ":" is syntactical sugar for creating a method
		you could also use:
			StateManager.processKey = function(self, key)
			function StateManager.processKey(self,key)
		Here, "self" reffers to StateManager, ":" just hides its declaration.
		"key" is the key that is detected and pass over from love.keyreleased
		]]
      local returnState = self.state.actionList[key] -- this looks for the key in the actionlist table
      if   (returnState ~= nil) then --this could be written "if returnStat then" - it just checks to see if the state had an option for the key that was pressed
         self.state = returnState --sets the state of the current state to the state that is assigned to the key (e.g. presssing space would return state 2 the first time around)
      end
   end
   
   function StateManager:show() --function called when we want to print the text that the state contains
      return self.state.message -- return passes the message back to the calling function
   end
   
   function StateManager.init(state) --this creates a new state
      local stateManager = { state = state, processKey = StateManager.processKey, show = StateManager.show }
	  --[[
		 this is kind of semi object oriented - it creates a new statemanager, and assigns the functions of the StateManager to the new state
		 and stores the state passed to this function inside the new state manager
	  ]]
      return stateManager --passes the newly created statemanager back to the calling function
   end
   
   
   
   State = {} -- creates a new table called State
   
   function State.init(message, actionList) -- function that converts the two parameters passed to it into a table, and then returns that table
      local state = { message = message, actionList = actionList }
      return state
   end
   
   state1 = State.init("Hello Wörld! \n\n (Press SPACE to proceed.)", nil)
   
   state2 = State.init("Are you impressed? \n\n\n1. YES \n\n2. NO", nil)
   
   state3 = State.init("Good. You should be. After all, neobums rock! \n\n (Press ENTER to restart)", nil)
   
   state4 = State.init("Saw through it did you? Yeah, Drav wrote this shit and I just copied it. \nI don't even know what half this code does! \n\n (Press ENTER to restart)", nil)
   
   
   --these assign options to the above states:
   state1.actionList = { [" "] = state2 }
   state2.actionList = { ["1"] = state3, ["2"] = state4 }
   state3.actionList = { ["return"] = state1 }
   state4.actionList = { ["return"] = state1 }
   
   
   StateManager = StateManager.init(state1) --sets up the first state

end

function love.keyreleased(key)--detects any key presses
   StateManager:processKey(key)--passes them to the statemanager
end

function love.draw()
   love.graphics.setColor(0, 255, 255, 255)
   love.graphics.print(StateManager:show(), 100, 100)--calls the show function to find out what to print
end

User avatar
a neobum
Prole
Posts: 5
Joined: Fri Mar 30, 2012 6:46 pm

Post by a neobum »

It did clear a lot up for me, yeah. As before, I'll fiddle with it and see what I can come up with. Thanks a bunch.
User avatar
mr_happy
Citizen
Posts: 84
Joined: Fri Mar 18, 2016 8:57 pm

Re:

Post by mr_happy »

For anyone that needs this necro-revival:
a neobum wrote:That much I gathered and I was told that it's far from a smooth implementation. But I was hoping somebody would walk me through the process; function by function and explain what's doing what to what, in what order, why things are where they are and so on. Yes, folks, I'm really that thick.
This is a very simple state machine set-up and might help, especially as it consists of very few lines of code! This code goes in main.lua and needs two other files, 'menu.lua' and 'options.lua', representing the stuff that goes on in the main menu and the options screen of a game (in reality you'd have a bunch of other states such a 'playing' or 'game over' or whatever.

I've created a table called states{}, holding the two possible states, and a variable called state. In essence all that happens is some substitution in the love.update() and love.draw() functions.

The file menu.lua has an update() function and a draw() function and the file options.lua also has its own update() and draw() functions - which gets called depends simply upon the value of the 'state' variable.

Code: Select all


local menuModule = require("menu")
local optionsModule = require("options")

-- some states
local states = {
    ["menu"] = menuModule,
    ["options"] = optionModule
}

-- set the initial game state to states["menu"] which has the value 'menuModule' in the above table
-- it's easier to type 'states.menu' than states["menu"] so that's what I've done here:
local state = states.menu
-- the state variable now holds the value 'menuModule'

-- in the standard love.update() callback function...
function love.update(dt)

   -- call the update function for the current state 
   state.update(dt)
   -- so in our case the above line acts as though it was written menuModule.update(dt)
   -- and as a result, the function update(dt) in the file 'menu.lua' gets called
   -- if the current state was set to state.options then the above line would be interpreted as optionModule.update(dt) and
   -- update(dt) in 'options.lua' would be called instead
end


-- in the standard love.draw() callback function...
function love.draw() 
    -- this works in the same way as the update function above
    -- so in our case it will call menuModule.draw()
    state.draw()
    -- if the current state was set to state.options then the above line would be interpreted as optionModule.draw() and
    -- draw() in the file 'options.lua' would be called    
end

function love.keypressed(k)
   -- this would work in the same way as the two functions above so I'm not going to type it all out again :P
end

end
Using this scheme you can create whatever states you need and keep the code for each state in its own file. How to change states? Well in the menu.lua file foer example, you'll have some code that detects when the user chooses to start a game and you change the state from 'menu' to 'playing'. Hope that helps - I don't suppose it's the best method but it seems not too hard to figure out IMHO.

Errors and omissions excepted!
User avatar
Rucikir
Party member
Posts: 129
Joined: Tue Nov 05, 2013 6:33 pm

Re: Text adventures & state machines (a newb approaches!)

Post by Rucikir »

There's a good read imo Game Programming Patterns. It has a chapter on State Machines with drawings, explanations on where and why use them, and code examples.
User avatar
mr_happy
Citizen
Posts: 84
Joined: Fri Mar 18, 2016 8:57 pm

Re: Text adventures & state machines (a newb approaches!)

Post by mr_happy »

Rucikir wrote:There's a good read imo Game Programming Patterns. It has a chapter on State Machines with drawings, explanations on where and why use them, and code examples.
That's a handy link - thanks! :D
Post Reply

Who is online

Users browsing this forum: No registered users and 246 guests