Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Simple and Powerful Game Engine

0.00/5 (No votes)
23 Jul 2007 1  
This article talks about a simple and powerful game engine to make game programming simpler

Screenshot - image001.jpg

This picture was taken from 3ds Max. This F-16 has 58,133 vertices that make 174,399 triangles.

Introduction

This article talks about a simple and powerful game engine that uses DirectX �3D, Input, Music and Show. With this engine you can render complex 3D models like terrains, cities, people, cars, etc., as well as play music and add 3D effects like velocity, position and direction to the music. You can play movies -- any movie that can be played by Windows Media Player -- and also use direct input for getting the keyboard, mouse and joystick input at full speed and in advanced usage. This article includes a plug-in for 3ds Max, i.e. Number 1 3D modeling for games, that exports 3ds Max scenes to the SZM format. The SZM model format is a very simple but powerful modeling format. Here is the documentation of the SZM model format.

Using the code

Every 3D game has an Init_Game(), Game_Loop() and a Game_Clean_Up() function. In Init_Game(), you initialize DirectX and load the needed models, music and videos. In the game loop, you check if there is any input from the keyboard or mouse and you process the input. You also calculate where each object should be. After all this, you render it to the screen. In the game clean-up, you do any cleaning up that is needed when quitting the game.

Initializing the game

First, create a window. Then, before entering the message loop, start the initializing like this:

//------------------------------------------------

::ShowWindow(g_GlobalData.hWnd,SW_SHOW);
::UpdateWindow(g_GlobalData.hWnd);
//------------------------------------------------


//

//Intialize Program.

//


if(!::Init_Game())
{
    DestroyWindow(g_GlobalData.hWnd);
    Return false;
}

//------------------------------------------------

Below is the Init_Game() routine and this is what it does:

  1. Initialize some global variables, for example the zoom and the eye position, displaying the bounding boxes, etc.
  2. Initialize some variables of DirectX3D, i.e. to enable Z buffer, Global Ambient, 3DLighting, etc.
  3. Call InitDirectX3D().
  4. Create an object to be displayed.
  5. Play some video, only if you want to see a video now.
  6. After the video finishes playing, if you decided to play a video.
  7. You can play some music.
  8. Initialize direct input, i.e. mouse and keyboard input.
  9. Initialize a CD3DFont class.
  10. You are done initializing!
////////////////////////////////////////////////////

//

////////////////////////////////////////////////////

BOOL Init_Game()
{
    //Move Eye position back, so you can see the scene.

    g_GlobalData.CameraEyeZ = -15000;
    g_GlobalData.EyeX_Rotation=0;
    g_GlobalData.EyeY_Rotation=0;
    g_GlobalData.EyeZ_Rotation=0;
    
    // How much to zoom (when using the mouse wheel).

    g_GlobalData.m_dwMouseWheel = 10;
    g_GlobalData.m_dwCameraMoveY = 10;
    g_GlobalData.m_dwCameraMoveZ = 10;
    g_GlobalData.bShowBoundingBoxs = TRUE;
    g_GlobalData.bShowObjectBoundingBoxs = TRUE;
    g_GlobalData.bShowWireFrame = FALSE;
    g_GlobalData.dwInputSystemOldTime = 0;
    g_GlobalData.bProcessInput = TRUE;

    //---------------------------------

    // Init the values that would be past to SetRenderState

    g_DirectX3D.m_bEnableZBuffer = TRUE;
    g_DirectX3D.m_dwGlobalAmbient = D3DCOLOR_ARGB(255,0,0,0);
    g_DirectX3D.m_bEnableDirect3DLighting = TRUE;
    g_DirectX3D.m_bEnableBackFacing = FALSE;
    
    //---------------------------------

    //Now Init DirectX 3d.

    if(!g_DirectX3D.InitDirectX3D(g_GlobalData.hWnd))
    return FALSE;

    //---------------------------------

    // g_DirectX3D.WritePC_CapabilitiesToFile("PC_Capabilities.txt");

    //---------------------------------

    
    //Create or Load an object to display for an example.

    if(!::CreateAnObjectToDisplay())
    return FALSE;
    
    //---------------------------------

    //g_DirectX3D.ShowFog();

    //---------------------------------

    
    //Clear Display

    g_DirectX3D.Clear_Display(TRUE,TRUE,FALSE,DISPLAY_COLOR);
    g_DirectX3D.Flip();
    //---------------------------------

    
    //->Show a Video.

    //---------------------------------

    g_DirectShow.Play(L"Movies\\Nsync - Bye Bye Bye.mpg",g_GlobalData.hWnd);
    
    //->Turn on some Music.

    //---------------------------------

    if(!g_DirectSound.Init())
    return FALSE;
    
    if(!g_DirectSound.Load(L"Music\\Britney Spears � Crazy.mp3"))
    return FALSE;
    
    if(!g_DirectSound.CreateAudioPath(FALSE))
    return FALSE;

    if(!g_DirectSound.SetVolume(-1600))
    return FALSE;
    
    if(!g_DirectSound.PlayAudioPath(0))
    return FALSE;

    //---------------------------------

    g_GlobalData.bInitWasDone = TRUE;
    //---------------------------------

    if(!g_Keyboard.Init())return FALSE;
    if(!g_Mouse.Init())return FALSE;
    //---------------------------------


    //----------------------------

    g_pFont = new CD3DFont( _T("Times New Roman"), 14, D3DFONT_BOLD );
    
    g_pFont->InitDeviceObjects( g_DirectX3D.m_pD3dDevice );
    g_pFont->RestoreDeviceObjects();
    //----------------------------


    return TRUE;
}

Game loop

In the message loop, call Prog_Loop() like this:

//------------------------------------------------

