Начал писать каркас игры разбивая его на состояния.
В книге Джима Адамса "Программирование ролевых игр с DirectX" прочитал:
Выбор действий, которые ваше приложение должно выполнить для каждого кадра, может привести к появлению такого уродливого кода, как показанный ниже:
switch(CurrentState) {
case STATE_TITLESCREEN:
DoTitleScreen();
break;
case STATE_MAINMENU:
DoMainMenu();
break;
case STATE_INGAME:
DoGameFrame();
break;
}
Очень похоже на то, что написал я.
Далее Джим Адамс пишет:
Вместо этого лучше использовать технику, которую я называю программированием на основании состояний (state-based programming или, для краткости, SBP). Основу этой техники составляет перенаправление потока исполнения на основе стека состояний. Каждое состояние представляется объектом или набором функций. Если вам потребовались функции, вы добавляете их в стек. Когда работа с функциями завершена, они удаляются из стека.
cStateManager SM;
Вот его пример:
// Макрос для простого вызова функции MessageBox
#define MB(s) MessageBox(NULL, s, s, MB_OK);
// Прототипы функций состояний - следуйте этим прототипам!
void Func1() { MB("1"); SM.Pop(); }
void Func2() { MB("2"); SM.Pop(); }
void Func3() { MB("3"); SM.Pop(); }
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev,
LPSTR szCmdLine, int nCmdShow)
{
SM.Push(Func1);
SM.Push(Func2);
SM.Push(Func3);
while(SM.Process() == TRUE);
}
Как работает стек состояния я понимаю. Может кто нибудь объяснить мне в каком порядке и когда укладывать туда мои функции, ну например вот такие?
Game_Init();
Game_Menu();
Game_Menu_Draw();
Game_Start();
Game_Start_Draw();
Game_Main();
Game_Main_Draw();
Game_Restar();
Game_Exit();
Как я понял, укладывать надо по мере необходимости: в момент выполнения функции с вершины стека, она может поместить туда ещё несколько. Другое дело, что судя по коду, помещать в стек ничего из текущей функции нельзя, т.к. после выполнения удаляется верхушка стека, а не текущая функция.
Вот если заменить стек на очередь, то в моём представлении всё складывается в нормальную картину.
Game_Init // возможно имелось ввиду Prog_init
Game_Exit // мэби это выход из програмы.
Game_Menu // вероятно здесь Обновление.
Game_Menu_Draw // рисование того что наобновлялось
Game_Start // вероятно это меню выбора этапа, где будем играть.
Game_Main // вероятно это Обновление интитей игры.
Game_Restart // это перезапуск програмы или просто Загрузка нового этапа в игре ?
// --------
От себя рекомендую обойтись более класическими способами.
Не понимаю почему первый вариант с кейсами является Уродливым.
я как-то делал по другому, объявлял тип функции и указатель на данный тип... в зависимости от режима (игровой, меню и т.д.) брал для указателя значение на игровую функцию, менюшную и т.д. Вполне удобно и ничего лишнего.
class IGameState
{
public:
virtual void Update() = 0;
virtual void Render() = 0;
};
дальше наследуешь от него все игровые состояния и храниш их в стеке. каждое состояние может либо положить на верх стека еще какое-либо состояние, либо снять. игровой цикл работает с верхним на стеке
ITALY
++
Также в помощь тебе паттерн проектирования "State": http://en.wikipedia.org/wiki/State_pattern
Sergio10 и ITALY спасибо! Действительно очень похоже на то что я искал. Начал читать книгу "Приемы объектно-ориентированного проектирования. Паттерны проектирования".
У меня тоже на подобии того что предложил ITALY:
class ClientScreen{ public: SCREEN_DECL(ClientScreen); ClientScreen( std::vector<ClientScreen*>* screenList ); virtual ~ClientScreen( ); ClientScreen* find( const char* name ); virtual bool init( ) = 0; virtual void free( ) = 0; virtual void render( float ifps ) = 0; virtual void enable( ); virtual void disable( ); bool isEnabled( ); protected: bool enabled; std::vector<ClientScreen*>* screenList; };
От этого наследуются MainMenuScreen,GameScreen,etc.
ITALY
> class IGameState
> {
> public:
> virtual void Update() = 0;
> virtual void Render() = 0;
> };
>
> дальше наследуешь от него все игровые состояния и храниш их в стеке. каждое
> состояние может либо положить на верх стека еще какое-либо состояние, либо
> снять. игровой цикл работает с верхним на стеке
FUUUUUUUUUUUUuuuuuuuuuuuuuu!!!
Зачем только с верхним работать? Можно работать со всеми - к примеру одно состояние - MainMenu, второе - BackGroundMainMenu. Если переходим в экран опций - MainMenu удаляем, BackGroundMainMenu оставляем. Ввод также надо инжектировать по слоям до тех пор, пока в ответку не придет "поймано". Можно еще ставить флаги для принудительного инжекта в любом случае.
Тема в архиве.