Engine Source Code

Every C++ project in this book will have the same basic folder structure. First, there will be a main folder named after the project (for instance, Alien Invaders). This folder will contain the source code files for the project. Contained within this folder will be subfolders named for the compilers that are supported (in the form of project files—.sln for Visual C++ and .dev for Dev-C++). The Dev-C++ folder is called devcpp, while the Visual C++ 2005 folder is called msvc8. If you have a different compiler, you can just follow the basic instructions in this chapter to create the project for your preferred compiler, and if you have a DirectX library available for it, then the code will be shared. This is the only way to build a game engine; don’t even bother building one for a single compiler, because that is not practical. Even if you are a diehard Microsoft fan, you still need to make project files available for all the various versions of Visual C++ (because none of them are compatible).

Advice

Visual C++ solution files have an extension of .sln. A solution may have multiple project files, which each have an extension of .vcproj.


Within the main project folder (that is, Engine), there will be a .in folder that will contain the compiled executable for a given project. In this .in folder you should put any assets that are needed by your game. Obviously this doesn’t apply to the engine itself, only to games you build using the engine. (Consider this a free tip for future reference.) Because the .in folder is not a default option, we must set it in the project, and this is not an issue now because we are not yet working on an executable program, just a library project.

Advice

An asset in the context of a game includes all media and data files used by the game (such as art assets, model assets, audio assets, and so on), and therefore must be distributed with the game.


Are you ready? The code we’ll be going over here in order to build the core engine will require tons of serious mental torque! So, it’s time to downshift and get your RPMs way up as we enter the first corner of the proverbial track toward building this engine.

Advanced2D.h

Here is the source code for the Advanced2D.h header file. The code is the same regardless of whether you are using Dev-C++ or Visual C++. The only problems you may experience (other than the usual typos that must be fixed) are linker errors related to the project configuration. This file describes the core structure of the game engine at this point.

// Advanced2D Engine
// Main header file

#ifndef _ADVANCED2D_H
#define _ADVANCED2D_H 1

#include <iostream>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dxerr9.h>
#include "Timer.h"

#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define REVISION 0

//external variables and functions
extern bool gameover;
extern bool game_preload();
extern bool game_init(HWND);
extern void game_update();
extern void game_end();

namespace Advanced2D
{
    class Engine {
    private:
        int p_versionMajor, p_versionMinor, p_revision;
        HWND p_windowHandle;
        LPDIRECT3D9 p_d3d;
        LPDIRECT3DDEVICE9 p_device;
        LPDIRECT3DSURFACE9 p_backbuffer;
        LPD3DXSPRITE p_sprite_handler;
        std::string p_apptitle;
        bool p_fullscreen;
        int p_screenwidth;
        int p_screenheight;
        int p_colordepth;
        bool p_pauseMode;
        D3DCOLOR p_ambientColor;
        bool p_maximizeProcessor;
        Timer p_coreTimer;
        long p_frameCount_core;
        long p_frameRate_core;
        Timer p_realTimer;
        long p_frameCount_real;
        long p_frameRate_real;

    public:
        Engine();
        virtual ~Engine();
        int Init(int width, int height, int colordepth, bool fullscreen);
        void Close();
        void Update();
        void message(std::string message, std::string title = "ADVANCED 2D");
        void fatalerror(std::string message, std::string title = "FATAL ERROR");
        void Shutdown();
        void ClearScene(D3DCOLOR color);
        void SetDefaultMaterial();
        void SetAmbient(D3DCOLOR colorvalue);
        int RenderStart();
        int RenderStop();
        int Release();

        //accessor/mutator functions expose the private variables
        bool isPaused() { return this->p_pauseMode; }
        void setPaused(bool value) { this->p_pauseMode = value; }
        LPDIRECT3DDEVICE9 getDevice() { return this->p_device; }
        LPDIRECT3DSURFACE9 getBackBuffer() { return this->p_backbuffer; }
        LPD3DXSPRITE getSpriteHandler() { return this->p_sprite_handler; }
        void setWindowHandle(HWND hwnd) { this->p_windowHandle = hwnd; }
        HWND getWindowHandle() { return this->p_windowHandle; }
        std::string getAppTitle() { return this->p_apptitle; }
        void setAppTitle(std::string value) { this->p_apptitle = value; }
        int getVersionMajor() { return this->p_versionMajor; }
        int getVersionMinor() { return this->p_versionMinor; }
        int getRevision() { return this->p_revision; }
        std::string getVersionText();
        long getFrameRate_core() { return this->p_frameRate_core; };
        long getFrameRate_real() { return this->p_frameRate_real; };
        int getScreenWidth() { return this->p_screenwidth; }
        void setScreenWidth(int value) { this->p_screenwidth = value; }
        int getScreenHeight() { return this->p_screenheight; }
        void setScreenHeight(int value) { this->p_screenheight = value; }
        int getColorDepth() { return this->p_colordepth; }
        void setColorDepth(int value) { this->p_colordepth = value; }
        bool getFullscreen() { return this->p_fullscreen; }
        void setFullscreen(bool value) { this->p_fullscreen = value; }
        bool getMaximizeProcessor() { return this->p_maximizeProcessor; }
        void setMaximizeProcessor(bool value) { this->p_maximizeProcessor = value;}

    }; //class

}; //namespace

