SkeletonAnimation
继承于SkeletonRenderer
播放动画
spTrackEntry* SkeletonAnimation::setAnimation (int trackIndex, const std::string& name, bool loop) {spAnimation* animation = spSkeletonData_findAnimation(_skeleton->data, name.c_str());if (!animation) {log("Spine: Animation not found: %s", name.c_str());return 0;}return spAnimationState_setAnimation(_state, trackIndex, animation, loop);
}
首先根据name去寻找动画,spAnimation对象主体是spTimeline,也就是Spine的时间线。
spTrackEntry* spAnimationState_setAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop) {spTrackEntry* entry;_spAnimationState* internal = SUB_CAST(_spAnimationState, self);int interrupt = 1;spTrackEntry* current = _spAnimationState_expandToIndex(self, trackIndex);if (current) {if (current->nextTrackLast == -1) {/* Don't mix from an entry that was never applied. */self->tracks[trackIndex] = current->mixingFrom;_spEventQueue_interrupt(internal->queue, current);_spEventQueue_end(internal->queue, current);_spAnimationState_disposeNext(self, current);current = current->mixingFrom;interrupt = 0;} else_spAnimationState_disposeNext(self, current);}entry = _spAnimationState_trackEntry(self, trackIndex, animation, loop, current);_spAnimationState_setCurrent(self, trackIndex, entry, interrupt);_spEventQueue_drain(internal->queue);return entry;
}
AnimationState
按时间顺序应用动画, 队列待播放的后续动画, 在动画间混合(淡入淡出), 以及在各动画之上相互堆叠应用多个动画(动画分层).
这是Spine提供的两种动画之一————动画状态。
在几乎所有情况下, 都建议使用动画状态API而非时间轴API. 与底层的时间轴API相比, 动画状态API会让诸如在某段时间内应用动画、排队动画、mix动画、以及同时应用多个动画等任务变得更加容易. 动画状态API内部使用的也是时间轴API, 因此可以将其看作是时间轴API的封装器.
AnimationState建立在时间轴API上,可处理多数播放需求,向后播放除外。如需反向播放,可直接使用时间轴API,或使用框选缩放来复制和反向播放动画。
通道(Track)
Track可分层应用动画,每个track存储了一个动画和播放参数。Track编号从零累加(track索引在内部是一个数组索引)。在将AnimationState应用到一个骨架后,track动画会从最低的track号开始依序应用。
播放
spAnimationState_setAnimation的本质就是播放track动画,将使用指定的动画取代该track中的当前动画和任何已排队的动画。如果在前一动画和当前动画之间定义了混合动画时间,则当前动画将在混合动画时间上混出,让动画过渡更流畅。
会返回一个spTrackEntry对象,可以多种方式自定义播放。
默认动画将继续应用直至另一动画播放或track清空。要在一个具体时间后停止动画,可设置spTrackEntry trackEnd时间。
排队播放
spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop,float delay) {spTrackEntry* entry;_spAnimationState* internal = SUB_CAST(_spAnimationState, self);spTrackEntry* last = _spAnimationState_expandToIndex(self, trackIndex);if (last) {while (last->next)last = last->next;}entry = _spAnimationState_trackEntry(self, trackIndex, animation, loop, last);if (!last) {_spAnimationState_setCurrent(self, trackIndex, entry, 1);_spEventQueue_drain(internal->queue);} else {last->next = entry;if (delay <= 0) {float duration = last->animationEnd - last->animationStart;if (duration != 0)delay += duration * (1 + (int)(last->trackTime / duration)) - spAnimationStateData_getMix(self->data, last->animation, animation);elsedelay = 0;}}entry->delay = delay;return entry;
}
spAnimationState_addAnimation安排该动画在此track当前动画或最后排队的动画后播放。如果此track空了,则等于调用setAnimation
返回 spTrackEntry对象。
空动画
如果清空了一个track,动画将停止应用。要混入或混出一个动画,可指定一个空动画,即没有时间轴的动画。空动画作为一个占位符,可设置混合时间。
spTrackEntry* spAnimationState_addEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration, float delay) {spTrackEntry* entry;if (delay <= 0) delay -= mixDuration;entry = spAnimationState_addAnimation(self, trackIndex, SP_EMPTY_ANIMATION, 0, delay);entry->mixDuration = mixDuration;entry->trackEnd = mixDuration;return entry;
}
要从装配姿势混入一个动画,可设置一个空动画,然后设置混合时间:
state.setEmptyAnimation(track, 0);
TrackEntry entry = state.addAnimation(track, "run", true, 0);
entry.mixDuration = 1.5;
要混出一个动画到装配姿势,可设置或排队一个指定了混合时间的空动画:
state.setAnimation(track, "run", true, 0);
state.addEmptyAnimation(track, 1.5, 0);
当动画到达TrackEntry trackEnd时间时,该动画设置的关键帧属性将设置装配姿势,并清空track。可根据需要使用setEmptyAnimation或addEmptyAnimation将骨架混回到装配姿势,而非让其即刻发生。
spTrackEntry
lua本身没有导出
设置或排队动画的方法返回一个TrackEntry,可用于进一步自定义播放。
struct spTrackEntry {spAnimation* animation;spTrackEntry* next;spTrackEntry* mixingFrom;spAnimationStateListener listener;int trackIndex;int /*boolean*/ loop;float eventThreshold, attachmentThreshold, drawOrderThreshold;float animationStart, animationEnd, animationLast, nextAnimationLast;float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;spIntArray* timelineData;spTrackEntryArray* timelineDipMix;float* timelinesRotation;int timelinesRotationCount;void* rendererObject;void* userData;#ifdef __cplusplusspTrackEntry() :animation(0),next(0), mixingFrom(0),listener(0),trackIndex(0),loop(0),eventThreshold(0), attachmentThreshold(0), drawOrderThreshold(0),animationStart(0), animationEnd(0), animationLast(0), nextAnimationLast(0),delay(0), trackTime(0), trackLast(0), nextTrackLast(0), trackEnd(0), timeScale(0),alpha(0), mixTime(0), mixDuration(0), interruptAlpha(0), totalAlpha(0),timelineData(0),timelineDipMix(0),timelinesRotation(0),timelinesRotationCount(0) {}
#endif
};
在里面有一个spAnimationStateListener对象,表示监听事件。
void _spEventQueue_drain (_spEventQueue* self) {int i;if (self->drainDisabled) return;self->drainDisabled = 1;for (i = 0; i < self->objectsCount; i += 2) {spEventType type = (spEventType)self->objects[i].type;spTrackEntry* entry = self->objects[i+1].entry;spEvent* event;switch (type) {case SP_ANIMATION_START:case SP_ANIMATION_INTERRUPT:case SP_ANIMATION_COMPLETE:if (entry->listener) entry->listener(SUPER(self->state), type, entry, 0);if (self->state->super.listener) self->state->super.listener(SUPER(self->state), type, entry, 0);break;case SP_ANIMATION_END:if (entry->listener) entry->listener(SUPER(self->state), type, entry, 0);if (self->state->super.listener) self->state->super.listener(SUPER(self->state), type, entry, 0);/* Fall through. */case SP_ANIMATION_DISPOSE:if (entry->listener) entry->listener(SUPER(self->state), SP_ANIMATION_DISPOSE, entry, 0);if (self->state->super.listener) self->state->super.listener(SUPER(self->state), SP_ANIMATION_DISPOSE, entry, 0);_spAnimationState_disposeTrackEntry(entry);break;case SP_ANIMATION_EVENT:event = self->objects[i+2].event;if (entry->listener) entry->listener(SUPER(self->state), type, entry, event);if (self->state->super.listener) self->state->super.listener(SUPER(self->state), type, entry, event);i++;break;}}_spEventQueue_clear(self);self->drainDisabled = 0;
}