Author Topic: finite state machines?  (Read 5571 times)

0 Members and 1 Guest are viewing this topic.

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
finite state machines?
« on: November 06, 2007 »
ive been looking into ways of writting more structured code of late and ive been looking at state machines for ai, i think they would work really well as the frame work of a game (controlling and processing the main loop start up shut down etc) but i was wanting to know if its good pratice to use them in this way for games as everthing ive looked at say there primarily used for ai.
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: finite state machines?
« Reply #1 on: November 06, 2007 »
Use them everywhere - state machines form the basis of all good software.

It's always good to know what state you are in - what data it has and what processes, and then to know what inputs you need to move from that state to another state.

Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: finite state machines?
« Reply #2 on: November 11, 2007 »
hey jim im just messing around with your escher state machine the now im trying to get it to go into a class and im nearly all the way there but im having a spot of bother here.

Code: [Select]
typedef enum
{
  STATE_INIT,
STATE_SHUTDOWN,

STATE_USER=STATE_SHUTDOWN,

//put user states here
STATE_DINGOSDREAM,
//end user states

STATE_LAST,

//flags controlling state control
STATE_NEXT=0x20000000,
STATE_PREV=0x40000000

} GAME_STATE;

typedef struct
{
void (*init)(void);
GAME_STATE (*main)(void);
void (*shutdown)(void);
char *title;
} GAME_JUMPS;

#define START_STATE STATE_DINGOSDREAM

class StateMachine
{
public:

StateMachine( HWND Hwnd, HDC Hdc );

~StateMachine();

void init_state_machine( void );
bool run_state_machine( void );
void kill_state_machine( void );
void set_state_override( GAME_STATE );
void global_init( void );
void global_shutdown( void );
GAME_STATE global_main( void );
void empty_init( void );
void empty_shutdown( void );
GAME_STATE empty_main( void );
        void DingosDreamInit( void );
GAME_STATE DingosDreamMain( void );
void DingosDreamShutDown( void );
       
        //**problem here***
        //------------------------------------------------------------------------------------
        //------------------------------------------------------------------------------------
GAME_JUMPS game_states[] =
{
        {global_init, global_main, empty_shutdown, "startup"},
{empty_init, empty_main, global_shutdown, "shutdown"},

{DingosDreamInit, DingosDreamMain, DingosDreamShutDown, "DingosDream"},
};
        //-------------------------------------------------------------------------------------
private:

HWND CurWindow ;
HDC CurHdc ;
GAME_STATE curr_state;
GAME_STATE state_override;
       
};

and i get lots of errors like this.
Error   2   error C2334: unexpected token(s) preceding '{'; skipping apparent function body   e:\documents and settings\nino\desktop\apophis\ApophisStateMachine.h   53   

it obviously builds when its not in a class form but im no expert with classes so i cant see how to make it work.

if you like i can upload the project its in vc as i finaly got round to learning it.
« Last Edit: November 11, 2007 by ninogenio »
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: finite state machines?
« Reply #3 on: November 12, 2007 »
The error must be somewhere else, that snippet you posted is fine.  I suggest checking the bit just before it to make sure it's closed off properly.  If you want, PM me the project and I'll take a look.

Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: finite state machines?
« Reply #4 on: November 12, 2007 »
hey jim,

im still having a spot of bother pointing the struct members to my class members ive made it look like this

Code: [Select]
void StateMachine::init_state_machine( void )
{

game_states[0].init = this->global_init ;
game_states[0].main = this->global_main ;
game_states[0].shutdown = this->empty_shutdown ;
game_states[0].title = "startup" ;

game_states[1].init = this->empty_init ;
game_states[1].main = this->empty_main ;
game_states[1].shutdown = this->global_shutdown ;
game_states[1].title = "shutdown" ;

game_states[2].init = this->DingosDreamInit ;
game_states[2].main = this->DingosDreamMain ;
game_states[2].shutdown = this->DingosDreamShutDown ;
game_states[2].title = "DingosDream" ;

curr_state = STATE_INIT;
game_states[curr_state].init();
}

but i get two errors from each line that i try to point game states to the member functions that looks like this.

Error   1   error C3867: 'StateMachine::global_init': function call missing argument list; use '&StateMachine::global_init' to create a pointer to member   e:\Documents and Settings\nino\Desktop\apophis\ApophisStateMachine.cpp   30   

Error   2   error C2440: '=' : cannot convert from 'void (__thiscall StateMachine::* )(void)' to 'void (__cdecl *)(void)'   e:\Documents and Settings\nino\Desktop\apophis\ApophisStateMachine.cpp   30   

it looks like the only problem is with converting a member function to a c function, do you know any way around this?
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: finite state machines?
« Reply #5 on: November 13, 2007 »
The error is telling you what to do...
Code: [Select]
game_states[0].init = &StateMachine::global_init ;
game_states[0].main = &StateMachine::global_main ;
game_states[0].shutdown = &StateMachine::empty_shutdown ;
game_states[0].title = "startup" ;
and
Code: [Select]
//forward reference to StateMachine
class StateMachine;

typedef struct
{
void (StateMachine::*init)(void);
GAME_STATE (StateMachine::*main)(void);
void (StateMachine::*shutdown)(void);
char *title;
} GAME_JUMPS;
That gets it some way along, but it doesn't think
Code: [Select]
game_states[0].init()
is a function, for some reason.
Quote
error C2064: term does not evaluate to a function taking 0 arguments
Apart from removing all this stuff from the class, I can't see how to make it work.  And then you still have the problem of how to reference the 'Dingos' class anyway.

Jim
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: finite state machines?
« Reply #6 on: November 13, 2007 »
OK, here's a far more C++ way of doing things.

apophisstatemachine.h, get rid of ALL the GAME_JUMPS stuff.
Code: [Select]
#include <vector>

using namespace std;
...
//virtual base class - all game states deriving from this class must support init, main, shutdown.
class State {
public:
virtual void init(void) = 0;
virtual GAME_STATE main(void) = 0;
virtual void shutdown(void) = 0;
};

//default startup state
class InitState : State {
void init(void);
void shutdown(void);
GAME_STATE main(void);
};

//default shutdown state
class ShutdownState : State {
void init(void);
void shutdown(void);
GAME_STATE main(void);
};

//state machine
class StateMachine
{
public:

StateMachine( HWND Hwnd, HDC Hdc ){
State *s;
//create the default states
s = (State *)new InitState();
add_state(s);

s = (State *)new ShutdownState();
add_state(s);
};

~StateMachine();

void init_state_machine( void );
bool run_state_machine( void );
void kill_state_machine( void );

void add_state(State *state) {
game_states.push_back(state);
}
//all states stored in here
vector <State *> game_states;

private:

HWND CurWindow ;
HDC CurHdc ;
GAME_STATE curr_state;
GAME_STATE state_override;
       
};

dingosdream.h
Code: [Select]
//dingosdream state - must implement init, shutdown, main, as well as anything else you might need
class DingosDream : State
{
public:
       
~DingosDream();

DingosDream( HWND Hwnd );

GAME_STATE main(void);

void shutdown(void);

void init(void);

private:

};

apophisstatemachine.cpp
Code: [Select]
#include <windows.h>
#include "ApophisStateMachine.h"
#include "DingosDream.h"

void StateMachine::init_state_machine( void )
{
curr_state = STATE_INIT;
game_states[curr_state]->init();
}

void StateMachine::kill_state_machine( void )
{
game_states[curr_state]->shutdown();
}

//code for the default states
void InitState::init( void )
{

}

void InitState::shutdown( void )
{

}

GAME_STATE InitState::main( void )
{
return START_STATE;
}

void ShutdownState::init( void )
{
}

void ShutdownState::shutdown( void )
{
}

GAME_STATE ShutdownState::main( void )
{
return STATE_SHUTDOWN;
}

apophis.cpp
Code: [Select]
...
                case WM_CREATE:
...                   
                     CodeStates = new StateMachine( hwnd ,OffScreenDC );

//create the dingos dream object and add it to the states list
CodeStates->add_state((State *)new DingosDream(hwnd));

CodeStates->init_state_machine();
...
                     break; 

Might even change my own code round to using this... :)

Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: finite state machines?
« Reply #7 on: November 13, 2007 »
how clever is that  :cheers:

it really gives me an idea for how my code should be laid out cheers jim k+!
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: finite state machines?
« Reply #8 on: December 11, 2007 »
It was so good an idea, I actually switched my engine over to do this yesterday. :)
Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: finite state machines?
« Reply #9 on: December 11, 2007 »
cool! was it easy enough to add to escher?

how does your demo states look now there held in classes? are there any fall backs in linking to your sprite and your 3dobject class from your state class.
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: finite state machines?
« Reply #10 on: December 11, 2007 »
It was easy.  I pretty much just commented everything out and pasted in the new stuff :)
It means that every demo is now held in a class, so theoretically I could have lots of the same demos running all at once ;)  It doesn't affect anything else.
One really nice feature is now I don't have to manually add all my states to a state table.  I can just use a class factory and static initialisers to add States, and not have to worry about adding stuff all over the place when I add a new source file.
ie.
Code: [Select]
static StateMachine *machine = NULL;

int StateMachineFactory(State *state) {
if (machine == NULL) {
machine = new StateMachine();
}
return machine->add_state(state);
}
and then in a new source file, if I come up with a new class, all I need to do to add it to the state table is to add a line
Code: [Select]
int GasketID = StateMachineFactory((State *)new Gasket());
That will get called before main() and added to the state table.  The only bad news is you don't get to pick what order they come in, but you could fix that later by fiddling with the states vector.

Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: finite state machines?
« Reply #11 on: December 11, 2007 »
cool.

its made life easy for me with my comp entry being able to switch to diffrent states intro menu ending level from the one point and letting them take care of themselfs was a god send.

wish i had thought of the StateMachineFactory though thats a really good inclusion ;)
Challenge Trophies Won: