N00B Tries Pong

Started by
10 comments, last by pulpfist 16 years, 10 months ago
First of all, I am so happy that I found this website, I know I'm going to spend countless hours reading the amazing content that you have all created here. Also I apologize if this question is in the wrong section, but this is my first game so I thought it would be a good place for it to go. I am a student at Portland State for Computer Science. I am finishing up my first year, but I've been learning c++ as a hobby for close to three years now. C++ is the first language I learned, so I seem to design everything to be OO, or as much as I possibly can. My biggest problem is I continually forget the KISS concept, and try to add to many "abilities" to a program. I am trying this game on my own as I just learned how to work with the Win32 API and GDI so please do tell me anything that I'm doing wrong here. I have three classes so far (I can't get my app past this point) one called Engine, one called EventHandler, and one called MyPong. At this point my engine class is setting up my window, and assigning an event handler. The EventHandler class is pure virtual and is used to register a class for handling events with the engine. The pong application handles all the game related stuff. I'm using Visual C++ 2005, and I can't debug, or even step into the application without receiving this: "Unhandled exception at 0x0040168a in MyPong.exe: 0xC0000005: Access violation reading location 0x00000000." I select to break and am informed that there is no source code available for the current location. My Call Stack looks like this: MyPong.exe!Engine::HandleEvent() + 0x7a bytes C++
MyPong.exe!WndProc() + 0x2a bytes C++
user32.dll!7e418734()
Here is my code thus far: Pong.h

#ifndef PONG_H
#define PONG_H

#include <cstdlib>

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#include "Resource.h"

//Default CALLBACK for Windows Events
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);

//Engine event callback type
class EventHandler
{
public:
	virtual BOOL HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam) = 0;
};

//Game System Callbacks
class PongApplication
{
public:
	virtual BOOL GameInitialize(HINSTANCE hInstance);
	virtual void GameStart(HWND hWindow);
	virtual void GameEnd();
	virtual void GameActivate(HWND hWindow);
	virtual void GameDeactivate(HWND hWindow);
	virtual void GamePaint(HDC hDC);
	virtual void GameCycle();

	void setDefault(BOOL default) { m_Default = default; }
	BOOL isDefault() { return m_Default; }

protected:
	BOOL m_Default;
};	


class Engine
{
public:

	//Engine Constructor
	//Input: 
	//Application Instance, WindowClassName, Title, Icons, Width and Height of Window
	//
	Engine(HINSTANCE hInstance, LPSTR szWindowClass, LPSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, 
		int iHeight, int iFrameDelay = 50);
	
	//Engine Destructor
	//
	virtual ~Engine();

	//GetEngine
	//Input: None
	//Returns the singleton Engine object.
	static Engine* GetEngine() { return m_pGameEngine; }

	//Initialize
	//Input: Show Window
	//Returns: True if Engine created, false otherwise
	BOOL Initialize(int iCmdShow);

	//HandleEvent
	//Event Handler: 
	//This will call Event Handler callbacks set by 
	//the applicaiton.
	//Input: standard MFC message information
	//Returns: LRESULT handled event?
	LRESULT	HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);

	//AccessorMethods
	HINSTANCE GetInstance() const {return m_hInstance; }
	HWND	  GetWindow() const { return m_hWindow; }
	void	  SetWindow(HWND hWnd) { m_hWindow = hWnd; }
	WORD	  GetIcon() const { return m_wIcon; }
	WORD	  GetSmallIcon() const { return m_wSmallIcon; }
	int		  GetWidth() const { return m_iWidth; }
	int		  GetHeight() const { return m_iHeight; } 
	void	  GetDimensions(int &width, int &height) { width = m_iWidth; height = m_iHeight; }
	int		  GetFrameDelay() const { return m_iFrameDelay; }
	void	  SetFrameRate(int iFrameRate) { m_iFrameDelay = iFrameRate; }
	BOOL	  GetSleep() const { return m_bSleep; }
	void	  SetSleep(BOOL bSleep) { m_bSleep = bSleep; }
	void	  SetEventHandler(EventHandler* pHandler);
	void	  SetApplication(PongApplication* pGame);

protected:
	//Variables
	static Engine*		m_pGameEngine;
	HINSTANCE				m_hInstance;
	HWND					m_hWindow;
	TCHAR					m_szWindowClass[32];
	TCHAR					m_szTitle[32];
	WORD					m_wIcon, m_wSmallIcon;
	int						m_iWidth, m_iHeight;
	int						m_iFrameDelay;
	BOOL					m_bSleep;
	EventHandler*			m_pEventHandler;
	PongApplication*		m_pGame;

};

#endif

PongEngine.cpp

#include "Pong.h"

Engine* Engine::m_pGameEngine = NULL;

