Simple WIN32 Game Loop

Started by
2 comments, last by RedKMan 13 years, 9 months ago
I'm going to do a simple WIN32 GDI only Connect Four game. Just wondering if the WinMain .cpp code below is OK enough for something like this. Can I make it more effecient or is it fit for the purpose? I used something similar for a Tic Tac Toe game and it worked fine just wondering if there's a better more elegant solution out there. Is it OK to organise things into the three functions initGame(), mainGame(), shutdownGame() like this?

[Source]#include <windows.h>#include <tchar.h>#include <time.h>#include <stdio.h>#include <stdlib.h>// Windows application variables.HWND				g_hWnd				 = 0;			  HINSTANCE			g_hInstance			 = 0;		  BOOL				g_bActive			 = FALSE;	BOOL				g_bDone				 = FALSE;TCHAR				g_szAppClass[]		 = TEXT( "Application" );TCHAR				g_szAppTitle[]		 = TEXT( "Connect Four" );// Windows application function prototypes.LRESULT	WINAPI MsgProc( HWND, UINT, WPARAM, LPARAM );// Game application function prototypes.void initGame();void mainGame();void shutdownGame();//---------------------------------------------------------------// Name: WinMain()// Desc: Called as soon as the Application is loaded.//---------------------------------------------------------------int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,				    LPSTR lpCmdLine, int nCmdShow ){	WNDCLASSEX	wndclass;	HWND		hWnd;	MSG			msg;	// Setup the window attributes	wndclass.cbSize			=	sizeof( wndclass );	wndclass.style			=	CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;	wndclass.lpfnWndProc	=	MsgProc;	wndclass.cbClsExtra		=	0;	wndclass.cbWndExtra		=	0;	wndclass.hInstance		=	hInstance;	wndclass.hIcon			=	LoadIcon( NULL, IDI_APPLICATION );	wndclass.hCursor		=	LoadCursor( NULL, IDC_ARROW );	wndclass.hbrBackground	=	( HBRUSH ) ( COLOR_WINDOW );	wndclass.lpszMenuName	=	NULL;	wndclass.lpszClassName	=	g_szAppClass; // Registered class name	wndclass.hIconSm		=	LoadIcon( NULL, IDI_APPLICATION );	// Register the new window	if( RegisterClassEx( &wndclass ) == 0 )		exit(1);	// Create the main window.  Window parameters make the main window	// 640X480 only	hWnd = CreateWindow( g_szAppClass,			// Class Name						 g_szAppTitle,			// Name displayed on title bar						 WS_OVERLAPPEDWINDOW,	// Window style, using Popup						 0,						// X Position (Top Left)						 0,						// Y Position (Top Right)						 640,					// X Position (Bottom Right)						 640,					// Y Position (Bottom Left)						 NULL,						 NULL,						 hInstance,						 NULL );	// Tell windows to show the application	ShowWindow( hWnd, nCmdShow );	// Update the client by sending an initial WM_PAINT msg	UpdateWindow( hWnd );	// Save the main window handle	g_hWnd = hWnd;	// Save the main application instance	g_hInstance	= hInstance;	initGame();   // Main Message Loop.  Keep looping through this application until	// the user chooses to exit.  When the user chooses exit by clicking	// the red X box top right of the window for example.  A WM_QUIT	// message will be sent to the application which prompts the application	// to exit.   ZeroMemory( &msg, sizeof( msg ) );   while( !g_bDone )    {		while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )		{			TranslateMessage( &msg );			DispatchMessage( &msg );		}		if(msg.message == WM_QUIT)		{			break;		}		if( g_bActive )		{			mainGame();		}   }   shutdownGame();			   UnregisterClass( g_szAppClass, hInstance );   // Return back to Windows   return( ( int ) msg.wParam );}//---------------------------------------------------------------// Name: MsgProc()// Desc: This is the main window message handling function//---------------------------------------------------------------LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ){	PAINTSTRUCT ps;	HDC			hdc;	switch( msg )	{	case WM_PAINT:		hdc = BeginPaint(hWnd, &ps);		EndPaint(hWnd, &ps);	break;	case WM_ACTIVATE:		// The message WM_ACTIVE is sent whenever the application has focus.		// For example if the application is minimised then it is not in focus.		// As the application is not in focus it is pointless wasting clock		// cycles and graphics hardware to render to what cannot be seen.		g_bActive = ( BOOL ) wParam;	break;	case WM_KEYDOWN:		// Whenever a key is pressed (non-accelerated ie shift and f5, then		// this message is sent.		switch( wParam )		{		case VK_ESCAPE:			// If the user presses the Escape key then we will quit the 			// application			g_bDone = TRUE;			PostMessage( g_hWnd, WM_CLOSE, 0, 0 );			return 0;		};	break;	case WM_DESTROY:		// The message WM_DESTROY is sent by the Windows Messaging System		// when our message queue recieves the WM_QUIT message from the		// application.  It just cleans everything up and quits the 		// application		g_bDone = TRUE;		PostQuitMessage( 0 );		return 1;	break;	default: break;	}		// If any messages reach here then it was not handled by our application.	// So we will return it back to the queue so the next Windows application	// like a background task.	return DefWindowProc( hWnd, msg, wParam, lParam );}//---------------------------------------------------------------// Name: initGame()// Desc: //---------------------------------------------------------------void initGame(){	// Initialisation.}//---------------------------------------------------------------// Name: mainGame()// Desc: //---------------------------------------------------------------void mainGame(){	// Game calls.}//---------------------------------------------------------------// Name: shutdownGame()// Desc: //---------------------------------------------------------------void shutdownGame(){	// Cleanup.}[/Source]
Advertisement
Yeah, i think it's okay! Of course you could do a lot of things in terms of abstraction and generalization, but for a game like Connect it wouldn't mean much.
I think it's important, in the process of learning, to build your game and find out what elements tend to repeat, come out over and over again and make classes for them, functions and organize them into a little framework. Don't think there's a need to over complicate yourself if you don't need to :D. Concentrate on the game and care less about building an engine, now.
If it doesn't bother you and you can keep up with it, it should be ok. I personally do things differently from you and what matters to me is to keep it consistent so that I can find my way around the code. Make sure to make your code intuitive so that when you come back to it or someone else is looking over it, it'll be as self-explanatory as it can. As an example, I'll show you my main class for the Tetris game I'm working on at the moment:

