Jump to content
  • Advertisement
Sign in to follow this  
Kr00pS

Correct design for a DirectX Engine

This topic is 3938 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

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]

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

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


Share this post


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

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!