我正在用C语言做一个小游戏,我试着用函数指针以面向对象的方式编程。
这一次我真的很想继续前进,不要把事情搞得太笼统,我经常迷失在这一点上。使用普通的老式C语言帮助我更快更好地编程。
目前,我使用以下方法来描述“游戏状态”:
/* 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;虽然你可能欣赏或不欣赏这种风格,但我已经喜欢上了它,到目前为止,它在这个小的(私人的..)项目。
例如,我有一个简单地从一个游戏状态转换到另一个游戏状态的“转换”游戏状态。
然而,当我将不同的游戏状态链接在一起时,我得到了一些丑陋的东西,比如:
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);此外,对于每个游戏状态,我都有如下内容:
menu.c
struct MenuModel menu_model; /* The singleton 'menu' model */game.c
struct GameModel game_model; /* The singleton 'game' model */..which是在整个程序执行过程中保留在堆上的全局数据片段。当然,这些字段通常包含指向动态内存的指针,这些动态内存以及哪些内容会随着游戏状态的变化而变化。
虽然一开始我觉得这很疯狂,但我开始喜欢它了。但是,当链接的另一个.o也具有这样的"menu_model“符号时,可能会导致名称空间冲突。
第一个问题:这是不是很疯狂,有没有更好的方法来做这样的事情?人们通常做些什么来避免这些可能的符号名称冲突?
第二个问题是我必须重新发布不同的..._setup_routine/..draw_routine/.函数使用"extern..“在一个源文件/目标文件中包含以下类型的函数:
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;
}
}因为否则在编译时它不知道符号"menu_setup_routine“。
无论如何,欢迎任何建议,我对C语言还有点陌生,虽然我真的很喜欢用它编程,但我想知道在这种情况下我是否正确地使用了它。
发布于 2010-11-24 20:10:52
一些非小型游戏使用类似的范例。我脑海中浮现的第一个例子是Neverball。
你可能想下载它的源代码(这是一个OpenSource游戏),看看他们是怎么做的。
就个人而言,我认为你应该检查一下C++。直到几年前,我一直只使用C,就像你现在做的那样;然后我发疯了(主要是因为名称冲突),切换到C++让我发现了一个新的世界。不管怎样,我知道你可能会因为一些原因而想要避免它。
对于像您的menu_model这样的对象,其名称与其他C源文件中的其他menu_model冲突,您只需将其声明为static
static struct MenuModel menu_model; /* The singleton 'menu' model */该menu_model将在声明它的C源文件中可见(您将无法在其他C源文件中使用它,甚至不能通过extern调用它),并且它的名称不会与其他C源文件中声明的同名static变量冲突。
关于第二个问题,没有太多的事情要做。您使用的函数和变量必须声明。
发布于 2010-11-24 20:11:33
我有点困惑,但我认为你不应该需要所有的menu_setup_routine等等才能有外部链接。相反,应该为每个例程定义一个包含一个函数指针struct game_vtable,然后让"menu“和"debug”中的每一个都提供对结构实例的访问。要在组件上调用函数,请执行以下操作:
// vtable is a global symbol
component##_##vtable.setup或
// vtable is acquired from a function
component##_##getvtableptr()->setup或者,您可以将vtable指针作为参数传递,而不是GameStateType,这样就可以摆脱一些switch语句。
至于全局变量-您没有提供太多细节,但避免全局菜单的方法是在本地创建一个高级别的菜单,然后将其传递给任何需要它的人。如果您决定更喜欢全局,则必须给它一个唯一的名称,才能使它在其TU之外可见。
https://stackoverflow.com/questions/4266414
复制相似问题