// Windows template// Main.cpp#include <windows.h>#include "Tetris.h"#include "Constants.h"Tetris* gTetris = 0;LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){	switch(msg)	{		case WM_CREATE:			gTetris = new Tetris(GetModuleHandle(0), hWnd);					break;		case WM_CLOSE:			DestroyWindow(hWnd);		break;		case WM_DESTROY:			PostQuitMessage(0);		break;		default:			return DefWindowProc(hWnd, msg, wParam, lParam);		break; // break just for sake of keeping uniformity	}		return 0;}int Run(HWND hMainWnd){		__int64 timerFrequency;	if(!QueryPerformanceFrequency((LARGE_INTEGER *)&timerFrequency))	{		MessageBox(0, L"Performance timer does not exist!", L"Error!", MB_OK);		return 0;	}		__int64 currCount;	__int64 oldCount;	QueryPerformanceCounter((LARGE_INTEGER *)&currCount);	double elapsedTime = 0.0f; 		MSG msg;	ZeroMemory(&msg, sizeof(MSG));	while(msg.message != WM_QUIT)	{		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) 		{			TranslateMessage(&msg);			DispatchMessage(&msg);		} else {			oldCount = currCount;			QueryPerformanceCounter((LARGE_INTEGER *)&currCount);						// Find the difference in counter clicks and convert into seconds			// then add to the elapsed time			elapsedTime += (double)((currCount - oldCount) * timerFrequency);						// Update everything			gTetris->Update(elapsedTime);		}	}	return (int)msg.wParam;}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdArgs, int iCmdShow){	WNDCLASSEX exWndClass;	char szClassName[] = "Main Window"; // I don't like "magic numbers" and so "magic strings" shouldn't exist either ;)	HWND hMainWnd;		exWndClass.cbSize 			= sizeof(WNDCLASSEX);	exWndClass.style 			= CS_HREDRAW | CS_VREDRAW;	exWndClass.lpfnWndProc 		= WndProc;	exWndClass.hInstance 		= hInstance;	exWndClass.lpszClassName	= (LPCWSTR)szClassName;	exWndClass.cbWndExtra		= 0;	exWndClass.cbClsExtra		= 0;	exWndClass.lpszMenuName		= 0;	exWndClass.hbrBackground 	= (HBRUSH)GetStockObject(WHITE_BRUSH);	exWndClass.hCursor 			= LoadCursor(0, IDC_ARROW);	exWndClass.hIcon			= LoadIcon(0, IDI_APPLICATION);	exWndClass.hIconSm			= LoadIcon(0, IDI_APPLICATION);		if( !RegisterClassEx(&exWndClass) )	{		MessageBox(NULL, L"Window registration failed!", L"Error!", MB_OK | MB_ICONERROR);		return 0; // If registration fails, exit	}		hMainWnd = CreateWindowEx(WS_EX_CLIENTEDGE, (LPCWSTR)szClassName, L"Main Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 								WND_WIDTH, WND_HEIGHT, NULL, NULL, hInstance, NULL);		if(hMainWnd == NULL)	{		MessageBox(0, L"Creating window failed!", L"Error!", MB_OK | MB_ICONERROR);		return 0;	}		ShowWindow(hMainWnd, iCmdShow);	UpdateWindow(hMainWnd);		return Run(hMainWnd);}


I have minimized the amount of global variables to one, and that variable handles a lot of the main game logic (which I still have yet to implement of course :D ). Have a look. :]

Yo dawg, don't even trip.

Very nice boogyman19946, given me some ideas :) Thanks Deliverance. I'm pretty happy with it and it works. I've already made some mods since my last project. It will be a process as I complete and move onto other projects.

This topic is closed to new replies.

Advertisement