//define the global engine object (visible everywhere!)
extern Advanced2D::Engine *g_engine;

#endif

Advanced2D.cpp

The Advanced2D.cpp file contains the source code for the Engine class. Note that the Engine class is embedded inside a namespace called Advanced2D. This was done to keep the Engine and its support classes and functions contained to prevent conflicts with other entities in the global namespace.

Advice

Are you getting lost already with these discussions of namespaces and so forth? This is basic C++ programming! If you’re struggling with it, you’ll need a crash course before proceeding. I recommend Effective C++, 3rd Edition (Addison-Wesley Professional, 2005) by Scott Meyers. If you are a complete C++ newbie and you need serious help, then read C++ Programming for the Absolute Beginner (Course Technology PTR, 2002) by Dirk Henkemans and Mark Lee.


 // Advanced2D Engine
 // Main source code file

 //includes
 #include "Advanced2D.h"
 #include <cstdlib>
 #include <ctime>
 #include <string>
 #include <sstream>
 #include <list>
 #include "winmain.h"

 namespace Advanced2D
 {
     Engine::Engine()
     {
         srand((unsigned int)time(NULL));
         p_maximizeProcessor = false;
         p_frameCount_core = 0;
         p_frameRate_core = 0;
         p_frameCount_real = 0;
         p_frameRate_real = 0;
         p_ambientColor = D3DCOLOR_RGBA(255,255,255, 0);
         p_windowHandle = 0;
         p_pauseMode = false;
         p_versionMajor = VERSION_MAJOR;
         p_versionMinor = VERSION_MINOR;
         p_revision = REVISION;

         //set default values
         this->setAppTitle("Advanced2D");
         this->setScreenWidth(640);
         this->setScreenHeight(480);
         this->setColorDepth(32);
         this->setFullscreen(false);

         //window handle must be set later on for DirectX!
         this->setWindowHandle(0);

   }

      Engine::~Engine()
      {
            if (this->p_device) this->p_device->Release();
            if (this->p_d3d) this->p_d3d->Release();
      }

      std::string Engine::getVersionText()
      {
          std::ostringstream s;
          s << "Advanced2D Engine v" << p_versionMajor << "." << p_versionMinor
               << "." << p_revision;
          return s.str();
      }

      void Engine::message(std::string message, std::string title)
      {
          MessageBox(0, message.c_str(), title.c_str(), 0);
      }

      void Engine::fatalerror(std::string message, std::string title)
      {
          this->message(message,title);
          Shutdown();
      }

      int Engine::Init(int width, int height, int colordepth, bool fullscreen)
      {
          //initialize Direct3D
          this->p_d3d = Direct3DCreate9(D3D_SDK_VERSION);
          if (this->p_d3d == NULL) {
              return 0;
          }

          //get system desktop color depth
          D3DDISPLAYMODE dm;
          this->p_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm);

          //set configuration options for Direct3D
          D3DPRESENT_PARAMETERS d3dpp;
          ZeroMemory(&d3dpp, sizeof(d3dpp));
          d3dpp.Windowed = (!fullscreen);
          d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
          d3dpp.EnableAutoDepthStencil = TRUE;
          d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
          d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

          d3dpp.BackBufferFormat = dm.Format;
          d3dpp.BackBufferCount = 1;
          d3dpp.BackBufferWidth = width;
          d3dpp.BackBufferHeight = height;
          d3dpp.hDeviceWindow = p_windowHandle;

          //create Direct3D device
          this->p_d3d->CreateDevice(
              D3DADAPTER_DEFAULT,
              D3DDEVTYPE_HAL,
              this->p_windowHandle,
              D3DCREATE_HARDWARE_VERTEXPROCESSING,
              &d3dpp,
              &this->p_device);

          if (this->p_device == NULL) return 0;

          //clear the backbuffer to black
          this->ClearScene(D3DCOLOR_XRGB(0,0,0));

          //create pointer to the back buffer
          this->p_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &this-
  >p_ backbuffer);

          //use ambient lighting and z-buffering
          this->p_device->SetRenderState(D3DRS_ZENABLE, TRUE);
          this->p_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
          this->SetAmbient(this->p_ambientColor);

          //initialize 2D renderer
          HRESULT result = D3DXCreateSprite(this->p_device, &this->p_sprite_handler);
          if (result != D3D_OK) return 0;

          //call game initialization extern function
          if (!game_init(this->getWindowHandle())) return 0;
          //set a default material
          SetDefaultMaterial();
          return 1;
      }

      void Engine::SetDefaultMaterial()
      {
          D3DMATERIAL9 mat;
          memset(&mat, 0, sizeof(mat));
          mat.Diffuse.r = 1.0f;
          mat.Diffuse.g = 1.0f;
          mat.Diffuse.b = 1.0f;
          mat.Diffuse.a = 1.0f;
          p_device->SetMaterial(&mat);
      }

      void Engine::ClearScene(D3DCOLOR color)
      {
          this->p_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
  color, 1.0f, 0);
      }

      void Engine::SetAmbient(D3DCOLOR colorvalue)
      {
          this->p_ambientColor = colorvalue;
          this->p_device->SetRenderState(D3DRS_AMBIENT, this->p_ambientColor);
      }

      int Engine::RenderStart()
      {
          if (!this->p_device) return 0;
          if (this->p_device->BeginScene() != D3D_OK) return 0;
          return 1;
      }

      int Engine::RenderStop()
      {
            if (!this->p_device) return 0;
            if (this->p_device->EndScene() != D3D_OK) return 0;
            if (p_device->Present(NULL, NULL, NULL, NULL) != D3D_OK) return 0;
            return 1;
      }

      void Engine::Shutdown()
      {
          gameover = true;
      }

      void Engine::Update()
      {
          static Timer timedUpdate;
        //calculate core framerate
        p_frameCount_core+ +;
        if (p_coreTimer.stopwatch(999)) {
            p_frameRate_core = p_frameCount_core;
            p_frameCount_core = 0;
        }

        //fast update with no timing
        game_update();

        //update with 60fps timing
        if (!timedUpdate.stopwatch(14)) {
            if (!this->getMaximizeProcessor())
            {
                Sleep(1);
            }
        }
        else {
             //calculate real framerate
             p_frameCount_real+ +;
             if (p_realTimer.stopwatch(999)) {
                 p_frameRate_real = p_frameCount_real;
                 p_frameCount_real = 0;
             }

             //begin rendering
             this->RenderStart();

             //done rendering
             this->RenderStop();
         }
     }

     void Engine::Close()
     {
         game_end();
    }
} //namespace

