Correct design for a DirectX Engine

Started by
12 comments, last by Dancin_Fool 16 years, 7 months ago
Hi ! I'm making a 2D DirectX Engine and I'm looking for a correct design. I have currently a problem with circular include. The Design is something like this : I've a class D3DManagement who manage things like window creation, drawing, message loop, setting matrices ... A class named GameManager who manage the game logic: input, rules ... (All of theses classes are singletons) I'm trying to make a little tank game so I've a class called Tank who is derived of a Sprite class (a layer who manage 2d textured sprite). Without the Tank class, everything works. The error is :
Quote:[...] 1>Sprite.cpp 1>c:\documents and settings\tom\mes documents\visual studio 2005\projects\emptyproject\tank.h(12) : error C2504: 'Sprite' : base class undefined [...]
GameManager.h (singleton)
class D3DManagement;
class Tank;

#include "D3DManagement.h"
#include "Tank.h"

class GameManager
{
Tank *tank;
//...
};

D3DManagement.h (singleton)
class GameManager;

#include "GameManager.h"

class D3DManagement;
{
//...
};

Sprite.h
#include "D3DManagement.h"

class Sprite
{
//...
};

Tank.h
class Sprite;

#include "Sprite.h"

class Tank : public Sprite
{
//...
};

Thanks in advance. [Edited by - Kr00pS on October 10, 2007 10:06:15 AM]
Advertisement
You have a circular dependancy. Tank.h includes Sprite.h which includes GameManager.h which includes D3DManagement.h which includes Tank.h.

You need to fix that loop somehow, by removing one of the includes.
Hum I know, but I can't remove any include.

D3DManagement has the Direct3D device. I want to separate the lib (DirectX) and the game, but I can't with this design. :/

D3DManagement (Direct3D: draw, matrices, etc) and GameManager (gameloop, init game) have dependencies together.
Oh whoops - I didn't notice you knew there was a circular include, sorry.

To me it looks like D3DManagement.h shouldn't include GameManagement.h - why does the lower level D3D related class need to know about the higher level game related class?
Use pointers (or references), forward-declare your classes in your .h files and include files only in cpp if possible.

In your example :
class D3DManagement;class Tank;#include "D3DManagement.h"#include "Tank.h"class GameManager{Tank *tank;//...};


First, forward-declaring and THEN including is useless.
Second, as your working only with pointer to tanks, only forward declare and include in GameManager.cpp :
// GameManager.hclass D3DManagement;class Tank;class GameManager{Tank *tank;//...};// GameManager.cpp#include "GameManager.h"#include "D3DManagement.h"#include "Tank.h"// do stuff that uses Tank and D3DManagement here
Quote:Original post by Evil Steve

To me it looks like D3DManagement.h shouldn't include GameManagement.h - why does the lower level D3D related class need to know about the higher level game related class?


The D3DManagement need GameManager because my design is crap. I don't know how to separate properly the lower layer of the higher layer. Look at theses files:

D3DManagement.h
#pragma once#include <d3dx9.h>#include <iostream>#include <sstream>class GameManager;#include "GameManager.h"#include "Logger.h"using namespace std;#define D3DSAFE_RELEASE(p) if (p != NULL) { p->Release(); }class D3DManagement{private:	LPDIRECT3D9 m_D3DObject;	LPDIRECT3DDEVICE9 m_D3DDevice;	HWND m_MainWindowHandle;	int m_Width;	int m_Height;	bool m_Keys[256];// ------------------------------------------------------------------------------------------------	D3DManagement() {}	~D3DManagement() {}// ------------------------------------------------------------------------------------------------public:// ------------------------------------------------------------------------------------------------	static D3DManagement *GetInstance()	{		static D3DManagement instance;		return &instance;	}// ------------------------------------------------------------------------------------------------	HRESULT BuildWindow(string name, int width, int height);	HRESULT InitD3D();	void MessageLoop();	void Draw();	void Cleanup();	void SetMatrices();	LPDIRECT3DDEVICE9 GetDevice();	HWND GetHandle();	bool GetKey(int key);	void SetKey(int key, bool value);};LRESULT CALLBACK MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);