Engine::Engine(HINSTANCE hInstance, LPSTR szWindowClass, LPSTR szTitle, WORD wIcon, 
			   WORD wSmallIcon, int iWidth, int iHeight, int iFrameDelay)
{
	m_pGameEngine = this;
	m_hInstance = hInstance;
	m_hWindow = NULL;
	if ((lstrlen(szWindowClass) > 0) && (lstrlen(szWindowClass) <=32 ))
	{
		lstrcpy(m_szWindowClass, szWindowClass);
	}
	else
		lstrcpy(m_szWindowClass, "DefaultClass");

	if ((lstrlen(szTitle) > 0) && (lstrlen(szTitle) <= 32))
	{
		lstrcpy(m_szTitle, szTitle);
	}
	else
		lstrcpy(m_szTitle, "DefaultTitle");

	m_wIcon = wIcon;
	m_wSmallIcon = wSmallIcon;
	m_iWidth = iWidth;
	m_iHeight = iHeight;
	m_iFrameDelay = iFrameDelay;
	m_bSleep = TRUE;
	m_pGame = NULL;
	m_pEventHandler = NULL;

}

Engine::~Engine()
{
	if(m_pGame != NULL)
	{

		if(m_pGame->isDefault())
		{
			delete m_pGame;
			m_pGame = NULL;
		}
		else
		{
			m_pGame = NULL;
		}
	}
	if (m_pGameEngine = this)
		m_pGameEngine = NULL;
		
}

BOOL Engine::Initialize(int iCmdShow)
{
	WNDCLASSEX		wndclass;

	// Create the window class for the main window
	wndclass.cbSize			= sizeof(wndclass);
	wndclass.style			= CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc			= WndProc;
	wndclass.cbClsExtra			= 0;
	wndclass.cbWndExtra			= 0;
	wndclass.hInstance			= m_hInstance;
	wndclass.hIcon				= LoadIcon(m_hInstance, 
		MAKEINTRESOURCE(GetIcon()));
	wndclass.hIconSm			= LoadIcon(m_hInstance, 
		MAKEINTRESOURCE(GetSmallIcon()));
	wndclass.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground		= (HBRUSH)(COLOR_WINDOW + 1);
	wndclass.lpszMenuName		=	NULL;
	wndclass.lpszClassName		= m_szWindowClass;

	// Register the window class
	if (!RegisterClassEx(&wndclass))
		return FALSE;

	//Calc the size and pos.
	int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CYFIXEDFRAME) *2, 
		iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + 
		  GetSystemMetrics(SM_CYCAPTION);
	if (wndclass.lpszMenuName != NULL)
		iWindowHeight += GetSystemMetrics(SM_CYMENU);
	int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
		iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;
	
	//Create Window
	m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW | 
		WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth, 
		iWindowHeight, NULL, NULL, m_hInstance, NULL);
	if (!m_hWindow)
		return FALSE;


	//Show and update window
	ShowWindow(m_hWindow, iCmdShow);
	UpdateWindow(m_hWindow);

	//Check application instance
	//If no instance exists create default
	if(m_pGame == NULL)
	{
		m_pGame = new PongApplication();
		m_pGame->setDefault(TRUE);
	}


	return TRUE;
}

LRESULT Engine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		// Set the game window and start the game
		SetWindow(hWindow);
		m_pGame->GameStart(hWindow);
		return 0;
	case WM_ACTIVATE:
		//Activate/deactivate the game and update the sleep status
		if (wParam != WA_INACTIVE)
		{
			m_pGame->GameActivate(hWindow);
			SetSleep(FALSE);
		}
		else
		{
			m_pGame->GameDeactivate(hWindow);
			SetSleep(TRUE);
		}
		return 0;
	case WM_PAINT:
		HDC		hDC;
		PAINTSTRUCT ps;
		hDC = BeginPaint(hWindow, &ps);

		m_pGame->GamePaint(hDC);

		EndPaint(hWindow, &ps);
		return 0;

	case WM_DESTROY:
		m_pGame->GameEnd();
		PostQuitMessage(0);
		return 0;
	}
	if(m_pEventHandler != NULL)
	{
		if(!m_pEventHandler->HandleEvent(hWindow, msg, wParam, lParam))
			return DefWindowProc(hWindow, msg, wParam, lParam);
	}

	return DefWindowProc(hWindow, msg, wParam, lParam);
}

void Engine::SetEventHandler(EventHandler *pHandler)
{
	if(m_pEventHandler == NULL)
		m_pEventHandler = pHandler;
}

void Engine::SetApplication(PongApplication *pGame)
{
	if(m_pGame == NULL)
		m_pGame = pGame;
}
			

BOOL PongApplication::GameInitialize(HINSTANCE hInstance)
{
	Engine::GetEngine()->SetFrameRate(15);

	return TRUE;
}

void PongApplication::GameStart(HWND hWindow)
{
	srand(GetTickCount());
}

void PongApplication::GameEnd()
{
	Engine* pEngine = Engine::GetEngine();
	delete pEngine;
}

void PongApplication::GameActivate(HWND hWindow)
{
	HDC hDC;
	RECT rect;

	GetClientRect(hWindow, &rect);
	hDC = GetDC(hWindow);
	DrawText(hDC, TEXT("Engine Works!"), -1, &rect, 
		DT_SINGLELINE | DT_CENTER | DT_VCENTER);
	ReleaseDC(hWindow, hDC);
}