Did the code in the Engine class seem like a huge and complex detail that we simply skipped over? Not to worry—you will become familiar with it in future chapters. But our immediate goal is to get the core engine built and working in order to delve into advanced rendering in the next chapter. That calls for a blitzkrieg of code. I apologize if the term invokes negative connotations from World War II, but it is a good term—we need to blitz through the basics and get the core engine built quickly, without stopping to regroup until the goal has been achieved.

Timer.h

The Timer class provides quick and easy timing facilities to your games, and to the core engine itself in the form of frame-rate estimation and reporting. The Timer’s stopwatch() method was designed to be self-contained so that you can repeatedly call stopwatch() until the specified amount of time (in milliseconds) has passed—at which point the Timer object will reset itself for the next call to stopwatch(). It is an elegant design that greatly simplifies timing code.

/* Timer class provides timing and stopwatch features to the engine */
#pragma once
#include <time.h>
#include <windows.h>
namespace Advanced2D {
class Timer
{
private:
    DWORD timer_start;
    DWORD stopwatch_start;
public:
    Timer(void);
    ~Timer(void);
    DWORD getTimer();
    DWORD getStartTimeMillis();
    void sleep(int ms);
    void reset();
    bool stopwatch(int ms);
};
};

Timer.cpp

The implementation of the Timer class’ methods is contained in the Timer.cpp file. There are several accessor methods, such as getStartTimeMillis() (which returns the number of milliseconds since the program started), in addition to the valuable stopwatch() method.

#include "Timer.h"
namespace Advanced2D {
Timer::Timer(void)
{
    timer_start = timeGetTime();
    reset();
}

Timer::~Timer(void)
{
}

DWORD Timer::getTimer()
{
    return (DWORD) (timeGetTime());
}

DWORD Timer::getStartTimeMillis()
{
    return (DWORD) (timeGetTime() - timer_start);
}


void Timer::sleep(int ms)
{
    DWORD start = getTimer();
    while (start + ms > getTimer());
}

void Timer::reset()
{
    stopwatch_start = getTimer();
}

bool Timer::stopwatch(int ms)
{
    if ( timeGetTime() > stopwatch_start + ms ) {
           stopwatch_start = getTimer();
           return true;
    }
    else return false;
}
};

