libgdx -spine动画实现倒序播放

最近用libgdx进行游戏开发,开发过程中遇上一个让spine倒序播放的需求,但之前并没有接触过这个功能,遇上搬出程序员神器->百度一下,你就知道。然鹅悲催的是逛了3小时,并没有实际的进展,虽然有部分人也在问同样的问题,但回答的可用性几乎为零。于是,这篇文章就出来了。
为了实现这个功能,我们首先进入到源码中去,在spine-libgdx的源码中,在AnimationState中,有一个内部类TrackEntry,里面定义了么一个属性,且看:

        /** Current time in seconds this track entry has been the current track entry. The track time determines
         * {@link #getAnimationTime()}. The track time can be set to start the animation at a time other than 0, without affecting
         * looping. */
        public float getTrackTime () {
            return trackTime;
        }

        public void setTrackTime (float trackTime) {
            this.trackTime = trackTime;
        }

从注释中我们可以发现这个trackTime就是当前通道实体的起始位置。就好像帧动画中的起始帧一般,而这里的实体,实际上是和这两个东西匹配的:

...setAnimation(trackindex,animationanme,loop)方法源码
    /** Sets the current animation for a track, discarding any queued animations.
     * @param loop If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its
     *           duration. In either case {@link TrackEntry#getTrackEnd()} determines when the track is cleared.
     * @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
     *         after the {@link AnimationStateListener#dispose(TrackEntry)} event occurs. */
    public TrackEntry setAnimation (int trackIndex, Animation animation, boolean loop) {
        if (animation == null) throw new IllegalArgumentException("animation cannot be null.");
        boolean interrupt = true;
        TrackEntry current = expandToIndex(trackIndex);
        if (current != null) {
            if (current.nextTrackLast == -1) {
                // Don't mix from an entry that was never applied.
                tracks.set(trackIndex, current.mixingFrom);
                queue.interrupt(current);
                queue.end(current);
                disposeNext(current);
                current = current.mixingFrom;
                interrupt = false; // mixingFrom is current again, but don't interrupt it twice.
            } else
                disposeNext(current);
        }
        TrackEntry entry = trackEntry(trackIndex, animation, loop, current);
        setCurrent(trackIndex, entry, interrupt);
        queue.drain();
        return entry;
    }
.....addAnimation(trackindex,animationanme,loop)方法源码
public TrackEntry addAnimation (int trackIndex, Animation animation, boolean loop, float delay) {
        if (animation == null) throw new IllegalArgumentException("animation cannot be null.");

        TrackEntry last = expandToIndex(trackIndex);
        if (last != null) {
            while (last.next != null)
                last = last.next;
        }

        TrackEntry entry = trackEntry(trackIndex, animation, loop, last);

        if (last == null) {
            setCurrent(trackIndex, entry, true);
            queue.drain();
        } else {
            last.next = entry;
            if (delay <= 0) {
                float duration = last.animationEnd - last.animationStart;
                if (duration != 0)
                    delay += duration * (1 + (int)(last.trackTime / duration)) - data.getMix(last.animation, animation);
                else
                    delay = 0;
            }
        }

        entry.delay = delay;
        return entry;
    }

是的,我们的通道实体实际上在我们设置或者增加某个动画的时候就会创建出来,所谓通道,是保证各动画平稳过度的层级关系,这里我一般使用0。话说道这里,我们继续往下走一波:看看setAnimation和AddAnimation是怎么创建这个trackentry的:

/** @param last May be null. */
    private TrackEntry trackEntry (int trackIndex, Animation animation, boolean loop, TrackEntry last) {
        TrackEntry entry = trackEntryPool.obtain();
        entry.trackIndex = trackIndex;
        entry.animation = animation;
        entry.loop = loop;

        entry.eventThreshold = 0;
        entry.attachmentThreshold = 0;
        entry.drawOrderThreshold = 0;

        entry.animationStart = 0;
        entry.animationEnd = animation.getDuration();
        entry.animationLast = -1;
        entry.nextAnimationLast = -1;

        entry.delay = 0;
        entry.trackTime = 0;
        entry.trackLast = -1;
        entry.nextTrackLast = -1;
        entry.trackEnd = Float.MAX_VALUE;
        entry.timeScale = 1;

        entry.alpha = 1;
        entry.mixAlpha = 1;
        entry.mixTime = 0;
        entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
        return entry;
    }

在这里,我们发现,我们上面说的,trackTime默认为0,与之对应的另一个参数timescale,则为1.这两者的意思为:
从trackTime开始,下一次播放的时间轴点为trackTime+timescale。则根据这个以下是解决办法:

1.animationState.getCurrent(trackIndex).setTrackTime(animationState.getCurrent(trackIndex).getAnimation().getDuration());
2.animationState.getCurrent(trackIndex).setTimeScale(-1);

以上两行的意思为:设置trackTime的初始节点为整个动画的全部,即时间轴末端,然后设置setTimeScale的值为-1,如此便可实现倒序播放。注意这样写的话比较麻烦,应该根据实际的项目进行进一步的封装,但核心代码保持这样就好。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页