Sign in to follow this  

N00B Tries Pong

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites
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:
...

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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 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();

//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;
}

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 functions
LRESULT 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 Application
class 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 exceptions
int 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

Share this post


Link to post
Share on other sites
Quote:
Original post by TheN00B
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.


Thats good to know. I reinstalled MSVC++ 2005 express edition a few days ago and it turns out that "Program Database for Edit & Continue" is the default setting on my system so I never had a problem with that :D

Maybe the tooth fairy has been fiddeling with your settings :O

Share this post


Link to post
Share on other sites

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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