游戏编程入门(17):开发 Space Out(逃离太空)游戏

本文开发一个垂直太空设计游戏,它利用了之前我们在游戏引擎中内置的所有特性。

本文内容包括:

  • Space Out 游戏的玩法
  • 如何设计Space Out 游戏
  • 编写 Space Out 游戏的具体细节

接上文 游戏编程入门(16):教游戏思考


游戏的玩法

在Space Out 游戏中,玩家是一个艰难穿越沙漠的绿色小汽车的司机(该沙漠没有尽头),用左右键控制汽车行驶方向。在天空上方,外星人不停的对我们投掷导弹,试图阻止我们穿越沙漠,当我们被外星人的导弹损坏3辆汽车时,我们则失败。当然,我们也可以按空格键发射导弹,击杀外星人,获取得分。游戏一个有三种外星人,每一种外星人都有自己的移动模式和攻击风格。

设计游戏

玩家的汽车能够水平驶过游戏屏幕,这意味着汽车的位置仅限于x轴。玩家能够垂直向上设计,玩家的导弹终止于屏幕顶端。外星人能够在任意方向上以不同的速度移动,所有外星人都向下方玩家的汽车发射导弹,当导弹击中汽车或者地面时,导弹就结束了。外星人不受自己的导弹影响,因此它们不会彼此碰撞。

Space Out 游戏没有不同的级别,除了生存之外也没有其他目标。不过,这个游戏的难度级别会随着玩家玩游戏的时间而逐渐增加。游戏难度的增加是通过添加速度更快的新外星人来实现的。最后,玩家将与永不尽头的外星人大军战斗,忙的不可开交。

Space Out 游戏的布局如下图所示:

这里写图片描述

这幅图显示了沙漠的背景图像,它是汽车到处行驶的背景。布满星星的背景仍然占据了屏幕的大部分,而背景图像显示了汽车所在的沙漠。背景图像实际上包括一段水平的天空,这有助于将沙漠风景与布满星星的背景融为一体。汽车子画面在沙漠背景图像之上移动。外星人出现在天空中并到处移动,试图发射各种导弹以击中汽车。当然,汽车也向外星人发射导弹。游戏的得分及剩余的生命(汽车)数显示在屏幕的右上角。

在理解了游戏的基本情况后,下面是S**pace Out 游戏涉及的子画面**:

  • 汽车子画面
  • 外星人子画面
  • 导弹子画面(从外星人和汽车发出)
  • 爆炸子画面

爆炸子画面用来显示被导弹炸死的外星人以及被外星人导弹炸毁的汽车。在Space Out 游戏中,实际上使用了两个不同大小的爆炸子画面。较大的爆炸子画面用来显示被炸毁的外星人或者汽车,而较小的爆炸子画面用来显示导弹爆炸。

除了子画面以外,Space Out 游戏还需要几个位图。下面是Space Out 游戏需要的位图图像:

  • 背景沙漠图像
  • 汽车图像(参见图19.2)
  • 汽车发出的导弹图像(参加图19.3)
  • 动画外星人图像:Blobbo、Jelly、Timmy(参加图19.4)
  • 三种外星人发出的导弹图像(参加图19.5)
  • 小的和大的动画爆炸图像(参加图19.6)
  • 小汽车图像(参加图19.7)
  • 游戏结束图像

这里写图片描述

Space Out 游戏的设计使我们了解到游戏必须管理以下信息:

  • 剩余生命(汽车)的数量
  • 得分
  • 难度级别
  • 游戏结束布尔变量
  • 发射输入的延迟变量

向游戏引擎添加另一个子画面特性

在游戏引擎中,缺少一个对于Space Out 游戏非常关键的特性,即子画面自动创建另一个子画面的能力

这个需求似乎有一点奇怪,但是考虑一下Space Out 游戏中正在向汽车发射一枚导弹的外星人。这枚导弹必须得像是外星人发出的,意味着这枚导弹必须知道外星人的位置。不仅如此,因为各个外星人都发射不同类型的导弹,所以这枚导弹还必须是发射它的外星人所特有的。

