Sign in to follow this  

Starting 2d dx programming and general code structure

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

Before I begin, I'd like to say sorry for creating such a large thread title. Unfortunately I couldn't think of anything better. Regardless, I've just started to work with the Win32 and Directx 9 API's. So far I have a basic window setup along with all the important DX interfaces created (and I even had a little demo with a sprite being drawn on the screen, however I removed it to make the below code less cluttered). My main intent is to create 2d Apps. I've tried using SDL, but I decided to move over to DX9. (more on this after the code). In order to make the code more organized, I created a class called system that initializes a new window, along with managing all of the major game processes (rendering, main loop, etc). Just a quick Note, this class isn't complete by any means, it's just a skeleton with a few implemented features. here is the system header
//=========================================================================
// Class CSystem: handles all global process related to win32 and D3D
//=========================================================================

#ifndef SYSTEM_H
#define SYSTEM_H

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>

//system config settings
struct Config
{
	int Width, Height;
	bool Windowed;
	int CmdShow;
	LPSTR CmdLine;
};

//system class
class CSystem
{
	private:
			/*----variables for main window----*/

			HWND m_hWnd; //handle to the main window
			LPCWSTR m_lpsWndName; //name of the window

			/*----preferences for main window----*/

			int  m_nScreenWidth, m_nScreenHeight; //window height and width
			bool m_bWindowed; //whether the main window is windowed or not
			int m_nCmdShow; 
			LPSTR m_lpCmdLine;

			/*----D3D variables----*/ 
			IDirect3D9* m_pD3D9; //pointer to the D3D interface
			IDirect3DDevice9* m_pD3DDevice9; //pointer to the D3D Device

			bool InitD3D( void );
						
	public:
			//CSystem();

			bool CreateMainWindow( HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow, WNDPROC WndProc );

			void SetConfig( const Config* CFG )
			{
				m_nScreenHeight = CFG->Height;
				m_nScreenWidth = CFG->Width;
				m_bWindowed = CFG->Windowed;
				m_nCmdShow = CFG->CmdShow;
				m_lpCmdLine = CFG->CmdLine;
			}

			const HWND& GetWnd( void ) const { return m_hWnd; }

			//const WNDCLASSEX& GetWndstyle( void ) const { return Wndstyle; }

			//main state functions
			bool Init( void );
			void Render( void );
			void Destroy( void );
};

#endif; //SYSTEM_H


and the system cpp
#include "system.h"

//============================================================
// Purpose: creates the main window used for whole program
//============================================================
bool CSystem::CreateMainWindow( HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow, WNDPROC WndProc )
{
	//create a new class for the main window
	WNDCLASSEX wc;

	//clear the memory of the wc struct
	ZeroMemory( &wc, sizeof( WNDCLASSEX ) );

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.lpfnWndProc   = (WNDPROC)WndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hInstance	 = hInstance;
    wc.hCursor		 = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;    // not needed any more

    wc.lpszClassName = L"WindowClass";
	m_lpsWndName = L"WindowClass";

	wc.lpszMenuName  = NULL;
	wc.hIconSm = LoadIcon( NULL, IDI_WINLOGO );

	if( !RegisterClassEx( &wc ) )
		return false;

	if( m_bWindowed )
	{
		wc.style = CS_HREDRAW | CS_VREDRAW;

		m_hWnd = CreateWindowEx( NULL, L"WindowClass", L"D3D Test", 
								 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
								 0, 0, m_nScreenWidth, m_nScreenHeight, 
								 NULL, NULL, hInstance, NULL );
	}
	else
	{
		wc.style = CS_HREDRAW | CS_VREDRAW;

		m_hWnd = CreateWindowEx( NULL, L"WindowClass", L"D3D Test", 
								 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
								 0, 0, m_nScreenWidth, m_nScreenHeight, 
								 NULL, NULL, hInstance, NULL );
	}

	//check to see if the window was created properly
	if( m_hWnd == NULL )
		return false;

	return true;
}

//============================================================
// Purpose: creates the main window used for whole program
//============================================================
bool CSystem::Init( void )
{
	//show the window
	ShowWindow( m_hWnd, m_nCmdShow );

	//initialize D3D
	if( !InitD3D() )
		return false;

	return true;
}

//============================================================
// Purpose: Init DirectX
//============================================================
bool CSystem::InitD3D( void )
{
    m_pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);

	if( m_pD3D9 == NULL )
		return false;

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = true;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = m_hWnd;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.BackBufferWidth = m_nScreenWidth;
    d3dpp.BackBufferHeight = m_nScreenHeight;

    // create a device class using this information and the info from the d3dpp stuct
    m_pD3D9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, 
						D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                       &d3dpp, &m_pD3DDevice9 );

	if( m_pD3DDevice9 == NULL )
		return false;

	return true;
}



