• Unity
  • unity timeline custom spine track test

Related Discussions
...

Multi Track Blending(Support Preview),
Clip Blending,
Start Time Offset(Clip In),
Speed Multiplier,
Select AnimationName from drop down,
Reverse

Looks great, congrats !

Do you plan to share by any chance ?

This looks very nice!

Which version of Unity did you implement this for?
Would you be interested in contributing your code to Spine?

Harald a écrit

This looks very nice!

Which version of Unity did you implement this for?
Would you be interested in contributing your code to Spine?

Unity 2018.3.
This is experimental code. I need more test.
It has some issues and not optimized yet.

public class CustomSpineAnimationMixerBehaviour : PlayableBehaviour
{
    private const float FIRST_CLIP_BLEND_DURATION = 0.2f;
    private const float FINAL_CLIP_BLEND_DURATION = 0.2f;

private SkeletonAnimation _trackBindingSkeletonAnimation;
private Skeleton _trackBindingSkeleton;
private TrackEntry _oldTrack;
private bool _firstFrameHappend;

public override void PrepareFrame(Playable playable, FrameData info)
{
    if (_trackBindingSkeletonAnimation == null) return;
    if (_trackBindingSkeleton == null) _trackBindingSkeleton = _trackBindingSkeletonAnimation.Skeleton;

    int inputCount = playable.GetInputCount();
    for (int i = 0; i < inputCount; i++)
    {
        ScriptPlayable<CustomSpineAnimationBehaviour> inputPlayable = (ScriptPlayable<CustomSpineAnimationBehaviour>)playable.GetInput(i);
        CustomSpineAnimationBehaviour clipBehaviourData = inputPlayable.GetBehaviour();
        clipBehaviourData.Init(_trackBindingSkeleton.Data);
        if (clipBehaviourData.animation == null) continue;

        //set key
        clipBehaviourData.animation.SetKeyedItemsToSetupPose(_trackBindingSkeleton);
    }
}

public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
    _trackBindingSkeletonAnimation = playerData as SkeletonAnimation;
    if (_trackBindingSkeletonAnimation == null) return;
    if (_trackBindingSkeleton == null) _trackBindingSkeleton = _trackBindingSkeletonAnimation.Skeleton;

    if (_firstFrameHappend == false)
    {
        _oldTrack = _trackBindingSkeletonAnimation.AnimationState?.GetCurrent(0);
        _firstFrameHappend = true;
    }

    if (_oldTrack != null)
    {
        var playableTime = (float)playable.GetTime();
        var playableDuration = (float)playable.GetGraph().GetRootPlayable(0).GetDuration();
        var firstClipBlend = Mathf.Max(0, 1 - playableTime / FIRST_CLIP_BLEND_DURATION);
        var finalClipBlend = Mathf.Max(0, 1 - (playableDuration - playableTime) / FINAL_CLIP_BLEND_DURATION);
        _oldTrack.Alpha = firstClipBlend + finalClipBlend;
    }

    int inputCount = playable.GetInputCount();
    int currentInputs = 0;

    for (int i = 0; i < inputCount; i++)
    {
        //check weight
        float inputWeight = playable.GetInputWeight(i);
        if (Mathf.Approximately(inputWeight, 0f)) continue;

        //check animation
        ScriptPlayable<CustomSpineAnimationBehaviour> inputPlayable = (ScriptPlayable<CustomSpineAnimationBehaviour>)playable.GetInput(i);
        CustomSpineAnimationBehaviour clipBehaviourData = inputPlayable.GetBehaviour();

        clipBehaviourData.Init(_trackBindingSkeleton.Data);
        if (clipBehaviourData.animation == null) continue;

        //set animation
        float time = clipBehaviourData.reverse ? (float)(inputPlayable.GetDuration() - inputPlayable.GetTime()) : (float)inputPlayable.GetTime();
        MixBlend mixBlend = currentInputs == 0 ? MixBlend.Setup : MixBlend.First;
        MixDirection mixDirection = inputWeight < 1 && currentInputs > 1 ? MixDirection.Out : MixDirection.In;
        clipBehaviourData.animation.Apply(_trackBindingSkeleton, 0f, time, clipBehaviourData.loop, null, inputWeight, mixBlend, mixDirection);

        currentInputs++;
    }

    _trackBindingSkeletonAnimation.Update(0);
    _trackBindingSkeletonAnimation.LateUpdate();
}

public override void OnPlayableDestroy(Playable playable)
{
    if (_trackBindingSkeleton != null) _trackBindingSkeleton.SetToSetupPose();
    if (_trackBindingSkeletonAnimation != null) _trackBindingSkeletonAnimation.Update(0);
}
}

This is part of mixbehaviour class.
Is it possible to contribute my code?
(Sorry for my bad English grammer...)


bali33 a écrit

Looks great, congrats !

Do you plan to share by any chance ?

I hope so 🙂

Thanks very much for sharing the code here!

Since you wrote it for 2018, I fear we will have to come up with something different for Unity 2019 (since the API seems to have changed in this regard, I didn't get to have a more in-depth look at it yet), but still - thanks for the input! I will very soon get to updating the whole timeline features to Unity 2019, so every input is welcome!