Click here to Skip to main content
15,867,704 members
Articles / Desktop Programming / MFC
Article

Grausteroids - an Asteroids game using DirectX and C++

Rate me:
Please Sign up or sign in to vote.
4.95/5 (22 votes)
7 Apr 20023 min read 225.1K   5K   39   47
An Asteroid's clone which needs some work but will illustrate some points about writing games.

Sample Image - Grausteroids.jpg

Overview

I've noticed more and more people asking questions about writing games, so I thought I'd post this. It was written some time ago, as an excuse to learn various parts of DirectX. This game will detect if you have a joystick, and use it. If the joystick has force-feedback, it is supported. It is not complete, it is playable, but the main splash screen opens an area that should have included various options such as key selection and was never written. The release version also sometimes crashes on exit and leaks memory during the shutdown of the sound engine ( i.e., for half a second ). It's not perfect, but I don't have time to fix it, and on the other hand, I don't see any reason to let it rot on my hard drive when it might be enough to help someone. I believe the core code to manage the window in MFC came from CP, the code to play MP3s certainly did, although I did not use it. This code compiled under DirectX version 8, which was a pleasant surprise, so it can be taken on by anyone who wants to experiment with it.

The Sound Engine

The sound engine was designed to simplify the process of using Direct Sound by allowing sounds to be added to a player and called from there. I wanted to also use MP3s, but the only code I could find was in C, so only one mp3 is supported. This is a shame, because the sounds and images are the bulk of the download. The player also reports memory leaks during shutdown. Anyone who wants to take this on and finish it is welcome.

Game Speed

One area that I was able to get to where I wanted it was game speed. The game draws at the fastest frame rate it can manage and keeps it's speed constant by checking on each draw if a certain amount of time has elapsed in order to call functions to move the ship and the asteroids. They are separate so that, as the game gets harder, the asteroids move faster but the ship does not.

if (timeGetTime() - m_LastTime > (unsigned int) (30-((m_iLevel>15) ? 
                                                 30 : m_iLevel * 2)))
{
    m_LastTime = timeGetTime();
    MoveStuff();
}

if (timeGetTime() - m_ShipTimer > 30)
{
    m_ShipTimer = timeGetTime();
    MoveShip();
}

The game defines two classes for data storage:

class floatpoint
{
public:
    float x;
    float y;
};

class gameitem 
{
public:
    int          Type;
    floatpoint   ptPos;
    float        iMoveX;
    float        iMoveY;
    int          iSprite;
    int          iTime;
    CRect        rcPosition;
};

floatpoint is used to track the ship's acceleration and position, because I use trig to figure out an acceleration based on the angle I am pointing. The gameitem class is used to track asteroids. Those who know me well will be amused to note that I used to use CArray rather than vector, and that this game uses MFC.

There is a lot of other stuff going on in this game, but I don't remember most of it. If you find this article interesting but you don't understand how something works, just email me, and as I recollect details through emails I get, I will update the article. Initially my desire is simply that someone get use out of this, because I am not doing anything with it.

Pixel perfect collisions

Another area I did a lot of work was in making collisions pixel perfect. The methodology is the same as used in my C# game, Collision, I suggest you read that article for a thorough explanation. The code under C++/DirectX looks like this:

while (rcShip.top > 0 && rcAsteroid.top > 0)
{
    rcShip.OffsetRect(CPoint(0,-1));
    rcAsteroid.OffsetRect(CPoint(0,-1));
}

while (rcShip.left > 0 && rcAsteroid.left > 0)
{
    rcShip.OffsetRect(CPoint(-1,0));
    rcAsteroid.OffsetRect(CPoint(-1,0));
}

CRect rcTest;
if (!rcTest.IntersectRect(rcShip, rcAsteroid))
    TRACE ("Intersect failed\r\n");

RECT rc;

rc.top = ((m_iShipSprite/10) * 32)+576;
rc.left = ((m_iShipSprite%10) * 32);
rc.right = rc.left + 32;
rc.bottom = rc.top + 32;

if (m_bShield)
{
    rc.bottom += 128;
    rc.top += 128;
}

DDBLTFX dbltfx;
dbltfx.dwSize = sizeof(DDBLTFX);
dbltfx.dwFillColor = RGB(0,0,0);
m_pIShip->Blt(NULL,NULL,NULL,DDBLT_COLORFILL,&dbltfx);
m_pIShip->BltFast(rcShip.left,rcShip.top,m_pISprites,
                  &rc, DDBLTFAST_WAIT);

switch (m_Items[i].Type)
{
    case big:
        rc.top = ((m_Items[i].iSprite/5) * 64);
        rc.left = ((m_Items[i].iSprite%5) * 64);
        rc.right = rc.left + 62;
        rc.bottom = rc.top + 64;
        break;
    case mid:
        rc.top = ((m_Items[i].iSprite/10) * 32) + 384;
        rc.left = ((m_Items[i].iSprite%10) * 32);
        rc.right = rc.left + 32;
        rc.bottom = rc.top + 32;
        break;
    case sml:
        rc.top = ((m_Items[i].iSprite/20) * 16) + 512;
        rc.left = ((m_Items[i].iSprite%20) * 16);
        rc.right = rc.left + 16;
        rc.bottom = rc.top + 16;
        break;    
}

m_pIAsteroid->Blt(NULL,NULL,NULL,DDBLT_COLORFILL,&dbltfx);
m_pIAsteroid->BltFast(rcAsteroid.left,rcAsteroid.top,
                      m_pISprites,&rc,DDBLTFAST_WAIT);

