您的位置:首页 > 编程语言

游戏编程入门(19):使用演示模式展示游戏

2017-07-06 20:12 281 查看
演示模式是在没有玩游戏时显示的一个动画片断,它演示了游戏的玩法。除了作为闪屏的作用外,演示模式让玩家快速浏览游戏的实际玩法。

本章内容包括:

为什么演示模式对于向人们展示游戏的玩法很有用

向游戏添加演示模式所需要的工作

如何向 Space Out 游戏添加演示模式

接上文 游戏编程入门(18):使用闪屏增添游戏的活力

什么是演示模式

演示模式优于闪屏,它不仅仅是显示一个游戏名称或者一幅静态的游戏图形。演示模式试图显示游戏的实际玩法,或者至少显示游戏中的一些运动的主要角色。

与闪屏不同的是,演示模式不是只在游戏开始时出现,在游戏开始之后就看不到了。相反,演示模式是在各个游戏之间显示的。当游戏结束时,游戏会有一个短暂的停顿,玩家在这段时间会意识到游戏实际上结束了,然后游戏会返回演示模式。然后如果想要开始新游戏,也是在演示模式中开始的。

演示模式的细节

与闪屏类似,演示模式是一种与“游戏结束”和“游戏未结束”模式不同的模式,初次运行游戏程序以及游戏之间都将进入这个模式。

为游戏创建演示模式的一种容易的方法是仅仅显示游戏中的角色是如何到处移动的,而不试图模拟游戏中的真实玩家。

开发 Space Out 3 游戏

将添加演示模式的Space Out 游戏,命名为 Space Out 3 。

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

Space Out 3 目录结构和效果图

Space Out 3 目录结构:



Space Out 3 效果图:



编写游戏代码

SpaceOut.h 头文件中包括了两个新的全局变量,其中一个变量代替了在上一章中为闪屏添加的 g_bSplash 全局变量。如下所示:

int               g_iGameOverDelay;                     //游戏结束时进入演示模式的延迟
BOOL              g_bDemo;                              //是否进入演示模式


还需要在GameStart( ) 函数中初始化 g_bDemo 的值。

GamePaint( )

GamePaint( ) 函数绘制游戏图形,同时考虑到了演示模式。

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

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

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

if (g_bDemo)
{
// Draw the splash screen image
g_pSplashBitmap->Draw(hDC, 142, 100, TRUE);
}
else
{
// 绘制得分
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 GamePaint(HDC hDC)
{
// 绘制背景
g_pBackground->Draw(hDC);

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

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

if (g_bDemo)
{
// Draw the splash screen image
g_pSplashBitmap->Draw(hDC, 142, 100, TRUE);
}
else
{
// 绘制得分
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);
}
}

// 游戏循环
void GameCycle()
{
if (!g_bGameOver)
{
if (!g_bDemo)
{
// 随机添加外星人
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);
}
else
if (--g_iGameOverDelay == 0)
{
// 停止音乐并切换到演示模式
g_pGame->PauseMIDISong();
g_bDemo = TRUE;
NewGame();
}
}


HandleKeys( )

HandleKeys( ) 函数支持演示模式,如果游戏正退出演示模式以开始一个新游戏,那么它将更改 g_bDemo 的值。

// 监听键盘
void HandleKeys()
{
if (!g_bGameOver && !g_bDemo)
{
// 按下左/右键移动汽车
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 (GetAsyncKeyState(VK_RETURN) < 0)
if (g_bDemo)
{
// 退出演示模式,以开始一个新游戏
g_bDemo = FALSE;
NewGame();
}
else if (g_bGameOver)
{
// 开始一个新游戏
NewGame();
}
}


SpriteCollision( )函数中的新代码涉及到游戏结束屏幕的计时延迟。将 g_iGameOverDelay 变量设置为150,意味着在游戏返回演示模式之前游戏屏幕将显示150个周期。游戏的帧频设置为每秒30帧,也就是每秒钟30个周期。可以得知游戏屏幕的延迟为5秒。代码如下:

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


SpriteDying( )

SpriteDying( ) 确保在游戏处于演示模式时,不会播放外星人导弹爆炸的声音。

// 删除子画面
void SpriteDying(Sprite* pSpriteDying)
{
// 查看是否正在删除外星人导弹子画面
if (pSpriteDying->GetBitmap() == g_pBMissileBitmap ||
pSpriteDying->GetBitmap() == g_pJMissileBitmap ||
pSpriteDying->GetBitmap() == g_pTMissileBitmap)
{
if (!g_bDemo)
// 播放较小的爆炸声音
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();

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

if (g_bDemo)
{
// 向演示添加几个外星人
for (int i = 0; i < 6; i++)
AddAlien();
}
else
{
// 创建汽车子画面
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_pGame->PlayMIDISong(TEXT("Music.mid"));
}
}


源代码下载

http://pan.baidu.com/s/1ge2Vzr1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: