Я делаю небольшую игру на 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);
Кроме того, для каждого игрового состояния у меня есть такие вещи, как:
меню.c
struct MenuModel menu_model; /* The singleton 'menu' model */
game.c
struct GameModel game_model; /* The singleton 'game' model */
.., которые представляют собой глобальные фрагменты данных, которые остаются в куче на протяжении всего выполнения программы.Конечно, их поля обычно состоят из указателей на динамическую память, которые и какое содержимое меняются при изменении состояний игры.
Хотя сначала я думал, что это безумие, мне это начало нравиться.Однако это может вызвать конфликты пространства имен, когда связан другой .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, и хотя мне действительно нравится программировать в нем, мне интересно, правильно ли я использую его в этом случае.