int main( int argc, char* argv[] )
{
UNUSED(argc);
UNUSED(argv);
vsSystem *system = new vsSystem();
/* Create our game */
core::Init();
core::SetGame( coreGameRegistry::GetMainMenu() );
core::Go();
core::Deinit();
delete system;
return 0;
}
This is the 'main()' from all of my VectorStorm games, such as MMORPG Tycoon. "core" is the default game manager; you can plug in an arbitrary number of games, and it'll let you swap between them at runtime.. though most of my releases only actually have one game in them. The main loop itself is inside Core::Go, and it looks like this:
/**
* core::Go
*
* This is our high-level game-running logic. It brings new games in and out, as requested.
* It also initiates memory validation checks between games, and prints out game statistics at that time.
*/
void
core::Go()
{
// Create the basic game systems which are shared among all games. (Physics, input, etc)
coreGame::CreateGameSystems();
// In theory, everything persistant has now been allocated. Anything allocated
// from now on will be allocated by an individual game inside our special s_gameHeap,
// and will need to be deallocated by that game before it exits. Tell our game-only
// memHeap that it's at its position for leak testing.
memHeap::Push(s_gameHeap);
s_gameHeap->SetMarkForLeakTesting();
// 's_exit' is set when a game requests the whole VectorStorm application exit.
// Usually this is done by the coreGame watching for the 'esc' button, but
// any game can also set this value manually, by calling core::SetExit().
while ( !s_exit )
{
if ( s_nextGame ) // if we've marked a game to switch to
{
if ( s_game ) // if we're already running a game
{
s_game->StopTimer(); // stop gathering game stats first, so we don't
s_game->Deinit(); // penalise a game's average FPS for how long their Deinit() takes.
s_gameHeap->CheckForLeaks(); // verify that game actually deleted everything it allocated
}
s_gameHeap->PrintStatus(); // print the current memory stats to our log
s_game = s_nextGame; // activate the new game, and start its profiling timers.
s_game->Init();
s_game->StartTimer();
s_nextGame = NULL;
}
s_game->Go(); // run a frame of the current game.
}
if ( s_game ) // if we're already running a game
{
s_game->StopTimer(); // stop gathering game stats first, so we don't
s_game->Deinit(); // penalise a game's average FPS for how long their Deinit() takes.
s_gameHeap->CheckForLeaks(); // verify that game actually deleted everything it allocated
}
memHeap::Pop(s_gameHeap); // pop our gameHeap back off the stack.
}
..it's actually commented rather well, imagine my surprise!
But it's really all about sanity checking when switching from one coreGame to another, or when exiting the program; making sure there are no memory leaks or etc; the real stuff happens inside coreGame::Go(), which is called repeatedly from core::Go(), and looks like this:
void
coreGame::Go()
{
m_framesRendered++;
for ( int i = 0; i < GameSystem_MAX; i++ )
if ( m_system[i]->IsActive() )
m_system[i]->Update( m_timeStep );
Update( m_timeStep );
if ( m_currentMode )
m_currentMode->Update( m_timeStep );
vsSystem::GetScreen()->Update( m_timeStep );
for ( int i = 0; i < GameSystem_MAX; i++ )
if ( m_system[i]->IsActive() )
m_system[i]->PostUpdate( m_timeStep );
vsSystem::GetScreen()->Draw();
}
"m_system" is an array of things derived from coreGameSystem.. these are things like input readers, sound players, physics libraries, timers, etc. So first we update all of these, then we call 'Update' on the coreGame (this is usually an overridden virtual function, where game logic happens internally), then we call an Update on our screen itself (which sends 'update' messages to everything within the game's scenegraphs, then we do a PostUpdate on the systems (really just for physics to be able to recover from whatever movements happened during the scenegraph update), and finally we call draw.
Yay, complicated!