D3DManagement.cpp
#include "D3DManagement.h"const int FPS = 100;const int FRAMERATE = FPS/1000;LRESULT CALLBACK MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){	switch (msg)	{	case WM_DESTROY:		PostQuitMessage(0);		return 0;	case WM_SETCURSOR:		SetCursor(NULL);		//D3DManagement::GetInstance()->GetDevice()->ShowCursor(FALSE);		return 0;	case WM_KEYDOWN:		if(wParam == VK_ESCAPE)		{			GameManager::GetInstance()->SetGameOver(true);			PostQuitMessage(0);			return 0;		}		D3DManagement::GetInstance()->SetKey((int)wParam, true);		return 0;	case WM_KEYUP:		D3DManagement::GetInstance()->SetKey((int)wParam, false);		return 0;	}	return DefWindowProc(hwnd, msg, wParam, lParam);}bool D3DManagement::GetKey(int key){	return m_Keys[key];}void D3DManagement::SetKey(int key, bool value){	m_Keys[key] = value;}HRESULT D3DManagement::BuildWindow(string name, int width, int height){	m_Width = width;	m_Height = height;	WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, MsgProc, 0L, 0L, GetModuleHandle(NULL), 		NULL, LoadCursor(NULL, IDC_ARROW), NULL, NULL, "D3DM", NULL};	if(!RegisterClassEx(&wc))	{		MSG_ERROR("Window registration failed");		return E_FAIL;	}	MSG_SUCCESS("Windows registration succeeded");	m_MainWindowHandle = CreateWindow("D3DM", "Direct3D Test", WS_POPUP | WS_SYSMENU | WS_VISIBLE, 100, 100,		m_Width, m_Height, GetDesktopWindow(), NULL, wc.hInstance, NULL);	if (m_MainWindowHandle == NULL)	{		MSG_ERROR("Window creation failed");		return E_FAIL;	}	MSG_SUCCESS("Windows creation succeeded");	ShowWindow(m_MainWindowHandle, SW_SHOW);	UpdateWindow(m_MainWindowHandle);	for (int i = 0; i < 256; i++)	{		m_Keys = false;	}	return S_OK;}void D3DManagement::MessageLoop(){	float last_time = timeGetTime();	MSG msg;	PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);	while (msg.message != WM_QUIT)	{		float current_time = timeGetTime();		float delta_time = (current_time - last_time) * 0.001f;		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))		{			TranslateMessage(&msg);			DispatchMessage(&msg);			GameManager::GetInstance()->ManageInput();		}		if ((current_time - last_time) >= FRAMERATE)		{						GameManager::GetInstance()->ManageGame();			Draw();			last_time = current_time;		}	}}HRESULT D3DManagement::InitD3D(){	if (((m_D3DObject = Direct3DCreate9(D3D_SDK_VERSION)) == NULL))	{		MSG_ERROR("Can't create Direct3D object");		return E_FAIL;	}	MSG_SUCCESS("Direct3D object creation succeeded");	D3DPRESENT_PARAMETERS d3dpp;	ZeroMemory(&d3dpp, sizeof(d3dpp));	d3dpp.Windowed               = TRUE;	d3dpp.EnableAutoDepthStencil = TRUE;	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;	d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;	d3dpp.BackBufferWidth        = m_Width;	d3dpp.BackBufferHeight       = m_Height;	d3dpp.BackBufferFormat       = D3DFMT_X8R8G8B8;	d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;	if (FAILED(m_D3DObject->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_MainWindowHandle,		D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &m_D3DDevice)))	{		MSG_ERROR("Can't create Direct3D device");		return E_FAIL;	}	MSG_SUCCESS("Direct3D device creation succeeded");	m_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);	m_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);	// Desactivation du Z buffer	m_D3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, false);	SetMatrices();	return S_OK;}void D3DManagement::Draw(){	m_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);	m_D3DDevice->BeginScene();	GameManager::GetInstance()->Draw();	m_D3DDevice->EndScene();	m_D3DDevice->Present(NULL, NULL, NULL, NULL);}void D3DManagement::Cleanup(){	D3DSAFE_RELEASE(m_D3DObject);	D3DSAFE_RELEASE(m_D3DDevice);}void D3DManagement::SetMatrices(){	D3DXMATRIX matWorld;	D3DXMatrixTranslation(&matWorld, 2, 2, 0);	m_D3DDevice->SetTransform(D3DTS_WORLD, &matWorld);	// ------------------------------------------------------------------------------------------------	D3DXVECTOR3 vecEyePt (0.0f, 3.0f, 0.0f);	D3DXVECTOR3 vecLookatPt (0.0f, 0.0f, 0.0f);	D3DXVECTOR3 vecUpVec (0.0f, 1.0f, 0.0f);	D3DXMATRIXA16 matView;	D3DXMatrixLookAtLH(&matView, &vecEyePt, &vecLookatPt, &vecUpVec);	m_D3DDevice->SetTransform(D3DTS_VIEW, &matView);	// ------------------------------------------------------------------------------------------------	D3DXMATRIX matOrtho;	D3DXMatrixOrthoLH(&matOrtho, D3DX_PI/4, 1.0f, 1.0f, 100.0f);	m_D3DDevice->SetTransform(D3DTS_PROJECTION, &matOrtho);}LPDIRECT3DDEVICE9 D3DManagement::GetDevice(){	return m_D3DDevice;}HWND D3DManagement::GetHandle(){	return m_MainWindowHandle;}


