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,如此便可實現倒序播放。注意這樣寫的話比較麻煩,應該根據實際的項目進行進一步的封裝,但核心代碼保持這樣就好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章