bada 2D游戏编程之十——关键帧动画原理
前面提到的逐帧动画有一个关键的缺点就是需要为动画中的每一帧都提供一张单独的图片,由于每一帧的图片都需要单独提供,制作起来比较麻烦,图片量也比较大。用关键帧动画可以很好的解决这个问题,下面就给大家进行讲解bada平台的关键帧动画。
1, 什么是关键帧动画
关键帧动画的原理是只需要提供一张图片(或者对象),然后对这张图片(或者对象)进行处理产生出不同的效果,例如进行放缩处理产生出大小不同的效果,还有进行透明度处理产生出透明度不同的效果等,连续播放处理出来的效果就形成了动画。
往往这种处理的效果有限,只能进行一些常规的处理,如改变大小、透明度、位置、角度等,所以关键帧动画不适合表现复杂的变形,例如在逐帧动画中提到的描述一株向日葵从苗芽状态成长到绽放花朵的过程动画就不能用关键帧动画来完成,因为这个动画中的效果不是仅仅通过变形就能实现的,而是需要对图片中内容的改变,这种情况下就只有采用逐帧动画了。
下面给大家描述如何用一张图片来实现一个动画。
这是提供的一张金鱼的图片,
然后用关键帧动画来实现这条金鱼从小长到大的变化过程,
这个动画就是对提供的图片进行大小的改变来实现的,不需要对每个大小的图片都提供一张图片,中间图片的产生交给系统去处理,节省了很多的工作量。
2, 相关概念
关键帧动画是对图片(或者对象)的属性值进改变,一般可以改变的属性为大小、透明度、位置坐标和旋转角度等,这样来理解的话,关键帧动画也可以称为属性动画。下面要介绍的这些概念都是和属性值相关的:
开始值:起始帧中的的属性值;
结束值:结束帧中的的属性值;
关键值:在起始帧和结束帧之间产生的属性值,这些值都是按照一定的方式计算出来的;
持续时间:动画持续的时间;
关键帧:开始值和结束值表示的帧都称为关键帧;
过渡帧:关键值表示的帧称为过渡帧;
插值器:可以理解为关键值产生的计算方式;
3, 相关类
在关键帧动画中最重要的是如何得到关键值,因为关键值得依靠各种复杂的计算来得到。bada在这方面提供了为开发者提供了很大的帮助,来帮助开发者很方便的获取关键值。
3.1 属性动画
下面的这些属性动画都是用于模拟从一个属性值变化到另一个属性值的变化过程的,都提供了 ()或者其它函数来根据时间获取属性值。这样将得到的属性值作为帧属性进行播放就可以形成动画了。
类 | 功能说明 |
IntegerAnimation | 用于计算整数型的属性值,如用于计算旋转角度属性值 |
FloatAnimation | 用于计算浮点型的属性值,如用于计算旋转角度属性值。可以得到浮点值,更加精确 |
PointAnimation | 用于计算位置属性 |
DimensionAnimation | 用于计算大小属性 |
RectangleAnimation | 用于同时计算位置和大小属性 |
RotateAnimation | 用于计算旋转角度属性 |
这些类都可以通过传入开始值、结束值、持续时间和插值器来进行初始化。
3.2 插值器
插值器定义了属性值的计算方式,能够让关键值呈一定的规律进行变化。这种变化最终体现在动画的播放速度上,可以产生匀速播放、加速播放和减速播放等各种效果。
其实改变动画的播放速度有两种方式,第一种是在动画中各帧的属性值保持不变的情况下去改变帧的持续时间,如原有每帧的持续时间是40ms,先进将这值改为20ms,这样播放速度就明显加快了;第二种就是在保持原有动画中各帧持续时间不变的情况下去改变各帧的属性值,将属性值变的更大或者更小,这样也能产生加速或者减速的效果。
插值器就可以帮助开发者通过第二种方式来实现各种动画效果。它能够让动画帧的属性值与时间成各种线性关系。
如线型插值器,属性值随时间均匀变化。这样在相同的时间间隔内去取属性值的话,得到的增量都是相同的。所以动画也是表现出匀速播放。
如这种曲线关系,在相同的时间间隔内去取属性值的话,刚开始得到的值之间变化较小,越往后得到的值的变化越来越大,则表现在动画的播放速度是从慢到快的。
下面是bada提供的插值器类型:
类型 | 功能说明 |
ANIMATION_INTERPOLATOR_LINEAR | 属性值按照线性规律变化,动画播放则表现为匀速播放。 |
ANIMATION_INTERPOLATOR_DISCRETE | 属性值在最后一帧时才发生变化,表现在动画上则是前段时间不变,最后突然发送一下变化。可以用于实现那种突隐突现,来回闪烁的效果。 |
ANIMATION_INTERPOLATOR_EASE_IN | 属性值变化先小后大,动画播放则表现为先慢后快。 |
ANIMATION_INTERPOLATOR_EASE_OUT | 属性值变化先大后小,动画播放则表现为先快后慢。 |
ANIMATION_INTERPOLATOR_EASE_IN_OUT | 属性值变化前期是先小后大,后期先大后小,动画播放则是由慢到快,再由快到慢。 |
ANIMATION_INTERPOLATOR_BEZIER | 这个是按照贝塞尔曲线变化,可以实现进行自定义变化。 |
4, 实例
接下来用代码实现前面的金鱼由小变大的例子。
Step 1,准备动画图片
只需要准备一张图片就可以了。图片的尺寸为134×105像素。
Step 2,解析图片
将图片解析成系统支持的位图。
AppResource* pAppRes = Application::GetInstance()->GetAppResource();
__pFishBmp = pAppRes->GetBitmapN("fish.png");
Step 3,创建属性动画
创建尺寸属性动画,用于改变长度属性值。动画的初始值为提供的原始图片的1/3,结束值为原始图片的2倍,动画持续的时间为1秒,采用的是线性插值器。
__pFishAni = new DimensionAnimation(Dimension(134/3,105/3),
Dimension(134*2,105*2),1000,ANIMATION_INTERPOLATOR_LINEAR);
Step 4,获取属性值
结合我们前面的文章用到的模块代码,采用“基于时间的固定间隔游戏循环”,只需要在UpdateLogic(int frameInterval) 函数中,根据时间值去获取属性值就可以了。
__aniDuration = __aniDuration + frameInterval;
if(__aniDuration <= 1000)
{
__pFishAni->GetAnimatedValue(__aniDuration, __aniDimension);
}
else
{
__aniDuration = 0;
}
Step 5,绘制图片形成动画
在Form的OnDraw()函数中根据得到的属性值去进行绘制。
Canvas* pCanvas = this->GetCanvasN();
pCanvas->SetBackgroundColor(Color::COLOR_WHITE);
pCanvas->Clear();
pCanvas->DrawBitmap(Rectangle(100,100,__aniDimension.width,
__aniDimension.height), *__pFishBmp);
delete pCanvas;
这样在游戏状态的更加接口Draw()函数中调用RequestRedraw()去请求更新,然后OnDraw()函数不断的被调用来进行重新绘制就形成了动画。