解决这个问题的一个好办法是,允许子画面在需要的时候创建另一个子画面。例如,可以允许外星人子画面创建导弹子画面本身,这样就很容易根据外星人的位置来确定导弹的位置,更不用说为不同类型的外星人创建适当的导弹了。这种方法的问题在于不可能以通用的方式向 Sprite 子画面类 添加这个功能。因为需要创建哪一种子画面的细节对于各个游戏都是不同的,因此无法在 Sprite 子画面类 中实现。不过, Sprite 子画面类 可以创建完成这项任务的接口

这个新的“添加子画面”功能的一个重要部分是名为 SA_ADDSPRITE 的新子画面动作。 以下代码显示了 SA_ADDSPRITE 子画面动作是如何添加到现有的子画面动作中的。

//子画面动作
typedef WORD        SPRITEACTION;
const SPRITEACTION  SA_NONE   = 0x0000L,    //不做任何事情
                    SA_KILL   = 0x0001L,    //从子画面列表删除一个子画面并破坏它
                    SA_ADDSPRITE = 0x0002L; //添加子画面

子画面动作是用来通知游戏引擎必须对一个子画面采取某种特定的动作。SA_ADDSPRITE 子画面动作只是导致在应用此动作的 Sprite 对象上调用一个特殊方法。这个方法名为 AddSprite( ),如下所示。

virtual Sprite*         AddSprite();         //添加子画面

AddSprite( ) 的设计思想是,调用它来允许子画面向游戏引擎添加一个新的子画面,并返回新创建的子画面的指针。新子画面的具体情况完全取决于各个不同的游戏。事实上,Sprite 类中的 AddSprite( ) 方法不做任何事情,如下所示。

// 添加子画面(具体实现留给派生的子画面类重写此方法)
Sprite* Sprite::AddSprite()
{
  return NULL;
}

这段代码显示Sprite::AddSprite( ) 基类方法什么都不做,表明通过AddSprite( ) 方法添加子画面的任务留给了派生的子画面类。因此,为了使用这个方法,就必须为特定类型的子画面派生一个子画面类,然后使用一个真正创建子画面的版本来重写AddSprite( )方法。

要想游戏引擎支持从一个子画面内部添加另一个子画面,还有最后一步。在游戏引擎的GameEngine::UpdateSprites( )方法中加入判断,并调用Sprite的AddSprite( )方法创建子画面,再通过游戏引擎的AddSprite( )方法,将子画面插入到子画面列表中。如下所示:

 // 如果从Sprite::Update()返回的子画面动作是SA_ADDSPRITE
    if (saSpriteAction & SA_ADDSPRITE)
      // 允许该子画面添加子画面
      AddSprite((*siSprite)->AddSprite());

开发游戏

注意:若出现编译错误,请在项目设置->连接->对象/库模块中 加入 msimg32.lib winmm.lib

Space Out 目录结构和效果图

Space Out 目录结构:

这里写图片描述

Space Out 效果图:

这里写图片描述

这里写图片描述

这里写图片描述

编写游戏代码

AlienSprite.h

虽然本书以前的游戏开发都是从游戏的头文件开始的,但是 Space Out 游戏略有不同,它依赖于一个自定义的子画面类。因此, Space Out 游戏的代码开始于这个自定义子画面类 AlienSprite 的头文件,如下所示。

#pragma once

//-----------------------------------------------------------------
// 包含的文件
//-----------------------------------------------------------------
#include <windows.h>
#include "Sprite.h"

//-----------------------------------------------------------------
// AlienSprite 外星人子画面
//-----------------------------------------------------------------
class AlienSprite : public Sprite
{
public:
  // 构造函数/析构函数
          AlienSprite(Bitmap* pBitmap, RECT& rcBounds,
            BOUNDSACTION baBoundsAction = BA_STOP);
  virtual ~AlienSprite();

  // 常规方法
  virtual SPRITEACTION  Update();
  virtual Sprite*       AddSprite();
};

