using UnityEngine; using UnityEngine.Playables; namespace Timeline.Samples { // The runtime instance of a Tween track. It is responsible for blending and setting // the final data on the transform binding. public class TweenMixerBehaviour : PlayableBehaviour { static AnimationCurve s_DefaultCurve = AnimationCurve.Linear(0.0f, 0.0f, 1.0f, 1.0f); bool m_ShouldInitializeTransform = true; Vector3 m_InitialPosition; Quaternion m_InitialRotation; // Performs blend of position and rotation of all clips connected to a track mixer // The result is applied to the track binding's (playerData) transform. public override void ProcessFrame(Playable playable, FrameData info, object playerData) { Transform trackBinding = playerData as Transform; if (trackBinding == null) return; // Get the initial position and rotation of the track binding, only when ProcessFrame is first called InitializeIfNecessary(trackBinding); Vector3 accumPosition = Vector3.zero; Quaternion accumRotation = QuaternionUtils.zero; float totalPositionWeight = 0.0f; float totalRotationWeight = 0.0f; // Iterate on all mixer's inputs (ie each clip on the track) int inputCount = playable.GetInputCount(); for (int i = 0; i < inputCount; i++) { float inputWeight = playable.GetInputWeight(i); if (inputWeight <= 0) continue; Playable input = playable.GetInput(i); float normalizedInputTime = (float)(input.GetTime() / input.GetDuration()); // get the clip's behaviour and evaluate the progression along the curve TweenBehaviour tweenInput = GetTweenBehaviour(input); float tweenProgress = GetCurve(tweenInput).Evaluate(normalizedInputTime); // calculate the position's progression along the curve according to the input's (clip) weight if (tweenInput.shouldTweenPosition) { totalPositionWeight += inputWeight; accumPosition += TweenPosition(tweenInput, tweenProgress, inputWeight); } // calculate the rotation's progression along the curve according to the input's (clip) weight if (tweenInput.shouldTweenRotation) { totalRotationWeight += inputWeight; accumRotation = TweenRotation(tweenInput, accumRotation, tweenProgress, inputWeight); } } // Apply the final position and rotation values in the track binding trackBinding.position = accumPosition + m_InitialPosition * (1.0f - totalPositionWeight); trackBinding.rotation = accumRotation.Blend(m_InitialRotation, 1.0f - totalRotationWeight); trackBinding.rotation.Normalize(); } void InitializeIfNecessary(Transform transform) { if (m_ShouldInitializeTransform) { m_InitialPosition = transform.position; m_InitialRotation = transform.rotation; m_ShouldInitializeTransform = false; } } Vector3 TweenPosition(TweenBehaviour tweenInput, float progress, float weight) { Vector3 startPosition = m_InitialPosition; if (tweenInput.startLocation != null) { startPosition = tweenInput.startLocation.position; } Vector3 endPosition = m_InitialPosition; if (tweenInput.endLocation != null) { endPosition = tweenInput.endLocation.position; } return Vector3.Lerp(startPosition, endPosition, progress) * weight; } Quaternion TweenRotation(TweenBehaviour tweenInput, Quaternion accumRotation, float progress, float weight) { Quaternion startRotation = m_InitialRotation; if (tweenInput.startLocation != null) { startRotation = tweenInput.startLocation.rotation; } Quaternion endRotation = m_InitialRotation; if (tweenInput.endLocation != null) { endRotation = tweenInput.endLocation.rotation; } Quaternion desiredRotation = Quaternion.Lerp(startRotation, endRotation, progress); return accumRotation.Blend(desiredRotation.NormalizeSafe(), weight); } static TweenBehaviour GetTweenBehaviour(Playable playable) { ScriptPlayable tweenInput = (ScriptPlayable)playable; return tweenInput.GetBehaviour(); } static AnimationCurve GetCurve(TweenBehaviour tween) { if (tween == null || tween.curve == null) return s_DefaultCurve; return tween.curve; } } }