Andriod游戏开发 第七章 精灵帧动画与碰撞检测Word文档下载推荐.docx
《Andriod游戏开发 第七章 精灵帧动画与碰撞检测Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Andriod游戏开发 第七章 精灵帧动画与碰撞检测Word文档下载推荐.docx(13页珍藏版)》请在冰豆网上搜索。
以我们的目标游戏《坦克大战》为例,在游戏中有这样一些图形元素:
我方和敌方的坦克、坦克发出的子弹、地面、墙体、水域掩体等。
这些元素虽然外观不同,但是本质上却非常相似:
都是在特定位置以特定尺寸显示一个或一组位图,有些位图位置还会变动,Layer就定义了位置,尺寸,显示等相关的功能。
之所以叫做Layer,与游戏中分层地图的概念有关,先让我们了解一下什么是分层地图:
还是说坦克大战,当我们的坦克行驶在普通地面上时,坦克的图像肯定是覆盖了地面的图像,这样我们能看到坦克。
当坦克行驶到掩体时,我们会发现,掩体的图像覆盖了坦克的图像,如图所示:
实际上,我们在程序中,只要首先显示地面的图像,然后显示坦克的图像,最后显示掩体的图像(掩体图片是镂空的),就能达到这种效果,这就是分层地图。
通常我们把最下面的叫做地面层,中间的叫做物件层,最上面的叫做天空层。
关于地图我们就讲这么多,这里只介绍图形意义上的分层,是为了帮助大家理解Layer一词的意义。
关于地图的详细内容我们在第十章会深入讲解。
首先让我们看一下抽象类Layer的定义:
packagemon;
importandroid.graphics.Canvas;
publicabstractclassLayer{
intx=0;
//Layer的横坐标
inty=0;
//Layer的纵坐标
intwidth=0;
//Layer的宽度
intheight=0;
//Layer的高度
booleanvisible=true;
//Layer是否可见
Layer(intwidth,intheight){
setWidthImpl(width);
setHeightImpl(height);
}
/**
*设定Layer的显示位置
*
*@paramx
*横坐标
*@paramy
*纵坐标
*/
publicvoidsetPosition(intx,inty){
this.x=x;
this.y=y;
*相对于当前的位置移动Layer
*@paramdx
*横坐标变化量
*@paramdy
*纵坐标变化量
publicvoidmove(intdx,intdy){
x+=dx;
y+=dy;
*取得Layer的横坐标
*@return横坐标值
publicfinalintgetX(){
returnx;
*取得Layer的纵坐标
*@return纵坐标值
publicfinalintgetY(){
returny;
*取得Layer的宽度
*@return宽度值
publicfinalintgetWidth(){
returnwidth;
*取得Layer的高度
*@return高度值
publicfinalintgetHeight(){
returnheight;
*设置Layer是否可见
*@paramvisible
*trueLayer可见,falseLayer不可见
publicvoidsetVisible(booleanvisible){
this.visible=visible;
*检测Layer是否可见
*@returntrueLayer可见,falseLayer不可见
publicfinalbooleanisVisible(){
returnvisible;
*绘制Layer,必须被重载
*@paramc
publicabstractvoidpaint(Canvasc);
*设置Layer的宽度
*@paramwidth
voidsetWidthImpl(intwidth){
if(width<
0){
thrownewIllegalArgumentException();
this.width=width;
*设置Layer的高度
*@paramheight
voidsetHeightImpl(intheight){
if(height<
this.height=height;
Layer的代码不多,根据函数名称就可以知道它的功能,主要是Layer尺寸、位置的设定和获取。
其中最重要的方法paint是虚方法,Layer图像就是通过这个方法显示出来的。
因此继承自Layer的所有类都要实现这个方法。
Sprite(精灵)继承自Layer,同时又增加了帧动画,图形变换和碰撞检测的功能。
Sprite是我们这一章重点介绍的内容。
首先让我们了解一下精灵的概念。
Sprite这个词在2D游戏中非常常见,一般指游戏中具有独立外观和属性的个体元素。
如主角、NPC、宝箱、子弹等等,这些都是精灵。
下面就让我们来创建Sprite类并使其继承自Layer。
创建完毕时,IDE会提示必须实现paint方法。
但是这时候我们会发现,paint方法要显示那些图形呢?
没有。
因此我们需要为Sprite增加一个Bitmap类型变量,用来存放paint要显示的图形。
同时,我们要创建一个构造函数用来初始化这个Bitmap变量。
publicSprite(Bitmapimage){
super(image.getWidth(),image.getHeight());
initializeFrames(image,image.getWidth(),image.getHeight(),false);
initCollisionRectBounds();
this.setTransformImpl(TRANS_NONE);
虽然这个构造函数只有几行,却涉及到不少的知识。
super不用说了,initializeFrames是做什么的呢?
这就要提到帧动画的概念了。
什么是帧动画呢?
如下图,我们看到一组星星的图片(4张16x16的位图)
当我们在同一个位置以一定的时间间隔连续显示这几幅图片的时候就变成了这个样子
我们看到,星星在发光,这就是帧动画。
即取得一个连续画面中的几个关键帧,在一定的时间间隔下连续的显示这些帧从而形成动画,initializeFrames的功能就是初始化这些关键帧。
那么又为什么要初始化呢?
通常情况下,我们为了节省空间也为了便于管理,会将一组动画的多个帧保存在同一个图片文件中(如上图的星星)。
这样一来每次显示的时候就不能显示整张图片,而只能显示这个图片的一部分。
因此,我们要计算每一帧在整张图片上的位置以便正确显示。
就让我们来看看initializeFrames的定义
privatevoidinitializeFrames(Bitmapimage,intfWidth,intfHeight,
booleanmaintainCurFrame)
initializeFrames作了这样的工作,首先取得一个位图,然后根据用户设置的单一帧的宽度和高度计算这个位图中包括多少帧。
如刚刚我们看到的星星的图片(64x16像素),当单一帧的宽度和高度与图片相同的时候,就只有一帧。
但是当一帧的宽度和高度均为16像素时,整个图片就可以分为4帧了。
这时候,函数会计算每一帧的顶点坐标,如第一帧的顶点是(0,0),第二帧的顶点是(16,0),并依次类推。
这个函数还可以处理更复杂的情况,如下图:
函数会将计算好的各个帧的顶点横纵坐标分别保存在两个数组中(frameCoordsX[],frameCoordsY[]),下次我们使用帧的序号访问各个帧时(在paint中)就可以很快找到它的所对应的位图区域了。
在这个Sprite的构造函数中,initializeFrames使用了image.getWidth()和image.getHeight()作为一帧的高度和宽度,所以这个精灵注定只能有一帧。
Sprite的构造函数还有其他样式,如
publicSprite(Bitmapimage,intframeWidth,intframeHeight)
这时候我们就可以指定帧的宽度和高度,定义具有多个帧的Sprite了。
说完了为帧动画做初始化工作的initializeFrames,让我们来看下一个函数initCollisionRectBounds。
privatevoidinitCollisionRectBounds(){
collisionRectX=0;
collisionRectY=0;
collisionRectWidth=this.width;
collisionRectHeight=this.height;
这个函数的代码不多,名字翻译过来就是“初始化碰撞矩形边缘”。
由此引出另外一个游戏中的重要概念——碰撞检测。
在我们的目标游戏坦克大战中,碰撞检测可是少不了的,我们的子弹击中敌方坦克就是一次碰撞,只有进行了碰撞检测才能够触发这次击中事件,不然我们就没法消灭敌人的坦克了。
2D游戏中的碰撞检测有几种,最简单的是矩形碰撞检测,复杂一些的有多边形检测和像素检测等。
这里我们只介绍一下矩形检测。
如图:
我们取坦克和子弹的矩形外框,当这两个矩形重叠的时候就认为是碰撞了。
函数initCollisionRectBounds的功能就是设定Sprite的矩形外框。
同前面初始化帧的原理一样,我们需要这个函数在多个帧组合成的图片中确定一帧的大小。
现在我们还剩下构造函数中最后一行代码了:
这行代码设置了Sprite图形的变换方式。
变换一共有8种,定义如下:
publicstaticfinalintTRANS_NONE=0;
publicstaticfinalintTRANS_ROT90=5;
publicstaticfinalin