载入中
自定义HTML载入中... loading
游戏开发基础(7)1 [转贴 2008-04-19 07:44:48]  删除... 
字体变小 字体变大

 

游戏开发基础(7)1


第七章 游戏编程的特点
第一节 概述:


电脑游戏在计算机发展使用中可以说扮演了一个极为有趣的角色,一方面不为很多人所赞同,认为是一种浪费;而另一方面电脑游戏却是推动计算机的各项技术迅速发展的最有力的力量之一。

这一点,可以从3d类游戏对硬件无止境的需求,游戏迷对游戏图像的质量、游戏的交互性、人机界面的友好性等方面的需求体现出来(当然游戏迷对游戏的的构思、创意的要求也是苛刻且无止境的,但这一点只有靠您自己的想象力,我们是爱莫能助了)。

从游戏近期的发展中,我们从3d游戏的发展,可以看到从Doom到现在的古墓丽影2、雷神之锤2,3d游戏的画面从生硬单调的多边形到今天柔和复杂几进乱真的场景、道具、怪物,敌人从只会疯狂向你冲来到今天会偷袭、会审时度势地采取合适的方式方法向你进攻;游戏无论从硬件支持还是编程技术方面都有突飞猛进的进展。在游戏发展的过程中,很多技术也随之发展起来了,例如各种图形加速卡的出现和发展,directx的出现,和各个成功游戏中采用的各种优化技术都推动了计算机技术的发展。

游戏可以说是集合了每个时期计算机行业中最先进的硬件技术和最新的编程思想,比如近期的游戏都是采用了面向对象的编程思想的基于Windows的软件,大部分图象要求高的游戏都要求或支持图形加速卡。同时游戏编程中也有自己基本的方式方法、结构和理论,在这一章的学习中我们将讨论这些问题。
在这一章中我们将讨论下面几个问题:

程序入口 即是游戏获取外部操作的讯息,得到下次刷新所需的新参数的手段。如同一般的SDK Windows应用程序一样,程序的入口为WINMAIN()。

游戏初始化 包括创建标准的WINDOWS程序所需的初始化程序以及游戏内部的初始化程序,例如游戏系统初始化、游戏图形的装入、游戏声音的装入等。

游戏内部循环: 游戏的循环入口是WINDOWS消息循环内部的一个函数调用,游戏内部循环包括刷新游戏单位、画游戏单位两部分。

刷新游戏单位: 用于每一帧刷新游戏单位的状态,例如改变游戏单位的状态、改变游戏单位的位置、获取外部信息等。

画游戏单位: 用于每一帧往屏幕上画游戏单位的图象,并进行特殊处理以提高速度。

计算机人工智能: 主要用于受计算机处理的游戏单位的行为控制算法,程序部分位于刷新计算机游戏单位部分中。

游戏内存管理: 这一部分对于优质高效的游戏软件是十分重要的,内存管理不当会导致游戏性能的降低,甚至引起死机。

游戏交互设计: 交互设计是游戏可玩性的关键,友好的交互界面和交互方式可以使游戏增色不少。

游戏图象底层设计: 游戏软件的主要处理时间花在处理图象和画图象上,所以游戏图象底层的设计对于游戏的最终效果是十分重要的。

游戏多媒体设计: 主要包括图形界面设计、游戏音乐音效设计、游戏动画设计、游戏影象设计的几个方面,更广泛的说还包括游戏所有运行过程的功能设计。


第二节 程序入口

这个标题看起来似乎很难理解,它的意思就是当游戏被启动时,计算机从什么地方开始运行程序的。在Windows的应用程序上,Winmain()函数一般就是程序入口。游戏开始后,就调用Winmain()函数,然后再按语句的顺序或所接受到的消息调用相应的函数。

从第三章Windows编程基础中我们了解到Winmain()函数的的结构、运行过程,现在我们就游戏编程的角度来讨论Winmain()函数的编制。

