Windows游戏编程 第八章.doc_第1页
Windows游戏编程 第八章.doc_第2页
Windows游戏编程 第八章.doc_第3页
Windows游戏编程 第八章.doc_第4页
Windows游戏编程 第八章.doc_第5页
已阅读5页,还剩44页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

347 第8章 生 成 游 戏第8章 生 成 游 戏在第六章中策划了一个游戏,并做了三个例子。其中最后一个例子已经具有游戏的外形了。但距离所策划的那个游戏所要达到的效果还很远。本章将把角色动画、场景转换、立体效果、怪物、音效以及本章即将介绍的文字显示系统、战斗系统和人工智能加入游戏中。把这些功能都加入游戏后,将使游戏变得更加完善。8.1 文字显示系统在不使用DirectDraw的应用程序中,很容易显示文字;只需产生一个WM_PAINT消息,再获得设备内容句柄,调用GDI函数就可以显示文字了。而使用了DirectDraw的应用程序特别是游戏程序中,使用上面的方法显示会出现一些怪现象。因为,游戏程序的运行过程是这样的:在消息机制中不断地循环显示后台缓冲表面中的内容。当在一个WM_PAINT消息中显示文字后,将被后台表面的内容的覆盖;而显示文字的这个过程不一定与消息机制的循环相同步;所以,会出现文字的闪烁现象。为了不产生文字的闪烁现象,应该把文字显示在后台表面或者其他的表面上,再通过消息机制把这些表面的内容显示在客户区上,就不会出现闪烁现象了。这一节主要讨论如何在表面中显示文字。8.1.1 获得表面的设备内容要调用GDI函数接口显示文字,就必须获得设备内容。可以通过调用IDirectDrawSurface7:GetDC方法获得表面设备内容,其只有一个指向设备内容变量的指针参数。调用IDirectDrawSurface7:ReleaseDC方法则释放指定的设备内容,其参数表示设备内容。所以,要在指定表面显示文字的通常做法是这样的:HDC hdc;Surf-GetDC( &hdc );TextOut(hdc,.); /显示文字Surf-ReleaseDC( hdc );其中Surf表示表面指针。8.1.2 建立文字显示类游戏的程序在每次消息机制循环时都把新的内容放入后台缓冲表面,就是说,如果在后台缓冲表面中显示文字,文字只被显示一次(速度快的个人电脑将看不到显示的文字)。在大多数情况下,只在后台缓冲表面中显示文字。所以有必要设置文字的显示时间。把文字的显示归为一个类,对于设置文字的显示时间是很容易的事情。本小节将建立一个文字显示类CText。1. 定义CText类CText类的定义代码在CText.h文件里。该文件的代码如下:/*-| CText.h| 字符串输出相关功能声明| (c) mochsh,2004-*/#include #include #include #define MAX_TEXT 200typedef struct TEXTDESC LPDIRECTDRAWSURFACE7 lpSurf;/在哪个表面显示 POINT Pos;/在什么位置显示 TCHAR TextMAX_TEXT;/指向的字符串 DWORD color;/字符的颜色 DWORD StartTime;/开始显示的时间 DWORD LastTime;/显示多长时间 TEXTDESC * next;/链表节点 TEXTDESC, * PTEXTDESC;classCTextprotected: DWORD Time;/当前时间 PTEXTDESC pText;/字符串链表 LPDIRECTDRAWSURFACE7 lpDDSBack;/后台缓冲表面public: /获得后台缓冲表面 void Set_lpDDSBack( LPDIRECTDRAWSURFACE7 lpBackSurf ) lpDDSBack = lpBackSurf; /构造函数 CText(); /析构函数 CText(); /删除字符串链表 void DeleteText( void ); /获得要显示的字符串并设置对应字符描述的属性 void GetText( LPDIRECTDRAWSURFACE7 lpSurf, LONG x,LONG y, LPCSTR lpText, DWORD color, DWORD LastTime); /获得要显示的整数并设置对应字符描述的属性 void GetText( LPDIRECTDRAWSURFACE7 lpSurf, LONG x,LONG y, LONG lNumber, DWORD color, DWORD LastTime); /获得要显示的浮点数并设置对应字符描述的属性 void GetText( LPDIRECTDRAWSURFACE7 lpSurf, LONG x,LONG y, FLOAT fNumber, DWORD color, DWORD LastTime); /指定要在后台缓冲表面中显示的字符串 void TextBackSurf( LONG x,LONG y, LPCSTR lpText, DWORD color, DWORD LastTime); /指定要在后台缓冲表面中显示的整数 void TextBackSurf( LONG x,LONG y, LONG lNumber, DWORD color, DWORD LastTime); /指定要在后台缓冲表面中显示的浮点数 void TextBackSurf( LONG x,LONG y, FLOAT fNumber, DWORD color, DWORD LastTime); /显示字符串链表中的字符串 void ReMain( void );代码分析:可以这样来实现设置文字显示时间长度等属性的:当要显示指定文字时,把表面、在表面中的显示位置、文字、文字的颜色、开始显示的时间和显示多长时间这六个属性记录下来;每一次的消息循环都显示记录下来的文字,直到显示的时间长度达到指定的长度才把记录下来的文字删除。很显然,必需为文字定义一个属性的描述。这些属性就是前面提到的那六个属性。所以,CText类的声明文件CText.h的头部定义了一个文字描述结构TEXTDESC。在文字描述间,以链表的形式来把它们连接起来。程序通过调用Set_lpDDSBack成员获取后台缓冲表面。通过调用重载成员函数GetText来记录要显示的文字的属性。每一次的消息机制循环都调用成员函数ReMain,来显示文字链表中的字符串。2. 构造与析构首先是构造函数和析构函数。其具体代码如下:CText:CText( void ) pText = NULL; lpDDSBack = NULL;CText:CText() DeleteText();对于这两个函数,不用再说什么了。3. 删除文件链表当退出程序后,CText类中的文字链表必须删除。DeleteText成员的功能就是删除文字链表。该成员只用于析构函数中。其代码如下:/* 函数名:DeleteText(.)* 功能:释放字符链表空间* (c) mochsh, 2004*/void CText:DeleteText( void ) PTEXTDESC pTemp = pText,pTemp2=NULL; while( pTemp!=NULL ) pTemp2 = pTemp-next; delete pTemp; pTemp = pTemp2; 4. 获得字符串在让CText对象显示某字符串之前,必须把该字符串送给CText对象。可以通过调用CText对象的GetText,把指定的字符串传给CText对象。GetText的功能是获取要显示的字符串以及设置该字符串的属性。其代码如下:/* 函数名: GetText(.)* 功能: 获得要显示的字符串并设置对* 应字符描述的属性* (c) mochsh, 2004*/void CText:GetText(LPDIRECTDRAWSURFACE7 lpSurf, LONG x, LONG y, LPCSTR lpText, DWORD color, DWORD LastTime) PTEXTDESC pTemp = NULL; /选择字符链表的最后一个成员 pTemp = pText; while( 1 ) /链表非空 if( pTemp!=NULL ) if( pTemp-next=NULL ) break; pTemp = pTemp-next; /链表是空的 else pText = new TEXTDESC; pText-next = NULL; /设置新字符描述的属性 pText-lpSurf = lpSurf; pText-color = color; pText-Pos.x = x; pText-Pos.y = y; strcpy(pText-Text,lpText); pText-StartTime = GetTickCount(); pText-LastTime = LastTime; return; /为新的字符描述开辟空间 pTemp-next = new TEXTDESC; pTemp = pTemp-next; pTemp-next = NULL; /设置新字符描述的属性 pTemp-lpSurf = lpSurf; pTemp-color = color; pTemp-Pos.x = x; pTemp-Pos.y = y; strcpy(pTemp-Text,lpText); pTemp-StartTime = GetTickCount(); pTemp-LastTime = LastTime;代码分析:这个成员函数有六个参数。第1个是表示要在其中显示文字的表面lpSurf,第2个和第3个表示文字的显示位置(x,y),第4个是要显示的字符串,第5个是表示要显示的字符串的颜色,第6个是表示要显示的时间长度。该成员的处理过程是这样的:每次调用它时,都检测文字链表是否为空。如果不是空的,则转到链表的最后一个成员;然后为该成员的next成员开辟一个文字描述结构TEXTDESC空间,并根据函数的参数来设置这个新的文字描述的属性。如果是空的,则为CText类的pText成员开辟空间,并根据函数的参数来设置文字描述的属性。需要指出的是,在设置文字描述的参数时,必须把next成员设为NULL。5. 获得整数和浮点数GetText成员是记录指定的字符串。而在有些时候,需要显示一整数或者浮点数的值,所以,应该有显示整数和浮点数的成员。可以以函数重载的方式调用GetText成员。下面分别是记录指定整数属性和浮点数属性的GetText重载函数代码:/* 函数名:GetText(.)* 功能:获得要显示的整数并设置对应字符描述的属性* (c) mochsh, 2004*/void CText:GetText(LPDIRECTDRAWSURFACE7 lpSurf, LONG x, LONG y, LONG lNumber, DWORD color, DWORD LastTime) TCHAR szBufferMAX_TEXT; /将整数转化为字符串 sprintf( szBuffer, TEXT(%d), lNumber ); GetText(lpSurf,x,y,szBuffer,color,LastTime);/* 函数名:GetText(.)* 功能:获得要显示的浮点数并设置对应字符描述的属性* (c) mochsh, 2004*/void CText:GetText(LPDIRECTDRAWSURFACE7 lpSurf, LONG x, LONG y, FLOAT fNumber, DWORD color, DWORD LastTime) TCHAR szBufferMAX_TEXT; /将浮点数转化为字符串 sprintf( szBuffer, TEXT(%f), fNumber ); GetText(lpSurf,x,y,szBuffer,color,LastTime);代码分析:这两个重载成员都调用到了格式化输出函数sprintf,将整数或者浮点数转化为字符串,再把这个字符串传送给设置字符串属性功能的GetText成员。6. 给后台表面指定文字通过调用GetText把文字显示在后台表面上,较麻烦的是GetText的参数较多,易混淆。为了更好地在后台表面显示文字,CText类设置了表示后台表面指针的lpDDSBack成员,并可以通过调用Set_lpDDSBack成员从外部获得后台缓冲表面。同时声明了可以重载的用于在后台表面显示文字的TextBackSurf成员函数。/* 函数名:TextBackSurf(.)* 功能:指定要在后台缓冲表面中显示的字符串* (c) mochsh, 2004*/void CText:TextBackSurf(LONG x,LONG y, LPCSTR lpText, DWORD color, DWORD LastTime) GetText(lpDDSBack,x,y,lpText,color,LastTime);/* 功能: 指定要在后台缓冲表面中显示的整数*/void CText:TextBackSurf(LONG x,LONG y, LONG lNumber, DWORD color, DWORD LastTime) GetText(lpDDSBack,x,y,lNumber,color,LastTime);/* 功能: 指定要在后台缓冲表面中显示的浮点数*/void CText:TextBackSurf(LONG x,LONG y, FLOAT fNumber, DWORD color, DWORD LastTime) GetText(lpDDSBack,x,y,fNumber,color,LastTime);这三个成员都只有一个调用GetText成员的语句。其以lpDDSBack指定表面,而lpDDSBack指定的表面是后台缓冲表面。在调用这三个成员之前,必须调用Set_lpDDSBack成员获得后台缓冲表面,否则将出错。7. 显示文字设置了文字的显示属性后,需要通过在消息机制中显示文字,才能看到文字。成员函数ReMain的功能是显示文字链表中的字符串。在消息循环机制中,通过调用它来实现文字的显示。其具体代码如下:/* 函数名:ReMain(.)* 功能:显示字符串链表中的字符串* (c) mochsh, 2004*/void CText:ReMain( void ) Time = GetTickCount(); PTEXTDESC pTemp = pText,oldpTemp=NULL; HDC hdc; while( pTemp!=NULL ) /显示的时间还未到达指定的长度 if( (Time-pTemp-StartTime)LastTime | pTemp-LastTime=0 ) /只显示一次的字符串 if( pTemp-LastTime=0 ) pTemp-LastTime=1; /获得设备内容 pTemp-lpSurf-GetDC(&hdc); /透明模式 SetBkMode(hdc,TRANSPARENT); /设置字符颜色 SetTextColor(hdc,pTemp-color); /获得系统提供的字体 SelectObject( hdc, GetStockObject(OEM_FIXED_FONT) ); /输出字符串 TextOut( hdc, pTemp-Pos.x, pTemp-Pos.y, pTemp-Text, strlen(pTemp-Text) ); /释放设备内容 pTemp-lpSurf-ReleaseDC(hdc); /显示的时间已经达到指定的长度 else /删除字符描述 PTEXTDESC next = pTemp-next; delete pTemp; /不是链表的第1个 if( oldpTemp!=NULL ) oldpTemp-next = next; pTemp = oldpTemp; /是链表的第1个,则重新开始 else pText = next; pTemp = pText; continue; oldpTemp = pTemp; pTemp = pTemp-next; 代码分析:显示链表成员字符串时,需要检测是否已经达到了显示的时间长度。如果还未达到则显示,否则释放该文字描述成员的空间,并调整文字链表。当文字描述的LastTime成员等于0时,表示该文字描述里的字符串只显示一次。当需要在后台表面中永远的显示一字符串时,可以通过在每次消息循环机制中调用TextBackSurf设置该字符串,并将LastTime成员设置为0,就可以实现在后台表面中永远显示该字符串了。8.1.3 使用CText类这一个小节来做一个使用CText类的例子。创建一个这个例子的空的工程后,把CText.cpp文件加入该工程,然后建立主程序文件main.cpp,其代码如下:/*-| main.cpp| 主程序| (c) mochsh, 2004-*/#include #include resource.h#include CApplication.h#include CDDraw.h#include CText.h/ 应用程序对象CApplicationApp;/ DirectDraw对象CDDraw DDraw;/ CText对象CText Text;LPDIRECTDRAWSURFACE7 lpDDSImage = NULL;/ 消息处理LRESULT CALLBACK WinProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) switch( uMsg ) /重画处于后台时被覆盖的部分 case WM_PAINT: if( !App.Get_m_bFullScreen() ) DDraw.Flip( &App.Get_rectWin(); return 0; return App.MsgProc(hWnd,uMsg,wParam,lParam);/ WinMain程序入口int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) HWND hWnd = NULL; BOOL bFullScr = FALSE; LONG ClientW = 200, ClientH = 150; DWORD dwTime = 1000, dwOldTime = 0; / CApplication的使用 App.isFullScreen( bFullScr ); / 客户区背景色是黑色 App.SetClientBKColor( BLACK_BRUSH ); / 载入自定义图标 App.Set_hIcon( hInstance, IDI_STUDY ); / 载入自定义光标 App.Set_hCursor( hInstance, IDC_MYCURSOR ); / 禁止改变窗口尺寸 App.ChangeSizeAble( FALSE ); / 设置窗口大小 App.SetScreenW( ClientW ); App.SetScreenH( ClientH ); / 创建窗口 App.CreateWin( WinProc, hInstance, 第八章文字显示程序, WS_OVERLAPPEDWINDOW & WS_MAXIMIZEBOX ); / 获得窗口句柄 hWnd = App.Get_hWnd(); / CDDraw的使用 / 初始化 DDraw.InitDDraw( hWnd, bFullScr, App.Get_ScreenW(), App.Get_ScreenH(),16 ); / 载入图像文件给lpDDSMen表面 DDraw.CreateBMPSurface(lpDDSImage,0,0, Image.bmp, DDSCAPS_SYSTEMMEMORY ); DDraw.BlitBack(0,0,lpDDSImage,NULL,0); / CText的使用 Text.Set_lpDDSBack(DDraw.Get_lpDDSBack(); Text.TextBackSurf(10,10,显示五秒, RGB(255,255,0),5000); Text.TextBackSurf(10,30,显示十秒, RGB(0,255,0),10000); Text.TextBackSurf(10,50,显示十五秒, RGB(0,255,255),15000); Text.TextBackSurf(10,70,显示二十秒, RGB(255,0,0),20000); / 运行消息机制 MSG msg; while( TRUE ) if( App.Get_bActive() ) if(PeekMessage(&msg,NULL,0,0,PM_REMOVE) TranslateMessage(&msg); DispatchMessage(&msg); dwTime = GetTickCount(); DDraw.BlitBack(0,0,lpDDSImage,NULL,0); Text.TextBackSurf(10,100, 当前时间(毫秒),RGB(255,255,0),0); /显示整数 Text.TextBackSurf(50,120, (LONG)dwTime,RGB(255,255,0),0); /显示文字链表的字符串 Text.ReMain(); /显示后台表面到屏幕 DDraw.Flip( &App.Get_rectWin(); else if( GetMessage(&msg,NULL,0,0) ) TranslateMessage(&msg); DispatchMessage(&msg); else return msg.wParam; return msg.wParam;这个例子共显示六个文字描述的字符串。其中一个是显示整数,这个整数来源于机器的时间数(以毫秒为单位)。在设置这个整数的文字描述属性时,把LastTime成员设为0,并在每次消息循环机制中设置该整数的文字描述,所以将永远显示该整数的值。这个例子的最后运行效果如图 8-1 所示。图 8-1 文字显示效果8.2 定义游戏类从第一章到这里,介绍了很多内容。却没有真正做出一个像样的游戏例子。从本节开始,将做一个功能较齐全的游戏。这个游戏是根据第六章中策划的那个游戏方案来做的。在第六章中策划的方案中,把怪物设为邪恶、一般和中立,把角色的属性设为生命值、生命最大值、防御力、攻击力、悟性、角色等级、角色经验值、武功修为等级、修为经验值等,同时也设置了物品装备。作为例子,这里把怪物只设为一般怪物,其属性有生命值、生命最大值、攻击力、死后给角色的经验值和死后给角色加的生命值五种属性。角色属性设为生命值、生命最大值、攻击力、角色等级、角色经验值五种属性。并且不设置物品装备系统。共有五个场景,怪物在场景中的出现方式是随机的。角色可以攻击怪物也可以采取防守。怪物也是可以攻击角色也可以防守。本节的任务是定义游戏类CGame。游戏类CGame的定义代码在CGame.h文件里。该文件的代码如下:/*-| CGame.h| 游戏类成员声明| (c) mochsh,2004-*/#include #include #include CDDraw.h#include CDInput.h#include CDSound.h#include CGetPath.h#include CText.h#include StructDefine.hclass CGameprivate: CDDraw DDraw;/DirectDraw CDInput Input;/输入设备对象 CDSound Sound;/声音对象 CGetPath GPa;/路径对象 CText Text;/字符串对象 LPDIRECTDRAWSURFACE7 lpDDSHeader;/开头画面 LPDIRECTDRAWSURFACE7 lpDDSButtonUp;/按钮凸起 LPDIRECTDRAWSURFACE7 lpDDSButtonDown;/按钮凹下 LPDIRECTDRAWSURFACE7 lpDDSCircle;/选择怪物的圈 LPDIRECTDRAWSURFACE7 lpDDSPassageway;/通道 LPDIRECTDRAWSURFACE7 lpDDSRole;/角色源表面 LPDIRECTDRAWSURFACE7 lpDDSSpirit01;/怪物1 LPDIRECTDRAWSURFACE7 lpDDSSpirit02;/怪物2 LPDIRECTDRAWSURFACE7 lpDDSGroundRC;/地面的源表面 LPDIRECTDRAWSURFACE7 lpDDSHighTree;/大树的源表面 LPDIRECTDRAWSURFACE7 lpDDSLowTree;/小树的源表面 LPDIRECTDRAWSURFACE7 lpDDSGrass;/草的源表面 HWND hWnd;/程序句柄 BOOL bFullScr;/屏幕模式 LONG ScreenW;/客户区的宽度 LONG ScreenH;/客户区的高度 LONG ColorBits;/图像颜色深度 LONG LevelHeight;/显示行的高度 LONG MinLevel;/最小显示等级 LONG MaxLevel;/最高显示等级 LONG MaxWObject;/对象矩形图片的最大宽度 LONG MaxHObject;/对象矩形图片的最大高度 LONG lSpiritCX;/怪物激活区横向扩展量 LONG lSpiritCY;/怪物激活区纵向扩展量 ROLE Role;/角色 LONG GrnWidth;/地面方块宽度(为正方形) SCENE * Scene;/当前场景指针变量 SCENE * oldScene;/旧场景指针变量 LONG lSceneID;/当前所处的场景 LONG lOldSceneID;/旧场景的序号 POINT ScrPos;/屏幕的位置 LONG lAppState;/应用程序状态 DWORD Time;/当前时间 LONG StepFocus;/动作焦点 PKEY KeyKB;/键盘状态指针 PKEY KeyMouse;/鼠标状态指针(屏幕中的) POINT MouseScePos;/鼠标转化到场景中的位置 OBJECT * ShowOb100;/对象显示数组 SPIRIT * ShowSpirit50;/怪物显示数组 LONG RoleMessage;/角色消息public: CGame(void); CGame(); / 设置成员变量 void Set_hWnd( HWND hwnd ) hWnd = hwnd; void Set_bFullScr( BOOL bFullScreen ) /设置屏幕模式 bFullScr = bFullScreen; /设置客户区宽度 void Set_ScreenW( LONG ScrW ) ScreenW = ScrW; /设置客户区高度 void Set_ScreenH( LONG ScrH ) ScreenH = ScrH; void Set_ColorBits( LONG ColorBitCount ) /设置颜色深度 ColorBits = ColorBitCount; void Set_LevelHeight( LONG LevelH ) /设置显示行的高度 LevelHeight = LevelH; MaxLevel =(ScreenH+MaxHObject)/LevelHeight; MinLevel =(-MaxHObject)/LevelHeight; void Set_MaxWObject( LONG MaxWideObject ) /设置对象图片的最大宽度 MaxWObject = MaxWideObject; void Set_MaxHObject( LONG MaxHeightObject ) /设置对象图片的最大高度 MaxHObject = MaxHeightObject; MaxLevel =(ScreenH+MaxHObject)/LevelHeight; MinLevel =(-MaxHObject)/LevelHeight; /设置场景序号 void Set_lSceneID( LONG SceID )lSceneID=SceID; / 辅助成员函数 /获取一个矩形 RECT GetRect(LONG left,LONG top, LONG right,LONG bottom); LONG DoFor( LONG lIndex, LONG lCount, LONG lStartNum ); INT GetDirection( POINT p1, POINT p2 ); RECT GetActionRect( LONG lIndex, LONG lWidth, LONG lHeight, LONG lCount); / 初始化DirectX成员函数 /载入游戏的图像数据 void LoadImageSurf( void ); /初始化DirectX(外部通过调用它来初始化游戏) void InitDirectX( HWND _hWnd ); / 游戏开头画面相关处理函数 /画按钮 void DrawButton( LPDIRECTDRAWSURFACE7 Surf, WORD state, LPRECT lpRect ); /游戏开头画面 void Header( void ); / 初始化数据相关处理函数 /生成单个地面描述数据 void MakeGroundDescTrait( FILE *file, GROUNDDESC * grnDesc ); /生成指定场景的地面描述 void MakeGroundDescs( FILE *file, SCENE *sce ); /生成单个怪物的属性 void MakeSpiritsTrait( LONG sceID,SPIRIT *spi ); /生成指定场景的怪物 void MakeSpirits( SCENE *sce ); /生成单个对象的属性 void MakeObjectTrait( FILE *file, OBJECT * object ); /生成指定场景的对象 void MakeObjects

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论