HDC hdcShip;
m_pIShip->GetDC(&hdcShip);
CDC dcShip;
dcShip.Attach(hdcShip);

HDC hdcAsteroid;
m_pIAsteroid->GetDC(&hdcAsteroid);
CDC dcAsteroid;
dcAsteroid.Attach(hdcAsteroid);

for ( int x = rcTest.left; x<=rcTest.right; x++)
    for ( int y = rcTest.top;y<=rcTest.bottom; y++)
        if (dcShip.GetPixel(x,y) != RGB(0,0,0) &&
            dcAsteroid.GetPixel(x,y) != RGB(0,0,0))
        {
            dcAsteroid.Detach();
            m_pIAsteroid->ReleaseDC(hdcAsteroid);
            dcShip.Detach();
            m_pIShip->ReleaseDC(hdcShip);
            return true;
        }
        
dcAsteroid.Detach();
m_pIAsteroid->ReleaseDC(hdcAsteroid);
dcShip.Detach();
m_pIShip->ReleaseDC(hdcShip);
return false;

The main point of interest is the last nested loop, where we use GetPixel to establish if any pixels in the intersected area indicate a collision. Nowadays I would not use GetPixel, but for all that, it works surprisingly well. I would instead use a DIBSection and access the bits directly.

Playing the game

Basically to play the game, just use the arrow keys, left and right to rotate, up to accelerate and down to use your limited ( but recharging ) shield. Space fires.

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


Written By
Software Developer (Senior)
Australia Australia
Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time, and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there, I've started working from home, at first for Code Project and now for a vet telemedicine company. I owned part of a company that sells client education software in the vet market, but we sold that and I worked for the owners for five years before leaving to get away from the travel, and spend more time with my family. I now work for a company here in Hobart, doing all sorts of Microsoft based stuff in C++ and C#, with a lot of T-SQL in the mix.

Comments and Discussions

 
QuestionAdding a second player. Pin
Helix118-Jan-13 6:39
Helix118-Jan-13 6:39 
GeneralMy vote of 1 Pin
teststuffjj120-Apr-11 20:27
teststuffjj120-Apr-11 20:27 
GeneralRe: My vote of 1 Pin
#realJSOP17-Dec-11 9:40
mve#realJSOP17-Dec-11 9:40 
GeneralRe: My vote of 1 Pin
merano17-Mar-12 14:11
merano17-Mar-12 14:11 
GeneralRe: My vote of 1 Pin
#realJSOP17-Mar-12 15:19
mve#realJSOP17-Mar-12 15:19 
GeneralRe: My vote of 1 Pin
Christian Graus17-Mar-12 19:22
protectorChristian Graus17-Mar-12 19:22 
GeneralRe: My vote of 1 Pin
#realJSOP18-Mar-12 1:50
mve#realJSOP18-Mar-12 1:50 
GeneralRe: My vote of 1 Pin
Christian Graus17-Mar-12 19:21
protectorChristian Graus17-Mar-12 19:21 
GeneralMy vote of 1 Pin
jessca23-Feb-10 0:13
jessca23-Feb-10 0:13 
GeneralMy vote of 1 Pin
arvinder_aneja29-Jan-10 23:51
arvinder_aneja29-Jan-10 23:51 
Generalnice it did work! Pin
Anderson Valerio7-Apr-09 4:35
Anderson Valerio7-Apr-09 4:35 
GeneralMFC APPLICATION LOAD ERROR Pin
cacheoveride20-Oct-07 14:51
cacheoveride20-Oct-07 14:51 
GeneralThis's just... Pin
Muammar©26-Aug-07 2:00
Muammar©26-Aug-07 2:00 
Generalneed joystick interface file Pin
nandeeshds8-Oct-04 2:32
nandeeshds8-Oct-04 2:32 
GeneralRe: need joystick interface file Pin
Christian Graus10-Oct-04 8:57
protectorChristian Graus10-Oct-04 8:57 
GeneralMP3 SDK for you.. Pin
jfugate16-Apr-02 2:48
jfugate16-Apr-02 2:48 
GeneralRe: MP3 SDK for you.. Pin
Christian Graus16-Apr-02 10:26
protectorChristian Graus16-Apr-02 10:26 
GeneralRe: MP3 SDK for you.. Pin
jfugate16-Apr-02 10:58
jfugate16-Apr-02 10:58 
GeneralRe: MP3 SDK for you.. Pin
Johnhans22-Aug-02 7:31
Johnhans22-Aug-02 7:31 
GeneralRe: MP3 SDK for you.. Pin
Larry Antram8-Dec-02 22:37
Larry Antram8-Dec-02 22:37 
GeneralRe: MP3 SDK for you.. Pin
Christian Graus8-Dec-02 22:47
protectorChristian Graus8-Dec-02 22:47 
GeneralRe: MP3 SDK for you.. Pin
Larry Antram8-Dec-02 23:12
Larry Antram8-Dec-02 23:12 
GeneralNo keyboard control :( Pin
Paul A. Howes10-Apr-02 9:09
Paul A. Howes10-Apr-02 9:09 
GeneralRe: No keyboard control :( Pin
Christian Graus10-Apr-02 13:52
protectorChristian Graus10-Apr-02 13:52 
GeneralHard! Pin
Mazdak9-Apr-02 1:41
Mazdak9-Apr-02 1:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.