while(1)
{
    if( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
    {
        if( !::GetMessage( &msg, NULL, 0, 0 ) )
        return msg.wParam;

        ::TranslateMessage(&msg); 
        ::DispatchMessage(&msg);
    }
    else if(g_GlobalData.bInitWasDone)
    {
        ::Game_Loop();
    }
    else
    {
        // make sure we go to sleep if we have nothing else to do

        ::WaitMessage();
    }
}
//------------------------------------------------
This is what Game_Loop() does:
  1. It checks if it is time to get input from the keyboard and mouse. Note that you can change it and make it check for input each time it enters Game_Loop().
  2. Sets transformation.
  3. Clears the display.
  4. Renders an object.
  5. Draws text.
  6. Flips the back buffer to the front buffer to display the new rendered scene.
  7. Rendering is done until the next loop.
////////////////////////////////////////////////////

//

////////////////////////////////////////////////////

BOOL Game_Loop()
{
    //Get time.

    g_GlobalData.dwCurrentTime = ::timeGetTime();
    //--------------------------------------

    //If passed a certain time check if there is any input.

    if(g_GlobalData.dwCurrentTime > (g_GlobalData.dwInputSystemOldTime+10))
    {
        if(g_GlobalData.bProcessInput)
        {
            g_Keyboard.ProcessKey();
            g_Mouse.ProcessMouse();
        }
    
        g_GlobalData.dwInputSystemOldTime = g_GlobalData.dwCurrentTime;
    }
    
    //--------------------------------------

    //Set Transformations.

    if(!g_DirectX3D.SetTransformations())
    return FALSE;

    //Clear Display.

    g_DirectX3D.Clear_Display(TRUE,TRUE,FALSE,DISPLAY_COLOR);

    //Render Object.

    g_DirectX3D.RenderObject(&g_GlobalData.Object);
    
    //--------------------------------------

    //Draw Text.

    g_pFont->DrawText( 380, 0, D3DCOLOR_ARGB(255,255,0,0), "My-Game!" );
    //--------------------------------------


    //Flip backbuffer to front buffer.

    g_DirectX3D.Flip();

    return TRUE;
}

The results

This is what you get:

Screenshot - image003.jpg

Additional futures

  • Fog
  • Displaying text
  • Lights
  • Tilling textures
  • Writing PC capabilities to a file
  • Windowed or full screen

How to compile

You must have the DirectX 8 or 9 SDK. This is how to include the paths of the "include" and "lib" directories of the DX SDK:

Visual Studio 6

Tools -> Options -> Directories (tab) -> (Include files) / (Library files) from the combo box-> add paths

Visual Studio 8 (VS 2005)

Tools -> Options -> Projects and solutions (from the tree view) -> Visual C++ directories -> (Include files) / (Library files) -> add paths

It is best to make the new added paths the last in the list.

SZM 3D model format

At the beginning of the file, there is an SZM_File_header structure. Right after it, there is a DWORD that holds the number of MyMesh structures. This is the header:

////////////////////////////////////////////////////

// Header

////////////////////////////////////////////////////


typedef struct _SZM_File_header
{
    DWORD SZM_Sig;//'SZM '

    DWORD version1;//'0004'

    DWORD version2;//'0001'

} 

SZM_File_header;

This is the DWORD that holds the number of meshes in the file:

////////////////////////////////////////////////////

//

////////////////////////////////////////////////////


DWORD nMeshesInFile; // Number of meshes in the file.

This is how MyMesh is declared:

////////////////////////////////////////////////////

// Now comes nMeshesInFile of MyMesh structures.

////////////////////////////////////////////////////


typedef struct _MyMesh
{
    //--------------------------

    DWORD nMeshName; // strlen(szMeshName)+1

    char *szMeshName; // Mesh name.

    DWORD nTextureFileName; // strlen(szTextureFileName)+1

    char *szTextureFileName;// Texture File Name.

    float UTilingData; // Texture n tiling at the U axis.

    float VTilingData; // Texture n tiling at the V axis. 

    float UOffsetData; // Texture Offset at the U axis.

    float VOffsetData; // Texture Offset at the V axis.

    //--------------------------


    D3DXMATRIX matTransformation;
    //The transformation matrix that was applied to the mesh.

    DWORD nVertex; // Number of Vertex of the mesh.

    MyD3DVERTEX *pVertex; // Vertex Buffer.

    //--------------------------


    MyMaterial Material; // Mesh Material.


    //--------------------------

    // Is there a Bounding Box.

    // The reason that some meshes don't have bounding boxes 

    // is that some meshes are part of a mesh that came before 

    // them, so that mesh bounding box also bounds on the following 

    // mesh (The reason that some mashed are divided to parts are 

    // because it can be meshes that different parts have a different 

    // material, so the mesh was divided to parts).


    BOOL bHasBoundingBox; 
    Box BoundingBox; // Bounding Box.

    Box3 MaxMinBox; // Mesh Max and Min points.

    //--------------------------


    DWORD nMesh; // Mesh-ID.

    DWORD dwLevelInTree; // Level in the tree.

    //--------------------------

} 
MyMesh;

////////////////////////////////////////////////////

//

////////////////////////////////////////////////////

For more information, see SZM_Format.h and SZM_Loader.cpp.

How to make 3D models

The number 1 modeling program for games is 3ds Max, but 3ds Max is not free. If you want a free 3D modeling program, you can use Blender 3D. I don't really like to use Blender 3D, but maybe you can get used to it. For more information, you can read here.

Resources and references

  • For more information on game engines, see here.
  • For information on physics engines, see here.
  • For information on game developing, see here.

Comments, suggestions or fixes

I would like to hear any comments, suggestions or fixes regarding this article. This would give me the motivation to write more articles. Enjoy your game!

History

  • 23 July, 2007 -- Original version posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here