71游戏动画II.docx
《71游戏动画II.docx》由会员分享,可在线阅读,更多相关《71游戏动画II.docx(28页珍藏版)》请在冰豆网上搜索。
71游戏动画II
一、帧动画
前面的精灵类中缺少一个功能,即精灵不能改变其外观。
例如,在BattleOffice游戏中,如果跑动的同事的腿是摆动的,会给人一种更真实的印象。
帧动画的基本工作原理是什么?
回顾一下,没有帧动画的基本精灵使用一个单独的位图图像来表现其外观。
在绘制精灵时,就是绘制位图图像。
而使用帧动画的精灵使用一系列位图来表现精灵的多个外观。
可以将这一系列图像看作是传统的电影放映机上的一卷胶片。
帧动画的错觉是通过循环播放胶片上的图像实现的。
另外一个与帧动画有关的问题是控制循环播放帧图像的速度,因此需要有一种方法来确定时间延迟,控制改变帧的速度。
二、设计动画精灵
首先需要设计它的位图图像。
有各种不同的方法来存储动画精灵的一系列图像,但是最容易的是在一个单独的图像中垂直存储帧图像。
然后编写精灵类的绘图代码保证只绘制代表当前帧的帧图像。
有了动画精灵图像之后,还需要记录精灵的当前帧索引和最大帧数。
同时,为了控制循环播放帧图像的速度,还需要一个帧延迟数据,该数据决定了在更改帧图像之前必须经历多少个游戏周期。
为了实现动画精灵的帧延迟特性,必须使用一个触发器(计数器),它将这个延迟向下计数,指出在什么时候移动到下一帧。
因此,最初开始动画精灵时,将触发器设置为帧延迟,然后随着每一个游戏周期开始向下计数,当触发器到达0时,精灵移动到下一帧并再次将触发器重置为帧延迟。
总结一下,除了包含垂直放置的帧图像的位图图像之外,新的动画精灵还需要以下信息:
1、总帧数
2、当前帧索引
3、帧延迟
4、帧触发器
三、为游戏添加动画精灵支持
第一步:
更改Bitmap类,添加绘制部分位图的功能。
//对动画帧的支持
voidBitmap:
:
DrawPart(HDChDC,intx,inty,intxPart,intyPart,intwPart,inthPart,boolbTrans,COLORREFcrTransColor){
if(m_hBitmap!
=NULL){
HDChMemDC=CreateCompatibleDC(hDC);
HBITMAPhOldBitmap=(HBITMAP)SelectObject(hMemDC,m_hBitmap);
if(!
bTrans)
BitBlt(hDC,x,y,wPart,hPart,hMemDC,xPart,yPart,SRCCOPY);
else
TransparentBlt(hDC,x,y,wPart,hPart,hMemDC,xPart,yPart,wPart,hPart,crTransColor);
SelectObject(hMemDC,hOldBitmap);
DeleteDC(hMemDC);
}
}
voidBitmap:
:
Draw(HDChDC,intx,inty,boolbTrans,COLORREFcrTransColor)
{DrawPart(hDC,x,y,0,0,GetWidth(),GetHeight(),bTrans,crTransColor);//修改Draw方法}
第二步:
修改Sprite类,提供对帧动画的支持
//添加精灵动画支持变量,并在构造函数中初始化
intm_iNumFrames,m_iCurFrame;
intm_iFrameDelay,m_iFrameTrigger;
Sprite:
:
Sprite(Bitmap*pBitmap){
……
//增加动画帧初始化代码
m_iNumFrames=1;m_iCurFrame=m_iFrameDelay=m_iFrameTrigger=0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//添加精灵动画支持(修改计算帧高度函数,同时也适用于无动画帧的精灵)
intGetHeight(){return(m_pBitmap->GetHeight()/m_iNumFrames);};
//添加精灵动画支持(要想将一个普通精灵转换为一个帧动画精灵,只需设置帧数以及帧延迟即可)
voidSetNumFrames(intiNumFrames);
voidSetFrameDelay(intiFrameDelay){m_iFrameDelay=iFrameDelay;};
voidUpdateFrame();
inlinevoidSprite:
:
UpdateFrame(){//由Update方法调用
if((m_iFrameDelay>=0)&&(--m_iFrameTrigger<=0)){
m_iFrameTrigger=m_iFrameDelay;//重置帧触发器
//帧索引加1
if(++m_iCurFrame>=m_iNumFrames)m_iCurFrame=0;}}
//添加精灵动画支持
/*注意:
因为图像现在包括了多帧,不再使用完整的图像大小,所以必须重新计算精灵位置
*/
inlinevoidSprite:
:
SetNumFrames(intiNumFrames){
m_iNumFrames=iNumFrames;//设置帧数
//重新计算位置
RECTrect=GetPosition();
rect.bottom=rect.top+((rect.bottom-rect.top)/iNumFrames);
SetPosition(rect);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SPRITEACTIONSprite:
:
Update()//完成精灵移动和碰撞检测
{UpdateFrame();//更新帧
……
}
//修改以支持帧动画
voidSprite:
:
Draw(HDChDC){
//Drawthespriteifitisn'thidden
if(m_pBitmap!
=NULL&&!
m_bHidden){
if(m_iNumFrames==1)
m_pBitmap->Draw(hDC,m_rcPosition.left,m_rcPosition.top,TRUE,RGB(255,0,255));
else
m_pBitmap->DrawPart(hDC,m_rcPosition.left,m_rcPosition.top,0,m_iCurFrame*GetHeight(),GetWidth(),GetHeight(),true,RGB(255,0,255));
}
}
举例:
BattleOffice2
rcBounds.left=340;
g_pGuySprites[3]=newSprite(g_pGuyBitmaps[3],rcBounds,BA_WRAP);
//这里没有设置帧延迟,意味着它将以最快的速度循环显示各个帧
g_pGuySprites[3]->SetNumFrames(4);
g_pGuySprites[3]->SetPosition(500,10);
g_pGuySprites[3]->SetVelocity(-3,0);
g_pGuySprites[3]->SetZOrder
(1);
g_pGuySprites[3]->SetHidden(TRUE);
AddSprite(g_pGuySprites[3]);
rcBounds.left=385;
g_pGuySprites[4]=newSprite(g_pGuyBitmaps[4],rcBounds,BA_WRAP);
g_pGuySprites[4]->SetNumFrames(4);
g_pGuySprites[4]->SetFrameDelay(5);
g_pGuySprites[4]->SetPosition(260,60);
g_pGuySprites[4]->SetVelocity(5,0);
g_pGuySprites[4]->SetZOrder
(1);
g_pGuySprites[4]->SetHidden(TRUE);
AddSprite(g_pGuySprites[4]);
四、地图类
1、游戏背景的类型
(1)纯色背景
(2)图像背景
(3)动画背景
(4)滚动背景
(1)纯色背景
纯色背景是只有一种纯色的背景。
它是所有背景中最单调的一种,在游戏中较少使用。
总的来说,在能够使用纯色背景的任何时候都可以使用一个图像背景,从而获得好得多的效果。
例如开发一个足球游戏,创建一个纯绿色背景来表示球场上的草或许是可以的,不过,在同样的情况下使用一个带草状纹理的图像看起来会好得多,并且也不需要更多的开发工作。
补充:
纯色背景很有用的一种情况是在测试游戏时。
例如,如果有一个包含复杂背景图像的游戏,精灵很容易混在一起,这时在一个简单背景上查看它们会更容易调整这些精灵。
通过使用一个对比度强的纯色背景来临时替换背景图像,就可以很容易地了解精灵的情况并修改它们存在的问题。
(2)图像背景
使用一个位图图像来代表游戏的背景。
使用图像背景的主要工作是创建图像本身。
从编程的角度来看,游戏引擎中的Bitmap类已经处理了使用图像背景的大多数工作。
使用图像位图的问题在于,它是静态的,不能移动,缺乏真实感。
(3)动画背景
动画背景是随着时间以某种方式改变其外观的背景,这有一点类似于动画精灵。
不过,动画背景不一定涉及一系列帧图像。
创建通过自定义代码实现动画效果的动画背景是完全可能的。
实现动画背景的关键是提供某种机制来更新和绘制背景。
这类似于精灵,也必须定期更新,以便传达一种运动的感觉。
注意:
尽管动画背景与动画精灵相似,但是背景通常比精灵图像大得多。
因此,对背景使用一系列帧图像会消耗大量内存,特别是在考虑到背景的宽度和高度通常达数百像素时。
专注于考虑一些有趣并且更有效的方法来实现动画背景会更好。
创建动画背景的另一种方法是使用几个更小的图像,它们能够在背景上四处移动。
例如,如果希望增强布满星星的背景,那么完全可以在远处添加一些行星,它们时常稍微改变其外观,这就实现了背景动画。
对于应该将这些行星创建为精灵还是创建为背景的一部分,目前还存在一些争论。
(4)滚动背景
滚动背景就是一个图像或者一组图形对象在屏幕上飘动或者滚动。
理解滚动背景的最佳方式是想像一个比游戏屏幕大得多的背景,要想查看背景的其他部分,就必须将视图滚动到另一部分。
(如上图所示)
上图显示了游戏屏幕只显示了一个更大背景的一部分。
这种背景在冒险游戏中用得非常多,在这种游戏中,玩家控制一个角色在很大的虚拟世界中走动。
开发滚动背景比开发其他类型的背景复杂得多,这是因为它们涉及多得多的游戏逻辑。
例如,背景必须在玩家移动角色时响应,使它们看起来是与背景一起滚动的。
不仅如此,滚动背景还经常必须能够环绕,以免角色碰到边界。
在2D游戏中经常使用的一种很有趣的滚动背景是视差滚动背景,这是一种以不同滚动速度滚动的背景。
视差滚动需要使用多层背景图像,例如前景中的建筑物、中景中的树木以及背景中的山脉。
其思想是通过以不同的速度移动各个图像来给人一种深度的错觉。
因此,因为山脉离得最远,所以它们的移动是最慢的,树木的移动要快一些,而建筑物的移动是最快的。
这模拟了在真实世界中我们经过不同距离的物体时观察到的移动效果。
五、为游戏添加背景类
1、创建基本背景类——Background(包含纯色背景和图像背景)
#pragmaonce
#include"Bitmap.h"
classBackground{
protected:
intm_iWidth,m_iHeight;//背景的宽高
COLORREFm_crColor;//背景的颜色,只适用于纯色背景
Bitmap*m_pBitmap;//用作绘制图像背景的位图图像
public:
Background(intiWidth,intiHeight,COLORREFcrColor);//构造纯色背景
Background(Bitmap*pBitmap);//构造图像背景
virtual~Background();
//更新背景的外观,对于纯色背景和图像背景,它不做任何事情
//该函数的目的是使派生的动画背景能够使用它来更新自身
virtualvoidUpdate();
virtualvoidDraw(HDChDC);
intGetWidth(){returnm_iWidth;}
intGetHeight(){returnm_iHeight;}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include"Background.h"
Background:
:
Background(intiWidth,intiHeight,COLORREFcrColor){
//Initializethemembervariables
m_iWidth=iWidth;m_iHeight=iHeight;
m_crColor=crColor;
m_pBitmap=NULL;
}
Background:
:
Background(Bitmap*pBitmap){
//Initializethemembervariables
m_crColor=0;m_pBitmap=pBitmap;
m_iWidth=pBitmap->GetWidth();m_iHeight=pBitmap->GetHeight();
}
Background:
:
~Background(){}
//-----------------------------------------------------------------
//BackgroundGeneralMethods
//-----------------------------------------------------------------
voidBackground:
:
Update(){//Donothingsincethebasicbackgroundisnotanimated}
voidBackground:
:
Draw(HDChDC){
//Drawthebackground
if(m_pBitmap!
=NULL)m_pBitmap->Draw(hDC,0,0);
else{
RECTrect={0,0,m_iWidth,m_iHeight};
HBRUSHhBrush=CreateSolidBrush(m_crColor);
FillRect(hDC,&rect,hBrush);
DeleteObject(hBrush);
}
}
2、创建动画背景类——StarryBackground
//模拟星空的动画背景类
classStarryBackground:
publicBackground{
protected:
intm_iNumStars;//在背景上显示的星星总数
intm_iTwinkleDelay;//星星闪烁频率,延迟越长,闪烁得越慢
POINTm_ptStars[100];//星星的位置(最多100颗星星,自己可以适当调整数量)
COLORREFm_crStarColors[100];//星星的颜色
public:
StarryBackground(intiWidth,intiHeight,intiNumStarts=100,intiTwinkleDelay=50);
virtual~StarryBackground();
virtualvoidUpdate();
virtualvoidDraw(HDChDC);
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StarryBackground:
:
StarryBackground(intiWidth,intiHeight,intiNumStars,intiTwinkleDelay):
Background(iWidth,iHeight,0){
//Initializethemembervariables
m_iNumStars=min(iNumStars,100);
m_iTwinkleDelay=iTwinkleDelay;
//Createthestars
for(inti=0;im_ptStars[i].x=rand()%iWidth;
m_ptStars[i].y=rand()%iHeight;
//将各个星星的颜色设置为一个中性颜色,即不太亮也不太暗的灰色
m_crStarColors[i]=RGB(128,128,128);
}
}
StarryBackground:
:
~StarryBackground(){}
//-----------------------------------------------------------------
//StarryBackgroundGeneralMethods
//-----------------------------------------------------------------
voidStarryBackground:
:
Update(){
//随机更改星星的灰度,使之闪烁
intiRGB;
for(inti=0;iif((rand()%m_iTwinkleDelay)==0){
iRGB=rand()%256;
//只要R,G,B3个部分是相同的,星星就是灰色的
//例如RGB(128,128,128)是一个中度灰色。
RGB(255,255,255)是一个白色。
RGB(0,0,0)是一个黑色。
m_crStarColors[i]=RGB(iRGB,iRGB,iRGB);
}
}
voidStarryBackground:
:
Draw(HDChDC){
//绘制纯黑色背景
RECTrect={0,0,m_iWidth,m_iHeight};
HBRUSHhBrush=CreateSolidBrush(RGB(0,0,0));
FillRect(hDC,&rect,hBrush);
DeleteObject(hBrush);
//Drawthestars
for(inti=0;i}
举例:
Roids
#defineWINDOW_WIDTH500
#defineWINDOW_HEIGHT400
//GLOBALS////////////////////////////////////////////////
Bitmap*g_pAsteroidBitmap;//行星精灵包含14个动画帧,用来模拟在太空中翻滚的行星
StarryBackground*g_pBackground;//游戏背景
//FUNCTIONS//////////////////////////////////////////////
voidGameStart(HDChDC){
//生成随机生成器种子
srand(GetTickCount());
//创建屏幕外设备环境和位图
g_hOffscreenDC=CreateCompatibleDC(hDC);
g_hOffscreenBitmap=CreateCompatibleBitmap(hDC,WINDOW_WIDTH,WINDOW_HEIGHT);
SelectObject(g_hOffscreenDC,g_hOffscreenBitmap);
//创建并加载行星位图
g_pAsteroidBitmap=newBitmap(hDC,"Res/Asteroid.bmp");
//创建布满星星的背景
g_pBackground=newStarryBackground(WINDOW_WIDTH,WINDOW_HEIGHT);
//Createtheasteroidsprites
RECTrcBounds={0,0,500,400};
Sprite*pSprite;
pSprite=newSprite(g_pAsteroidBitmap,rcBounds,BA_WRAP);
pSprite->SetNumFrames(14);
pSprite->SetFrameDelay
(1);
pSprite->SetPosition(250,200);
pSprite->SetVelocity(-3,1);
AddSprite(pSprite);
pSprite=newSprite(g_pAsteroidBitmap,rcBounds,BA_WRAP);
pSprite->SetNumFrames(14);
pSprite->SetFrameDelay
(2);
pSprite->SetPosition(250,200);
pSprite->SetVelocity(3,-2);
AddSprite(pSp