Sign in to follow this  
davedrake1

What's your WinMain look like?

Recommended Posts

I'm just starting out in game programming, and I'm thinking about how I want to architect my game. I would like the majority of the generic game stuff to be reusable. I am not concerned right now with OS or graphics API independence, but I may be in the future. I was thinking about how the WinMain routine would call the game routines, and thought maybe you all would have some suggestions. My idea is similar to this (pseudocode): WinMain() { Game g = new Game; g.InitalizeGraphics(); g.CreateWindow(); TerrainManager t = new TerrainManager; t.LoadLevel(0); ModelManager m = new ModelManager; UnitManager u = new UnitManager(m); u.LoadUnits(0); ... while(!done) { ProcessWindowsMessages(); UpdateGameData(); RenderScene(); } DestroyAll(); } What do you think? What kind of generic layout do you guys use?

Share this post


Link to post
Share on other sites
Quote:
Original post by Dave Hunt


I agree. The responsiblity of WinMain is to be an entry point for the linker. It should delegate all other responsibilities to other functions.

Share this post


Link to post
Share on other sites
Here's the main from my current project (SDL)

int main( int argc, char** argv )
{
try
{
Ultra1::App app;

app.Run( );
}
catch( Sim::Exception exc )
{
exc.WriteError( );
}
catch( ... )
{
Sim::Exception exc( "Unknown", "A built in exception occured" );
exc.WriteError( );
}

return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by Dave Hunt


I agree. The responsiblity of WinMain is to be an entry point for the linker. It should delegate all other responsibilities to other functions.


Why?

Share this post


Link to post
Share on other sites
Quote:
Original post by simon10k
Quote:
Original post by ToohrVyk
Quote:
Original post by Dave Hunt


I agree. The responsiblity of WinMain is to be an entry point for the linker. It should delegate all other responsibilities to other functions.


Why?


1. WinMain is merely an entry point for an application.
2. More importantly, no function should do more than one thing.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dave
1. WinMain is merely an entry point for an application.
2. More importantly, no function should do more than one thing.


Could you refer me to some documentation that states this?

Share this post


Link to post
Share on other sites
Quote:
Original post by simon10k
Could you refer me to some documentation that states this?


Common sense. Separation of responsibilities increases modularity, and modularity decreases the amount of work necessary when you have to change something. And decreasing the amount of work necessary to achieve something is inherently a Good ThingTM.

Typically, when a function F does A and B, you are bound to end up with a situation where you need a function G that does A but not B (solved by responsibility separation).

Share this post


Link to post
Share on other sites
An entry point is a symbol that is used by the linker to perform the first call when the program is executed. This is the role of WinMain. If you don't want to execute the code inside WinMain upon launch, you are in deep trouble, as you would have to rename it and write another WinMain function with all the entry-point code associated with it or move away all the code into another function and rework all local variable definitions and usage, as well as separate by hand the intertwined entry-point logic from the code you want to move away.

On the contrary, if an user-defined function is called from WinMain, it is a simple matter to change it so it is not called upon launch anymore (as easy as commenting out a line), simply because it's not an entry point.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
An entry point is a symbol that is used by the linker to perform the first call when the program is executed. This is the role of WinMain.


Actually, that is the role of WinMainCRTStartup, which sets up the runtime and calls WinMain. But, your point is still valid. WinMain's purpose is to get your application up and running and shouldn't contain anything else.

Share this post


Link to post
Share on other sites
You should create a window in CreateWindow( ). It may seem silly at first to split stuff up so much, but if you want to go back and change it, it makes it much easier. Also, this modularity allows you to reuse the same code in different projects.

Share this post


Link to post
Share on other sites
I usually abstract the whole WinMain thing away like this:


CORE.H:
class Core {
// implement setup functionaily
...
}
extern int main();

//------------------------------------
CORE.CPP:
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPTSTR, int) {
return main();
}

//------------------------------------
MYPROG.CPP:
int main() {
// use Core functionaily
...
return 0;
}