GameManager.h
#pragma onceclass D3DManagement;class Sprite;class Tank;#include "D3DManagement.h"#include "Sprite.h"#include "Tank.h"#include "TextManager.h"class GameManager{private:	Tank *m_Player;	bool m_GameOver;// ------------------------------------------------------------------------------------------------	GameManager() {}	~GameManager() {}// ------------------------------------------------------------------------------------------------public:// ------------------------------------------------------------------------------------------------	static GameManager *GetInstance()	{		static GameManager instance;		return &instance;	}// ------------------------------------------------------------------------------------------------	void Init();	void Destroy();	void Draw();	void GameLoop();	void ManageGame();	void ManageInput();	void SetGameOver(bool value);	bool IsGameOver();};


GameManager.cpp
#include "GameManager.h"void GameManager::Init(){	D3DManagement::GetInstance()->BuildWindow("D3DApp", 1024, 768);	D3DManagement::GetInstance()->InitD3D();	//D3DManagement::GetInstance()->Init();	TextManager::GetInstance()->Init();	m_Player = new Tank("tank.bmp", 300, 200);	m_GameOver = false;}void GameManager::Destroy(){	TextManager::GetInstance()->Destroy();	D3DManagement::GetInstance()->Cleanup();	delete m_Player;}void GameManager::GameLoop(){	while (m_GameOver != true)	{		D3DManagement::GetInstance()->MessageLoop();	}}void GameManager::Draw(){	m_Player->Draw();	TextManager::GetInstance()->DrawMessage("Tank Battle 0.1a", 10, 10, D3DCOLOR_XRGB(255,255,255));}void GameManager::ManageGame(){	m_Player->Update();}void GameManager::ManageInput(){	if (D3DManagement::GetInstance()->GetKey(VK_UP))	{		m_Player->DecreasePosition(false, true, 32);	}	if (D3DManagement::GetInstance()->GetKey(VK_DOWN))	{		m_Player->IncreasePosition(false, true, 32);	}	if (D3DManagement::GetInstance()->GetKey(VK_LEFT))	{		m_Player->DecreasePosition(true, false, 32);	}	if (D3DManagement::GetInstance()->GetKey(VK_RIGHT))	{		m_Player->IncreasePosition(true, false, 32);	}}void GameManager::SetGameOver(bool value){	m_GameOver = value;}bool GameManager::IsGameOver(){	return m_GameOver;}
D3DManagement.h doesn't need GameManager.h as far as I can see - you cna include it in the source file instead.
Now, it works with the Trillian's post.

Thanks for all.
As I can see, 'GameManager.h' doesn't even use 'D3DManagement', 'GameManager.cpp' does.

Remove both 'class D3DManagement' and '#include "D3DManagement.h"' from 'GameManager.h' and add only '#include "D3DManagement.h"' to the beginning of 'GameManager.cpp'.

Do the same thing for Tank, except that you should keep 'class Tank' in 'GameManager.h' because this files declares a pointer to a Tank.

I haven't looked at all your code but I'm sure you can also do this elsewhere. The rule is :
(class X is located in X.h)
- If a .h header uses in any way an instance (non-pointer) of X or accesses X's members (via a pointer or not), it needs to include X.h
- If a .h header uses only pointers or references to an X and does not accesses X's members, it needs to forward declare class X
- If a .cpp source file uses in any way an instance (non-pointer) of X or accesses X's members (via a pointer or not) AND the .h header file did not include X.h, it must include X.h

Anyways I think there's some nice tutorial on organising C++ code files in the ressources section of this website, check it out.
Quote:Original post by Trillian
Anyways I think there's some nice tutorial on organising C++ code files in the ressources section of this website, check it out.


Organizing Code Files in C and C++.

This topic is closed to new replies.

Advertisement