Stick the Landing: A Nice Clean Exit


Your game won't run forever. Even the best games will take a back seat to food and water, regardless of what Microsoft's XBox ads seem to imply. There may be a temptation to simply call exit(0) and be done with it. This isn't a wise choice because system resources won't be released properly.

DirectX drivers sometimes handle hard exits badly, causing VRAM or other limited resources to remain tied up by a half dead application. When the player attempts to restart the game they find that it won't start, due to a lack of VRAM or perhaps something else. The player might have to reboot their machine. Rebooting used to be a normal thing and every gamer was used to multiple reboots every day. The latest operating systems like Windows XP are much more resilient and reboots are very rare. Players who find that your game requires a reboot after they're done will get pretty annoyed and most likely return your game.

If you don't have a decent exit mechanism you'll also find it impossible to determine where your game is leaking memory or other resources. After all, a hard exit is basically a huge memory leak. A tight exit mechanism will show you a single byte of leaked memory before returning control to the operating system. This is important for all games, but it is especially true for console games.

Best Practice

Console games are not allowed to leak memory. Period. To be honest, Windows games shouldn't be given any slack either. The reality of it is that some Win32 calls leak resources and you just have to live with it. That's no reason your game code should be sloppy; hold yourself to a high standard and you won't get a reputation for crappy software.

How Do I Get Out of Here?

There are two ways to stop a game from executing without yanking the power or causing some kind of exception:

  • The player quits the game on purpose.

  • The system shuts the application down (Win32).

If the player chooses to stop playing, the first thing you should do is ask the player if he or she wants to save their game. The last thing someone needs is to lose six hours of progress only to hit the wrong button by accident. A good standard detects if the current state of the game has changed since the last time the user saved, and only if the state is different does the system ask if the player wants to save his or her game. It is equally annoying to save your game, quit, and have the idiot application ask if the game needs saving all over again.

Remember the piece of code just outside the main message pump:

 if (m_bQuitting) {    PostMessage(WM_CLOSE, 0, 0);    return ret; } 

This code implies that you shouldn't perform any quit mechanism while you are pumping messages. This little trick solves the problem of hitting the quit hotkey fifteen times really fast, where a badly engineered game might recursively call the quit code. The user interface control that receives the quit button click event or the hotkey event should simply set a Boolean variable to true, which will be checked after the last message in the queue has been handled.

Console programmers can stop here and simply run their exit code, destroying all the game systems generally in the reverse order in which they were created. Windows programmers, as usual, don't get off nearly that easy.

When the Win32 OS decides your game has to shut down, it sends a different message. Win32 apps should intercept the WM_SYSCOMMAND message, and look for SC_CLOSE in the wParam. MFC applications can overload the CWnd::OnSysCommand() to grab the same message. This is what Win32 sends to applications that are being closed, perhaps against their will. This can happen if the machine is shut down or if the player hits ALT-F4.

The problem with this message is that Alt-F4 should act just like your normal exit, asking you if you want to quit and all of that. If you can save to a temporary location and load that state the next time the player starts, your players will thank you. Most likely they were just getting to the boss monster and the batteries on their laptop finally ran out of motivated electrons.

Again, you have to double check for multiple entries into this code with a Boolean variable. If the player hits Alt-F4 and brings up a dialog box in your game asking if they want to quit, nothing is keeping them from hitting Alt-F4 again. If your players are like the folks at Microsoft's test labs they'll hit it about fifty times. Your game is still pumping messages, so the WM_SYSCOMMAND will get through every time a player hits Alt-F4. Make sure you handle that by filtering it out.

If your game is minimized you have to do something to catch the player's attention. If your game runs in full-screen mode and you've tabbed away to another app, your game will act just as if it is minimized. If your player uses the system menu, by right clicking on the game in the start bar, your game should exhibit standard windows behavior and flash. This is what well-behaved windows applications do when they are minimized but require some attention from a human being. Call the following function to flash your window until it is maximized:

 // Wait for the application to be restored // before going any further with the new // screen.  Flash until the person selects // that they want to restore the game. void CMyApp::FlashWhileMinimized() {    // Flash the application on the taskbar    // until it's restored.    if ( ! m_pMainWnd )       return;    // Blink the application if we are minimized,    // waiting until we are no longer minimized    if ( m_pMainWnd->IsIconic() )    {       // Make sure the app is up when creating a new screen       // Make sure the app is up when creating a new screen       // this should be the case most of the time, but when       // we close the app down, minimized, and a confirmation       // dialog appears, we need to restore       DWORD now = timeGetTime();       DWORD then = now;       MSG msg;       m_pMainWnd->FlashWindow( true );       for (;;)       {          if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )          {             // Bug with closing the game multiple times while minimized             // Uncomment this to fix the problem             if ( msg.message == WM_SYSCOMMAND && msg.wParam == SC_CLOSE )             {                // Swallow close messages                // (we are already minimized and closing)                GetMessage( &msg, NULL, 0, 0 );             }             else             {               // Default processing               PumpMessage();             }             // Are we done?             if ( ! m_pMainWnd->IsIconic() )             {                m_pMainWnd->FlashWindow( false );                break;             }          }          else          {            now = timeGetTime();            if ( abs( now - then ) > 1000 )            {               then = now;               m_pMainWnd->FlashWindow( true );            }          }       }    } } 

Shutting the Game Down

With some exceptions, you should shut down or deallocate game systems in the reverse order from which they were created. This is a good rule of thumb to use whenever you are grabbing and releasing multiple resources that depend on each other. Each data structure should be traversed and freed. Take care that any code that is run inside destructors has the resources it needs to properly execute. It's pretty easy to imagine a situation where the careless programmer has uninitialized something in the wrong order and a destructor somewhere fails catastrophically. Be extremely aware of your dependencies, and where multiple dependencies exist, lean on a reference counting mechanism to hold on to resources until they really aren't needed anymore.

After big systems shut down check the heap for corruption. ANSI C applications can do this with _CrtCheckMemory. Heap walkers are excellent for finding problems when memory is being released en masse. Close all your open files, release any system resources, and unload DLLs. Finally get rid of DirectX objects and restore the desktop to its natural state.




Game Coding Complete
Game Coding Complete
ISBN: 1932111751
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net