Alex Russell's Game Programming Tutorial using DX

Introduction . Chapter 1 . Chapter 2 . Chapter 3 . Chapter 4 . Chapter 5 . Chapter 6 . Chapter 7

Chapter 7

Using a Windows Application with DX

Console mode programs were never meant to run graphics full screen. We will now create a basic windows program skeleton suitable for use with DX games.

  • Start VC++
  • Click File / New
  • Choose "Win32 Application"
  • Enter a project name, and click OK
  • Choose "Empty Project", and click Finish, then OK
  • Click on the menu Project/Settings
  • General tab, set "Using MFC in a shared DLL"
  • Link Tab, all configurations add "Ddraw.lib dxguid.lib winmm.lib dinput.lib"
  • Link Tab, debug/Release settng add "DXSmithLIB.lib" with the path to the debug/Release verison
  • Add a new document
  • Add the following code
  • Save as breakout.cpp
  • Add breakout.cpp to the project (right click on the text to get the menu)

/*

  breakout.cpp

  march 8, 2004 jar - initial file

  */

#include "stdafx.h"

HWND        g_hMainWnd;
HINSTANCE   g_hInst;

// Foward declarations of functions included in this code module:
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);

CgsdxIO *gb_pDX=NULL;

typedef struct
    {
    int paused;
    int done;
    }
GameInfo_t;

GameInfo_t gb_Info;

HWND InitWindow(int iCmdShow)
{
   HWND      hWnd;
   WNDCLASS  wc;

   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = g_hInst;
   wc.hIcon = LoadIcon(g_hInst, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH);
   wc.lpszMenuName = TEXT("");
   wc.lpszClassName = TEXT("Basic DDX");
   RegisterClass(&wc);

   hWnd = CreateWindowEx(
      WS_EX_TOPMOST,
      TEXT("Basic DDX"),
      TEXT("Basic DDX"),
      WS_POPUP,
      0,
      0,
      GetSystemMetrics(SM_CXSCREEN),
      GetSystemMetrics(SM_CYSCREEN),
      NULL,
      NULL,
      g_hInst,
      NULL);

   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);
   SetFocus(hWnd);

   return hWnd;

}

int DoLogic(CgsdxIO *dx)
{
    if ( gb_Info.paused )
        return gb_Info.done;

    if ( dx->IsKeyDown(DIK_ESCAPE) )
        gb_Info.done=1;

    return gb_Info.done;
}

int DoDraw(CgsdxIO *dx)
{
    int err=0;

    if ( gb_Info.paused )
        return 0;

    dx->RectFill(NULL, 0); // fill screen black
    dx->Flip();

    return err;
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	MSG msg;

    g_hInst = hInstance;
    g_hMainWnd = InitWindow(nCmdShow);

    if(!g_hMainWnd)
        return -1;

    // dx
    CgsdxIO dx;

     if ( dx.InitResource(0) )
        return 0;
    if ( dx.InitGraphics(1024, 768, 32, GSDX_FULL_SCREEN, g_hInst, g_hMainWnd) )
        return 0;
    if ( dx.InitSound() )
        return 0;
    if ( dx.InitIO() )
        return 0;

    gb_pDX=&dx;

    gb_Info.done=0;
    gb_Info.paused=0;

    TRACE("[main] peek message\n");
    PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE);

    // run till completed
    while ( msg.message!=WM_QUIT ) 
        {
        // is there a message to process?
        if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE) ) 
            {
            // dispatch the message
            TranslateMessage(&msg);
            DispatchMessage(&msg); 
            } 
        else 
            {
            if ( !DoLogic(&dx) )
                DoDraw(&dx);
            else
                {
		    //PostQuitMessage(0);
                break;  // all done - esc pressed
                }
            }

        }

    ShowCursor(TRUE);

	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 
lParam)
{
   WORD fActive, fMinimized;

   switch (message)
       {
       case WM_DESTROY:
          PostQuitMessage(0);
          return 0;

        case WM_ACTIVATE:
            fActive = LOWORD(wParam);           // activation flag 
            fMinimized = (BOOL) HIWORD(wParam); // minimized flag 
            TRACE("[WndProc] active= %d\n", fActive);
            if ( fActive )
                {
                // un-pause
                gb_Info.paused=0;
                if ( gb_pDX )
                    {
                    gb_pDX->AcquireAllDevices();
                    }
                }
            else
                {
                // pause the game
                gb_Info.paused=1;
                }
            break;
       } // switch

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

  • Next open a new file, add the following text and save as stdafx.h

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers


// Windows Header Files:
//#include 
#include 
#include 

// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

// Local Header Files

// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)

At this point Build the project. It should compile and link. If run all it will do is show a black screen until you press ALT-F4. This is a good starting point for any simple DX project.

A Simple Game - Breakout

Now on to a small game - breakout. Before coding anything you must do at least some design work. For larger games more design work is required to have any chance of success. You should have the following things documented at a minimum before starting any coding:

  • Rules for the game
  • List of art work required
  • Sound and Music requirements
  • Major code components required
    • Game engine
    • Sound, Music
    • User i/o
    • User interface design
    • Supporting Utilities (editors, drawing programs, game map editors, etc...)
  • Overview of game logic flow

A commercial game may have a books worth of documents done before any coding is done. For a simple game like this we only need a simple design doc.

Design Doc for Breakout

This is a very simple breakout game. There are two sections of bricks near the top of the screen. A single paddle controled by the mouse at the bottom of the screen, one ball, and a score indicator. The ball starts on the paddle, moving up when a mouse-button is pressed. A ball is 'lost' each time it is alowed to move off the bottom of the screen. Each brick in the lower section is worth 1 point, each brick in the upper sections is worth 5 points.

Art: brick, ball, paddle

Music: looping midi song

Sound: a bounce sound each time the ball hits something.

Game flow

Start with intro screen. Text only, display for 5 seconds or a mouse button is pressed. Game play then starts. The ball starts on the paddle, 'glued' in place until a mouse button is pressed. The ball then bouces on screen, making bricks dissapear as they are struck. Each brick struck gains points. The game ends if 5 balls are lost, or all bricks are hit. The player is given an option to play again. Repeat.

Code Details

Art will be done on one bmp file. The spriteEdit util will be used to make a single frame sprite from the bmp for the ball, brick, and paddle. A finite state machine will be used. There will be strict seperation of drawing and logic code.

Download the game source to see the details!

Introduction . Chapter 1 . Chapter 2 . Chapter 3 . Chapter 4 . Chapter 5 . Chapter 6 . Chapter 7

Copyright 2004 (c), Alex Russell, All rights reserved