Hi, thanks for the answer, I will do some pseudo code here to explain better what I’m trying to achieve, because I don’t even know if it is possible at all with the current API, also I’m not too deep in playables and timeline, I just know the basics (to create tracks, clips, and mixers that use the serialized data of the clips to do stuff for me), but I don’t know the advanced stuff or API’s.
The “Follow Path” custom track consist of the TrackAsset class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
[TrackClipType(typeof(FollowPathAsset))]
[TrackBindingType(typeof(Animator))]
[TrackColor(.21f,.60f,.90f)]
public class FollowPathTrack : TrackAsset
{
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
{
return ScriptPlayable<FollowPathMixerBehaviour>.Create(graph, inputCount);
}
}
then the playableBehaviour (clips)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
[System.Serializable]
public class FollowPathBehaviour : PlayableBehaviour
{
// reference to a spline to use as the path to follow, these splines could be
// custom implementations or maybe taken from some asset to save time, such as Dreamtecks Splines
public SplinePath Path;
// we can let the user trim the path to start and end at whatever value he wants.
[Range(0,1)]
public float PathStart = 0;
[Range(0, 1)]
public float PathEnd = 1;
}
then the playableAsset:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
public class FollowPathAsset : PlayableAsset
{
public FollowPathBehaviour template;
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
var playable = ScriptPlayable<FollowPathBehaviour>.Create(graph, template);
return playable;
}
}
and then the main part, the mixer behaviour:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
public class FollowPathMixerBehaviour : PlayableBehaviour
{
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
// get the animator from the binding to this FollowPath track
Animator trackBinding = playerData as Animator;
Vector3 finalPosition = Vector3.zero;
Quaternion finalRotation = Quaternion.identity;
float finalSpeed = 0;
if (!trackBinding)
return;
int inputCount = playable.GetInputCount(); //get the number of all clips on this track
for (int i = 0; i < inputCount; i++)
{
float inputWeight = playable.GetInputWeight(i);
ScriptPlayable<FollowPathBehaviour> inputPlayable = (ScriptPlayable<FollowPathBehaviour>)playable.GetInput(i);
FollowPathBehaviour input = inputPlayable.GetBehaviour();
// Use the above variables to process each frame of this playable.
// somehow, get the current traversing clip start and end times, so we can interpolate
// and get a 0-1 value to use with the path
TrackClip currentClip = GetCurrentClip(i); // I need to know how to get this
double currentTimelineTime = GetCurrentTimelineTime(); // I need to know how to get this
// only compute if we are inside the clip
if (currentClip.StartTime <= currentTimelineTime && currentTimelineTime <= currentClip.EndTime)
{
float currentClipValue = Mathf.InverseLerp(currentClip.StartTime, currentClip.EndTime, currentTimelineTime);
// we evaluate the spline path at the current position in the clip, given by a o-1 value
EvaluationResult er = input.Path.EvaluateAt(currentClipValue);
// we get the evaluated position, rotation and speed of the moving obj (ignore if speed doesn't make sense, I'm still thinking ways to compute it, it will be useful
// later to blend between idle/walk/run animations.
Vector3 pos = er.position;
Quaternion rot = er.rotation;
float speed = er.speed;
finalPosition += pos * inputWeight;
finalRotation = Quaternion.Slerp(finalRotation, rot, inputWeight);
finalSpeed += speed * inputWeight;
}
}
//now with the results we position the character
//I know this is not how RootMotion works, but I'm not very experienced so I just coded what intuitively I want to get done.
trackBinding.transform.position = finalPosition;
trackBinding.transform.rotation = finalRotation;
// lets do the "blend tree" part
AnimationClip idleClip = GetIdleClip(); // ignore how I get this, I'll figure out later, probably it could be serialized as part of the FollowPathBehaviour (the clip)
AnimationClip walkClip = GetWalkClip(); // ignore how I get this, I'll figure out later, probably it could be serialized as part of the FollowPathBehaviour (the clip)
AnimationClip runClip = GetRunClip(); // ignore how I get this, I'll figure out later, probably it could be serialized as part of the FollowPathBehaviour (the clip)
// here is one of the main things I wanna learn how to do, I know I'm oversimplifying here with this pseudo code, but it's because I have zero idea how to achieve this.
AnimationBlended finalClip = BlendClips(idleClip, walkClip, runClip, finalSpeed);
trackBinding.OverrideCurrentAnimation(finalClip);
}
}
please ignore the fact that I’m using overly simplified methods that magically do what I want, the reason is that those are the parts that I’m struggling to get working, and I have very little idea how to solve, the code above is all commented so anyone can have a good grasp of what I’m trying to achieve here, Imagine, with this tool the amounts of headaches people will avoid when animating character locomotion in timeline!