void PongApplication::GameDeactivate(HWND hWindow)
{
	HDC hDC;
	RECT rect;

	GetClientRect(hWindow, &rect);
	hDC = GetDC(hWindow);
	DrawText(hDC, TEXT("Engine Works!"), -1, &rect, 
		DT_SINGLELINE | DT_CENTER | DT_VCENTER);
	ReleaseDC(hWindow, hDC);
}

void PongApplication::GamePaint(HDC hDC)
{
}

void PongApplication::GameCycle()
{
}

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
	Engine *pEngine = Engine::GetEngine();
	if (pEngine != NULL)
		return pEngine->HandleEvent(hWindow, msg, wParam, lParam);

	return DefWindowProc(hWindow, msg, wParam, lParam);
}

MyPong.cpp

#include "Pong.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	MSG msg;
	static int iTickTrigger = 0;
	int			iTickCount;

	Engine *pEngine = new Engine(hInstance, TEXT("MyPong"), 
		TEXT("MyPong"), IDI_MYPONG, IDI_MYPONG_SM, 800, 600, 50);
	if(pEngine == NULL)
		return FALSE;

	if(!pEngine->Initialize(nShowCmd))
		return FALSE;

	//Enter the main message loop
	while (TRUE)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			//Process the message
			if (msg.message == WM_QUIT	)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			//Make sure the game engine isn't sleeping
			if (!pEngine->GetSleep())
			{
				//Check the tick count to see if a gam cycle has elapsed
				iTickCount = GetTickCount();
				if (iTickCount > iTickTrigger)
				{
					iTickTrigger = iTickCount + 
						pEngine->GetFrameDelay();
				}
			}
		}
	}
	return(int)msg.wParam;
}


I'm completely at a loss as to what to do to fix this problem. If anyone can give me even a nudge in the right direction that would be great. Also if anyone has some tips, or info on what I can do differently as far as the "structure" of my game is concerned that would be great!
Advertisement
I noticed this line in your Engine destructor

if (m_pGameEngine = this)

It might not be the cause of the runtime error but it should be fixed.
Part from that it sounds to me that you are using an invalid pointer somewhere.
I suggest going through your code and add sanity checks on your pointers.

