简单的C++游戏载入界面设计

        源代码下载地址:

        网盘:http://pan.baidu.com/s/1eQ33c7w

        优快云:http://download.youkuaiyun.com/detail/u011418173/9080501


        最近,看了下毛星云的书:《逐梦旅程 windows游戏编程之从零开始》,对其十分的感兴趣,这本书的内容浅显易懂,很适合像我们这些编程的入门者阅读。目前只看了该书的前半部分,也就是2D游戏的编程,自己便尝试着在浅墨大神的基础框架上做一些别的东西。


    在一些大型游戏的游戏资源载入的过程中,都是会有专门的载入界面,让玩家在等待的过程中不至于太无聊和焦虑。一般,载入界面运行是要有动态效果的,这一般可以说明该游戏没有崩溃,让玩家不要慌:别急,游戏正在加载呢,等等吧。之前玩文明5时的载入界面令我印象深刻,由于自己的电脑不好,一次载入都要好几分钟,有时候游戏死了都不知道ORZ


1、材料的准备

首先要准备一些图片资源,一些简单的资源我都是自己制作的,这里我准备了一个旋转的loading效果图片,如下所示:



当然了,想通过C++载入gif图片是有些困难的,于是我将这些图片分解为8帧,使用透明遮罩法来显示,具体的制作方法这里就不详述。下面是制成的图片,:


载入时的背景图片:


还有正式游戏时的背景图片:


2、建立loading线程

在浅墨的框架中,有三个关键的函数,分别是:

 1
 2
 3
      
      
BOOL Game_Init ( HWND hwnd ); //在此函数中进行资源的初始化
VOID Game_Paint ( HWND hwnd ); //在此函数中进行绘图代码的书写
BOOL Game_CleanUp ( HWND hwnd ); //在此函数中进行资源的清理

那么载入界面的代码当然是要写在初始化中了,为了不影响其他资源的载入,我考虑创建一个线程,在这里实现loading界面的绘制。同时主程序实现加载游戏资源的任务。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
      
      
BOOL Game_Init ( HWND hwnd )
{
HBITMAP bmp ;
g_hdc = GetDC ( hwnd );
g_mdc = CreateCompatibleDC ( g_hdc ); //创建一个和hdc兼容的内存DC
g_bufdc = CreateCompatibleDC ( g_hdc ); //再创建一个和hdc兼容的缓冲DC
bmp = CreateCompatibleBitmap ( g_hdc , WINDOW_WIDTH , WINDOW_HEIGHT );
SelectObject ( g_mdc , bmp );
//预先加载Loading界面
g_hLoading = ( HBITMAP ) LoadImage ( NULL , L"loading.bmp" , IMAGE_BITMAP , 2400 , 600 , LR_LOADFROMFILE );
g_hlLoading_bk = ( HBITMAP ) LoadImage ( NULL , L"loading_bk.bmp" , IMAGE_BITMAP , 1500 , 1000 , LR_LOADFROMFILE );
//设置字体
HFONT hFont ;
hFont = CreateFont ( 80 , 0 , 0 , 0 , 700 , 0 , 0 , 0 , GB2312_CHARSET , 0 , 0 , 0 , 0 , TEXT ( "钟齐段宁行书" ));
SelectObject ( g_mdc , hFont );
SetBkMode ( g_mdc , TRANSPARENT );
_beginthread(ThreadFunc,0,NULL);
//加载其他各种正式的游戏资源的地方
g_hBackGround = ( HBITMAP ) LoadImage ( NULL , L"场景1.bmp" , IMAGE_BITMAP , WINDOW_WIDTH , WINDOW_HEIGHT , LR_LOADFROMFILE );
//为了测试效果,强行增加Loading时间,正常运行时,只要资源加载完后,就可以退出线程
Sleep ( 3000 );
g_bFinishThread = 1 ;
Game_Paint ( hwnd );
return TRUE ;
}
前面的g_hdc,g_mdc,g_bufdc三个设备上下文句柄,是用于实现三缓冲绘图用的,这里就不再详述了。后面是设置字体,这里选用的是“ 钟齐段宁行书”字体,个人比较喜欢这种风格。载入完成这些资源后,就可以进入线程函数ThreadFunc了。当所有的游戏资源全部完成之后,将bool类型的g_bFinishTread置为1,从而让线程退出。

由于这个例子的资源极少,载入极快,故几乎看不到载入时的效果,所以添加了一句:Sleep(3000)。这样我们能够看到3秒的loading界面,从而判断效果的好坏。

