65.9K
CodeProject 正在变化。 阅读更多。
Home

Grausteroids - 使用 DirectX 和 C++ 的太空侵略者游戏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (19投票s)

2002 年 4 月 8 日

3分钟阅读

viewsIcon

231262

downloadIcon

5053

一个需要改进的“小行星”克隆游戏,但可以说明编写游戏的一些要点。

Sample Image - Grausteroids.jpg

概述

我注意到越来越多的人在问关于编写游戏的问题,所以我认为我应该发布这个游戏。它是在一段时间前编写的,作为学习 DirectX 各个部分的借口。这个游戏会检测你是否有一个操纵杆,并使用它。如果操纵杆有力反馈功能,它也会被支持。它并不完整,可以玩,但主启动画面打开了一个应该包含各种选项的区域,例如按键选择,但从未编写。发布版本有时也会在退出时崩溃,并在声音引擎关闭期间(即,半秒钟内)泄漏内存。它并不完美,但我没有时间修复它,另一方面,我认为没有理由让它在我的硬盘上腐烂,因为它可能足以帮助别人。我相信在 MFC 中管理窗口的核心代码来自 CP,播放 MP3 的代码确实是,尽管我没有使用它。这段代码在 DirectX 版本 8 下编译通过,这是一个令人愉快的惊喜,所以任何想尝试它的人都可以使用它。

声音引擎

声音引擎旨在通过允许将声音添加到播放器并从那里调用来简化使用 Direct Sound 的过程。我还想使用 MP3,但我能找到的唯一代码是用 C 编写的,所以只支持一个 MP3。这是一个遗憾,因为声音和图像是下载的大部分。播放器还在关闭期间报告内存泄漏。欢迎任何想要接手并完成它的人。

游戏速度

我能够达到预期目标的一个领域是游戏速度。游戏以它能管理的最快帧率绘制,并通过在每次绘制时检查是否经过了一定的时间来调用函数来移动飞船和小行星,从而保持其速度恒定。它们是分开的,因此,随着游戏变得越来越难,小行星移动得更快,但飞船不会。

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();
}

该游戏定义了两个用于数据存储的类

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 用于跟踪飞船的加速度和位置,因为我使用三角函数来计算基于我指向的角度的加速度。gameitem 类用于跟踪小行星。那些了解我的人会觉得有趣的是,我过去常常使用 CArray 而不是 vector,并且这个游戏使用 MFC。

这个游戏中还有很多其他的东西,但我不记得大部分了。如果你发现这篇文章很有趣,但你不明白某些东西是如何工作的,请给我发电子邮件,当我想起通过电子邮件获得的细节时,我会更新这篇文章。最初,我的愿望仅仅是有人能使用它,因为我没有用它做任何事情。

像素完美碰撞

我做了很多工作的另一个领域是使碰撞达到像素完美。该方法与我在 C# 游戏 Collision 中使用的方法相同,我建议你阅读那篇文章以获得 thorough 的解释。 C++/DirectX 下的代码如下

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;

主要的兴趣点是最后一个嵌套循环,我们在其中使用 GetPixel 来确定交集区域中的任何像素是否指示碰撞。如今我不会使用 GetPixel,但尽管如此,它运行得非常好。我会改用 DIBSection 并直接访问这些位。

玩游戏

基本上要玩这个游戏,只需使用箭头键,左右旋转,向上加速,向下使用你有限的(但可以充电的)护盾。空格键发射。

© . All rights reserved.