经过几篇文章的介绍,我们的引擎基本上已经开始慢慢成形了,上一节我们虽然我们实现了各种组件,但是这些组件和节点(Node)都处于静止状态,很显然既然是游戏,就不可能没有动画,那么这一片文章我们就将给大家介绍引擎的动画系统,这里我们将动画系统分为两个大类,其一是一些基本的动画,包括位置的移动,缩放、旋转,透明度变化等,其二是帧动画,即由许多帧构成的动画,在实现这些动画之前,我们都需要对一些基础类进行封装,以方便我们对各种动画的实现。动作(Action)这里我们说的动作类便是所有动画的基类,该类封装一个特定行为,在运行时赋予动作一个目标,于是这个目标将会执行该动作。同时需要注意只有节点(Node)的子类才能执行一个动作。有些动作只有特定的节点类型才能运行,但是大部分动作都不限制节点具体类型。由于是所有动画的基类,所以我们只需要实现动画的路框架,包括动画结束的回调函数等,具体的动画效果实现将在其子类来实现,如代码清单5-1所示。代码清单5-1:Action实现viewplaincopytoclipboardprint?1.publicabstractclassActionimplementsYFSCopyable{2.//非法的标记,为-13.publicstaticfinalintINVALID_TAG=-1;4.//源目标5.privateNodemOriginalTarget;6.//目标节点7.publicNodemTarget;8.//动画标记9.privateintmTag;10.//回调11.privateCallbackmCallback;12.//得到源节点13.publicNodegetOriginalTarget(){14.returnthis.mOriginalTarget;15.}16.//设置源节点17.publicvoidsetOriginalTarget(Nodevalue){18.this.mOriginalTarget=value;19.}20.//得到目标21.publicNodegetTarget(){22.returnthis.mTarget;23.}24.//设置目标25.publicvoidsetTarget(Nodevalue){26.this.mTarget=value;27.}28.//设置回调29.publicvoidsetCallback(Callbackcallback){30.this.mCallback=callback;31.}32.//得到回调33.publicCallbackgetCallback(){34.returnthis.mCallback;35.}36.//得到,设置tag37.publicintgetTag(){38.returnthis.mTag;39.}40.publicvoidsetTag(intvalue){41.this.mTag=value;42.}43.//构建动作44.protectedAction(){45.this.mTarget=(this.mOriginalTarget=null);46.this.mTag=INVALID_TAG;47.}48.//拷贝动作49.publicabstractActioncopy();50.//生成反响动作51.publicabstractActionreverse();52.//开始在指定节点上执行动作53.publicvoidstart(Nodetarger){54.this.mOriginalTarget=(this.mTarget=targer);55.}56.//停止57.publicvoidstop(){58.this.mTarget=null;59.}60.//是否完成61.publicbooleanisDone(){62.returntrue;63.}64.//单步执行65.publicabstractvoidstep(floatparamFloat);66.//更新67.publicabstractvoidupdate(floatparamFloat);68.//回调接口,动作完成之后进入69.publicstaticabstractinterfaceCallback{70.publicabstractvoidonDone(ActionparamAction);71.}72.}从代码中我们可以看到,我们构建了两个Node对象,mOriginalTarget和mTarget分别是源目标和当前的目标,源目标主要用于处理特定的动画,比如:屏幕的翻转效果等,当前目标表示当前动画在其目标上执行,同时我们还为每个动画都分配了一个标记(mTag),这样方便管理,这里特别需要说明的是我们的回调接口Callback,用于处理动画执行完毕之后触发其接口中的onDone函数,同时还提供了生成反向动画的函数reverse,当然这并不是每个动画都能生成反向动画,需要在子类进行实现,后面我们会有实例介绍。动作管理器(ActionManager)动作管理器则是用来管理整个引擎中的动画,我们知道由于一个节点可以包含很多个动画,因此我们就构建了一个HashElement来表示一个节点的动画情况,HashElement中包括了一个目标节点和该节点的动画序列以及是否暂停,线面我们看ActionManager的具体实现,如代码清单5-2所示。代码清单5-2:ActionManager实现viewplaincopytoclipboardprint?1.publicclassActionManager{2.//所有的动作列表,包括每个节点的动画3.privateConcurrentHashMapNode,HashElementtargets;4.privatestaticActionManagersInstance;5.static{6.sInstance=null;7.}8.//取得本类实例对象9.publicstaticActionManagergetInstance(){10.synchronized(ActionManager.class){11.if(sInstance==null){12.sInstance=newActionManager();13.}14.returnsInstance;15.}16.}17.//构建动作管理器对象18.privateActionManager(){19.synchronized(ActionManager.class){20.//构建一个计时器21.//指定更新函数为tick22.Scheduler.getInstance().schedule(newScheduler.Timer(this,tick));23.this.targets=newConcurrentHashMapNode,HashElement(131);24.}25.}26.//清楚动作27.privatevoiddeleteHashElement(HashElementelement){28.element.actions.clear();29.this.targets.remove(element.target);30.}31.//动作列表32.privatevoidactionAlloc(HashElementelement){33.if(element.actions==null)34.element.actions=newCopyOnWriteArrayListAction();35.}36.//移除指定动作37.privatevoidremoveAction(intindex,HashElementelement){38.element.actions.remove(index);39.40.if(element.actions.size()==0)41.deleteHashElement(element);42.}43.//暂停所有动作44.publicvoidpauseAllActions(Nodetarget){45.HashElementelement=(HashElement)this.targets.get(target);46.if(element!=null)47.element.paused=true;48.}49.//重新开始所有动作50.publicvoidresumeAllActions(Nodetarget){51.HashElementelement=(HashElement)this.targets.get(target);52.if(element!=null)53.element.paused=false;54.}55.//添加动作56.publicvoidaddAction(Actionaction,Nodetarget,booleanpaused){57.assert(action!=null):Argumentactionmustbenon-null;58.assert(target!=null):Argumenttargetmustbenon-null;59.60.HashElementelement=(HashElement)this.targets.get(target);61.if(element==null){62.element=newHashElement(target,paused);63.this.targets.put(target,element);64.}65.66.actionAlloc(element);67.68.assert(!element.actions.contains(action)):runAction:Actionalreadyrunning;69.70.element.actions.add(action);71.//开始动作72.action.start(target);73.}74.//移除所有动作75.publicvoidremoveAllActions(){76.for(HashElementelement:this.targets.values())77.removeAllActions(element.target);78.}79.//移除指定节点的所有动作80.publicvoidremoveAllActions(Nodetarget){81.if(target==null){82.return;83.}84.//先找到指定节点85.HashElementelement=(HashElement)this.targets.get(target);86.if(element!=null){87.element.actions.clear();88.deleteHashElement(element);89.}90.}91.//移除指定动作92.publicvoidremoveAction(Actionaction){93.//先找到动作的源目标94.HashElementelement=(HashElement)this.targets.get(action95..getOriginalTarget());96.if(element!=null){97.inti=element.actions.indexOf(action);98.if(i!=Action.INVALID_TAG)99.removeAction(i,element);100.}101.}102.//移除指定103.publicvoidremoveAction(inttag,Nodetarget){104.assert(tag!=Action.INVALID_TAG):Invalidtag;105.HashElementelement=(HashElement)this.targets.get(target)