A few beginner DirectX questions

Started by
4 comments, last by PureBlackSin 14 years, 3 months ago
Hello, As a beginner to DirectX game programming, I'm a bit puzzled with the fallowing things (If you can provide any source code that'd be EXTREMELY helpful): 1. Cameras - Overall, how do they work? How would they work in multiplayer? If I wanted to make a real-time strategy game (ex. Age of Empires), what would I do with the camera to make it look like it is at an overhead view with a slight angle? 2. Movement - I'm unsure of how to make it look like the character is smoothly moving. I'm guessing it's grid-based? Is there anyway to make it look smoother and more free? Do I move the character bit by bit and play a "Walking" animation to make it look like that? Any tips on how to make it look realistic or at least smooth? 3. Organization - With this example "make-a-window" code:

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>

// define the screen resolution
#define SCREEN_WIDTH  800
#define SCREEN_HEIGHT 600

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")

// global declarations
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class

// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory

// 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)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    // wc.hbrBackground = (HBRUSH)COLOR_WINDOW;    // not needed any more
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass",
                          L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP,    // fullscreen values
                          0, 0,    // the starting x and y positions should be 0
                          SCREEN_WIDTH, SCREEN_HEIGHT,    // set the window to 640 x 480
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(msg.message == WM_QUIT)
            break;

        render_frame();
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


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

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


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION); // create the Direct3D interface

    D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information

    ZeroMemory(&d3dpp, sizeof(d3dpp));    // clear out the struct for use
    d3dpp.Windowed = FALSE;    // program fullscreen, not windowed
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames
    d3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;    // set the back buffer format to 32-bit
    d3dpp.BackBufferWidth = SCREEN_WIDTH;    // set the width of the buffer
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;    // set the height of the buffer


    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a deep blue
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);

    d3ddev->BeginScene();    // begins the 3D scene

    // do 3D rendering on the back buffer here

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);   // displays the created frame on the screen
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    d3ddev->Release(); // close and release the 3D device
    d3d->Release(); // close and release Direct3D
}


I am unsure of how to organize it. I want to make the code smaller and more readable without having to dish out the comments. Is there anyway to make this a bit more simplified or at least organized and easy to read/edit? Well, these are my questions at the moment. Thank you for tanking time to read through all of that and thank you in advance if you answer of question of mine :)
Advertisement
Camera in D3D = view matrix + projection matrix
Character moves as you want, his position is the only thing D3D may want to know. Nothing there is based on anything, you could use grid movement, there could even be movement around the side of a circle all the time in your game -- D3D does't care. Movement in programming actually means that in one frame an object has one position and in another frame it has a different position and that those frames are displayed one after another and quite fast.
And organization - you chose C++. That means you will have lots of code. But you also have abitily to add spaces in the code almost any way you want. You can also take advantage of the way C++ handles big code - you can add as much source files and header files as you need.

To sum up, I think you're thinking about high-level coding where it doesn't fit in. You have C++ and a very simple example from DirectXTutorial.com, so you have to think about the low level code you need to write. Because there is no camera in Direct3D, it doesn't handle movement and it will never be as simple as you want here.

P.S. It's Direct3D9 (rendering API), not DirectX (which is just a container for all the Direct* and X* APIs).
yeah camera question is not so simple that you'll understand it now. I'd suggest understand more on how direct3d renders stuff and the transformations it does before tackling this.
Player movement is how you want it to be.
And finally, distribute the code over various header files, and use OOP concepts like classes and OOP design patterns to make the code easier to handle.
...it's like a cycle with every other MMO post. The poster first posts it without a template the day he joins. Then a month later posts it again this time with the template but due to the feedback stated as "ANY" he gets flamed badly. Recovering from such an event takes a couple of weeks before he dares post again but this time he remembers to state the feedback as "ENCOURAGING ONLY" and then no one posts and the project dies away. Tired, the poster returns to the confines of his cave and I don't know what happens after that....
As for the organization I suggest you to split your code into "at least" two classes. Of course there are many different ways to organize your code but the correct ones are those allowing you to develop reusable code and to spend your time on "good" code.

Actually there is no reason to assign the same code the responsibility of creating a window AND initializing the D3D device.

In theory the application code, the render system and the window code shouldn't be mixed in the same class.

Since D3D runs on windows many people believe it is ok to mix the device initialization and the window creation. Guess what? It is not.

I'd start with a "window" class and a "renderingapi" class (pick up the names you prefer, mine are awful!). If D3D needs data from the window class you could pass a pointer to your window class to the "renderingapi"'s constructor. If you have to do a dirty trick to get windows-specific data, you can "safely" do it inside your dx9 renderingapi class.

I realize this could seem pointless, after all a beginner isn't about to develop a next-gen AAA game. To me it's not pointless because:

1- it's easier to start developing stuff using good design practices than writing messy code now and force yourself to change coding style later
2- by splitting code into different classes you need "less comments". A method like "show" or "clear" or "drawrectangle" is self-explaining. The same stuff written inside a very long function is hard to remember and needs comments.
3- Until you get used to API functions, you'll have to parse your code to remember how things work. If you organize your code in classes it will be easier to find what you're looking for.
4- everybody make mistakes and for a beginner it is important to be able to identify bugs. It is easier to debug an application if you can easily find out where to put a breakpoint.

IMHO it is important to understand how a simple design like that affects the way you can modify and extend your code.

As an example, your base "application" class could have a pointer to a main window and a pointer to the renderingapi.

This way, to implement another renderingapi (DX10/DX11/OGL) is simpler because all you need to do is to write a new renderingapi.

Suppose tomorrow you want to learn something new. You derive a new application class and you can immediately start programming!
If the design is poor you'll have to copy/paste a lot of stuff, look for the parts to change, etc.

When I need to learn something new, I like being able to immediately work on the real thing.

If everytime I need to test something I'm forced to copy/paste/adjust/retest a lot of code, if I'm lucky I'm going to learn things slowly, otherwise I could even give up because the process needed to setup a test environment could be long and boring.

Sorry for being so tedious, hope this helps!
Simple post, but found some source code for the camera, also, search internet for Orthogonal Camera, as those are the ones used in RTS

http://www.dhpoware.com/demos/index.html (Check out their camera samples)
Don't forget to visit my blog here.
Hey! Just let noobs be noobs, after all, we once were noobs right?
Hi, read all of this through thoroughly, hopefully this helps! :)


1. I personally don't think the camera thing is that hard.

See if this makes sense:

I usually cheat a bit.

For my camera matrix i use a normal matrix and inverse it.

IMPORTANT!!!!!!
Deal with all matrices.
Then Set Camera
Then Transform World and Render Objects

DO NOT set matrices and render at the same time, found this out the hard way.
DO NOT set view after rendering objects.

e.g. (for following a person)

        D3DXMATRIX matView;        D3DXMATRIX matTemp;        D3DXMATRIX matModel;                //play with all matrices here! Do not Render from these here!        //when done with all model matrices and are ready to render...        //you can start playing with the view matrix        //NOTE Translate() and Rotate() are functions that deal with matrices that i have made.        matTemp = Translate(0,5,-20) * (Rotate(0,180,0) * matModel;	D3DXMatrixInverse(&matView,NULL,&matTemp);


Hopefully /\ answered question one. Manipulation of matrices (in the cameras case the matrix before the inverting of the matrix) controls the camera, like any other object.


2. I'm tempted to post some functions i use for moving as i think your trying to get at the moving in all directs stuff. Yes you can use grids, but depends on your application.

Scale does what it says on the tin to a matrix.
Ditto for Rotate.
Ditto for Translate.
Transform sets the world transform, transforms by a prepared matrix ready for rendering.

TransSINCOS is the special one, it sets the position depending on rotation.
e.g. move 10 units 30 degrees from north.
This coupled with rotation will give you "smooth movement"

Use Rotate(anglex, angley, anglez) on a matrix, then on the matrix you get out of that, use TransSINCOS (distanceofmovement, anglex, angley, anglez).

e.g.
        D3DXMATRIX matTemp;        D3DXMATRIX matModel;        matModel = Rotate(anglex, angley, anglez) * TransSINCOS (distanceofmovement, anglex, angley, anglez);        //apply matrix to model


Here are my matrix transformation functions:
	////////////////Transform Start////////////////////////////////////////////////////////////////////////////D3DXMATRIX Rotate(float Xrotate, float Yrotate, float Zrotate){			D3DXMATRIX matRotateX;			D3DXMATRIX matRotateY;			D3DXMATRIX matRotateZ;			D3DXMATRIX matRotate;			D3DXMatrixRotationX(&matRotateX, D3DXToRadian(Xrotate));			D3DXMatrixRotationY(&matRotateY, D3DXToRadian(Yrotate));			D3DXMatrixRotationZ(&matRotateZ, D3DXToRadian(Zrotate));			matRotate = matRotateX*matRotateY*matRotateZ;			return matRotate;}D3DXMATRIX Translate(float XTranslate, float YTranslate, float ZTranslate){			D3DXMATRIX matTranslate;			D3DXMatrixTranslation(&matTranslate, XTranslate, YTranslate, ZTranslate);			return matTranslate;}D3DXMATRIX Scale(float XScale,float YScale,float ZScale){			D3DXMATRIX matScale;			D3DXMatrixScaling(&matScale, XScale, YScale, ZScale);			return matScale;}void Transform(D3DXMATRIX mat)		{			d3ddev->SetTransform(D3DTS_WORLD, &mat); ////matRotateX*matRotateY*matRotateZ*matTranslate*matScale		}D3DXMATRIX TransSINCOS (float distance, float x, float y, float z){		D3DXMATRIX matTransSINCOS;	x = sin (D3DXToRadian(x));	y = sin (D3DXToRadian(y));	z = cos (D3DXToRadian(z));	x = distance*x;	y = distance*y;	z = distance*z;	D3DXMatrixTranslation(&matTransSINCOS, x, y, z);	return matTransSINCOS;}/////////////////transform End//////////////////////////////////////////////////////////////////////////////


3. My organisation can be awful, only way to fix this is through practice.
General aims are:
Small cpp files and Header files (not everything squashed into one)
Easy to read and follow code.

I feel that it is better to get the code working on the first project and dealing with organisation on the later ones when you get more confident at coding.

Hope that helps,

PureBlackSin
For Games, Articles and Custom High End Computers, come visit.Myndoko

This topic is closed to new replies.

Advertisement