SkeletonAnimation
继承于SkeletonRenderer
播放动画
cpp
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的时间线。
cpp
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时间。
排队播放
cpp
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);
else
delay = 0;
}
}
entry->delay = delay;
return entry;
}
spAnimationState_addAnimation安排该动画在此track当前动画或最后排队的动画后播放。如果此track空了,则等于调用setAnimation
返回 spTrackEntry对象。
空动画
如果清空了一个track,动画将停止应用。要混入或混出一个动画,可指定一个空动画,即没有时间轴的动画。空动画作为一个占位符,可设置混合时间。
cpp
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;
}
要从装配姿势混入一个动画,可设置一个空动画,然后设置混合时间:
cpp
state.setEmptyAnimation(track, 0);
TrackEntry entry = state.addAnimation(track, "run", true, 0);
entry.mixDuration = 1.5;
要混出一个动画到装配姿势,可设置或排队一个指定了混合时间的空动画:
cpp
state.setAnimation(track, "run", true, 0);
state.addEmptyAnimation(track, 1.5, 0);
当动画到达TrackEntry trackEnd时间时,该动画设置的关键帧属性将设置装配姿势,并清空track。可根据需要使用setEmptyAnimation或addEmptyAnimation将骨架混回到装配姿势,而非让其即刻发生。
spTrackEntry
lua本身没有导出
设置或排队动画的方法返回一个TrackEntry,可用于进一步自定义播放。
cpp
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 __cplusplus
spTrackEntry() :
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对象,表示监听事件。
cpp
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;
}