Here you can just include core.h and implement main() as usual, without having to worry about platform-specific parameters. It's reusable and allows for portable code. I believe the Torque engine uses this construct as well.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dave Hunt

WinMain()
{
Game *g = new Game();
g->Run();
delete g;
}


Thats pretty much what iv always used. Right now my entry point looks like this:

	cApp myApp("C:\\Application.alp");
return myApp.Run();


My quick and dirty Pong Clone was:

	Pong Game;

// Lets lock its FPS to 60
Game.LockFPS(60);

// Run the game til its completion, easy!
return Game.Run();


:)

Share this post


Link to post
Share on other sites
My main methods often look like: (pseudocode, C#)
main(){
Application.Run(MainForm);
}


... or, for a console app:
		public static void Main(string[] args)
{
Console.WriteLine("-- GAME SERVER STARTUP --");
// Load plugin game types
sgt.LoadGameTypes("ServerGameTypes");
DoCommand("help");
DoCommand("start");
String s;
while(true){
if(lobby == null) Console.Write("> ");
else Console.Write(lobby.Server.Port + ": ");
s = Console.ReadLine();
if(s == "exit") return;

DoCommand(s);
}
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Dave Hunt

WinMain()
{
Game *g = new Game();
g->Run();
delete g;
}


How about, instead:


{
std::auto_ptr<Game> g(new Game());
g->Run();
}


Which is shorter and will correctly call the destructor of Game if an exception is thrown.

Mark

Share this post


Link to post
Share on other sites
Quote:
Original post by markr
Quote:
Original post by Dave Hunt

WinMain()
{
Game *g = new Game();
g->Run();
delete g;
}


How about, instead:


{
std::auto_ptr<Game> g(new Game());
g->Run();
}


Which is shorter and will correctly call the destructor of Game if an exception is thrown.

Mark


Better yet, just allocate it on the stack, since there's no reliance on polymorphic behaviour here.


int main( int argc, char** argv )
{
try
{
Ultra1::App app;
app.Run( );
}
catch( Sim::Exception exc )
{
exc.WriteError( );
}
catch( ... )
{
Sim::Exception exc( "Unknown", "A built in exception occured" );
exc.WriteError( );
}
return 0;
}


I like this approach of Simian Man's, though I'd do it a bit differently - first of all I'd make use of the library exception hierarchy, likely provide the command line arguments to the app constructor, and apply some minor style changes (oh, and return an error code in the exception cases):


int main(int argc, char** argv) {
try {
std::vector<std::string> args(argv, argv + argc);
MyNamespace::App(args).run();
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
return -1;
} catch( ... ) {
std::cerr << "Caught non-standard exception" << std::endl;
return -1;
}
}

Share this post


Link to post
Share on other sites
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow )
{
if ( Wx::RegisterWindowClass( CS_OWNDC,
( WNDPROC )WindowProc,
hInstance,
s_AppName,
LoadCursor( NULL, IDC_ARROW )
) == NULL )
{
MessageBox( NULL, "Wx::RegisterWindowClass() failed.", "Error", MB_OK );
exit( 1 );
}

HWND hWnd = CreateWindowEx( 0,
s_AppName,
s_TitleBar,
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0, 0, 500, 500,
NULL,
NULL,
hInstance,
NULL );

if ( hWnd == NULL )
{
MessageBox( NULL, "CreateWindowEx() failed.", "Error", MB_OK );
exit( 1 );
}

ShowWindow( hWnd, nCmdShow );

return Wx::MessageLoop( hWnd, Update );
}

Share this post


Link to post
Share on other sites
Oh dear :-( I'm the record holder for the longest WinMain. I'm still on a modified NeHe base code.


//
// MAIN routine
//
int WINAPI WinMain( HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State
{

MSG msg; // Windows Message Structure
BOOL done=FALSE; // Bool Variable To Exit Loop

// setup memory monitoring and reporting
int debugFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
debugFlag = _CrtSetDbgFlag (_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF |
_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_REPORT_FLAG );

// open our log file
g_log.Open();
g_log << "Memory debug initialised\n";

// Initialise timer
if (!g_timer.Init()) {
g_log << "Timer: High resolution timing not supported by system. Using low res.\n";
//MessageBox(NULL,"HiRes Timer not supported.","ERROR",MB_OK|MB_ICONEXCLAMATION);
//next_time = timeGetTime();
} else {
g_log << "Timer: Initialised.\n";
}

// setup random seed
srand((unsigned int)time(0));

// Ask The User Which Screen Mode They Prefer
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO) {
FULLSCREEN=false;
}

// Create Our OpenGL Window and quit if not created
if (!CreateGLWindow(TITLE,SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_DEPTH,FULLSCREEN)) {
g_log << "Failed to create OpenGL window in stage one.\n";
ShowCursor(true); MessageBox(NULL,"Failed to create window","ERROR",MB_OK|MB_ICONSTOP);
// shutdown routine - kill window and release resources
if (!Shutdown()) {
g_log << "Failed to execute shutdown after OpenGL window creation in stage one.\n";
MessageBox(NULL,"Failed to shutdown properly","ERROR",MB_OK|MB_ICONEXCLAMATION);
}
return 1;
}

// do additional setup - loading images, creating textures, setting lights etc
if (!PRESETUPCOMPLETE) {
if (!PreSetup()) {
g_log << "Failed in PreSetup routine in stage one.\n";
ShowCursor(true); MessageBox(NULL,"Pre-Setup failed","ERROR",MB_OK|MB_ICONEXCLAMATION);
if (!Shutdown()) {
g_log << "Failed to execute shutdown after PreSetup failure in stage one.\n";
MessageBox(NULL,"Failed to shutdown properly","ERROR",MB_OK|MB_ICONEXCLAMATION);
}
return 1;
}
PRESETUPCOMPLETE=true;
}

while(!done) {

// check waiting messages
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
// check for quit message and process, otherwise execute translate and dispatch cycle
if (msg.message==WM_QUIT) {
done=true;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else {
// no windows messages, so check if we pressed escape and if not draw scene
if ((WINDOWACTIVE && !DrawGLScene()) || keys[VK_ESCAPE]) {
// signal quit
done=TRUE;
} else {
// update timer and render flag
g_timer.UpdateTimer();
// update our camera class
g_camera.Update(g_timer.GetElapsedSeconds());
// check for keypresses
CheckKeyPress();
// double buffering - swap buffers
SwapBuffers(hDC);
}

// check for F1 to toggle full or windowed screen modes
if (keys[VK_F1]) {
keys[VK_F1]=false;
FULLSCREEN=!FULLSCREEN;

// recreate OpenGL window and exit loop if theres an issue
if (!SwitchWindowMode(TITLE,SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_DEPTH,FULLSCREEN)) {
g_log << "Failed to switch window mode in stage two\nThe program will now exit.\n";
ShowCursor(true); MessageBox(NULL,"Failed to switch window mode in stage two\nThe program will now exit.","ERROR",MB_OK|MB_ICONSTOP);
done=true;
} else {
// do additional setup if necessary
if (!PRESETUPCOMPLETE) {
if (!PreSetup()) {
g_log << "Failed in PreSetup routine in stage two.\n";
ShowCursor(true); MessageBox(NULL,"Pre-Setup failed","ERROR",MB_OK|MB_ICONEXCLAMATION);
done=true;
}
}
}
}
}
}

// shutdown routine - kill window and release resources
if (!Shutdown()) {
g_log << "Failed to execute shutdown after PreSetup failure in stage two.\n";
ShowCursor(true); MessageBox(NULL,"Failed to shutdown properly","ERROR",MB_OK|MB_ICONEXCLAMATION);
}

// close our session log
g_log.Close();

// Prevent false positives

// let's catch our memory leaks
_CrtDumpMemoryLeaks();

// Exit The Program
return (int)(msg.wParam);

}







[Edited by - Fahrenheit451 on March 7, 2006 7:59:45 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this