下面是线程函数的具体内容:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
      
      
void ThreadFunc ( PVOID pvoid )
{
srand (( int ) time ( NULL )); //设置随机种子
int i = 0 ; //用于帧数的循环
DWORD tNow , tPre ; //保存当前时间和上一帧的时间
tPre = GetTickCount ();//获得当前的时间
//若游戏资源未加载完,则g_bFinishThread一直为0,将一直循环下去
while ( ! g_bFinishThread )
{
//载入loading时的背景图片
SelectObject ( g_bufdc , g_hlLoading_bk );
BitBlt ( g_mdc , 0 , 0 , WINDOW_WIDTH , WINDOW_HEIGHT , g_bufdc , 0 , 0 , SRCCOPY );
//载入loading时的旋转图片,使用透明遮罩法
SelectObject ( g_bufdc , g_hLoading );
BitBlt ( g_mdc , 600 , 300 , 300 , 300 , g_bufdc , i * 300 , 300 , SRCAND );
BitBlt ( g_mdc , 600 , 300 , 300 , 300 , g_bufdc , i * 300 , 0 , SRCPAINT );
//绘制提示文字
WCHAR str [ 50 ];
swprintf_s ( str , L"游戏资源正在拼命加载中,请稍后..." );
SetTextColor ( g_mdc , RGB ( rand () % 255 , rand () % 255 , rand () % 255 )); //使用随机数,呈现随机的颜色
TextOut ( g_mdc , 150 , 700 , str , wcslen ( str ));
//将最后的画面显示在窗口中
BitBlt ( g_hdc , 0 , 0 , WINDOW_WIDTH , WINDOW_HEIGHT , g_mdc , 0 , 0 , SRCCOPY );
i ++ ;
if ( i == 8 ) //每8张一个循环
i = 0 ;
do {
tNow = GetTickCount ();
} while ( tNow - tPre < 100 ); //当前后时间超过0.1s后,绘制下一帧
tPre = tNow ;
}
return ;
}

