I’m making a little game in C. I try to program in an object-oriented manner using function pointers.
I really wanted to push ahead this time and not overdo making things too generic, I often get lost in this. Using plain old C has helped me a lot in programming faster and better.
Currently, I describe “Game states” using:
/* macros */
#define SETUP_ROUTINE(component) component##_##setup_routine
#define DRAW_ROUTINE(component) component##_##draw_routine
#define EVENT_ROUTINE(component) component##_##event_routine
#define UPDATE_ROUTINE(component) component##_##update_routine
#define TEARDOWN_ROUTINE(component) component##_##teardown_routine
#define SETUP_ROUTINE_SIGNATURE void
#define DRAW_ROUTINE_SIGNATURE void
#define EVENT_ROUTINE_SIGNATURE SDL_Event evt, int * quit
#define UPDATE_ROUTINE_SIGNATURE double t, float dt
#define TEARDOWN_ROUTINE_SIGNATURE void
/* data */
typedef enum GameStateType {
GAME_STATE_MENU,
GAME_STATE_LEVELSELECT,
...
} GameStateType;
typedef struct GameState {
GameStateType state;
GameStateType nextState;
GameStateType prevState;
void (*setup_routine)(SETUP_ROUTINE_SIGNATURE);
void (*draw_routine)(DRAW_ROUTINE_SIGNATURE);
void (*event_routine)(EVENT_ROUTINE_SIGNATURE);
void (*update_routine)(UPDATE_ROUTINE_SIGNATURE);
void (*teardown_routine)(TEARDOWN_ROUTINE_SIGNATURE);
} GameState;
While you may or may not appreciate this style, I have grown to like it and it serves me well so far on this small (private..) project.
I for instance have a “transition” game state that simply transitions from one game state to the other.
However, when I link the different game states together, I get ugly things like:
extern GameState GAME; /* The 'singleton' "game" */
extern void menu_setup_routine(SETUP_ROUTINE_SIGNATURE);
extern void menu_draw_routine(DRAW_ROUTINE_SIGNATURE);
extern void menu_event_routine(EVENT_ROUTINE_SIGNATURE);
extern void menu_update_routine(UPDATE_ROUTINE_SIGNATURE);
extern void menu_teardown_routine(TEARDOWN_ROUTINE_SIGNATURE);
extern void debug_setup_routine(SETUP_ROUTINE_SIGNATURE);
extern void debug_draw_routine(DRAW_ROUTINE_SIGNATURE);
extern void debug_event_routine(EVENT_ROUTINE_SIGNATURE);
extern void debug_update_routine(UPDATE_ROUTINE_SIGNATURE);
extern void debug_teardown_routine(TEARDOWN_ROUTINE_SIGNATURE);
Also, for each game state I have things like:
menu.c
struct MenuModel menu_model; /* The singleton 'menu' model */
game.c
struct GameModel game_model; /* The singleton 'game' model */
..which are global pieces of data that remain on the heap throughout the execution of the program. Of course the fields of these usually consist of pointers to dynamic memory, which and which contents’ change as the game states change.
While at first I thought this was insane I started to like it. However it may cause namespace conflicts when another .o is linked that also has such a “menu_model” symbol.
First question: is this insane, is there a better way of doing things like this? What do people usually do to avoid these possible symbol name conflicts?
Second question is that I have to republish the different …_setup_routine/..draw_routine/.. functions using “extern..” in the one source file/object file that holds the following types of functions:
void (*get_setup_routine(GameStateType state))(SETUP_ROUTINE_SIGNATURE) {
switch(state) {
case GAME_STATE_MENU:
return SETUP_ROUTINE(menu);
break;
case GAME_STATE_LEVELSELECT:
return SETUP_ROUTINE(level_select);
break;
default: /* ... */ break;
}
}
Because otherwise when compiling it does not know the symbol “menu_setup_routine”.
Anyway, any advise is welcome, I’m a bit new to C and although I really like programming in it, I wonder if I’m using it right in this case.
Some non-small games use similar paradigm. The first example which pops into my mind is Neverball.
You might want to download its source code (its an OpenSource game) and see how they’re doing.
Personally I think you should check C++. I used to use C only, also in the way you’re doing, up to a some years ago; then I went crazy (mostly because of name clashes), and switching to C++ made me discover a new world. Anyway I understand you could want to avoid it for a number of reasons.
About objecst like your
menu_model, whose name clashes with othermenu_modelin other C source files, you should just declare them asstatic:That
menu_modelwill be visible in the C source file it’s declared in (you won’t be able to use it in other C source files, not even byexterning it), and its name won’t clash with otherstaticvariables with the same name declared in other C source files.About the second issue there’s not much to do. Functions and variables you use must be declared.