AlienSprite 是从Sprite 类派生的,并且声明一个构造函数和一个析构函数。更重要的是AlienSprite 重写了Sprite类中的两个方法:Update( )和 AddSprite( ) 。

在学习AlienSprite::AddSprite( )方法之前,先快速浏览下AlienSprite 类需要的一些外部变量。

//-----------------------------------------------------------------
// 外部全局变量(它们包括在SpaceOut.h中,因为AlienSprite引用了它们,所以必须进行外部声明)
//-----------------------------------------------------------------
extern Bitmap* g_pBlobboBitmap;     //Blobbo外星人位图
extern Bitmap* g_pBMissileBitmap;   //Blobbo的导弹
extern Bitmap* g_pJellyBitmap;      //Jelly外星人位图
extern Bitmap* g_pJMissileBitmap;   //Jelly的导弹
extern Bitmap* g_pTimmyBitmap;      //Timmy外星人位图
extern Bitmap* g_pTMissileBitmap;   //Timmy的导弹
extern int     g_iDifficulty;       //难度级别

这些全局变量是Space Out 主游戏代码的一部分,它们包括在文件 SpaceOut.h 中,因为 AlienSprite 代码引用了全局变量,所以必须在这段代码外部声明这些变量。
下面的 AlienSprite::AddSprite( )方法的代码,它利用了外部全局变量来确定添加哪一种导弹子画面。

AlienSprite::AddSprite( )

AlienSprite::AddSprite( )方法根据发射导弹的外星人来添加导弹子画面。

// 利用外部全局变量来确定添加哪一种导弹子画面
Sprite* AlienSprite::AddSprite()
{
  // 创建新的导弹子画面
  RECT    rcBounds = { 0, 0, 640, 410 };
  RECT    rcPos = GetPosition();
  Sprite* pSprite = NULL;
  if (GetBitmap() == g_pBlobboBitmap)
  {
    // Blobbo 导弹
    pSprite = new Sprite(g_pBMissileBitmap, rcBounds, BA_DIE);
    pSprite->SetVelocity(0, 7);
  }
  else if (GetBitmap() == g_pJellyBitmap)
  {
    // Jelly 导弹
    pSprite = new Sprite(g_pJMissileBitmap, rcBounds, BA_DIE);
    pSprite->SetVelocity(0, 5);
  }
  else
  {
    // Timmy 导弹
    pSprite = new Sprite(g_pTMissileBitmap, rcBounds, BA_DIE);
    pSprite->SetVelocity(0, 3);
  }

  // 设置并返回导弹子画面的位置(就在外星人子画面位置下方)
  pSprite->SetPosition(rcPos.left + (GetWidth() / 2), rcPos.bottom);
  return pSprite;
}

为了确定是哪一种外星人正在发射这枚导弹,这个方法检查子画面的位图图像。然后,根据哪一种外星人正在发射这枚导弹来创建导弹子画面。最后,设置新的导弹子画面,使其刚好出现在外星人下面,并返回这个子画面,这样就能够将其添加到游戏引擎的子画面列表中。

AlienSprite::Update( )

在AlienSprite 类中,Update( ) 方法也是重写的。如下所示:

// 随机设置saSpriteAction 子画面动作,使外星人发射导弹
SPRITEACTION AlienSprite::Update()
{
  // 调用子画面基类的Update( )方法,确保更新了子画面
  SPRITEACTION saSpriteAction;
  saSpriteAction = Sprite::Update();

  // 查看外星人是否应该发射导弹(外星人发射导弹的可能性由难度级别确定)
  if ((rand() % (g_iDifficulty / 2)) == 0)
    saSpriteAction |= SA_ADDSPRITE;

  return saSpriteAction;
}

在这个方法中,最重要的是调用基类的Update( )方法,确保更新了子画面。然后这个方法使用全局变量g_iDifficulty 作为基础,随机设置saSpriteAction 子画面动作 为 SA_ADDSPRITE ,这将导致外星人发射一枚导弹。

因为SA_ADDSPRITE 会导致调用 AlienSprite::AddSprite() 方法,而这个方法会创建一个新的导弹子画面并将其添加到游戏引擎中。

SpaceOut.h

SpaceOut.h 头文件声明用来管理游戏的全局变量。

#pragma once

//-----------------------------------------------------------------
// 包含的文件
//-----------------------------------------------------------------
#include <windows.h>
#include "Resource.h"
#include "GameEngine.h"
#include "Bitmap.h"
#include "Sprite.h"
#include "Background.h"
#include "AlienSprite.h"

//-----------------------------------------------------------------
// 全局变量
//-----------------------------------------------------------------
HINSTANCE         g_hInstance;                          //程序实例句柄
GameEngine*       g_pGame;                              //游戏引擎
HDC               g_hOffscreenDC;                       //屏幕外设备环境
HBITMAP           g_hOffscreenBitmap;                   //屏幕外位图
Bitmap*           g_pDesertBitmap;                      //背景沙漠
Bitmap*           g_pCarBitmap;                         //汽车位图
Bitmap*           g_pSmCarBitmap;                       //小汽车位图(用于显示剩余生命数)
Bitmap*           g_pMissileBitmap;                     //汽车发出的导弹
Bitmap*           g_pBlobboBitmap;                      //Blobbo外星人
Bitmap*           g_pBMissileBitmap;                    //Blobbo导弹
Bitmap*           g_pJellyBitmap;                       //Jelly外星人
Bitmap*           g_pJMissileBitmap;                    //Missile导弹
Bitmap*           g_pTimmyBitmap;                       //Timmy外星人
Bitmap*           g_pTMissileBitmap;                    //Missile导弹
Bitmap*           g_pSmExplosionBitmap;                 //小爆炸(导弹爆炸)
Bitmap*           g_pLgExplosionBitmap;                 //大爆炸(炸毁外星人或汽车)
Bitmap*           g_pGameOverBitmap;                    //游戏结束
StarryBackground* g_pBackground;                        //星空动画背景
Sprite*           g_pCarSprite;                         //汽车子画面
int               g_iFireInputDelay;                    //发射输入的延迟变量
int               g_iNumLives, g_iScore, g_iDifficulty; //剩余生命,得分,难度级别
BOOL              g_bGameOver;                          //游戏是否结束

//-----------------------------------------------------------------
// 该游戏独有的功能函数
//-----------------------------------------------------------------
void NewGame();                                         //开始新游戏
void AddAlien();                                        //添加外星人

GameStart( )

游戏的主要设置代码位于GameStart( ) 函数中。

// 初始化游戏的位图和背景并调用NewGame()
void GameStart(HWND hWindow)
{
  // 生成随机数生成器种子
  srand(GetTickCount());

  // 创建屏幕外设备环境和位图
  g_hOffscreenDC = CreateCompatibleDC(GetDC(hWindow));
  g_hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWindow),
    g_pGame->GetWidth(), g_pGame->GetHeight());
  SelectObject(g_hOffscreenDC, g_hOffscreenBitmap);

  // 创建并加载位图
  HDC hDC = GetDC(hWindow);
  g_pDesertBitmap = new Bitmap(hDC, IDB_DESERT, g_hInstance);
  g_pCarBitmap = new Bitmap(hDC, IDB_CAR, g_hInstance);
  g_pSmCarBitmap = new Bitmap(hDC, IDB_SMCAR, g_hInstance);
  g_pMissileBitmap = new Bitmap(hDC, IDB_MISSILE, g_hInstance);
  g_pBlobboBitmap = new Bitmap(hDC, IDB_BLOBBO, g_hInstance);
  g_pBMissileBitmap = new Bitmap(hDC, IDB_BMISSILE, g_hInstance);
  g_pJellyBitmap = new Bitmap(hDC, IDB_JELLY, g_hInstance);
  g_pJMissileBitmap = new Bitmap(hDC, IDB_JMISSILE, g_hInstance);
  g_pTimmyBitmap = new Bitmap(hDC, IDB_TIMMY, g_hInstance);
  g_pTMissileBitmap = new Bitmap(hDC, IDB_TMISSILE, g_hInstance);
  g_pSmExplosionBitmap = new Bitmap(hDC, IDB_SMEXPLOSION, g_hInstance);
  g_pLgExplosionBitmap = new Bitmap(hDC, IDB_LGEXPLOSION, g_hInstance);
  g_pGameOverBitmap = new Bitmap(hDC, IDB_GAMEOVER, g_hInstance);

  // 创建星空动画背景
  g_pBackground = new StarryBackground(600, 450);

  // 播放背景音乐
  g_pGame->PlayMIDISong(TEXT("Music.mid"));

  // 开始游戏
  NewGame();
}

