到现在为止,我们开发的游戏都存在一个问题就是它们在初次运行的时候没有充分的标识自己,虽然窗口的标题栏包含了游戏的名称,但是在初次运行游戏时清楚地标识自己是很重要的。而且我们的游戏,总是一点进去就开始了,这也给人以不好的体验。
游戏很像是电影,应该在片头显示名字并且给人以缓冲的时间。在电影中,这种介绍和缓冲被称为片头,而游戏中的名称被称为闪屏,它们可以包含有用的信息,例如版权通知和游戏玩法的介绍等。本章将介绍如何通过添加闪屏来装饰 Space Out 游戏。
本章内容包括:
- 为什么闪屏是所有游戏的一个重要部分
- 将闪屏结合到游戏中需要哪些工作
- 如何向 Space Out 游戏添加闪屏
接上文 游戏编程入门(17):开发 Space Out(逃离太空)游戏
闪屏的重要性
无论在游戏闪屏中显示的内容是多还是少,重要的是,至少要显示游戏的名称以及其他相关的信息,如游戏的版权。如果需要一个特定的动作才能开始游戏,那么在闪屏中提一下也不错。没有人喜欢游戏立即开始而不提醒玩家游戏将在何时开始,因此闪屏至少应该让玩家有机会准备好才开始玩游戏。
闪屏也是一个提供包括游戏的简短说明以及游戏玩法提示和技巧的位置。对于闪屏,要考虑的最后一点是高分列表。这个列表包含人们在游戏中获得的最高得分。在稍后的文章中,我们将介绍如何创建一个高分列表,将 Space Out 游戏的高分保存到一个文件中。
目前,许多商业游戏都不仅包含闪屏,还包括了介绍性的动画或者视频片断等。这种“闪动画”比简单的闪屏更加吸引人,但是创建它通常需要更多的动作,特别是当它是一个实际的视频时。另一方面,一些游戏只是显示直接来自于游戏的动画片断,这是作为游戏的一种演示。在稍后的文章中,我们也会介绍如何将闪屏转换为 Space Out 游戏的一个演示模式。
了解闪屏
实现闪屏最容易的方法就是创建屏幕的位图图像,然后在游戏开始之前将其显示在游戏屏幕上。闪屏的图像可以大到填满整个游戏屏幕,也可以稍微小一点,显示在现有的游戏背景之上。
下图显示了如何设计闪屏图像,以便覆盖在 Space Out 游戏中的背景之上。
从上面这幅图可以看得出来,显示闪屏没有什么难度。向游戏添加闪屏稍微有一点难度的地方是为闪屏创建一个单独的游戏模式。例如,之前创建的所有游戏都处于两种模式之中,“游戏结束”和“游戏未结束”。这两种模式直接由 g_bGameOver 全局变量控制,而这个变量有两个布尔值,TURE 或 FALSE。因此,我们将游戏扩展为以下3种模式:
- 闪屏
- 游戏结束
- 游戏未结束
虽然在显示闪屏时,游戏是结束的。但是对于“游戏结束”模式,因为没有显示闪屏,所以它不同于“闪屏”模式。
开发 Space Out 2 游戏
带有一个闪屏的 Space Out 游戏命名为 Space Out 2。游戏玩法没有变,只是添加了一个闪屏来装饰游戏。
注意:若出现编译错误,请在项目设置->连接->对象/库模块中 加入 msimg32.lib winmm.lib
Space Out 2 目录结构和效果图
Space Out 2 目录结构:
Space Out 2 效果图:
编写游戏代码
向 Space Out 2 游戏添加闪屏的第一步是向游戏添加几个全局变量,以便存储闪屏的位图以及闪屏模式。 它们是在 SpaceOut.h 中声明的。
Bitmap* g_pSplashBitmap; //闪屏位图
BOOL g_bSplash; //是否处于闪屏
g_bSplash 处于 TRUE 则游戏处于闪屏模式。在游戏开始时将g_bSplash 设置为TRUE,然后用户在按 Enter键开始游戏时,将其设置为FALSE。
初始化全局变量非常重要,这项任务是在 GameStart( ) 函数中完成的, GameStart( ) 函数的第一个变化是创建闪屏位图。
g_pSplashBitmap = new Bitmap(hDC, IDB_SPLASH, g_hInstance);
另外还要初始化 g_bSplash ,如下所示:
// 初始化全局变量
g_bSplash = TRUE;
g_bGameOver = TRUE;
GameActivate( ) 和 GameDeactivate( )
使闪屏正常的大部分工作是当游戏处于闪屏模式时禁用游戏的某些部分。
GameActivate( ) 和 GameDeactivate( ) 函数在继续活着暂停MIDI 背景音乐之前检查 g_bSplash 变量的值。
//激活游戏
void GameActivate(HWND hWindow)
{
if (!g_bSplash)
// 继续播放背景音乐
g_pGame->PlayMIDISong(TEXT(""), FALSE);
}
// 游戏未被激活时
void GameDeactivate(HWND hWindow)
{
if (!g_bSplash)
// 暂停背景音乐
g_pGame->PauseMIDISong();
}
GamePaint( )
与闪屏有关的最重要代码位于 GamePaint( ) 函数中,这里实际绘制闪屏的位置。
// 绘制游戏
void GamePaint(HDC hDC)
{
// 绘制背景
g_pBackground->Draw(hDC);
// 绘制沙漠位图
g_pDesertBitmap->Draw(hDC, 0, 371);
if (g_bSplash)
{
// 绘制闪屏位图
g_pSplashBitmap->Draw(hDC, 142, 100, TRUE);
}
else
{
// 绘制子画面
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);
}
}
HandleKeys( )
HandleKeys( ) 函数在游戏退出闪屏 以开始一个新游戏时更改 g_bSplash 的值。
// 监听键盘
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 (GetAsyncKeyState(VK_RETURN) < 0)
if (g_bSplash)
{
// 开始一个没有闪屏的新游戏
g_bSplash = FALSE;
NewGame();
}
else if (g_bGameOver)
{
// 开始一个新游戏
NewGame();
}
}