Reputation: 1586
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.
Upvotes: 2
Views: 295
Reputation: 279325
I'm a bit confused, but I don't think you should need all those menu_setup_routine
and so on to have external linkage. Instead, define a struct game_vtable
containing one function pointer for each routine, and then let each of "menu" and "debug" provide access to an instance of that struct. To call a function on a component, you do something like:
// vtable is a global symbol
component##_##vtable.setup
or
// vtable is acquired from a function
component##_##getvtableptr()->setup
or you can pass vtable pointers around as parameters, in place of your GameStateType, and maybe thereby get rid of some of your switch statements.
As for the globals - you don't provide a lot of detail, but the way to avoid a global Menu is to create one locally, at a high level, and then pass it around to anyone that needs it. If you decide that you prefer the global, you have to give it a unique name if it's going to be visible outside its TU.
Upvotes: 2
Reputation: 26060
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 other menu_model
in other C source files, you should just declare them as static
:
static struct MenuModel menu_model; /* The singleton 'menu' model */
That menu_model
will 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 by extern
ing it), and its name won't clash with other static
variables 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.
Upvotes: 2