CEGUI的动画系统实现了以下的功能:
1.通过属性系统驱动,即在每一帧通过将某种属性设为不同的值,实现该属性的动画;
2.驱动方式分为直接设值,在原有值上加一个值,和在原有值上乘一个值;
3.动画曲线通过关键帧控制,关键帧包含时间点和值,值可以是直接设置的,也可以是动画开始时,从驱动目标上取的属性值,该属性可以与被驱动的属性不同;
4.由一帧到另一帧的移动方式分为匀速、加速、减速和离散;
5.当时间点在两帧之间时,进行线性插值
6.动画可以设置播放一次的时长;
7.播放方式分为一次性播放、重复播放、往返重复播放;
8.动画能对某个外部对象发送事件,例如当动画开始时,能向它发送动画开始事件;
9.动画能监听某个外部对象,当它的某个事件发生时,能执行某个动作,例如监听一个按钮,当按钮按下时,动画启动;
10.动画能够执行的动作分为启动、结束、暂停、解除暂停和切换暂停。
动画系统的类图如下:
我觉得这个系统最大的特点是把动画的定义和实例进行了分离,动画的定义对应类图中的AniDef,动画的实例对应类图中的AniInstance。一个AniDef可以对应多个AniInstance,但一个AniInstance只能对应一个AniDef。
下面介绍每个类的核心字段和方法:
1.KeyFrame——对应需求中的关键帧
-position:关键帧所在的时间点;
-value:如果关键帧的值不是动画开始时从驱动目标上取的属性值,则存于该字段;
-sourceProperty:如果关键帧的值是动画开始时从驱动目标上取的属性值,则该属性的名字存于该字段;
-progression:由上一帧到这一帧的移动方式,有匀速、加速、减速和离散四种(具体解释见该类方法alterInterpolationPosition());
-savePropertyValue():从驱动目标上取属性值,并保存到相应的AniInstance对象中。这个方法是一个调用链,从AniInstance开始,经过AniDef、Affector,最后到KeyFrame;
-getValueForAnimation():返回该关键帧里面的值,如果不是动画开始时从驱动目标上取属性值,则返回value,否则返回取的值;
-alterInterpolationPostion():在动画更新流程中,会将当前时间点映射到[0,1]区间(0表示左关键帧,1表示右关键帧),将该映射值传人右关键帧的该方法,获得处理后的映射值。处理方法为:匀速直接返回,加速进行平方,减速进行开方,离散为输入值为1时才返回1其余时候返回0;
2.Interpolator——线性插值器,它是一个接口,具体的插值工作由它的实现类完成。插值参数都是以字符串的形式传人,在实现类的方法中转换为对应类型的数值,然后插值,插值结果还要经过一道处理才返回,处理的方式根据驱动方式而定:
-interpolateAbsolute():对应直接设值方式,直接返回插值结果;
-interpolateRelative():对应在原有值上加一个值的方式,原有值也是以参数的形式传人,加上插值结果后返回;
-interpolateMultiply():对应在原有值上乘一个值的方式,原有值也是以参数的形式传人,乘上插值结果后返回;
将驱动方式的不同处理放在Interpolator里面是因为,如果直接返回插值结果,那么因为它是以字符串的形式返回的,要和原有值进行加或者乘,就又要转换一次,这个一是效率会低一些,二是逻辑分散了,在Interpolator里面做是知道应该转换成那种类型的值的,如果在外面做就需要根据目标属性进转换,这会形成一个巨大的switch,以后加入一种新的属性值类型时,还要在这个switch里面加代码,非常不好。
3.Affector——一个动画定义可以包含多个属性驱动,而单一的属性驱动就封装在Affector这一层
-keyFrameList:关键帧的序列;
-applicationMethod:驱动方式,即直接设值,在原有值上加一个值,和在原有值上乘一个值;
-targetProperty:被驱动的属性的名字;
-interpolator:插值器;
-savePropertyValues():从驱动目标上取属性值,并保存到相应的AniInstance对象中,除了保存关键帧需要的值以为,如果驱动方式是在原有值上加一个值或在原有值上乘一个值,则还保存了被驱动的属性的原有值;
-apply():根据动画实例的当前位置,设置被驱动属性的值。apply()方法是一个调用链,由AniInstance类开始,结果AniDef类,最后到Affector类。
4.AniDef——动画定义
-affectorList:Affector对象的集合;
-replayMode:播放方式,分为一次性播放、重复播放、往返重复播放;
-duration:播放一次的时长;
-autoSubscriptions:事件和处理函数映射表,注册监听对象时会用到;
-savePropertyValues():从驱动目标上取属性值,并保存到相应的AniInstance对象中;
-apply():根据动画实例的当前位置,设置被驱动属性的值。
5.AniInstance——动画实例
-postion:当前时间点;
-savedPropertyValueMap:动画开始时,从驱动目标上取属性值保存在该字段;
-aniDef:对应的动画定义;
-target:驱动目标;
-eventReceiver:发射事件的目标,例如动画开始时,向它发送动画开始事件;
-eventSender:监听对象,例如它是一个按钮,当它被按下时,动画启动;
-step():更新当前时间点;
-apply():根据当前位置,设置被驱动属性的值。
-savePropertyValues():从驱动目标上取属性值,并保存到savedPropertyValueMap中;
-getSavedPropertyValue():获取保存的属性值;
-handleXXX():事件响应函数,共有响应启动、结束、暂停、解除暂停和切换暂停五种事件的函数;
-XXX():启动、结束、暂停、解除暂停和切换暂停的实际执行函数;
6.PropertySet——属性集合体,即被驱动的对象
7.EventSet——事件集合体,即监听的对象和发送事件的目标
8.AniManager——动画管理器
-aniDefList:动画定义列表;
-aniDefAndInstanceMap:动画定义和实例映射表;
-interpolatorList:插值器列表;
-stepInstances():驱动所有动画实例。
动画系统的时序图如下:
初始化
事件处理
驱动流程