GamePaint( )

// 绘制游戏
void GamePaint(HDC hDC)
{
  // 绘制背景
  g_pBackground->Draw(hDC);

  // 绘制沙漠位图
  g_pDesertBitmap->Draw(hDC, 0, 371);

  // 绘制子画面
  g_pGame->DrawSprites(hDC);

  // 绘制得分
  TCHAR szText[64];
  RECT  rect = { 460, 0, 510, 30 };
  wsprintf(szText, "%d", g_iScore);
  SetBkMode(hDC, TRANSPARENT);
  SetTextColor(hDC, RGB(255, 255, 255));
  DrawText(hDC, szText, -1, &rect, DT_SINGLELINE | DT_RIGHT | DT_VCENTER);

  // 绘制剩余生命(小汽车)数量
  for (int i = 0; i < g_iNumLives; i++)
    g_pSmCarBitmap->Draw(hDC, 520 + (g_pSmCarBitmap->GetWidth() * i),
      10, TRUE);

  // 绘制游戏结束
  if (g_bGameOver)
    g_pGameOverBitmap->Draw(hDC, 190, 149, TRUE);
}

GameCycle( )

GameCycle( ) 函数向游戏随机添加新的外星人。

// 游戏循环
void GameCycle()
{
  if (!g_bGameOver)
  {
    // 随机添加外星人
    if ((rand() % g_iDifficulty) == 0)
      AddAlien();

    // 更新背景
    g_pBackground->Update();

    // 更新子画面
    g_pGame->UpdateSprites();

    // 获得用于重新绘制游戏的设备环境
    HWND  hWindow = g_pGame->GetWindow();
    HDC   hDC = GetDC(hWindow);

    // 在屏幕外设备环境上绘制游戏
    GamePaint(g_hOffscreenDC);

    // 将屏幕外位图位块传送到游戏屏幕
    BitBlt(hDC, 0, 0, g_pGame->GetWidth(), g_pGame->GetHeight(),
      g_hOffscreenDC, 0, 0, SRCCOPY);

    // 清理
    ReleaseDC(hWindow, hDC);
  }
}

HandleKeys( )

HandleKeys( ) 函数允许用户使用键盘上的键来控制汽车子画面。左右键移动汽车,空格键发射导弹,按下Enter键时开始一个新游戏(只在游戏结束时有效)。

// 监听键盘
void HandleKeys()
{
  if (!g_bGameOver)
  {
    // 按下左/右键移动汽车
    POINT ptVelocity = g_pCarSprite->GetVelocity();
    if (GetAsyncKeyState(VK_LEFT) < 0)
    {
      // 向左移动(因为是向左行驶时倒退,所以汽车左行的最大速度小于右行的最大速度)
      ptVelocity.x = max(ptVelocity.x - 1, -4);
      g_pCarSprite->SetVelocity(ptVelocity);
    }
    else if (GetAsyncKeyState(VK_RIGHT) < 0)
    {
      // 向右移动
      ptVelocity.x = min(ptVelocity.x + 2, 6);
      g_pCarSprite->SetVelocity(ptVelocity);
    }

    // 按下空格键发射导弹
    if ((++g_iFireInputDelay > 6) && GetAsyncKeyState(VK_SPACE) < 0)
    {
      // 创建一个新的导弹子画面
      RECT  rcBounds = { 0, 0, 600, 450 };
      RECT  rcPos = g_pCarSprite->GetPosition();
      Sprite* pSprite = new Sprite(g_pMissileBitmap, rcBounds, BA_DIE);
      pSprite->SetPosition(rcPos.left + 15, 400);
      pSprite->SetVelocity(0, -7);
      g_pGame->AddSprite(pSprite);

      // 播放导弹发射声音
      PlaySound((LPCSTR)IDW_MISSILE, g_hInstance, SND_ASYNC |
        SND_RESOURCE | SND_NOSTOP);

      // 重置输入延迟
      g_iFireInputDelay = 0;
    }
  }

  // 按下Enter键时开始一个新游戏
  if (g_bGameOver && (GetAsyncKeyState(VK_RETURN) < 0))
    // 开始新游戏
    NewGame();
}

