Image

Imagetopright wrote in Imageru_cpp

Модульная архитектура игрового движка

При работе над архитектурой игрового движка (на C++) появилось следующее соображение. Если игра состоит из нескольких глав или режимов, каждый из которых загружает свои ресурсы и отличается своей игровой логикой, то каждый режим или главу удобно оформить в виде отдельного класса со своими Init/Release Resources, Process и Draw. Ниже описана архитектура такого приложения, и я буду благодарен за любые конструктивные замечания и советы. Возможно, есть более простые и идиоматические подходы, которые вам известны.



В класс-Game я вынес 1. ссылки на синглтоны Майерса draw, input, audio 2. методы инициализации игры и освобождения памяти из-под неё. Game сам является синглтоном. Обратите внимание на идиому, которую я при этом использую: Game - абстрактный класс, все его члены статические. Невозможно создать экземпляр этого класса, но его статические методы можно использовать. В отличие от традиционной идиомы определения синглтона (Банда Четырех, Александреску, Майерс), здесь нет нужды 1. определять метод Instance() 2. описывать конструкторы, оператор присваивания и деструктор как приватные. При этом мы выделяем синглтон на синтаксическом уровне: все обращения к его членам из функции main() осуществляются через "Game :: method()".



Каждый класс-GameMode должен иметь прямой доступ к draw, input, audio, и запись Game::draw нас не устраивает. По этой причине я решил сделать классы-GameMode наследниками класса-Game.



Game::Register() позволяет зарегистрировать новый режим игры. При запуске Game::Start() указывается стартовый режим и включается главный цикл приложения (обработка сообщений Windows). Игра крутится до тех пор, пока не произойдет выход из какого-нибудь режима и метод GameMode::Process() не вернет id несуществующего режима. Это значит, что игра завершается, а не переходит в новый режим.





 


 


 


const int MAX_MODES = 5;


const int UNDEFINED_ID = -1;


 


class Game


{


static Game*
modes[MAX_MODES];



static int mode_count;


protected:


static Direct3d& draw; // Singleton


//static DirectInput& input;


//static DirectAudio& audio; //
DMusic + DSound



 


public:






static int Init()


{


cout << "Game: Init() " << endl;


draw = Direct3d :: Instance();


//input
= DirectInput :: Instance();



//audio
= DirectAudio :: Instance();



return
1;



}


 


static void Release()


{


cout << "Game: Release() " << endl;


}


 


static void Register( Game* mode )


{


if
(mode_count == MAX_MODES)



return;


modes[ mode_count ] = mode;


mode_count++;


}





static int Start( int mode_id )


{


cout << "Game: Start() " << endl;


//
Main Windows Loop



 


while
(mode_id != UNDEFINED_ID)



{


// Process returns id of


mode_id = modes[mode_id]->Process();


}


return
1;



}


virtual int Process() = 0;


};


 


Game* Game :: modes[MAX_MODES];


int Game :: mode_count = 0;


 


 


class GameMode1 : public Game


{


public:


int Process()


{


cout << "GameMode1: Process() " <<
endl;



//
InitResources();



//
Logic(); Show();



//
ReleaseResources();



return
1;



}


};


class GameMode2 : public Game


{


public:


int Process()


{


cout << "GameMode2: Process() " <<
endl;



//
InitResources();



//
Logic(); Show();



//
ReleaseResources();



return
2;



}


};


class GameMode3 : public Game


{


public:


int Process()


{


cout << "GameMode3: Process() " <<
endl;



//
InitResources();



//
Logic(); Show();



//
ReleaseResources();



return
-1;



}


};


 


 


void main()


{


if ( Game::Init() )


{


GameMode1 gm1;


GameMode2 gm2;


GameMode3 gm3;


Game::Register(&gm1);


Game::Register(&gm2);


Game::Register(&gm3);


Game::Start(0);


}


 


Game::Release();


}




Результат работы приложения:



Game: Init()

Game: Start()

GameMode1: Process()

GameMode2: Process()

GameMode3: Process()

Game: Release()

Press any key to continue . . .