为了防止在绘制loading界面的同时进行正常游戏的绘制,我在winmain函数的循环体中加入了一条判断:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
      
      
while ( msg . message != WM_QUIT ) //使用while循环,如果消息不是WM_QUIT消息,就继续循环
{
if ( PeekMessage ( & msg , 0 , 0 , 0 , PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。
{
TranslateMessage ( & msg ); //将虚拟键消息转换为字符消息
DispatchMessage ( & msg ); //分发一个消息给窗口程序。
}
else
{
g_tNow = GetTickCount (); //获取当前系统时间
if ( g_tNow - g_tPre >= 100 ) //当此次循环运行与上次绘图时间相差0.1秒时再进行重绘操作
if(g_bFinishThread) //如果loading线程结束,则进行正常的游戏绘制
Game_Paint ( hwnd );
}
}

3、实现效果

最后我们看到效果图为:

加载完成后为:


4、补充内容

浅墨大神在游戏中添加的音乐资源都是wav格式的,而PlaySound函数只能支持wav格式的文件,同时该格式的文件不能够进行其他的更多的操作如声音的暂停、停止、音量的控制等。所以我在程序中选择了使用mp3格式的文件,并使用MCI媒体控制接口来实现音乐的播放,今后的游戏也尽量都使用这种方法。

但是这种方法的话,在编译器中调试时是发不出声音的!一开始还以为是代码的问题,后来尝试直接运行Debug文件夹下的.exe文件,发现代码原来没有问题。。。

注:需要链接winmm.lib库










C++写的一个简单界面演示系统 void CMiniDrawDoc::AddFigure (CFigure *PFigure) { m_FigArray.Add (PFigure); SetModifiedFlag (); } CFigure *CMiniDrawDoc::GetFigure (int Index) { if (Index m_FigArray.GetUpperBound ()) return 0; return (CFigure *)m_FigArray.GetAt (Index); } int CMiniDrawDoc::GetNumFigs () { return m_FigArray.GetSize (); } void CMiniDrawDoc::DeleteContents() { // TODO: Add your specialized code here and/or call the base class int Index = m_FigArray.GetSize (); while (Index--) delete m_FigArray.GetAt (Index); m_FigArray.RemoveAll (); CDocument::DeleteContents(); } void CMiniDrawDoc::OnEditClearAll() { // TODO: Add your command handler code here DeleteContents (); UpdateAllViews (0); SetModifiedFlag (); } void CMiniDrawDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable (m_FigArray.GetSize ()); } void CMiniDrawDoc::OnEditUndo() { // TODO: Add your command handler code here int Index = m_FigArray.GetUpperBound (); if (Index > -1) { delete m_FigArray.GetAt (Index); m_FigArray.RemoveAt (Index); } UpdateAllViews (0); SetModifiedFlag (); } void CMiniDrawDoc::OnUpdateEditUndo(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable (m_FigArray.GetSize ()); } // implementation of figure classes: IMPLEMENT_SERIAL (CFigure, CObject, 3) CRect CFigure::GetDimRect () { return CRect (min (m_X1, m_X2), min (m_Y1, m_Y2), max (m_X1, m_X2) + 1, max (m_Y1, m_Y2) + 1); } void CFigure::Serialize (CArchive& ar) { if (ar.IsStoring ()) ar << m_X1 << m_Y1 << m_X2 << m_Y2 <> m_X1 >> m_Y1 >> m_X2 >> m_Y2 >> m_Color; } IMPLEMENT_SERIAL (CLine, CFigure, 3) CLine::CLine (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = X1; m_Y1 = Y1; m_X2 = X2; m_Y2 = Y2; m_Color = Color; m_Thickness = Thickness; } void CLine::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CLine::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_SOLID, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); // draw figure: PDC->MoveTo (m_X1, m_Y1); PDC->LineTo (m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CRectangle, CFigure, 3) CRectangle::CRectangle (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = X1; m_Y1 = Y1; m_X2 = X2; m_Y2 = Y2; m_Color = Color; m_Thickness = Thickness; } void CRectangle::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CRectangle::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); PDC->SelectStockObject (NULL_BRUSH); // draw figure: PDC->Rectangle (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CRectFill, CFigure, 3) CRectFill::CRectFill (int X1, int Y1, int X2, int Y2, COLORREF Color) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; } void CRectFill::Draw (CDC *PDC) { CBrush Brush, *POldBrush; CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, 1, m_Color); POldPen = PDC->SelectObject (&Pen); Brush.CreateSolidBrush (m_Color); POldBrush = PDC->SelectObject (&Brush); // draw figure: PDC->Rectangle (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); PDC->SelectObject (POldBrush); } IMPLEMENT_SERIAL (CRectRound, CFigure, 3) CRectRound::CRectRound (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; m_Thickness = Thickness; } void CRectRound::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CRectRound::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); PDC->SelectStockObject (NULL_BRUSH); // draw figure: int SizeRound = (m_X2 - m_X1 + m_Y2 - m_Y1) / 6; PDC->RoundRect (m_X1, m_Y1, m_X2, m_Y2, SizeRound, SizeRound); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CRectRoundFill, CFigure, 3) CRectRoundFill::CRectRoundFill (int X1, int Y1, int X2, int Y2, COLORREF Color) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; } void CRectRoundFill::Draw (CDC *PDC) { CBrush Brush, *POldBrush; CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, 1, m_Color); POldPen = PDC->SelectObject (&Pen); Brush.CreateSolidBrush (m_Color); POldBrush = PDC->SelectObject (&Brush); // draw figure: int SizeRound = (m_X2 - m_X1 + m_Y2 - m_Y1) / 6; PDC->RoundRect (m_X1, m_Y1, m_X2, m_Y2, SizeRound, SizeRound); // remove pen/brush: PDC->SelectObject (POldPen); PDC->SelectObject (POldBrush); } IMPLEMENT_SERIAL (CCircle, CFigure, 3) CCircle::CCircle (int X1, int Y1, int X2, int Y2, COLORREF Color, int Thickness) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; m_Thickness = Thickness; } void CCircle::Serialize (CArchive& ar) { CFigure::Serialize (ar); if (ar.IsStoring ()) ar <> m_Thickness; } void CCircle::Draw (CDC *PDC) { CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, m_Thickness, m_Color); POldPen = PDC->SelectObject (&Pen); PDC->SelectStockObject (NULL_BRUSH); // draw figure: PDC->Ellipse (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); } IMPLEMENT_SERIAL (CCircleFill, CFigure, 3) CCircleFill::CCircleFill (int X1, int Y1, int X2, int Y2, COLORREF Color) { m_X1 = min (X1, X2); m_Y1 = min (Y1, Y2); m_X2 = max (X1, X2); m_Y2 = max (Y1, Y2); m_Color = Color; } void CCircleFill::Draw (CDC *PDC) { CBrush Brush, *POldBrush; CPen Pen, *POldPen; // select pen/brush: Pen.CreatePen (PS_INSIDEFRAME, 1, m_Color); POldPen = PDC->SelectObject (&Pen); Brush.CreateSolidBrush (m_Color); POldBrush = PDC->SelectObject (&Brush); // draw figure: PDC->Ellipse (m_X1, m_Y1, m_X2, m_Y2); // remove pen/brush: PDC->SelectObject (POldPen); PDC->SelectObject (POldBrush); }
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值