SpriteCollision( )

SpriteCollision( ) 函数响应导弹、外星人与汽车子画面之间的碰撞。

// 碰撞检测
BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
{
  // 查看玩家子画面是否与外星人子画面相撞
  Bitmap* pHitter = pSpriteHitter->GetBitmap();
  Bitmap* pHittee = pSpriteHittee->GetBitmap();
  if ((pHitter == g_pMissileBitmap && (pHittee == g_pBlobboBitmap ||
    pHittee == g_pJellyBitmap || pHittee == g_pTimmyBitmap)) ||
    (pHittee == g_pMissileBitmap && (pHitter == g_pBlobboBitmap ||
    pHitter == g_pJellyBitmap || pHitter == g_pTimmyBitmap)))
  {
    // 播放较小的爆炸声音
    PlaySound((LPCSTR)IDW_LGEXPLODE, g_hInstance, SND_ASYNC |
      SND_RESOURCE);

    // 删除这两个子画面
    pSpriteHitter->Kill();
    pSpriteHittee->Kill();

    // 在外星人的位置创建一个较大的爆炸子画面
    RECT rcBounds = { 0, 0, 600, 450 };
    RECT rcPos;
    if (pHitter == g_pMissileBitmap)
      rcPos = pSpriteHittee->GetPosition();
    else
      rcPos = pSpriteHitter->GetPosition();
    Sprite* pSprite = new Sprite(g_pLgExplosionBitmap, rcBounds);
    pSprite->SetNumFrames(8, TRUE);
    pSprite->SetPosition(rcPos.left, rcPos.top);
    g_pGame->AddSprite(pSprite);

    // 更新得分
    g_iScore += 25;
    g_iDifficulty = max(80 - (g_iScore / 20), 20);
  }

  // 查看外星人子画面是否与汽车相撞
  if ((pHitter == g_pCarBitmap && (pHittee == g_pBMissileBitmap ||
    pHittee == g_pJMissileBitmap || pHittee == g_pTMissileBitmap)) ||
    (pHittee == g_pCarBitmap && (pHitter == g_pBMissileBitmap ||
    pHitter == g_pJMissileBitmap || pHitter == g_pTMissileBitmap)))
  {
    // 播放较大的爆炸声音
    PlaySound((LPCSTR)IDW_LGEXPLODE, g_hInstance, SND_ASYNC |
      SND_RESOURCE);

    // 删除导弹子画面
    if (pHitter == g_pCarBitmap)
      pSpriteHittee->Kill();
    else
      pSpriteHitter->Kill();

    // 在汽车的位置创建一个较大的爆炸子画面
    RECT rcBounds = { 0, 0, 600, 480 };
    RECT rcPos;
    if (pHitter == g_pCarBitmap)
      rcPos = pSpriteHitter->GetPosition();
    else
      rcPos = pSpriteHittee->GetPosition();
    Sprite* pSprite = new Sprite(g_pLgExplosionBitmap, rcBounds);
    pSprite->SetNumFrames(8, TRUE);
    pSprite->SetPosition(rcPos.left, rcPos.top);
    g_pGame->AddSprite(pSprite);

    // 将汽车移回开始位置,以模拟创建一辆新汽车
    g_pCarSprite->SetPosition(300, 405);

    // 查看游戏是否结束
    if (--g_iNumLives == 0)
    {
      // 播放游戏结束声音
      PlaySound((LPCSTR)IDW_GAMEOVER, g_hInstance, SND_ASYNC |
        SND_RESOURCE);
      g_bGameOver = TRUE;
    }
  }

  return FALSE;
}