and then finally, the winmain cpp
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9math.h>
#include <dxerr9.h>

#include "system.h"

#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

CSystem System;

// the WindowProc function prototype
LRESULT CALLBACK WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

// the entry point for any Windows program
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
 
	//Setup the main window
	Config Cfg;
	Cfg.Width = 800;
	Cfg.Height = 600;
	Cfg.Windowed = true;
	Cfg.CmdLine = lpCmdLine;
	Cfg.CmdShow = nCmdShow;

	System.SetConfig( &Cfg );
	System.CreateMainWindow( hInstance, lpCmdLine, nCmdShow, WindowProc );

	if( !System.Init() )
		return 1; 

    // enter the main loop:

    MSG Msg;

    while( TRUE )
    {
        DWORD starting_point = GetTickCount();

        if( PeekMessage( &Msg, NULL, 0, 0, PM_REMOVE ) )
        {
            if ( Msg.message == WM_QUIT )
                break;

            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }

        if( KEY_DOWN( VK_ESCAPE ) )
            PostMessage( System.GetWnd(), WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    return Msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch( Msg )
    {
        case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
            break;
		}
    }

	 return DefWindowProc ( hWnd, Msg, wParam, lParam );
}



Hopefully you guys can give me some suggestions on how to improve this code. Now to get back to why I quit SDL. After working with SDL for quite a long period of time I felt like it was lacking in areas of implementation (I had to eventually add features, like a proper Vector2d class and Rect class because they were missing or not very well made). However, one of the other reasons as to why I stopped using SDL is my own fault. Because of SDL's lack of structure I always felt somewhat estranged on were to start coding. Of course I eventually figured out to make a game loop, I still have a really hard time seeing the big picture in game programming design and program structure (which is probably evident in the above code). I would greatly appreciate it if someone helped me figure out the large picture, and hopefully create a solid design for my up and coming 2d projects (but then again, I might be getting ahead of my self). BTW, I really appreciate all the help I've gotten from the Gamedev chat room, I don't think I could understand half the things I do now if it wasn't for your guy's help, so thanx :D EDIT: sorry, I was using code tags instead of source tags

Share this post


Link to post
Share on other sites
Here's a few suggestions after looking at your code:

-You may want to consider creating a seperate class for handling your window. IMO the window and your D3D graphics device are two separate entities (even though one depends on the other), and therefore its logical to keep the code for them in two different components or classes. Plus, personally, I always like keeping all the Win32 code in one place.

-There are ways to make your WNDPROC callback function a member function of a class. This is very handy for C++ window wrapper classes. Raymond Chen does this for his scratch program used in his examples on his blog, or if you search around for "C++ window wrapper class" you'll likely find plenty of hits (even on just this site alone).

-Be careful with checking error results from DX functions. For example, you're not checking the result of IDirect3D::CreateDevice properly. That method, like all COM methods, returns an HRESULT that indicates success or failure. In the case of failure, the value actually tells you what the problem was. The SUCCEEDED and FAILED macros let you simply check for success and failure. In the case of failure, you can use the DXGetErrorDescription function to get a string describing the error. You can also use the DXTrace function to pop up a message box with the error code.

-Posting WM_DESTROY isn't how you destroy a window.

Share this post


Link to post
Share on other sites
I've got some D3D Base Code that may be of use to you. It half keeps the D3D and window code separate; the window loop is handled by the base class, and the derived class can override the window loop to handle particular messages (You might want to change this for a larger app).

There's a few things my code does that yours doesn't:
1. You need to use AdjustWindowRect() to get the window size to pass to CreateWindowEx in windowed mode (If you're using a "classic" window style like WS_OVERLAPPEDWINDOW).
2. You need to check the return values for important D3D functions like CreateDevice() - checking if the pointer is NULL isn't guaranteed to work (ISTR that the docs never say that the pointer is valid if the function fails), and in any case, you can get an error message from the return value.
3. You need to check that the device can handle what you're asking it to (Although I'm pretty sure it should always be OK with your current present params setup)
4. That blocking while() loop at the end of your message loop is a really bad idea, it'll use 100% of the CPU, and it's not very accurate (GetTickCount() is only accurate to about 15ms, so using it to measure 25ms is a larger margin of error).

Share this post


Link to post
Share on other sites

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