edit:
If Im not mistaken this code is never executed:
...switch(msg){        // Missing case here? <-------        // Set the game window and start the game	SetWindow(hWindow);	m_pGame->GameStart(hWindow);	return 0;	case WM_ACTIVATE:        ...
The reason I had that line there is because I tried a singleton approach to this class, so when I call the destructor I wanted to ensure that that pointer is NULL, however I did remove it upon your advice, just wondering why I should remove it. I also have added checks to all my pointers.

Also I changed my Callback function to this:

LRESULT Engine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam){	switch(msg)	{	case WM_CREATE:		// Set the game window and start the game		SetWindow(hWindow);		m_pGame->GameStart(hWindow);		return 0;	case WM_ACTIVATE:		//Activate/deactivate the game and update the sleep status		if (wParam != WA_INACTIVE)		{			m_pGame->GameActivate(hWindow);			SetSleep(FALSE);		}		else		{			m_pGame->GameDeactivate(hWindow);			SetSleep(TRUE);		}		return 0;	case WM_PAINT:		HDC		hDC;		PAINTSTRUCT ps;		hDC = BeginPaint(hWindow, &ps);		m_pGame->GamePaint(hDC);		EndPaint(hWindow, &ps);		return 0;	case WM_DESTROY:		m_pGame->GameEnd();		PostQuitMessage(0);		return 0;	}	if(m_pEventHandler != NULL)	{		if(!m_pEventHandler->HandleEvent(hWindow, msg, wParam, lParam))			return DefWindowProc(hWindow, msg, wParam, lParam);	}	return DefWindowProc(hWindow, msg, wParam, lParam);}


I'm glad you noticed this so I could fix it, however I am still getting the same error. Its interesting that the Engine::HandleEvent is the last thing on the call stack, wouldn't my application go to the WinMain function before going to the handleEvent function? I have never had an error such as this (I couldn't even step into the WinMain function) so I'm at a bit of a loss.

At any rate thanks for the advice on my code thus far!
Quote:
just wondering why I should remove it

I was thinking more along the lines of changing it from
if (m_pGameEngine = this)
to
if (m_pGameEngine == this)

:D
Right! Wow I should really try getting more sleep sometimes... :-)

Thanks a lot!
So I finally got this thing to work, but I have no idea why...

I removed the MyPong class as well as the event handler class. I made a global instance of the engine, and the functions that were once methods of the mypong class are now global functions. I also moved the WinMain function into Engine.cpp (I'm just trying things here)... The code looks like this:

I appreciate any efforts to help me by looking through my code, I understand that its a lot to read through, and I really do appreciate any help

Engine.h
#pragma once#include <windows.h>LRESULT CALLBACK  WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);//-----------------------------------------------------------------// Game Function Declarations//-----------------------------------------------------------------BOOL GameInitialize(HINSTANCE hInstance);void GameStart(HWND hWindow);void GameEnd();void GameActivate(HWND hWindow);void GameDeactivate(HWND hWindow);void GamePaint(HDC hDC);void GameCycle();//-----------------------------------------------------------------// GameEngine Class//-----------------------------------------------------------------class GameEngine{protected:  // Member Variables  static GameEngine*  m_pGameEngine;  HINSTANCE           m_hInstance;  HWND                m_hWindow;  TCHAR               m_szWindowClass[32];  TCHAR               m_szTitle[32];  WORD                m_wIcon, m_wSmallIcon;  int                 m_iWidth, m_iHeight;  int                 m_iFrameDelay;  BOOL                m_bSleep;public:  // Constructor(s)/Destructor          GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass, LPTSTR szTitle,            WORD wIcon, WORD wSmallIcon, int iWidth = 640, int iHeight = 480);  virtual ~GameEngine();  // General Methods  static GameEngine*  GetEngine() { return m_pGameEngine; };  BOOL                Initialize(int iCmdShow);  LRESULT             HandleEvent(HWND hWindow, UINT msg, WPARAM wParam,                        LPARAM lParam);  // Accessor Methods  HINSTANCE GetInstance() { return m_hInstance; };  HWND      GetWindow() { return m_hWindow; };  void      SetWindow(HWND hWindow) { m_hWindow = hWindow; };  LPTSTR    GetTitle() { return m_szTitle; };  WORD      GetIcon() { return m_wIcon; };  WORD      GetSmallIcon() { return m_wSmallIcon; };  int       GetWidth() { return m_iWidth; };  int       GetHeight() { return m_iHeight; };  int       GetFrameDelay() { return m_iFrameDelay; };  void      SetFrameRate(int iFrameRate) { m_iFrameDelay = 1000 /              iFrameRate; };  BOOL      GetSleep() { return m_bSleep; };  void      SetSleep(BOOL bSleep) { m_bSleep = bSleep; };};


Engine.cpp
#include "Engine.h"GameEngine *GameEngine::m_pGameEngine = NULL;int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,  PSTR szCmdLine, int iCmdShow){  MSG         msg;  static int  iTickTrigger = 0;  int         iTickCount;  if (GameInitialize(hInstance))  {    // Initialize the game engine    if (!GameEngine::GetEngine()->Initialize(iCmdShow))      return FALSE;    // Enter the main message loop    while (TRUE)    {      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))      {        // Process the message        if (msg.message == WM_QUIT)          break;        TranslateMessage(&msg);        DispatchMessage(&msg);      }      else      {        // Make sure the game engine isn't sleeping        if (!GameEngine::GetEngine()->GetSleep())        {          // Check the tick count to see if a game cycle has elapsed          iTickCount = GetTickCount();          if (iTickCount > iTickTrigger)          {            iTickTrigger = iTickCount +              GameEngine::GetEngine()->GetFrameDelay();            GameCycle();          }        }      }    }    return (int)msg.wParam;  }  // End the game  GameEnd();  return TRUE;}LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam){  // Route all Windows messages to the game engine  return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);}//-----------------------------------------------------------------// GameEngine Constructor(s)/Destructor//-----------------------------------------------------------------GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,  LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight){  // Set the member variables for the game engine  m_pGameEngine = this;  m_hInstance = hInstance;  m_hWindow = NULL;  if (lstrlen(szWindowClass) > 0)    lstrcpy(m_szWindowClass, szWindowClass);  if (lstrlen(szTitle) > 0)    lstrcpy(m_szTitle, szTitle);  m_wIcon = wIcon;  m_wSmallIcon = wSmallIcon;  m_iWidth = iWidth;  m_iHeight = iHeight;  m_iFrameDelay = 50;   // 20 FPS default  m_bSleep = TRUE;}GameEngine::~GameEngine(){}//-----------------------------------------------------------------// Game Engine General Methods//-----------------------------------------------------------------BOOL GameEngine::Initialize(int iCmdShow){  WNDCLASSEX    wndclass;  // Create the window class for the main window  wndclass.cbSize         = sizeof(wndclass);  wndclass.style          = CS_HREDRAW | CS_VREDRAW;  wndclass.lpfnWndProc    = WndProc;  wndclass.cbClsExtra     = 0;  wndclass.cbWndExtra     = 0;  wndclass.hInstance      = m_hInstance;  wndclass.hIcon          = LoadIcon(m_hInstance,    MAKEINTRESOURCE(GetIcon()));  wndclass.hIconSm        = LoadIcon(m_hInstance,    MAKEINTRESOURCE(GetSmallIcon()));  wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);  wndclass.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);  wndclass.lpszMenuName   = NULL;  wndclass.lpszClassName  = m_szWindowClass;  // Register the window class  if (!RegisterClassEx(&wndclass))    return FALSE;  // Calculate the window size and position based upon the game size  int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,      iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +        GetSystemMetrics(SM_CYCAPTION);  if (wndclass.lpszMenuName != NULL)    iWindowHeight += GetSystemMetrics(SM_CYMENU);  int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,      iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;  // Create the window  m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW |    WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth,    iWindowHeight, NULL, NULL, m_hInstance, NULL);  if (!m_hWindow)    return FALSE;  // Show and update the window  ShowWindow(m_hWindow, iCmdShow);  UpdateWindow(m_hWindow);  return TRUE;}LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam){  // Route Windows messages to game engine member functions  switch (msg)  {    case WM_CREATE:      // Set the game window and start the game      SetWindow(hWindow);      GameStart(hWindow);      return 0;    case WM_ACTIVATE:      // Activate/deactivate the game and update the Sleep status      if (wParam != WA_INACTIVE)      {        GameActivate(hWindow);        SetSleep(FALSE);      }      else      {        GameDeactivate(hWindow);        SetSleep(TRUE);      }      return 0;    case WM_PAINT:      HDC         hDC;      PAINTSTRUCT ps;      hDC = BeginPaint(hWindow, &ps);      // Paint the game      GamePaint(hDC);      EndPaint(hWindow, &ps);      return 0;    case WM_DESTROY:      // End the game and exit the application      GameEnd();      PostQuitMessage(0);      return 0;  }  return DefWindowProc(hWindow, msg, wParam, lParam);}


MyPong.h - Just declares a global game engine
#pragma once#include <windows.h>#include "Resource.h"#include "Engine.h"//-----------------------------------------------------------------// Global Variables//-----------------------------------------------------------------GameEngine* _pGame;


MyPong.cpp - Implementation of global functions
#include "MyPong.h"BOOL GameInitialize(HINSTANCE hInstance){  // Create the game engine  _pGame = new GameEngine(hInstance, TEXT("My Pong"),    TEXT("My Pong"), IDI_MYPONG, IDI_MYPONG_SM);  if (_pGame == NULL)    return FALSE;    // Set the frame rate  _pGame->SetFrameRate(15);  return TRUE;}void GameStart(HWND hWindow){  // Seed the random number generator  srand(GetTickCount());}void GameEnd(){  // Cleanup the game engine  delete _pGame;}void GameActivate(HWND hWindow){  HDC   hDC;  RECT  rect;  // Draw activation text on the game screen  GetClientRect(hWindow, &rect);  hDC = GetDC(hWindow);  DrawText(hDC, TEXT("Activated!"), -1, &rect,    DT_SINGLELINE | DT_CENTER | DT_VCENTER);  ReleaseDC(hWindow, hDC);}void GameDeactivate(HWND hWindow){  HDC   hDC;  RECT  rect;  // Draw deactivation text on the game screen  GetClientRect(hWindow, &rect);  hDC = GetDC(hWindow);  DrawText(hDC, TEXT("Deactivated!"), -1, &rect,    DT_SINGLELINE | DT_CENTER | DT_VCENTER);  ReleaseDC(hWindow, hDC);}void GamePaint(HDC hDC){}void GameCycle(){  HDC   hDC;  HWND  hWindow = _pGame->GetWindow();  // Draw the skeleton icon at random positions on the game screen  hDC = GetDC(hWindow);  DrawIcon(hDC, rand() % _pGame->GetWidth(), rand() % _pGame->GetHeight(),    (HICON)(WORD)GetClassLong(hWindow, GCL_HICON));  ReleaseDC(hWindow, hDC);}


With this new structure the app. executes and shuts down fine, but I still can't step through the code (all I can step through is the disassembly...)

I have no idea why this structure works better then the other, and I am nervous about using globals (I've always been told they are "bad" programming).


If anyone has any idea why this is executing and the other is not, and why I still can not step into my code it would be great!
Sounds good.

I managed to get rid of the problem too.
Basically I had to remove any calls to the engine in the WM_CREATE, WM_ACTIVATE and WM_PAINT cases.
I guess your singleton wasn't properly initialized at those stages (I believe they are all executed at startup)

Your implementation of the singleton was probably not good.
There is well documented ways to do this that you might want to look into =)

edit:
Here is a dump of my debug code for reference:
#include <cstdlib>#define WIN32_LEAN_AND_MEAN#include <windows.h>//#include "Resource.h"//Default CALLBACK for Windows EventsLRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);//Engine event callback typeclass EventHandler{public:	virtual BOOL HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam) = 0;};//Game System Callbacksclass PongApplication{public:	virtual BOOL GameInitialize(HINSTANCE hInstance);	virtual void GameStart(HWND hWindow);	virtual void GameEnd();	virtual void GameActivate(HWND hWindow);	virtual void GameDeactivate(HWND hWindow);	virtual void GamePaint(HDC hDC);	virtual void GameCycle();	void setDefault(BOOL default) { m_Default = default; }	BOOL isDefault() { return m_Default; }protected:	BOOL m_Default;};	class Engine{public:	//Engine Constructor	//Input: 	//Application Instance, WindowClassName, Title, Icons, Width and Height of Window	//	Engine();		//Engine Destructor	//	virtual ~Engine();	//GetEngine	//Input: None	//Returns the singleton Engine object.	static Engine* GetEngine() 	{ 		static Engine e;		return &e; 	}	//Initialize	//Input: Show Window	//Returns: True if Engine created, false otherwise	BOOL Initialize(HINSTANCE hInstance, LPWSTR szWindowClass, LPWSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, 		int iHeight, int iFrameDelay = 50);	//HandleEvent	//Event Handler: 	//This will call Event Handler callbacks set by 	//the applicaiton.	//Input: standard MFC message information	//Returns: LRESULT handled event?	static LRESULT	HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);	//AccessorMethods	HINSTANCE GetInstance() const {return m_hInstance; }	HWND	  GetWindow() const { return m_hWindow; }	void	  SetWindow(HWND hWnd) { m_hWindow = hWnd; }	WORD	  GetIcon() const { return m_wIcon; }	WORD	  GetSmallIcon() const { return m_wSmallIcon; }	int		  GetWidth() const { return m_iWidth; }	int		  GetHeight() const { return m_iHeight; } 	void	  GetDimensions(int &width, int &height) { width = m_iWidth; height = m_iHeight; }	int		  GetFrameDelay() const { return m_iFrameDelay; }	void	  SetFrameRate(int iFrameRate) { m_iFrameDelay = iFrameRate; }	BOOL	  GetSleep() const { return m_bSleep; }	void	  SetSleep(BOOL bSleep) { m_bSleep = bSleep; }	void	  SetEventHandler(EventHandler* pHandler);	void	  SetApplication(PongApplication* pGame);protected:	//Variables	HINSTANCE				m_hInstance;	HWND					m_hWindow;	TCHAR					m_szWindowClass[32];	TCHAR					m_szTitle[32];	WORD					m_wIcon, m_wSmallIcon;	int						m_iWidth, m_iHeight;	int						m_iFrameDelay;	BOOL					m_bSleep;	EventHandler*			m_pEventHandler;	PongApplication*		m_pGame;};Engine::Engine(){	m_hWindow = NULL;	m_bSleep = TRUE;	m_pGame = NULL;	m_pEventHandler = NULL;}Engine::~Engine(){	if(m_pGame != NULL)	{		if(m_pGame->isDefault())		{			delete m_pGame;			m_pGame = NULL;		}		else		{			m_pGame = NULL;		}	}	}BOOL Engine::Initialize(HINSTANCE hInstance, LPWSTR szWindowClass, LPWSTR szTitle, WORD wIcon, 			   WORD wSmallIcon, int iWidth, int iHeight, int iFrameDelay){	WNDCLASSEX		wndclass;	m_hInstance = hInstance;	m_wIcon = wIcon;	m_wSmallIcon = wSmallIcon;	m_iWidth = iWidth;	m_iHeight = iHeight;	m_iFrameDelay = iFrameDelay;	if ((lstrlen(szWindowClass) > 0) && (lstrlen(szWindowClass) <=32 ))	{		lstrcpy(m_szWindowClass, szWindowClass);	}	else		lstrcpy(m_szWindowClass, TEXT("DefaultClass"));	if ((lstrlen(szTitle) > 0) && (lstrlen(szTitle) <= 32))	{		lstrcpy(m_szTitle, szTitle);	}	else		lstrcpy(m_szTitle, TEXT("DefaultTitle"));	// Create the window class for the main window	wndclass.cbSize			= sizeof(wndclass);	wndclass.style			= CS_HREDRAW | CS_VREDRAW;	wndclass.lpfnWndProc			= WndProc;	wndclass.cbClsExtra			= 0;	wndclass.cbWndExtra			= 0;	wndclass.hInstance			= m_hInstance;	wndclass.hIcon				= LoadIcon(m_hInstance, 		MAKEINTRESOURCE(GetIcon()));	wndclass.hIconSm			= LoadIcon(m_hInstance, 		MAKEINTRESOURCE(GetSmallIcon()));	wndclass.hCursor			= LoadCursor(NULL, IDC_ARROW);	wndclass.hbrBackground		= (HBRUSH)(COLOR_WINDOW + 1);	wndclass.lpszMenuName		=	NULL;	wndclass.lpszClassName		= m_szWindowClass;	// Register the window class	if (!RegisterClassEx(&wndclass))		return FALSE;	//Calc the size and pos.	int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CYFIXEDFRAME) *2, 		iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + 		  GetSystemMetrics(SM_CYCAPTION);	if (wndclass.lpszMenuName != NULL)		iWindowHeight += GetSystemMetrics(SM_CYMENU);	int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,		iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;		//Create Window	m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW | 		WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth, 		iWindowHeight, NULL, NULL, m_hInstance, NULL);	if (!m_hWindow)		return FALSE;	//Show and update window	ShowWindow(m_hWindow, TRUE);	UpdateWindow(m_hWindow);	//Check application instance	//If no instance exists create default	MessageBox(NULL, TEXT("0"), TEXT("Error"), MB_OK);	if(m_pGame == NULL)	{		m_pGame = new PongApplication();		m_pGame->setDefault(TRUE);	}	// Set the game window and start the game	GetEngine()->SetWindow(m_hWindow);	GetEngine()->m_pGame->GameStart(m_hWindow);	return TRUE;}LRESULT Engine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam){	switch(msg)	{	case WM_CREATE:		return 0;	case WM_ACTIVATE:		/*		//Activate/deactivate the game and update the sleep status		if (wParam != WA_INACTIVE)		{			GetEngine()->m_pGame->GameActivate(hWindow);			GetEngine()->SetSleep(FALSE);		}		else		{			GetEngine()->m_pGame->GameDeactivate(hWindow);			GetEngine()->SetSleep(TRUE);		}*/		return 0;	case WM_PAINT:		HDC		hDC;		PAINTSTRUCT ps;		hDC = BeginPaint(hWindow, &ps);		//GetEngine()->m_pGame->GamePaint(hDC);		EndPaint(hWindow, &ps);		return 0;	case WM_DESTROY:		GetEngine()->m_pGame->GameEnd();		PostQuitMessage(0);		return 0;	}	if(GetEngine()->m_pEventHandler != NULL)	{		if(!GetEngine()->m_pEventHandler->HandleEvent(hWindow, msg, wParam, lParam))			return DefWindowProc(hWindow, msg, wParam, lParam);	}	return DefWindowProc(hWindow, msg, wParam, lParam);}void Engine::SetEventHandler(EventHandler *pHandler){	if(m_pEventHandler == NULL)		m_pEventHandler = pHandler;}void Engine::SetApplication(PongApplication *pGame){	if(m_pGame == NULL)		m_pGame = pGame;}			BOOL PongApplication::GameInitialize(HINSTANCE hInstance){	Engine::GetEngine()->SetFrameRate(15);	return TRUE;}void PongApplication::GameStart(HWND hWindow){	srand(GetTickCount());}void PongApplication::GameEnd(){	Engine* pEngine = Engine::GetEngine();	delete pEngine;}void PongApplication::GameActivate(HWND hWindow){	HDC hDC;	RECT rect;	GetClientRect(hWindow, &rect);	hDC = GetDC(hWindow);	DrawText(hDC, TEXT("Engine Works!"), -1, &rect, 		DT_SINGLELINE | DT_CENTER | DT_VCENTER);	ReleaseDC(hWindow, hDC);}void PongApplication::GameDeactivate(HWND hWindow){	HDC hDC;	RECT rect;	GetClientRect(hWindow, &rect);	hDC = GetDC(hWindow);	DrawText(hDC, TEXT("Engine Works!"), -1, &rect, 		DT_SINGLELINE | DT_CENTER | DT_VCENTER);	ReleaseDC(hWindow, hDC);}void PongApplication::GamePaint(HDC hDC){}void PongApplication::GameCycle(){}LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam){	Engine *pEngine = Engine::GetEngine();	if (pEngine != NULL)		return pEngine->HandleEvent(hWindow, msg, wParam, lParam);	return DefWindowProc(hWindow, msg, wParam, lParam);}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){	MSG msg;	static int iTickTrigger = 0;	int			iTickCount;	/*Engine *pEngine = new Engine();	if(pEngine == NULL)		return FALSE;*/	if(!Engine::GetEngine()->Initialize(hInstance, TEXT("MyPong"), 		TEXT("MyPong"), 0, 0, 800, 600, 50))		return FALSE;	//Enter the main message loop	while (TRUE)	{		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))		{			//Process the message			if (msg.message == WM_QUIT	)				break;			TranslateMessage(&msg);			DispatchMessage(&msg);		}		else		{			//Make sure the game engine isn't sleeping			if (!Engine::GetEngine()->GetSleep())			{				//Check the tick count to see if a gam cycle has elapsed				iTickCount = GetTickCount();				if (iTickCount > iTickTrigger)				{					iTickTrigger = iTickCount + 						Engine::GetEngine()->GetFrameDelay();				}			}		}	}	return(int)msg.wParam;}
Hey thanks so much for you help, I really appreciate it. Now I understand that the Event handling function does get called before the Engine is created so I must use global functions for this app. I was curious if anyone had any other useful methods for handling a situation like this. The only reason I ask is I was under the impression that having global functions for handling the game was "bad", esp. for OOP.

Also I still can not seem to step into my code using the VC++ debugger (all I can step into is the disassembly), any reason why I can't step into my WinMain function, or any C++ code for that matter?
If anyone else has run into this problem (not being able to step into your code), I went into my project properties, then c/c++, in the general section. I changed Debug Information Format to Program Database for Edit & Continue. I performed a re-build and now I can step through my code with no problem.
Quote:Original post by TheN00B
Hey thanks so much for you help, I really appreciate it. Now I understand that the Event handling function does get called before the Engine is created so I must use global functions for this app. I was curious if anyone had any other useful methods for handling a situation like this. The only reason I ask is I was under the impression that having global functions for handling the game was "bad", esp. for OOP.


Personally I like to use virtual interfaces for event handling.
The skeleton I'm using:

//===================================#define WIN32_LEAN_AND_MEAN#include <windows.h>#include <string>//===================================// Base class for a windows application. // Provides a simple window and some events.class Application{public:	class Exception : public std::exception	{	public:		Exception(int error, const std::string& message) throw() : mError(error), mMessage(message) {}		virtual ~Exception() throw() {}		virtual const char* what() throw() { return mMessage.c_str(); }	private:		int mError;		std::string mMessage;	};	Application() {}	virtual ~Application() {}	void init(HINSTANCE hInstance, int iWidth, int iHeight, int iFrameDelay);	void exec();	void exit();protected:	// A simple singleton (There is more sophisticated ways to do this)	static Application* instance() { static Application a; return &a; }	// I would prefer to have these event handlers pure virtual, 	// but my very simple singleton implementation prevent me from this...	virtual void onCreate() {}		virtual void onInit() {}		virtual void onActivate() {}	virtual void onPaint(HDC& hDC, PAINTSTRUCT& paintStruct) {}	virtual void onIdle(DWORD frameTicks) {}	virtual void onExit() {}		virtual void onDestroy() {}private:	static LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);	HINSTANCE m_hInstance;	HWND m_hWindow;		int m_iWidth, m_iHeight;	DWORD m_iFrameDelay, m_iLastFrame;};//===================================void Application::init(HINSTANCE hInstance, int iWidth, int iHeight, int iFrameDelay){	WNDCLASSEX wndclass;	m_hInstance = hInstance;		m_iWidth = iWidth;	m_iHeight = iHeight;	m_iFrameDelay = iFrameDelay;	// Create the window class for the main window	wndclass.cbSize			= sizeof(wndclass);	wndclass.style			= CS_HREDRAW | CS_VREDRAW;	wndclass.lpfnWndProc	= WndProc;	wndclass.cbClsExtra			= 0;	wndclass.cbWndExtra			= 0;	wndclass.hInstance			= m_hInstance;	wndclass.hIcon				= LoadIcon(NULL, IDI_APPLICATION);	wndclass.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);	wndclass.hCursor			= LoadCursor(NULL, IDC_ARROW);	wndclass.hbrBackground		= (HBRUSH)(COLOR_WINDOW + 1);	wndclass.lpszMenuName		= NULL;	wndclass.lpszClassName		= TEXT("ApplicationClass");	// Register the window class	if(!RegisterClassEx(&wndclass))		throw Exception(-1, "RegisterClassEx failed");		//Create Window	m_hWindow = CreateWindow(TEXT("ApplicationClass"), TEXT("Title"), WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX, 		0, 0, iWidth, iHeight, NULL, NULL, m_hInstance, NULL);	if(!m_hWindow)		throw Exception(-1, "CreateWindow failed");	//Show and update window	ShowWindow(m_hWindow, TRUE);	UpdateWindow(m_hWindow);	onInit();}//===================================// Our event cruncher...void Application::exec(){	MSG msg;	while(TRUE)	{		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))		{			//Process the message			if (msg.message == WM_QUIT	)				break;			TranslateMessage(&msg);			DispatchMessage(&msg);		}		else		{										DWORD iTickCount = GetTickCount();			if(iTickCount >= m_iLastFrame + m_iFrameDelay)			{				// Fire off Applications onIdle event.				// Thanks to how virtual functions work, this will eventually 				// end up as a call to driver::onIdle				instance()->onIdle(iTickCount - m_iLastFrame);                                // TODO: Rename this to onFrame...                                m_iLastFrame = iTickCount;			}					}	}}//===================================void Application::exit(){	onExit();}//===================================// The Windows procedure. This is a static member of Application. // Static member functions use the same calling convention as global functionsLRESULT CALLBACK Application::WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam){	switch(msg)	{	case WM_CREATE:		// Fire off Application (and inherited) onCreate events		instance()->onCreate();		return 0;	case WM_ACTIVATE:		instance()->onActivate();		return 0;	case WM_PAINT:		HDC	hDC;		PAINTSTRUCT ps;		hDC = BeginPaint(hWindow, &ps);				instance()->onPaint(hDC, ps);		EndPaint(hWindow, &ps);		return 0;	case WM_DESTROY:				instance()->onDestroy();		PostQuitMessage(0);		return 0;	}	return DefWindowProc(hWindow, msg, wParam, lParam);}//===================================// The program driver. // This will typically be extended, inheriting from more interfaces than just Applicationclass Driver : public Application{public:protected:	void onCreate() 	{		// Init code goes here...		// Note that this event is called from WM_CREATE. 		// At that point the main application window is not yet fully created, 		// so we can not make any graphics happen here.	}			void onInit()	{		MessageBoxA(NULL, "onInit", "Event", MB_OK);	}	void onIdle(DWORD frameTicks) 	{		// Idle code goes here.		// Update graphics, network, etc...	}	void onExit()	{		MessageBoxA(NULL, "onExit", "Event", MB_OK);	}	void onDestroy() 	{		// Cleanup code	}private:};	//===================================// WinMain responsible for creating our driver and catch any exceptionsint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){	try	{		Driver driver;		driver.init(hInstance, GetSystemMetrics(SM_CXSCREEN) / 2, GetSystemMetrics(SM_CYSCREEN) / 2, 32);		driver.exec();		driver.exit();	}	catch(Application::Exception& e)	{		MessageBoxA(NULL, e.what(), "Exception", MB_OK);	}	catch(...)	{		MessageBoxA(NULL, "Unknown exception", "Exception", MB_OK);	}	return 0;}//===================================


More on singletons and game engines
The One: A Singleton Discussion
Learning Singletons thread
Enginuity

This topic is closed to new replies.

Advertisement