SpriteDying( )

SpriteDying( ) 函数在破坏一个外星人导弹子画面的任何时候创建一个较小的爆炸子画面。

// 删除子画面
void SpriteDying(Sprite* pSpriteDying)
{
  // 查看是否正在删除外星人导弹子画面
  if (pSpriteDying->GetBitmap() == g_pBMissileBitmap ||
    pSpriteDying->GetBitmap() == g_pJMissileBitmap ||
    pSpriteDying->GetBitmap() == g_pTMissileBitmap)
  {
    // 播放较小的爆炸声音
    PlaySound((LPCSTR)IDW_SMEXPLODE, g_hInstance, SND_ASYNC |
      SND_RESOURCE | SND_NOSTOP);

    // 在导弹的位置创建一个较小的爆炸子画面
    RECT rcBounds = { 0, 0, 600, 450 };
    RECT rcPos = pSpriteDying->GetPosition();
    Sprite* pSprite = new Sprite(g_pSmExplosionBitmap, rcBounds);
    pSprite->SetNumFrames(8, TRUE);
    pSprite->SetPosition(rcPos.left, rcPos.top);
    g_pGame->AddSprite(pSprite);
  }
}

NewGame( )

NewGame( ) 函数为新游戏做好一切准备。

// 开始新游戏
void NewGame()
{
  // 清空子画面
  g_pGame->CleanupSprites();

  // 创建汽车子画面
  RECT rcBounds = { 0, 0, 600, 450 };
  g_pCarSprite = new Sprite(g_pCarBitmap, rcBounds, BA_WRAP);
  g_pCarSprite->SetPosition(300, 405);
  g_pGame->AddSprite(g_pCarSprite);

  // 初始化游戏变量
  g_iFireInputDelay = 0;
  g_iScore = 0;
  g_iNumLives = 3;
  g_iDifficulty = 80;
  g_bGameOver = FALSE;

  // 播放背景音乐
  g_pGame->PlayMIDISong();
}

AddAlien( )

AddAlien( ) 函数简化添加外星人的动作,在一个随机的位置添加新外星人。

// 添加外星人
void AddAlien()
{
  // 创建一个新的随机外星人子画面
  RECT          rcBounds = { 0, 0, 600, 410 };
  AlienSprite*  pSprite;
  switch(rand() % 3)
  {
  case 0:
    // Blobbo
    pSprite = new AlienSprite(g_pBlobboBitmap, rcBounds, BA_BOUNCE);
    pSprite->SetNumFrames(8);
    pSprite->SetPosition(((rand() % 2) == 0) ? 0 : 600, rand() % 370);
    pSprite->SetVelocity((rand() % 7) - 2, (rand() % 7) - 2);
    break;
  case 1:
    // Jelly
    pSprite = new AlienSprite(g_pJellyBitmap, rcBounds, BA_BOUNCE);
    pSprite->SetNumFrames(8);
    pSprite->SetPosition(rand() % 600, rand() % 370);
    pSprite->SetVelocity((rand() % 5) - 2, (rand() % 5) + 3);
    break;
  case 2:
    // Timmy
    pSprite = new AlienSprite(g_pTimmyBitmap, rcBounds, BA_WRAP);
    pSprite->SetNumFrames(8);
    pSprite->SetPosition(rand() % 600, rand() % 370);
    pSprite->SetVelocity((rand() % 7) + 3, 0);
    break;
  }

  // 添加外形人子画面
  g_pGame->AddSprite(pSprite);
}

源代码下载

http://pan.baidu.com/s/1ge2Vzr1

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值