我正在用 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;
虽然您可能会或可能不喜欢这种风格,但我已经开始喜欢它,并且到目前为止,它在这个小型(私有(private)..)项目中对我很有帮助。
例如,我有一个“过渡”游戏状态,它只是从一种游戏状态过渡到另一种游戏状态。
但是,当我将不同的游戏状态链接在一起时,我会得到一些难看的东西,例如:
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);
此外,对于每个游戏状态,我都有如下内容:
菜单.c
struct MenuModel menu_model; /* The singleton 'menu' model */
游戏.c
struct GameModel game_model; /* The singleton 'game' model */
..这是在程序执行过程中保留在堆上的全局数据片段。当然,这些字段通常由指向动态内存的指针组成,动态内存的指针和内容会随着游戏状态的变化而变化。
虽然一开始我觉得这很疯狂,但我开始喜欢它。但是,当链接另一个也具有此类“menu_model”符号的 .o 时,可能会导致命名空间冲突。
第一个问题:这疯了吗?有更好的方法来做这样的事情吗?人们通常会做什么来避免这些可能的符号名称冲突?
第二个问题是我必须在包含以下类型函数的一个源文件/目标文件中使用“extern..”重新发布不同的 ..._setup_routine/..draw_routine/.. 函数:
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 有点陌生,虽然我真的很喜欢用它进行编程,但我想知道在这种情况下我是否正确使用了它。
最佳答案
一些非小型游戏使用类似的范式。我脑海中浮现的第一个例子是 Neverball .
您可能想下载它的源代码(它是一个开源游戏)并看看他们是如何做的。
我个人认为你应该检查C++。直到几年前,我以前只使用 C,也像你一样使用 C;然后我就发疯了(主要是因为名字冲突),转向 C++ 让我发现了一个新世界。无论如何,我知道您可能出于多种原因想要避免它。
关于像您的 menu_model
这样的 objecst,其名称与其他 C 源文件中的其他 menu_model
冲突,您应该将它们声明为 static
:
static struct MenuModel menu_model; /* The singleton 'menu' model */
该 menu_model
将在声明它的 C 源文件中可见(您将无法在其他 C 源文件中使用它,甚至不能通过 extern
ing 它),并且它的名称不会与其他 C 源文件中声明的同名的 static
变量冲突。
关于第二个问题,没什么可做的。您使用的函数和变量必须声明。
关于c - 在 C 中进行通用/类 OO 编程,同时避免符号冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4266414/