int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow )
{
MSG msg;

while( lpCmdLine[0] == '-' || lpCmdLine[0] == '/')
{
lpCmdLine++;

switch (*lpCmdLine++)
{
case 'e':
bUseEmulation = TRUE;
break;
case 'w':
bFullscreen = FALSE;
break;
case 'f':
bFullscreen = TRUE;
break;
case '1':
CmdLineBufferCount = 1;
break;
case '2':
case 'd':
CmdLineBufferCount = 2;
break;
case '3':
CmdLineBufferCount = 3;
break;
case 's':
bStretch = TRUE;
break;
case 'S':
bWantSound = FALSE;
break;
case 'x':
bStress= TRUE;
break;
case '?':
bHelp= TRUE;
bFullscreen= FALSE; // give help in windowed mode
break;
}

while( IS_SPACE(*lpCmdLine) )
{
lpCmdLine++;
}
}

GameMode.cx = getint(&lpCmdLine, 640);
GameMode.cy = getint(&lpCmdLine, 480);
GameBPP = getint(&lpCmdLine, 8);

/*
* create window and other windows things
*/
if( !initApplication(hInstance, nCmdShow) )
{
return FALSE;
}

/*
* Give user help if asked for
*
* This is ugly for now because the whole screen is black
* except for the popup box. This could be fixed with some
* work to get the window size right when it was created instead
* of delaying that work. see ddraw.c
*
*/

if( bHelp )
{
MessageBox(hWndMain,
"F12 - Quit\n"
"NUMPAD 2 - crouch\n"
"NUMPAD 3 - apple\n"
"NUMPAD 4 - right\n"
"NUMPAD 5 - stop\n"
"NUMPAD 6 - left\n"
"NUMPAD 7 - jump\n"
"\n"
"Command line parameters\n"
"\n"
"-e Use emulator\n"
"-S No Sound\n"
"-1 No backbuffer\n"
"-2 One backbuffer\n"
"-4 Three backbuffers\n"
"-s Use stretch\n"
"-x Demo or stress mode\n",
OUR_APP_NAME, MB_OK );
}

/*
* initialize for game play
*/

if( !InitGame() )
{
return FALSE;
}
dwFrameTime = timeGetTime();

while( 1 )
{
if (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE))
{
if (!GetMessage( &msg, NULL, 0, 0))

break;
}
TranslateMessage(&msg); 
DispatchMessage(&msg);
}
else if (!bPaused && (bIsActive || !bFullscreen))
{
ProcessFox(lastInput);
lastInput=0;
}
else
{
WaitMessage();
}
}

if (AveFrameRateCount)
{
AveFrameRate = AveFrameRate / AveFrameRateCount;
Msg("Average frame rate: %d", AveFrameRate);
}

return msg.wParam;

} /* WinMain */


我们知道在消息循环之中只有一个消息----WM_QUIT可以结束这个循环,退出WINDOWS。所以我们要在消息循环之前完成所有的工作即所有的初始化。关于初始化这个概念在下一节我们将详细讨论。在我们提供的例程(在光盘的sample目录中)中的foxbear.c里的WMain()中我们可以看到在消息循环之前先运行DDint()函数对DirectDraw进行初始化,检测命令行参数并对相关的参数进行赋值和确定显示模式,进行窗口的初始化,检测bhelp的值以确定是否显示帮助对话框,进行游戏的初始化。
在一个游戏的消息循环中除了包含一般Windows应用程序的消息循环所应包含的部分外还应有调用有关检测游戏单位状态位置、刷新游戏单位和重画新图以及有关人工智能的函数的部分。在例程中的消息循环部分包含了一个关于检测游戏单位状态位置、刷新游戏单位和重画新图函数的调用。

在这些调用中一般有两种方法:
1.在消息循环中直接调用有关函数。比如说在一个RPG的游戏中每个循环都检测主角的的位置是否发生改变,若改变了则在新位置上重画主角的图。
2.通过检测WM_TIMER消息,以决定是否调用有关函数。即是每隔一段时间(若干个时钟周期),检测一次,然后决定函数的调用与否。

在上面的两种方法里,第一种是现在较常用的,它的缺点是CPU资源占用相对较多,但对不同的机型适应性较强,较稳定。第二种在一些较老的游戏或对速度要求不高的游戏中较常见,与第一种相比它的CPU资源占用相对较少,但在不同的机型中表现差异极大。

在谈WinMain()的编制时,窗口函数(WINPROC)的编制是必须说的。窗口函数可以说是把功能不同的函数通过Switch-Case结构连起来组成一个复杂程序的线索。它的基本编写方法在Windows编程基础中我们就已经谈到了。仔细阅读例程中的MainWndProc()函数相信对您是有相当大的帮助的。

/*
* MainWndProc
*
* Callback for all Windows messages
*/
long FAR PASCAL MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;

switch( message )
{
case WM_SIZE:
case WM_MOVE:
if (IsIconic(hWnd))
{
Msg("FoxBear is minimized, pausing");
PauseGame();
}

if (bFullscreen)
{
SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
}
else
{
GetClientRect(hWnd, &rcWindow);
ClientToScreen(hWnd, (LPPOINT)&rcWindow);
ClientToScreen(hWnd, (LPPOINT)&rcWindow+1);
}
Msg("WINDOW RECT: [%d,%d,%d,%d]", rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom);
break;

case WM_ACTIVATEAPP:
bIsActive = (BOOL)wParam && GetForegroundWindow() == hWnd;

if (bIsActive)
Msg("FoxBear is active");
else
Msg("FoxBear is not active");

//
// while we were not-active something bad happened that caused us
// to pause, like a surface restore failing or we got a palette
// changed, now that we are active try to fix things
//
if (bPaused && bIsActive)
{
if (RestoreGame())
{
UnPauseGame();
}
else
{
if (GetForegroundWindow() == hWnd)
{
//
// we are unable to restore, this can happen when
// the screen resolution or bitdepth has changed
// we just reload all the art again and re-create
// the front and back buffers. this is a little
// overkill we could handle a screen res change by
// just recreating the front and back buffers we dont
// need to redo the art, but this is way easier.
//
if (InitGame())
{
UnPauseGame();
}
}
}
}
break;

case WM_QUERYNEWPALETTE:
//
// we are getting the palette focus, select our palette
//
if (!bFullscreen && lpPalette && lpFrontBuffer)
{
HRESULT ddrval;

ddrval = IDirectDrawSurface_SetPalette(lpFrontBuffer,lpPalette);
if( ddrval == DDERR_SURFACELOST )
{
IDirectDrawSurface_Restore( lpFrontBuffer );

ddrval= IDirectDrawSurface_SetPalette(lpFrontBuffer,lpPalette);
if( ddrval == DDERR_SURFACELOST )
{
Msg(" Failed to restore palette after second try");
}
}

//
// Restore normal title if palette is ours
//

if( ddrval == DD_OK )
{
SetWindowText( hWnd, OUR_APP_NAME );
}
}
break;

case WM_PALETTECHANGED:
//
// if another app changed the palette we dont have full control
// of the palette. NOTE this only applies for FoxBear in a window
// when we are fullscreen we get all the palette all of the time.
//
if ((HWND)wParam != hWnd)
{
if( !bFullscreen )
{
if( !bStress ) 
{
Msg("***** PALETTE CHANGED, PAUSING GAME");
PauseGame();
}
else
{
Msg("Lost palette but continuing");
SetWindowText( hWnd, OUR_APP_NAME 
" - palette changed COLORS PROBABLY WRONG" );
}
}
}
break;

case WM_DISPLAYCHANGE:
break;

case WM_CREATE:
break;

case WM_SETCURSOR:
if (bFullscreen && bIsActive)
{
SetCursor(NULL);
return TRUE;
}
break;

case WM_SYSKEYUP:
switch( wParam )
{
// handle ALT+ENTER (fullscreen)
case VK_RETURN:
bFullscreen = !bFullscreen;
ExitGame();
DDDisable(TRUE); // destroy DirectDraw object
GameMode.cx = 320;
GameMode.cy = 200;
InitGame();
break;
}
break;

case WM_KEYDOWN:
switch( wParam )
{
case VK_NUMPAD5:
lastInput=KEY_STOP;
break;
case VK_DOWN:
case VK_NUMPAD2:
lastInput=KEY_DOWN;
break;
case VK_LEFT:
case VK_NUMPAD4:
lastInput=KEY_LEFT;
break;
case VK_RIGHT:
case VK_NUMPAD6:
lastInput=KEY_RIGHT;
break;
case VK_UP:
case VK_NUMPAD8:
lastInput=KEY_UP;
break;
case VK_HOME:
case VK_NUMPAD7:
lastInput=KEY_JUMP;
break;
case VK_NUMPAD3:
lastInput=KEY_THROW;
break;
case VK_F5:
bShowFrameCount = !bShowFrameCount;
if( bShowFrameCount )
{
dwFrameCount = 0;
dwFrameTime = timeGetTime();
}
break;

case VK_F6:
{
static i;
//
// find our current mode in the mode list
//
if(bFullscreen)
{
for (i=0; i<NumModes; i++)
{
if (ModeList[i].bpp == (int)GameBPP &&
ModeList[i].w == GameSize.cx &&
ModeList[i].h == GameSize.cy)
{
break;
}
}
}else
{
for (i=0; i<NumModes; i++)
{
if (ModeList[i].w == GameSize.cx &&
ModeList[i].h == GameSize.cy)
{
break;
}
}
}
//
// now step to the next mode, wrapping to the first one.
//
if (++i >= NumModes)
{
i = 0;
}
Msg("ModeList %d %d",i,NumModes);
GameMode.cx = ModeList[i].w;
GameMode.cy = ModeList[i].h;
GameBPP = ModeList[i].bpp;
bStretch = FALSE;
InitGame();
}
break;
case VK_F7:
GameBPP = GameBPP == 8 ? 16 : 8;
InitGame();
break;

case VK_F8:
if (bFullscreen)
{
bStretch = !bStretch;
InitGame();
}
else
{
RECT rc;

GetClientRect(hWnd, &rc);

bStretch = (rc.right != GameSize.cx) ||
(rc.bottom != GameSize.cy);

if (bStretch = !bStretch)
SetRect(&rc, 0, 0, GameMode.cx*2, GameMode.cy*2);
else
SetRect(&rc, 0, 0, GameMode.cx, GameMode.cy);

AdjustWindowRectEx(&rc,
GetWindowStyle(hWnd),
GetMenu(hWnd) != NULL,
GetWindowExStyle(hWnd));

SetWindowPos(hWnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
case VK_F9: DevIndex ++;
bUseEmulation = FALSE;
if (DevIndex >= MaxDevIndex)
DevIndex = 0;

ExitGame();
DDDisable(TRUE); // destroy DirectDraw object
InitGame();
break;
case VK_F4:
// treat F4 like ALT+ENTER (fullscreen)
PostMessage(hWnd, WM_SYSKEYUP, VK_RETURN, 0);
break;

case VK_F3:
bPaused = !bPaused;
break;

case VK_ESCAPE:
case VK_F12:
PostMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
break;

case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
if (bPaused)
{
char *sz = "Game is paused, this is not a bug.";
TextOut(ps.hdc, 0, 0, sz, lstrlen(sz));
}
EndPaint( hWnd, &ps );
return 1;

case WM_DESTROY:
hWndMain = NULL;
lastInput=0;
DestroyGame(); // end of game
DDDisable(TRUE); // destroy DirectDraw object
PostQuitMessage( 0 );
break;
}

return DefWindowProc(hWnd, message, wParam, lParam);

} /* MainWndProc */


第三节 游戏初始化

游戏初始化包括三部分:
1.Windows的初始化。
2.游戏工具的初始化。
3.游戏的初始化。

在这三部分中Windows的初始化,也就是对窗口的注册、定义和初始化。我们在Win- dows编程基础中已经谈过,这里就不再详述了。

游戏工具的初始化,是指对游戏程序中用到的工具进行初始化。对于一个游戏而言我们需要针对游戏的需要使用一些对图形或声音管理绘制或播放的以及其他功能的系统,这些系统就是我们所说的游戏工具(有时人们也称之为游戏引擎)。这些工具有时是由一些游戏公司提供的,比如MICROSOFT的DirectX5 SDK,有时是自己针对游戏需要编制的或使用上一部作品中用过的系统。在本例程中是指对Directdraw和DirectSound进行初始化,您可以通过阅读DDinit()和 InitSound()以及InitGame()函数的一部分的原代码以及阅读我们提供有关Directdraw和DirectSound的章节来理解。

DDinit()和 InitSound()以及InitGame()函数的代码:
/*
* InitGame
*
* Initializing current game
*/
BOOL InitGame( void )
{
ExitGame();

GameSize = GameMode;

/*
* initialize sound
*/
InitSound( hWndMain );

/*
* init DirectDraw, set mode, ...
* NOTE GameMode might be set to 640x480 if we cant get the asked for mode.
*/
if( !PreInitializeGame() )
{
return FALSE;
}

if (bStretch && bFullscreen)
{
GameSize.cx = GameMode.cx / 2;
GameSize.cy = GameMode.cy / 2;
GameRect.left = GameMode.cx - GameSize.cx;
GameRect.top = GameMode.cy - GameSize.cy;
GameRect.right = GameMode.cx;
GameRect.bottom = GameMode.cy;

if (lpStretchBuffer)
Msg("Stretching using a system-memory stretch buffer");
else
Msg("Stretching using a VRAM->VRAM blt");
}
else
{
GameRect.left = (GameMode.cx - GameSize.cx) / 2;
GameRect.top = (GameMode.cy - GameSize.cy) / 2;
GameRect.right = GameRect.left + GameSize.cx;
GameRect.bottom = GameRect.top + GameSize.cy;
}

/*
* setup our palette
*/
if( GameBPP == 8 )
{
lpPalette = ReadPalFile( NULL ); // create a 332 palette

if( lpPalette == NULL )
{
Msg( "Palette create failed" );
return FALSE;
}

IDirectDrawSurface_SetPalette( lpFrontBuffer, lpPalette );
}

/*
* load all the art and things.
*/
if( !InitializeGame() )
{
return FALSE;
}

/*
* init our code to draw the FPS
*/
makeFontStuff();

/*
* spew some stats
*/
{
DDCAPS ddcaps;
ddcaps.dwSize = sizeof( ddcaps );
IDirectDraw_GetCaps( lpDD, &ddcaps, NULL );
Msg( "Total=%ld, Free VRAM=%ld", ddcaps.dwVidMemTotal, ddcaps.dwVidMemFree );
Msg( "Used = %ld", ddcaps.dwVidMemTotal- ddcaps.dwVidMemFree ); }

return TRUE;

} /* InitGame */

/*
* InitSound
*
* Sets up the DirectSound object and loads all sounds into secondary
* DirectSound buffers. Returns FALSE on error, or TRUE if successful
*/
BOOL InitSound( HWND hwndOwner )
{
int idx;
DSBUFFERDESC dsBD;
IDirectSoundBuffer *lpPrimary;

DSEnable(hwndOwner);

if (lpDS == NULL)
return TRUE;

/*
* Load all sounds -- any that can't load for some reason will have NULL
* pointers instead of valid SOUNDEFFECT data, and we will know not to
* play them later on.
*/
for( idx = 0; idx < NUM_SOUND_EFFECTS; idx++ )
{
if (SoundLoadEffect((EFFECT)idx))
{
DSBCAPS caps;

caps.dwSize = sizeof(caps);
IDirectSoundBuffer_GetCaps(lpSoundEffects[idx], &caps);

if (caps.dwFlags & DSBCAPS_LOCHARDWARE)
Msg( "Sound effect %s in hardware", szSoundEffects[idx]);
else
Msg( "Sound effect %s in software", szSoundEffects[idx]);
}
else
{
Msg( "cant load sound effect %s", szSoundEffects[idx]);
}
}

/*
* get the primary buffer and start it playing
*
* by playing the primary buffer, DirectSound knows to keep the
* mixer active, even though we are not making any noise.
*/

ZeroMemory( &dsBD, sizeof(DSBUFFERDESC) );
dsBD.dwSize = sizeof(dsBD);
dsBD.dwFlags = DSBCAPS_PRIMARYBUFFER;

if (SUCCEEDED(IDirectSound_CreateSoundBuffer(lpDS, &dsBD, &lpPrimary, NULL)))
{
if (!SUCCEEDED(IDirectSoundBuffer_Play(lpPrimary, 0, 0, DSBPLAY_LOOPING)))
{
Msg("Unable to play Primary sound buffer");
}

IDirectSoundBuffer_Release(lpPrimary);
}
else
{
Msg("Unable to create Primary sound buffer");
}

return TRUE;

} /* InitSound */


/*
* DDInit
*/
BOOL DDInit( void )
{
DirectDrawEnumerate(&DDEnumCallback,NULL);
DDEnumCallback((GUID *)DDCREATE_EMULATIONONLY, "Hardware Emulation Layer", "", NULL);
return TRUE;
}


游戏的初始化是指调入游戏中的图象、声音等资源和游戏中的角色、道具的属性、
初始位置、状态等并画出初始画面的图象以及游戏的系统、操作方法的定义、游戏的规则等。比如说在一个RPG游戏之中,在游戏开始时内存中就应装入主角的图象组(比如走时的几幅图,状态对话框中的图)、状态(级别、HP、MP、DP等等)、属性(性别、职业等)等,描述整个游戏世界的图,NPC的各种属性、游戏的规则(各种攻击方式的效果、升级所需的经验值等)等等,总之要装入您所设计的游戏世界的一切。在例程的InitGame()中调用的函数InitializeGame()就完成了这个任务。

InitializeGame()的代码:

/*
* InitializeGame
*/
BOOL InitializeGame ( void )
{
Splash();

hBitmapList = LoadBitmaps();
if( hBitmapList == NULL )
{
return FALSE;
}

InitTiles( &hTileList, hBitmapList, C_TILETOTAL );

InitPlane( &hForeground, &hForePosList, "FORELIST", C_FORE_W, C_FORE_H, C_FORE_DENOM );
TilePlane( hForeground, hTileList, hForePosList );

InitPlane( &hMidground, &hMidPosList, "MIDLIST", C_MID_W, C_MID_H, C_MID_DENOM );
TilePlane( hMidground, hTileList, hMidPosList );

InitPlane( &hBackground, &hBackPosList, "BACKLIST", C_BACK_W, C_BACK_H, C_BACK_DENOM );
TilePlane( hBackground, hTileList, hBackPosList );

InitSurface( &hSurfaceList, "SURFLIST", C_FORE_W, C_FORE_H );
SurfacePlane( hForeground, hSurfaceList );

InitFox( &hFox, hBitmapList );
InitBear( &hBear, hBitmapList );
InitApple( &hApple, hBitmapList );

DDClear(); // clear all the backbuffers.

return TRUE;

} /* InitializeGame */


在现在的大部分游戏中游戏世界中的每个组成部分通常是用结构或类分别定义储存的。比如一个即时战略游戏中,各种建筑物放在一个类中,而每个建筑物的属性就放在该类的一个子类中;各种武器放在一个类中,每种武器放在该类的一个子类中。

class Weapon
{
WEAPON_TYPE Type;
char Name;
DWORD Id;
WORD Defend;
WORD Attack;
...
};

第四节 游戏内部循环

游戏内部循环包括刷新游戏单位、画游戏单位两部分。它的实现过程是这样的:检测状态,作出判断,绘出新图。看起来这并不是一个循环,对吗?是的,游戏内部循环并不是一个真正的循环,它实际上是由消息循环完成循环作用的。让我们从例程中看看这是如何实现的吧!

在消息循环中的第一个else if语句是这样的
else if (!bPaused && (bIsActive || !bFullscreen))
{
ProcessFox(lastInput);
lastInput=0;
}

if 后的表达式的含义是:当游戏没有被暂停时(bPause为FLASE)或以窗口模式显示(bFullscreen为FLASE)且窗口处于活动状态(bIsActive为TRUE)时执行
{
ProcessFox(lastInput);
lastInput=0;
}
语句段。而函数ProcessFox(lastInput)通过调用ProcessInput()和NewGameFrame( )达成刷新游戏单元和重画新图的功能。(这三个函数的原代码见例程foxbear.c和gameproc.c两文件)。

ProcessFox(lastInput):

/*
* ProcessFox
*/
BOOL ProcessFox(SHORT sInput)
{
if ((lpFrontBuffer && IDirectDrawSurface_IsLost(lpFrontBuffer) == DDERR_SURFACELOST) ||
(lpBackBuffer && IDirectDrawSurface_IsLost(lpBackBuffer) == DDERR_SURFACELOST))
{
if (!RestoreGame())
{
PauseGame();
return FALSE;
}
}


ProcessInput(sInput);
NewGameFrame();
return TRUE;

} /* ProcessFox */

static HFONT hFont;

DWORD dwFrameCount;
DWORD dwFrameTime;
DWORD dwFrames;
DWORD dwFramesLast;
SIZE sizeFPS;
SIZE sizeINFO;
int FrameRateX;
char szFPS[] = "FPS %02d";
char szINFO[] = "%dx%dx%d%s F6=mode F8=x2 ALT+ENTER=Window";
char szINFOW[] = "%dx%dx%d%s F6=mode F8=x2 ALT+ENTER=Fullscreen";

char szFrameRate[128];
char szInfo[128];

COLORREF InfoColor = RGB(0,152,245);
COLORREF FrameRateColor = RGB(255,255,0);
COLORREF BackColor = RGB(255,255,255);

/*
* initNumSurface
*/
void initNumSurface( void )
{
HDC hdc;
RECT rc;
int len;

dwFramesLast = 0;

len = wsprintf(szFrameRate, szFPS, 0, 0);

if( lpFrameRate && IDirectDrawSurface_GetDC(lpFrameRate, &hdc ) == DD_OK )
{
SelectObject(hdc, hFont);
SetTextColor(hdc, FrameRateColor);
SetBkColor(hdc, BackColor);
SetBkMode(hdc, OPAQUE);
SetRect(&rc, 0, 0, 10000, 10000);
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, szFrameRate, len, NULL);
GetTextExtentPoint(hdc, szFrameRate, 4, &sizeFPS);
FrameRateX = sizeFPS.cx;
GetTextExtentPoint(hdc, szFrameRate, len, &sizeFPS);

IDirectDrawSurface_ReleaseDC(lpFrameRate, hdc);
}

if (bFullscreen)
len = wsprintf(szInfo, szINFO,
GameSize.cx, GameSize.cy, GameBPP,bStretch ? " x2" : "");
else
len = wsprintf(szInfo, szINFOW,
GameSize.cx, GameSize.cy, GameBPP,bStretch ? " x2" : "");

if( lpInfo && IDirectDrawSurface_GetDC(lpInfo, &hdc ) == DD_OK )
{
SelectObject(hdc, hFont);
SetTextColor(hdc, InfoColor);
SetBkColor(hdc, BackColor);
SetBkMode(hdc, OPAQUE);
SetRect(&rc, 0, 0, 10000, 10000);
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, szInfo, len, NULL);
GetTextExtentPoint(hdc, szInfo, len, &sizeINFO);

IDirectDrawSurface_ReleaseDC(lpInfo, hdc);
}

} /* initNumSurface */

NewGameFrame( ):

/*
* NewGameFrame
*/
int NewGameFrame( void )
{

SetSpriteX( hFox, 0, P_AUTOMATIC );
SetSpriteY( hFox, 0, P_AUTOMATIC );

SetPlaneVelX( hBackground, GetSpriteVelX(hFox), P_ABSOLUTE );
SetPlaneVelX( hMidground, GetSpriteVelX(hFox), P_ABSOLUTE );
SetPlaneVelX( hForeground, GetSpriteVelX(hFox), P_ABSOLUTE );

SetPlaneX( hBackground, 0, P_AUTOMATIC );
SetPlaneX( hMidground, 0, P_AUTOMATIC );
SetPlaneX( hForeground, 0, P_AUTOMATIC );

SetSpriteX( hBear, 0, P_AUTOMATIC );
SetSpriteX( hApple, 0, P_AUTOMATIC );
SetSpriteY( hApple, 0, P_AUTOMATIC );

/*
* once all sprites are processed, display them
*
* If we are using destination transparency instead of source
* transparency, we need to paint the background with the color key
* and then paint our sprites and planes in reverse order.
*
* Since destination transparency will allow you to only write pixels
* on the destination if the transparent color is present, reversing
* the order (so that the topmost bitmaps are drawn first instead of
* list) causes everything to come out ok.
*/
if( bTransDest )
{
gfxFillBack( dwColorKey );

DisplayFrameRate();

DisplaySprite( hBuffer, hApple, GetPlaneX(hForeground) );
DisplaySprite( hBuffer, hBear, GetPlaneX(hForeground) );
DisplaySprite( hBuffer, hFox, GetPlaneX(hForeground) );

DisplayPlane( hBuffer, hForeground );
DisplayPlane( hBuffer, hMidground );
DisplayPlane( hBuffer, hBackground );
}
else
{
DisplayPlane( hBuffer, hBackground );
DisplayPlane( hBuffer, hMidground );
DisplayPlane( hBuffer, hForeground );

DisplaySprite( hBuffer, hFox, GetPlaneX(hForeground) );
DisplaySprite( hBuffer, hBear, GetPlaneX(hForeground) );
DisplaySprite( hBuffer, hApple, GetPlaneX(hForeground) );

DisplayFrameRate();
}

gfxSwapBuffers();

return 0;

} /* NewGameFrame */

分类: 游戏编程
票数:
什么是“我顶”?
点击数:    评论数:
本文章引用通告地址(TrackBack Ping URL)为:
本文章尚未被引用。
发表评论
大 名:
(不填写则显示为匿名者)
网 址:
(您的网址,可以不填)
标 题:
内 容:
请根据下图中的字符输入验证码:
(您的评论将有可能审核后才能发表)
和讯个人门户 v1.0 | 和讯部落 | 客服中心