winmain.h

The winmain header file contains just the few include statements needed by the winmain source code file (coming up next).

#ifndef _WINMAIN_H
#define _WINMAIN_H 1

#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#include <iostream>
#include <windows.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include "Advanced2D.h"
#endif

winmain.cpp

#include <sstream>
#include "winmain.h"
#include "Advanced2D.h"

//macro to read the key states
#define KEY_DOWN(vk) ((GetAsyncKeyState(vk) & 0x8000)?1:0)

HINSTANCE g_hInstance;
HWND g_hWnd;
int g_nCmdShow;

//declare global engine object
Advanced2D::Engine *g_engine;

bool gameover;

//window event callback function
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
      case WM_QUIT:
      case WM_CLOSE:
      case WM_DESTROY:
             gameover = true;
             break;
        }
return DefWindowProc( hWnd, msg, wParam, lParam );
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int
nCmdShow)
{
     MSG msg;
     srand((unsigned int)time(NULL));
     g_hInstance = hInstance;
     g_nCmdShow = nCmdShow;
     DWORD dwStyle, dwExStyle;
     RECT windowRect;

     /**
       * Create engine object first!
     **/
     g_engine = new Advanced2D::Engine();

     //let main program have a crack at things before window is created
     if (!game_preload()) {
         MessageBox(g_hWnd, "Error in game preload!", "Error", MB_OK);
         return 0;
     }

     //get window caption string from engine
     char title[255];
     sprintf(title, "%s", g_engine->getAppTitle().c_str());

     //set window dimensions
     windowRect.left = (long)0;
     windowRect.right = (long)g_engine->getScreenWidth();
     windowRect.top = (long)0;
     windowRect.bottom = (long)g_engine->getScreenHeight();

     //create the window class structure
     WNDCLASSEX wc;
     wc.cbSize = sizeof(WNDCLASSEX);

     //fill the struct with info
     wc.style = CS_HREDRAW | CS_VREDRAW;
     wc.lpfnWndProc = (WNDPROC)WinProc;
     wc.cbClsExtra     = 0;
     wc.cbWndExtra     = 0;
     wc.hInstance     = hInstance;
     wc.hIcon = NULL;
     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
     wc.hbrBackground = NULL;
     wc.lpszMenuName = NULL;
     wc.lpszClassName = title;
     wc.hIconSm = NULL;

     //set up the window with the class info
     RegisterClassEx(&wc);

     //set up the screen in windowed or fullscreen mode?

     if (g_engine->getFullscreen())
     {
         DEVMODE dm;
         memset(&dm, 0, sizeof(dm));
         dm.dmSize = sizeof(dm);
         dm.dmPelsWidth = g_engine->getScreenWidth();
         dm.dmPelsHeight = g_engine->getScreenHeight();
         dm.dmBitsPerPel = g_engine->getColorDepth();
         dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

     if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
         MessageBox(NULL, "Display mode failed", NULL, MB_OK);
         g_engine->setFullscreen(false);
         }

         dwStyle = WS_POPUP;
         dwExStyle = WS_EX_APPWINDOW;
         ShowCursor(FALSE);
     }
     else {
         dwStyle = WS_OVERLAPPEDWINDOW;
         dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
     }

     //adjust window to true requested size
     AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

     //create the program window
     g_hWnd = CreateWindowEx( 0,
         title, //window class
         title, //title bar
         dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
         0, 0, //x,y coordinate
         windowRect.right - windowRect.left, //width of the window
         windowRect.bottom - windowRect.top, //height of the window
         0, //parent window
         0, //menu
         g_hInstance, //application instance
         0);    //window parameters

     //was there an error creating the window?
     if (!g_hWnd)    {
         MessageBox(g_hWnd, "Error creating program window!", "Error", MB_OK);
         return 0;
     }

     //display the window
     ShowWindow(g_hWnd, g_nCmdShow);
     UpdateWindow(g_hWnd);

     //initialize the engine
     g_engine->setWindowHandle(g_hWnd);
     if (!g_engine->Init(g_engine->getScreenWidth(), g_engine->getScreenHeight(),
 g_engine->getColorDepth(), g_engine->getFullscreen()))   {
          MessageBox(g_hWnd, "Error initializing the engine", "Error", MB_OK);
          return 0;
     }

     // main message loop
     gameover = false;
     while (!gameover)
     {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        g_engine->Update();
    }

    if (g_engine->getFullscreen()) {
        ShowCursor(TRUE);
    }

    g_engine->Close();
    delete g_engine;

    return 1;
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset