first commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0cd29fb1ad218b48b814bc3e6d8ac0e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,71 @@
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
class ActivationMixerPlayable : PlayableBehaviour
|
||||
{
|
||||
ActivationTrack.PostPlaybackState m_PostPlaybackState;
|
||||
bool m_BoundGameObjectInitialStateIsActive;
|
||||
|
||||
private GameObject m_BoundGameObject;
|
||||
|
||||
|
||||
public static ScriptPlayable<ActivationMixerPlayable> Create(PlayableGraph graph, int inputCount)
|
||||
{
|
||||
return ScriptPlayable<ActivationMixerPlayable>.Create(graph, inputCount);
|
||||
}
|
||||
|
||||
public ActivationTrack.PostPlaybackState postPlaybackState
|
||||
{
|
||||
get { return m_PostPlaybackState; }
|
||||
set { m_PostPlaybackState = value; }
|
||||
}
|
||||
|
||||
public override void OnPlayableDestroy(Playable playable)
|
||||
{
|
||||
if (m_BoundGameObject == null)
|
||||
return;
|
||||
|
||||
switch (m_PostPlaybackState)
|
||||
{
|
||||
case ActivationTrack.PostPlaybackState.Active:
|
||||
m_BoundGameObject.SetActive(true);
|
||||
break;
|
||||
case ActivationTrack.PostPlaybackState.Inactive:
|
||||
m_BoundGameObject.SetActive(false);
|
||||
break;
|
||||
case ActivationTrack.PostPlaybackState.Revert:
|
||||
m_BoundGameObject.SetActive(m_BoundGameObjectInitialStateIsActive);
|
||||
break;
|
||||
case ActivationTrack.PostPlaybackState.LeaveAsIs:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
|
||||
{
|
||||
if (m_BoundGameObject == null)
|
||||
{
|
||||
m_BoundGameObject = playerData as GameObject;
|
||||
m_BoundGameObjectInitialStateIsActive = m_BoundGameObject != null && m_BoundGameObject.activeSelf;
|
||||
}
|
||||
|
||||
if (m_BoundGameObject == null)
|
||||
return;
|
||||
|
||||
int inputCount = playable.GetInputCount();
|
||||
bool hasInput = false;
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
if (playable.GetInputWeight(i) > 0)
|
||||
{
|
||||
hasInput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_BoundGameObject.SetActive(hasInput);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e165a99d845c10e4ea0f546e542e8684
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.ComponentModel;
|
||||
#endif
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Playable Asset class for Activation Tracks
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[DisplayName("Activation Clip")]
|
||||
#endif
|
||||
class ActivationPlayableAsset : PlayableAsset, ITimelineClipAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a description of the features supported by activation clips
|
||||
/// </summary>
|
||||
public ClipCaps clipCaps { get { return ClipCaps.None; } }
|
||||
|
||||
/// <summary>
|
||||
/// Overrides PlayableAsset.CreatePlayable() to inject needed Playables for an activation asset
|
||||
/// </summary>
|
||||
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
|
||||
{
|
||||
return Playable.Create(graph);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fde0d25a170598d46a0b9dc16b4527a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Track that can be used to control the active state of a GameObject.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[TrackClipType(typeof(ActivationPlayableAsset))]
|
||||
[TrackBindingType(typeof(GameObject))]
|
||||
[ExcludeFromPreset]
|
||||
[TimelineHelpURL(typeof(ActivationTrack))]
|
||||
public class ActivationTrack : TrackAsset
|
||||
{
|
||||
[SerializeField]
|
||||
PostPlaybackState m_PostPlaybackState = PostPlaybackState.LeaveAsIs;
|
||||
ActivationMixerPlayable m_ActivationMixer;
|
||||
|
||||
/// <summary>
|
||||
/// Specify what state to leave the GameObject in after the Timeline has finished playing.
|
||||
/// </summary>
|
||||
public enum PostPlaybackState
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the GameObject to active.
|
||||
/// </summary>
|
||||
Active,
|
||||
|
||||
/// <summary>
|
||||
/// Set the GameObject to Inactive.
|
||||
/// </summary>
|
||||
Inactive,
|
||||
|
||||
/// <summary>
|
||||
/// Revert the GameObject to the state in was in before the Timeline was playing.
|
||||
/// </summary>
|
||||
Revert,
|
||||
|
||||
/// <summary>
|
||||
/// Leave the GameObject in the state it was when the Timeline was stopped.
|
||||
/// </summary>
|
||||
LeaveAsIs
|
||||
}
|
||||
|
||||
internal override bool CanCompileClips()
|
||||
{
|
||||
return !hasClips || base.CanCompileClips();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies what state to leave the GameObject in after the Timeline has finished playing.
|
||||
/// </summary>
|
||||
public PostPlaybackState postPlaybackState
|
||||
{
|
||||
get { return m_PostPlaybackState; }
|
||||
set { m_PostPlaybackState = value; UpdateTrackMode(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
|
||||
{
|
||||
var mixer = ActivationMixerPlayable.Create(graph, inputCount);
|
||||
m_ActivationMixer = mixer.GetBehaviour();
|
||||
|
||||
UpdateTrackMode();
|
||||
|
||||
return mixer;
|
||||
}
|
||||
|
||||
internal void UpdateTrackMode()
|
||||
{
|
||||
if (m_ActivationMixer != null)
|
||||
m_ActivationMixer.postPlaybackState = m_PostPlaybackState;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GatherProperties(PlayableDirector director, IPropertyCollector driver)
|
||||
{
|
||||
var gameObject = GetGameObjectBinding(director);
|
||||
if (gameObject != null)
|
||||
{
|
||||
driver.AddFromName(gameObject, "m_IsActive");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnCreateClip(TimelineClip clip)
|
||||
{
|
||||
clip.displayName = "Active";
|
||||
base.OnCreateClip(clip);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21bf7f712d84d26478ebe6a299f21738
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de9eb5e2046ffc9448f07e495c436506
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Animations;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
// Does a post processing of the weights on an animation track to properly normalize
|
||||
// the mixer weights so that blending does not bring default poses and subtracks, layers and
|
||||
// layer graphs blend correctly
|
||||
class AnimationOutputWeightProcessor : ITimelineEvaluateCallback
|
||||
{
|
||||
struct WeightInfo
|
||||
{
|
||||
public Playable mixer;
|
||||
public Playable parentMixer;
|
||||
public int port;
|
||||
}
|
||||
|
||||
AnimationPlayableOutput m_Output;
|
||||
AnimationMotionXToDeltaPlayable m_MotionXPlayable;
|
||||
readonly List<WeightInfo> m_Mixers = new List<WeightInfo>();
|
||||
|
||||
public AnimationOutputWeightProcessor(AnimationPlayableOutput output)
|
||||
{
|
||||
m_Output = output;
|
||||
output.SetWeight(0);
|
||||
FindMixers();
|
||||
}
|
||||
|
||||
void FindMixers()
|
||||
{
|
||||
var playable = m_Output.GetSourcePlayable();
|
||||
var outputPort = m_Output.GetSourceOutputPort();
|
||||
|
||||
m_Mixers.Clear();
|
||||
// only write the final output in playmode. it should always be 1 in editor because we blend to the defaults
|
||||
FindMixers(playable, outputPort, playable.GetInput(outputPort));
|
||||
}
|
||||
|
||||
// Recursively accumulates mixers.
|
||||
void FindMixers(Playable parent, int port, Playable node)
|
||||
{
|
||||
if (!node.IsValid())
|
||||
return;
|
||||
|
||||
var type = node.GetPlayableType();
|
||||
if (type == typeof(AnimationMixerPlayable) || type == typeof(AnimationLayerMixerPlayable))
|
||||
{
|
||||
// use post fix traversal so children come before parents
|
||||
int subCount = node.GetInputCount();
|
||||
for (int j = 0; j < subCount; j++)
|
||||
{
|
||||
FindMixers(node, j, node.GetInput(j));
|
||||
}
|
||||
|
||||
// if we encounter a layer mixer, we assume there is nesting occuring
|
||||
// and we modulate the weight instead of overwriting it.
|
||||
var weightInfo = new WeightInfo
|
||||
{
|
||||
parentMixer = parent,
|
||||
mixer = node,
|
||||
port = port,
|
||||
};
|
||||
m_Mixers.Add(weightInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var count = node.GetInputCount();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
FindMixers(parent, port, node.GetInput(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Evaluate()
|
||||
{
|
||||
float weight = 1;
|
||||
m_Output.SetWeight(1);
|
||||
for (int i = 0; i < m_Mixers.Count; i++)
|
||||
{
|
||||
var mixInfo = m_Mixers[i];
|
||||
weight = WeightUtility.NormalizeMixer(mixInfo.mixer);
|
||||
mixInfo.parentMixer.SetInputWeight(mixInfo.port, weight);
|
||||
}
|
||||
|
||||
// only write the final weight in player/playmode. In editor, we are blending to the appropriate defaults
|
||||
// the last mixer in the list is the final blend, since the list is composed post-order.
|
||||
if (Application.isPlaying)
|
||||
m_Output.SetWeight(weight);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a429b38ee9d48c7408c8870baf406034
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,325 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Animations;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// A Playable Asset that represents a single AnimationClip clip.
|
||||
/// </summary>
|
||||
[System.Serializable, NotKeyable]
|
||||
public partial class AnimationPlayableAsset : PlayableAsset, ITimelineClipAsset, IPropertyPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the source AnimationClip loops during playback.
|
||||
/// </summary>
|
||||
public enum LoopMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the loop time setting from the source AnimationClip.
|
||||
/// </summary>
|
||||
[Tooltip("Use the loop time setting from the source AnimationClip.")]
|
||||
UseSourceAsset = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The source AnimationClip loops during playback.
|
||||
/// </summary>
|
||||
[Tooltip("The source AnimationClip loops during playback.")]
|
||||
On = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The source AnimationClip does not loop during playback.
|
||||
/// </summary>
|
||||
[Tooltip("The source AnimationClip does not loop during playback.")]
|
||||
Off = 2
|
||||
}
|
||||
|
||||
|
||||
[SerializeField] private AnimationClip m_Clip;
|
||||
[SerializeField] private Vector3 m_Position = Vector3.zero;
|
||||
[SerializeField] private Vector3 m_EulerAngles = Vector3.zero;
|
||||
[SerializeField] private bool m_UseTrackMatchFields = true;
|
||||
[SerializeField] private MatchTargetFields m_MatchTargetFields = MatchTargetFieldConstants.All;
|
||||
[SerializeField] private bool m_RemoveStartOffset = true; // set by animation track prior to compilation
|
||||
[SerializeField] private bool m_ApplyFootIK = true;
|
||||
[SerializeField] private LoopMode m_Loop = LoopMode.UseSourceAsset;
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private AnimationOffsetPlayable m_AnimationOffsetPlayable;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The translational offset of the clip
|
||||
/// </summary>
|
||||
public Vector3 position
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Position = value;
|
||||
#if UNITY_EDITOR
|
||||
if (m_AnimationOffsetPlayable.IsValid())
|
||||
m_AnimationOffsetPlayable.SetPosition(position);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rotational offset of the clip, expressed as a Quaternion
|
||||
/// </summary>
|
||||
public Quaternion rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return Quaternion.Euler(m_EulerAngles);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
m_EulerAngles = value.eulerAngles;
|
||||
#if UNITY_EDITOR
|
||||
if (m_AnimationOffsetPlayable.IsValid())
|
||||
m_AnimationOffsetPlayable.SetRotation(value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rotational offset of the clip, expressed in Euler angles
|
||||
/// </summary>
|
||||
public Vector3 eulerAngles
|
||||
{
|
||||
get { return m_EulerAngles; }
|
||||
set
|
||||
{
|
||||
m_EulerAngles = value;
|
||||
#if UNITY_EDITOR
|
||||
if (m_AnimationOffsetPlayable.IsValid())
|
||||
m_AnimationOffsetPlayable.SetRotation(rotation);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether to use offset matching options as defined by the track.
|
||||
/// </summary>
|
||||
public bool useTrackMatchFields
|
||||
{
|
||||
get { return m_UseTrackMatchFields; }
|
||||
set { m_UseTrackMatchFields = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies which fields should be matched when aligning offsets.
|
||||
/// </summary>
|
||||
public MatchTargetFields matchTargetFields
|
||||
{
|
||||
get { return m_MatchTargetFields; }
|
||||
set { m_MatchTargetFields = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to make the animation clip play relative to its first keyframe.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This option only applies to animation clips that animate Transform components.
|
||||
/// </remarks>
|
||||
public bool removeStartOffset
|
||||
{
|
||||
get { return m_RemoveStartOffset; }
|
||||
set { m_RemoveStartOffset = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enable to apply foot IK to the AnimationClip when the target is humanoid.
|
||||
/// </summary>
|
||||
public bool applyFootIK
|
||||
{
|
||||
get { return m_ApplyFootIK; }
|
||||
set { m_ApplyFootIK = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the source AnimationClip loops during playback
|
||||
/// </summary>
|
||||
public LoopMode loop
|
||||
{
|
||||
get { return m_Loop; }
|
||||
set { m_Loop = value; }
|
||||
}
|
||||
|
||||
|
||||
internal bool hasRootTransforms
|
||||
{
|
||||
get { return m_Clip != null && HasRootTransforms(m_Clip); }
|
||||
}
|
||||
|
||||
// used for legacy 'scene' mode.
|
||||
internal AppliedOffsetMode appliedOffsetMode { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The source animation clip
|
||||
/// </summary>
|
||||
public AnimationClip clip
|
||||
{
|
||||
get { return m_Clip; }
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
name = "AnimationPlayableAsset of " + value.name;
|
||||
m_Clip = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the duration required to play the animation clip exactly once
|
||||
/// </summary>
|
||||
public override double duration
|
||||
{
|
||||
get
|
||||
{
|
||||
double length = TimeUtility.GetAnimationClipLength(clip);
|
||||
if (length < float.Epsilon)
|
||||
return base.duration;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a description of the PlayableOutputs that may be created for this asset.
|
||||
/// </summary>
|
||||
public override IEnumerable<PlayableBinding> outputs
|
||||
{
|
||||
get { yield return AnimationPlayableBinding.Create(name, this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root of a Playable subgraph to play the animation clip.
|
||||
/// </summary>
|
||||
/// <param name="graph">PlayableGraph that will own the playable</param>
|
||||
/// <param name="go">The gameobject that triggered the graph build</param>
|
||||
/// <returns>The root playable of the subgraph</returns>
|
||||
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
|
||||
{
|
||||
Playable root = CreatePlayable(graph, m_Clip, position, eulerAngles, removeStartOffset, appliedOffsetMode, applyFootIK, m_Loop);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
m_AnimationOffsetPlayable = AnimationOffsetPlayable.Null;
|
||||
if (root.IsValid() && root.IsPlayableOfType<AnimationOffsetPlayable>())
|
||||
{
|
||||
m_AnimationOffsetPlayable = (AnimationOffsetPlayable)root;
|
||||
}
|
||||
|
||||
LiveLink();
|
||||
#endif
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
internal static Playable CreatePlayable(PlayableGraph graph, AnimationClip clip, Vector3 positionOffset, Vector3 eulerOffset, bool removeStartOffset, AppliedOffsetMode mode, bool applyFootIK, LoopMode loop)
|
||||
{
|
||||
if (clip == null || clip.legacy)
|
||||
return Playable.Null;
|
||||
|
||||
|
||||
var clipPlayable = AnimationClipPlayable.Create(graph, clip);
|
||||
clipPlayable.SetRemoveStartOffset(removeStartOffset);
|
||||
clipPlayable.SetApplyFootIK(applyFootIK);
|
||||
clipPlayable.SetOverrideLoopTime(loop != LoopMode.UseSourceAsset);
|
||||
clipPlayable.SetLoopTime(loop == LoopMode.On);
|
||||
|
||||
Playable root = clipPlayable;
|
||||
|
||||
if (ShouldApplyScaleRemove(mode))
|
||||
{
|
||||
var removeScale = AnimationRemoveScalePlayable.Create(graph, 1);
|
||||
graph.Connect(root, 0, removeScale, 0);
|
||||
removeScale.SetInputWeight(0, 1.0f);
|
||||
root = removeScale;
|
||||
}
|
||||
|
||||
if (ShouldApplyOffset(mode, clip))
|
||||
{
|
||||
var offsetPlayable = AnimationOffsetPlayable.Create(graph, positionOffset, Quaternion.Euler(eulerOffset), 1);
|
||||
graph.Connect(root, 0, offsetPlayable, 0);
|
||||
offsetPlayable.SetInputWeight(0, 1.0F);
|
||||
root = offsetPlayable;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private static bool ShouldApplyOffset(AppliedOffsetMode mode, AnimationClip clip)
|
||||
{
|
||||
if (mode == AppliedOffsetMode.NoRootTransform || mode == AppliedOffsetMode.SceneOffsetLegacy)
|
||||
return false;
|
||||
|
||||
return HasRootTransforms(clip);
|
||||
}
|
||||
|
||||
private static bool ShouldApplyScaleRemove(AppliedOffsetMode mode)
|
||||
{
|
||||
return mode == AppliedOffsetMode.SceneOffsetLegacyEditor || mode == AppliedOffsetMode.SceneOffsetLegacy || mode == AppliedOffsetMode.TransformOffsetLegacy;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void LiveLink()
|
||||
{
|
||||
if (m_AnimationOffsetPlayable.IsValid())
|
||||
{
|
||||
m_AnimationOffsetPlayable.SetPosition(position);
|
||||
m_AnimationOffsetPlayable.SetRotation(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns the capabilities of TimelineClips that contain a AnimationPlayableAsset
|
||||
/// </summary>
|
||||
public ClipCaps clipCaps
|
||||
{
|
||||
get
|
||||
{
|
||||
var caps = ClipCaps.Extrapolation | ClipCaps.SpeedMultiplier | ClipCaps.Blending;
|
||||
if (m_Clip != null && (m_Loop != LoopMode.Off) && (m_Loop != LoopMode.UseSourceAsset || m_Clip.isLooping))
|
||||
caps |= ClipCaps.Looping;
|
||||
|
||||
// empty clips don't support clip in. This allows trim operations to simply become move operations
|
||||
if (m_Clip != null && !m_Clip.empty)
|
||||
caps |= ClipCaps.ClipIn;
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the offsets to default values
|
||||
/// </summary>
|
||||
public void ResetOffsets()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
eulerAngles = Vector3.zero;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void GatherProperties(PlayableDirector director, IPropertyCollector driver)
|
||||
{
|
||||
driver.AddFromClip(m_Clip);
|
||||
}
|
||||
|
||||
internal static bool HasRootTransforms(AnimationClip clip)
|
||||
{
|
||||
if (clip == null || clip.empty)
|
||||
return false;
|
||||
|
||||
return clip.hasRootMotion || clip.hasGenericRootTransform || clip.hasMotionCurves || clip.hasRootCurves;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 030f85c3f73729f4f976f66ffb23b875
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Animations;
|
||||
#if !UNITY_2020_2_OR_NEWER
|
||||
using UnityEngine.Experimental.Animations;
|
||||
#endif
|
||||
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
class AnimationPreviewUpdateCallback : ITimelineEvaluateCallback
|
||||
{
|
||||
AnimationPlayableOutput m_Output;
|
||||
PlayableGraph m_Graph;
|
||||
List<IAnimationWindowPreview> m_PreviewComponents;
|
||||
|
||||
public AnimationPreviewUpdateCallback(AnimationPlayableOutput output)
|
||||
{
|
||||
m_Output = output;
|
||||
|
||||
Playable playable = m_Output.GetSourcePlayable();
|
||||
if (playable.IsValid())
|
||||
{
|
||||
m_Graph = playable.GetGraph();
|
||||
}
|
||||
}
|
||||
|
||||
public void Evaluate()
|
||||
{
|
||||
if (!m_Graph.IsValid())
|
||||
return;
|
||||
|
||||
if (m_PreviewComponents == null)
|
||||
FetchPreviewComponents();
|
||||
|
||||
foreach (var component in m_PreviewComponents)
|
||||
{
|
||||
if (component != null)
|
||||
{
|
||||
component.UpdatePreviewGraph(m_Graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FetchPreviewComponents()
|
||||
{
|
||||
m_PreviewComponents = new List<IAnimationWindowPreview>();
|
||||
|
||||
var animator = m_Output.GetTarget();
|
||||
if (animator == null)
|
||||
return;
|
||||
|
||||
var gameObject = animator.gameObject;
|
||||
m_PreviewComponents.AddRange(gameObject.GetComponents<IAnimationWindowPreview>());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 755e3e942f7784d458bddba421c0bb72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d21dcc2386d650c4597f3633c75a1f98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,15 @@
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
interface ICurvesOwner
|
||||
{
|
||||
AnimationClip curves { get; }
|
||||
bool hasCurves { get; }
|
||||
double duration { get; }
|
||||
void CreateCurves(string curvesClipName);
|
||||
|
||||
string defaultCurvesName { get; }
|
||||
Object asset { get; }
|
||||
Object assetOwner { get; }
|
||||
TrackAsset targetTrack { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89b31ff5ca0a5eb4797ac65d43949807
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c04c8cb23b78e04492e0f310cdee93e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
partial class AnimationPlayableAsset : ISerializationCallbackReceiver
|
||||
{
|
||||
enum Versions
|
||||
{
|
||||
Initial = 0,
|
||||
RotationAsEuler = 1,
|
||||
}
|
||||
static readonly int k_LatestVersion = (int)Versions.RotationAsEuler;
|
||||
[SerializeField, HideInInspector] int m_Version;
|
||||
|
||||
[SerializeField, Obsolete("Use m_RotationEuler Instead", false), HideInInspector]
|
||||
private Quaternion m_Rotation = Quaternion.identity; // deprecated. now saves in euler angles
|
||||
|
||||
/// <summary>
|
||||
/// Called before Unity serializes this object.
|
||||
/// </summary>
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
m_Version = k_LatestVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after Unity deserializes this object.
|
||||
/// </summary>
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
if (m_Version < k_LatestVersion)
|
||||
{
|
||||
OnUpgradeFromVersion(m_Version); //upgrade derived classes
|
||||
}
|
||||
}
|
||||
|
||||
void OnUpgradeFromVersion(int oldVersion)
|
||||
{
|
||||
if (oldVersion < (int)Versions.RotationAsEuler)
|
||||
AnimationPlayableAssetUpgrade.ConvertRotationToEuler(this);
|
||||
}
|
||||
|
||||
static class AnimationPlayableAssetUpgrade
|
||||
{
|
||||
public static void ConvertRotationToEuler(AnimationPlayableAsset asset)
|
||||
{
|
||||
#pragma warning disable 618
|
||||
asset.m_EulerAngles = asset.m_Rotation.eulerAngles;
|
||||
#pragma warning restore 618
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6773203120b27984d9a8572fa3564f03
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
partial class AnimationTrack
|
||||
{
|
||||
// 649 is value is only assigned to. they can be updated from old files being serialized
|
||||
#pragma warning disable 649
|
||||
//fields that are used for upgrading should be put here, ideally as read-only
|
||||
[SerializeField, Obsolete("Use m_InfiniteClipOffsetEulerAngles Instead", false), HideInInspector]
|
||||
Quaternion m_OpenClipOffsetRotation = Quaternion.identity;
|
||||
|
||||
[SerializeField, Obsolete("Use m_RotationEuler Instead", false), HideInInspector]
|
||||
Quaternion m_Rotation = Quaternion.identity;
|
||||
|
||||
[SerializeField, Obsolete("Use m_RootTransformOffsetMode", false), HideInInspector]
|
||||
bool m_ApplyOffsets;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// Translation offset of a track in infinite mode.
|
||||
/// This property is obsolete. Use <see cref="UnityEngine.Timeline.AnimationTrack.infiniteClipOffsetPosition"/> instead.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("openClipOffsetPosition has been deprecated. Use infiniteClipOffsetPosition instead. (UnityUpgradable) -> infiniteClipOffsetPosition", true)]
|
||||
public Vector3 openClipOffsetPosition
|
||||
{
|
||||
get { return infiniteClipOffsetPosition; }
|
||||
set { infiniteClipOffsetPosition = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotation offset of a track in infinite mode.
|
||||
/// This property is obsolete. Use <see cref="UnityEngine.Timeline.AnimationTrack.infiniteClipOffsetRotation"/> instead.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("openClipOffsetRotation has been deprecated. Use infiniteClipOffsetRotation instead. (UnityUpgradable) -> infiniteClipOffsetRotation", true)]
|
||||
public Quaternion openClipOffsetRotation
|
||||
{
|
||||
get { return infiniteClipOffsetRotation; }
|
||||
set { infiniteClipOffsetRotation = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Euler angle representation of the rotation offset of the track when in infinite mode.
|
||||
/// This property is obsolete. Use <see cref="UnityEngine.Timeline.AnimationTrack.infiniteClipOffsetEulerAngles"/> instead.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("openClipOffsetEulerAngles has been deprecated. Use infiniteClipOffsetEulerAngles instead. (UnityUpgradable) -> infiniteClipOffsetEulerAngles", true)]
|
||||
public Vector3 openClipOffsetEulerAngles
|
||||
{
|
||||
get { return infiniteClipOffsetEulerAngles; }
|
||||
set { infiniteClipOffsetEulerAngles = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saved state of pre-extrapolation for clips converted to infinite mode.
|
||||
/// This property is obsolete. Use <see cref="UnityEngine.Timeline.AnimationTrack.infiniteClipPreExtrapolation"/> instead.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("openClipPreExtrapolation has been deprecated. Use infiniteClipPreExtrapolation instead. (UnityUpgradable) -> infiniteClipPreExtrapolation", true)]
|
||||
public TimelineClip.ClipExtrapolation openClipPreExtrapolation
|
||||
{
|
||||
get { return infiniteClipPreExtrapolation; }
|
||||
set { infiniteClipPreExtrapolation = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saved state of post-extrapolation for clips converted to infinite mode.
|
||||
/// This property is obsolete. Use <see cref="UnityEngine.Timeline.AnimationTrack.infiniteClipPostExtrapolation"/> instead.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("openClipPostExtrapolation has been deprecated. Use infiniteClipPostExtrapolation instead. (UnityUpgradable) -> infiniteClipPostExtrapolation", true)]
|
||||
public TimelineClip.ClipExtrapolation openClipPostExtrapolation
|
||||
{
|
||||
get { return infiniteClipPostExtrapolation; }
|
||||
set { infiniteClipPostExtrapolation = value; }
|
||||
}
|
||||
|
||||
internal override void OnUpgradeFromVersion(int oldVersion)
|
||||
{
|
||||
if (oldVersion < (int)Versions.RotationAsEuler)
|
||||
AnimationTrackUpgrade.ConvertRotationsToEuler(this);
|
||||
if (oldVersion < (int)Versions.RootMotionUpgrade)
|
||||
AnimationTrackUpgrade.ConvertRootMotion(this);
|
||||
if (oldVersion < (int)Versions.AnimatedTrackProperties)
|
||||
AnimationTrackUpgrade.ConvertInfiniteTrack(this);
|
||||
}
|
||||
|
||||
// 612 is Property is Obsolete
|
||||
// 618 is Field is Obsolete
|
||||
#pragma warning disable 612, 618
|
||||
static class AnimationTrackUpgrade
|
||||
{
|
||||
public static void ConvertRotationsToEuler(AnimationTrack track)
|
||||
{
|
||||
track.m_EulerAngles = track.m_Rotation.eulerAngles;
|
||||
track.m_InfiniteClipOffsetEulerAngles = track.m_OpenClipOffsetRotation.eulerAngles;
|
||||
}
|
||||
|
||||
public static void ConvertRootMotion(AnimationTrack track)
|
||||
{
|
||||
track.m_TrackOffset = TrackOffset.Auto; // loaded tracks should use legacy mode
|
||||
|
||||
// reset offsets if not applied
|
||||
if (!track.m_ApplyOffsets)
|
||||
{
|
||||
track.m_Position = Vector3.zero;
|
||||
track.m_EulerAngles = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ConvertInfiniteTrack(AnimationTrack track)
|
||||
{
|
||||
track.m_InfiniteClip = track.m_AnimClip;
|
||||
track.m_AnimClip = null;
|
||||
}
|
||||
}
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b0c53b13a1539949b3b212e049151d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,34 @@
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
partial class TimelineClip
|
||||
{
|
||||
enum Versions
|
||||
{
|
||||
Initial = 0,
|
||||
ClipInFromGlobalToLocal = 1
|
||||
}
|
||||
const int k_LatestVersion = (int)Versions.ClipInFromGlobalToLocal;
|
||||
[SerializeField, HideInInspector] int m_Version;
|
||||
|
||||
//fields that are used for upgrading should be put here, ideally as read-only
|
||||
|
||||
void UpgradeToLatestVersion()
|
||||
{
|
||||
if (m_Version < (int)Versions.ClipInFromGlobalToLocal)
|
||||
{
|
||||
TimelineClipUpgrade.UpgradeClipInFromGlobalToLocal(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class TimelineClipUpgrade
|
||||
{
|
||||
// version 0->1, clipIn move from global to local
|
||||
public static void UpgradeClipInFromGlobalToLocal(TimelineClip clip)
|
||||
{
|
||||
// case 936751 -- clipIn was serialized in global, not local offset
|
||||
if (clip.m_ClipIn > 0 && clip.m_TimeScale > float.Epsilon)
|
||||
clip.m_ClipIn *= clip.m_TimeScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a4f0c91a28ece04198b200dd55145d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,21 @@
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
partial class TimelineAsset
|
||||
{
|
||||
enum Versions
|
||||
{
|
||||
Initial = 0
|
||||
}
|
||||
const int k_LatestVersion = (int)Versions.Initial;
|
||||
[SerializeField, HideInInspector] int m_Version;
|
||||
|
||||
//fields that are used for upgrading should be put here, ideally as read-only
|
||||
|
||||
void UpgradeToLatestVersion()
|
||||
{ }
|
||||
|
||||
//upgrade code should go into this class
|
||||
static class TimelineAssetUpgrade
|
||||
{ }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95c91abdcc1ea03458c2ea4e9626a5d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
partial class TrackAsset : ISerializationCallbackReceiver
|
||||
{
|
||||
internal enum Versions
|
||||
{
|
||||
Initial = 0,
|
||||
RotationAsEuler = 1,
|
||||
RootMotionUpgrade = 2,
|
||||
AnimatedTrackProperties = 3
|
||||
}
|
||||
|
||||
const int k_LatestVersion = (int)Versions.AnimatedTrackProperties;
|
||||
|
||||
[SerializeField, HideInInspector] int m_Version;
|
||||
|
||||
[Obsolete("Please use m_InfiniteClip (on AnimationTrack) instead.", false)]
|
||||
[SerializeField, HideInInspector, FormerlySerializedAs("m_animClip")]
|
||||
internal AnimationClip m_AnimClip;
|
||||
|
||||
/// <summary>
|
||||
/// Called before a track is serialized.
|
||||
/// </summary>
|
||||
protected virtual void OnBeforeTrackSerialize() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called after a track has been deserialized.
|
||||
/// </summary>
|
||||
protected virtual void OnAfterTrackDeserialize() { }
|
||||
|
||||
internal virtual void OnUpgradeFromVersion(int oldVersion) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called before Unity serializes this object.
|
||||
/// </summary>
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
m_Version = k_LatestVersion;
|
||||
|
||||
//make sure children are correctly parented
|
||||
if (m_Children != null)
|
||||
{
|
||||
for (var i = m_Children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var asset = m_Children[i] as TrackAsset;
|
||||
if (asset != null && asset.parent != this)
|
||||
asset.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
OnBeforeTrackSerialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after Unity deserializes this object.
|
||||
/// </summary>
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
// Clear the clip cache when a deserialize is performed, or
|
||||
// we can get out of sync when performing Undo
|
||||
m_ClipsCache = null;
|
||||
Invalidate();
|
||||
|
||||
if (m_Version < k_LatestVersion)
|
||||
{
|
||||
UpgradeToLatestVersion(); //upgrade TrackAsset
|
||||
OnUpgradeFromVersion(m_Version); //upgrade derived classes
|
||||
}
|
||||
|
||||
foreach (var marker in GetMarkers())
|
||||
{
|
||||
marker.Initialize(this);
|
||||
}
|
||||
|
||||
OnAfterTrackDeserialize();
|
||||
}
|
||||
|
||||
//fields that are used for upgrading should be put here, ideally as read-only
|
||||
void UpgradeToLatestVersion()
|
||||
{ }
|
||||
|
||||
//upgrade code should go into this class
|
||||
static class TrackAssetUpgrade
|
||||
{ }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c68f34993bfe85e489158a29c99a20b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec817e5e5781e0a4983a1dc8875d1974
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,46 @@
|
||||
#if UNITY_EDITOR && UNITY_2021_1_OR_NEWER
|
||||
#define CAN_USE_CUSTOM_HELP_URL
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
#if CAN_USE_CUSTOM_HELP_URL
|
||||
|
||||
using UnityEditor.PackageManager;
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
class TimelineHelpURLAttribute : HelpURLAttribute
|
||||
{
|
||||
const string k_BaseURL = "https://docs.unity3d.com/Packages/com.unity.timeline@";
|
||||
const string k_MidURL = "/api/";
|
||||
const string k_EndURL = ".html";
|
||||
const string k_FallbackVersion = "latest";
|
||||
|
||||
static readonly string k_PackageVersion;
|
||||
|
||||
static TimelineHelpURLAttribute()
|
||||
{
|
||||
PackageInfo packageInfo = PackageInfo.FindForAssembly(typeof(TimelineAsset).Assembly);
|
||||
k_PackageVersion = packageInfo == null ? k_FallbackVersion : packageInfo.version.Substring(0, 3);
|
||||
}
|
||||
|
||||
public TimelineHelpURLAttribute(Type type)
|
||||
: base(HelpURL(type)) {}
|
||||
|
||||
static string HelpURL(Type type)
|
||||
{
|
||||
return $"{k_BaseURL}{k_PackageVersion}{k_MidURL}{type.FullName}{k_EndURL}";
|
||||
}
|
||||
}
|
||||
#else //HelpURL attribute is `sealed` in previous Unity versions
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
class TimelineHelpURLAttribute : Attribute
|
||||
{
|
||||
public TimelineHelpURLAttribute(Type type) { }
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c35b6ea5f1d7cd74eac79413eb70670c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute used to specify the color of the track and its clips inside the Timeline Editor.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class TrackColorAttribute : Attribute
|
||||
{
|
||||
Color m_Color;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Color color
|
||||
{
|
||||
get { return m_Color; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specify the track color using [0-1] R,G,B values.
|
||||
/// </summary>
|
||||
/// <param name="r">Red value [0-1].</param>
|
||||
/// <param name="g">Green value [0-1].</param>
|
||||
/// <param name="b">Blue value [0-1].</param>
|
||||
public TrackColorAttribute(float r, float g, float b)
|
||||
{
|
||||
m_Color = new Color(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c3d52cc5c46d7946a920e21901ff38e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d19b75372f4e44d4fa4b2cffbb54124b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
[Serializable]
|
||||
[NotKeyable]
|
||||
class AudioClipProperties : PlayableBehaviour
|
||||
{
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float volume = 1.0f;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d60a406ab64c434e9d731914e11a51e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using UnityEngine.Audio;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
[Serializable]
|
||||
class AudioMixerProperties : PlayableBehaviour
|
||||
{
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float volume = 1.0f;
|
||||
|
||||
[Range(-1.0f, 1.0f)]
|
||||
public float stereoPan = 0.0f;
|
||||
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float spatialBlend = 0.0f;
|
||||
|
||||
public override void PrepareFrame(Playable playable, FrameData info)
|
||||
{
|
||||
if (!playable.IsValid() || !playable.IsPlayableOfType<AudioMixerPlayable>())
|
||||
return;
|
||||
|
||||
var inputCount = playable.GetInputCount();
|
||||
|
||||
for (int i = 0; i < inputCount; ++i)
|
||||
{
|
||||
if (playable.GetInputWeight(i) > 0.0f)
|
||||
{
|
||||
var input = playable.GetInput(i);
|
||||
|
||||
if (input.IsValid() && input.IsPlayableOfType<AudioClipPlayable>())
|
||||
{
|
||||
var audioClipPlayable = (AudioClipPlayable)input;
|
||||
var audioClipProperties = input.GetHandle().GetObject<AudioClipProperties>();
|
||||
|
||||
audioClipPlayable.SetVolume(Mathf.Clamp01(volume * audioClipProperties.volume));
|
||||
audioClipPlayable.SetStereoPan(Mathf.Clamp(stereoPan, -1.0f, 1.0f));
|
||||
audioClipPlayable.SetSpatialBlend(Mathf.Clamp01(spatialBlend));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8c4a920f001ca64680ed6fdb52d1753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Audio;
|
||||
#if UNITY_EDITOR
|
||||
using System.ComponentModel;
|
||||
#endif
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// PlayableAsset wrapper for an AudioClip in Timeline.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if UNITY_EDITOR
|
||||
[DisplayName("Audio Clip")]
|
||||
#endif
|
||||
public class AudioPlayableAsset : PlayableAsset, ITimelineClipAsset
|
||||
{
|
||||
[SerializeField] AudioClip m_Clip;
|
||||
#pragma warning disable 649 //Field is never assigned to and will always have its default value
|
||||
[SerializeField] bool m_Loop;
|
||||
[SerializeField, HideInInspector] float m_bufferingTime = 0.1f;
|
||||
[SerializeField] AudioClipProperties m_ClipProperties = new AudioClipProperties();
|
||||
|
||||
// the amount of time to give the clip to load prior to it's start time
|
||||
internal float bufferingTime
|
||||
{
|
||||
get { return m_bufferingTime; }
|
||||
set { m_bufferingTime = value; }
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Playable m_LiveClipPlayable = Playable.Null;
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The audio clip to be played
|
||||
/// </summary>
|
||||
public AudioClip clip
|
||||
{
|
||||
get { return m_Clip; }
|
||||
set { m_Clip = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the audio clip loops.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use this to loop the audio clip when the duration of the timeline clip exceeds that of the audio clip.
|
||||
/// </remarks>
|
||||
public bool loop
|
||||
{
|
||||
get { return m_Loop; }
|
||||
set { m_Loop = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the duration required to play the audio clip exactly once
|
||||
/// </summary>
|
||||
public override double duration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Clip == null)
|
||||
return base.duration;
|
||||
|
||||
// use this instead of length to avoid rounding precision errors,
|
||||
return (double)m_Clip.samples / m_Clip.frequency;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a description of the PlayableOutputs that may be created for this asset.
|
||||
/// </summary>
|
||||
public override IEnumerable<PlayableBinding> outputs
|
||||
{
|
||||
get { yield return AudioPlayableBinding.Create(name, this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root of a Playable subgraph to play the audio clip.
|
||||
/// </summary>
|
||||
/// <param name="graph">PlayableGraph that will own the playable</param>
|
||||
/// <param name="go">The GameObject that triggered the graph build</param>
|
||||
/// <returns>The root playable of the subgraph</returns>
|
||||
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
|
||||
{
|
||||
if (m_Clip == null)
|
||||
return Playable.Null;
|
||||
|
||||
var audioClipPlayable = AudioClipPlayable.Create(graph, m_Clip, m_Loop);
|
||||
audioClipPlayable.GetHandle().SetScriptInstance(m_ClipProperties.Clone());
|
||||
|
||||
#if UNITY_EDITOR
|
||||
m_LiveClipPlayable = audioClipPlayable;
|
||||
#endif
|
||||
|
||||
return audioClipPlayable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the capabilities of TimelineClips that contain an AudioPlayableAsset
|
||||
/// </summary>
|
||||
public ClipCaps clipCaps
|
||||
{
|
||||
get
|
||||
{
|
||||
return ClipCaps.ClipIn |
|
||||
ClipCaps.SpeedMultiplier |
|
||||
ClipCaps.Blending |
|
||||
(m_Loop ? ClipCaps.Looping : ClipCaps.None);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal void LiveLink()
|
||||
{
|
||||
if (!m_LiveClipPlayable.IsValid())
|
||||
return;
|
||||
|
||||
var audioMixerProperties = m_LiveClipPlayable.GetHandle().GetObject<AudioClipProperties>();
|
||||
|
||||
if (audioMixerProperties == null)
|
||||
return;
|
||||
|
||||
audioMixerProperties.volume = m_ClipProperties.volume;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f10dd60657c6004587f237a7e90f8e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Audio;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// A Timeline track that can play AudioClips.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[TrackClipType(typeof(AudioPlayableAsset), false)]
|
||||
[TrackBindingType(typeof(AudioSource))]
|
||||
[ExcludeFromPreset]
|
||||
[TimelineHelpURL(typeof(AudioTrack))]
|
||||
public class AudioTrack : TrackAsset
|
||||
{
|
||||
[SerializeField]
|
||||
AudioMixerProperties m_TrackProperties = new AudioMixerProperties();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Playable m_LiveMixerPlayable = Playable.Null;
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Create an TimelineClip for playing an AudioClip on this track.
|
||||
/// </summary>
|
||||
/// <param name="clip">The audio clip to play</param>
|
||||
/// <returns>A TimelineClip with an AudioPlayableAsset asset.</returns>
|
||||
public TimelineClip CreateClip(AudioClip clip)
|
||||
{
|
||||
if (clip == null)
|
||||
return null;
|
||||
|
||||
var newClip = CreateDefaultClip();
|
||||
|
||||
var audioAsset = newClip.asset as AudioPlayableAsset;
|
||||
if (audioAsset != null)
|
||||
audioAsset.clip = clip;
|
||||
|
||||
newClip.duration = clip.length;
|
||||
newClip.displayName = clip.name;
|
||||
|
||||
return newClip;
|
||||
}
|
||||
|
||||
internal override Playable CompileClips(PlayableGraph graph, GameObject go, IList<TimelineClip> timelineClips, IntervalTree<RuntimeElement> tree)
|
||||
{
|
||||
var clipBlender = AudioMixerPlayable.Create(graph, timelineClips.Count);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
clipBlender.GetHandle().SetScriptInstance(m_TrackProperties.Clone());
|
||||
m_LiveMixerPlayable = clipBlender;
|
||||
#else
|
||||
if (hasCurves)
|
||||
clipBlender.GetHandle().SetScriptInstance(m_TrackProperties.Clone());
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < timelineClips.Count; i++)
|
||||
{
|
||||
var c = timelineClips[i];
|
||||
var asset = c.asset as PlayableAsset;
|
||||
if (asset == null)
|
||||
continue;
|
||||
|
||||
var buffer = 0.1f;
|
||||
var audioAsset = c.asset as AudioPlayableAsset;
|
||||
if (audioAsset != null)
|
||||
buffer = audioAsset.bufferingTime;
|
||||
|
||||
var source = asset.CreatePlayable(graph, go);
|
||||
if (!source.IsValid())
|
||||
continue;
|
||||
|
||||
if (source.IsPlayableOfType<AudioClipPlayable>())
|
||||
{
|
||||
// Enforce initial values on all clips
|
||||
var audioClipPlayable = (AudioClipPlayable)source;
|
||||
var audioClipProperties = audioClipPlayable.GetHandle().GetObject<AudioClipProperties>();
|
||||
|
||||
audioClipPlayable.SetVolume(Mathf.Clamp01(m_TrackProperties.volume * audioClipProperties.volume));
|
||||
audioClipPlayable.SetStereoPan(Mathf.Clamp(m_TrackProperties.stereoPan, -1.0f, 1.0f));
|
||||
audioClipPlayable.SetSpatialBlend(Mathf.Clamp01(m_TrackProperties.spatialBlend));
|
||||
}
|
||||
|
||||
tree.Add(new ScheduleRuntimeClip(c, source, clipBlender, buffer));
|
||||
graph.Connect(source, 0, clipBlender, i);
|
||||
source.SetSpeed(c.timeScale);
|
||||
source.SetDuration(c.extrapolatedDuration);
|
||||
clipBlender.SetInputWeight(source, 1.0f);
|
||||
}
|
||||
|
||||
ConfigureTrackAnimation(tree, go, clipBlender);
|
||||
|
||||
return clipBlender;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IEnumerable<PlayableBinding> outputs
|
||||
{
|
||||
get { yield return AudioPlayableBinding.Create(name, this); }
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal void LiveLink()
|
||||
{
|
||||
if (!m_LiveMixerPlayable.IsValid())
|
||||
return;
|
||||
|
||||
var audioMixerProperties = m_LiveMixerPlayable.GetHandle().GetObject<AudioMixerProperties>();
|
||||
|
||||
if (audioMixerProperties == null)
|
||||
return;
|
||||
|
||||
audioMixerProperties.volume = m_TrackProperties.volume;
|
||||
audioMixerProperties.stereoPan = m_TrackProperties.stereoPan;
|
||||
audioMixerProperties.spatialBlend = m_TrackProperties.spatialBlend;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
m_TrackProperties.volume = Mathf.Clamp01(m_TrackProperties.volume);
|
||||
m_TrackProperties.stereoPan = Mathf.Clamp(m_TrackProperties.stereoPan, -1.0f, 1.0f);
|
||||
m_TrackProperties.spatialBlend = Mathf.Clamp01(m_TrackProperties.spatialBlend);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b22792c3b570444eb18cb78c2af3a74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the timeline features supported by a clip
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ClipCaps
|
||||
{
|
||||
/// <summary>
|
||||
/// No features are supported.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The clip supports loops.
|
||||
/// </summary>
|
||||
Looping = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// The clip supports clip extrapolation.
|
||||
/// </summary>
|
||||
Extrapolation = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// The clip supports initial local times greater than zero.
|
||||
/// </summary>
|
||||
ClipIn = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// The clip supports time scaling.
|
||||
/// </summary>
|
||||
SpeedMultiplier = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// The clip supports blending between clips.
|
||||
/// </summary>
|
||||
Blending = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// The clip supports time scaling, and sets the default trim mode in the editor to scale the clip
|
||||
/// (speed multiplier) when the start/end of the clip is trimmed.
|
||||
/// </summary>
|
||||
AutoScale = 1 << 5 | SpeedMultiplier,
|
||||
|
||||
/// <summary>
|
||||
/// All features are supported.
|
||||
/// </summary>
|
||||
All = ~None
|
||||
}
|
||||
|
||||
static class TimelineClipCapsExtensions
|
||||
{
|
||||
public static bool SupportsLooping(this TimelineClip clip)
|
||||
{
|
||||
return clip != null && (clip.clipCaps & ClipCaps.Looping) != ClipCaps.None;
|
||||
}
|
||||
|
||||
public static bool SupportsExtrapolation(this TimelineClip clip)
|
||||
{
|
||||
return clip != null && (clip.clipCaps & ClipCaps.Extrapolation) != ClipCaps.None;
|
||||
}
|
||||
|
||||
public static bool SupportsClipIn(this TimelineClip clip)
|
||||
{
|
||||
return clip != null && (clip.clipCaps & ClipCaps.ClipIn) != ClipCaps.None;
|
||||
}
|
||||
|
||||
public static bool SupportsSpeedMultiplier(this TimelineClip clip)
|
||||
{
|
||||
return clip != null && (clip.clipCaps & ClipCaps.SpeedMultiplier) != ClipCaps.None;
|
||||
}
|
||||
|
||||
public static bool SupportsBlending(this TimelineClip clip)
|
||||
{
|
||||
return clip != null && (clip.clipCaps & ClipCaps.Blending) != ClipCaps.None;
|
||||
}
|
||||
|
||||
public static bool HasAll(this ClipCaps caps, ClipCaps flags)
|
||||
{
|
||||
return (caps & flags) == flags;
|
||||
}
|
||||
|
||||
public static bool HasAny(this ClipCaps caps, ClipCaps flags)
|
||||
{
|
||||
return (caps & flags) != 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 667a99762bdf5484fbaa02573fd396e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea998292f45ea494d9e100f5f6362f91
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,440 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Playable Asset that generates playables for controlling time-related elements on a GameObject.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[NotKeyable]
|
||||
public class ControlPlayableAsset : PlayableAsset, IPropertyPreview, ITimelineClipAsset
|
||||
{
|
||||
const int k_MaxRandInt = 10000;
|
||||
static readonly List<PlayableDirector> k_EmptyDirectorsList = new List<PlayableDirector>(0);
|
||||
static readonly List<ParticleSystem> k_EmptyParticlesList = new List<ParticleSystem>(0);
|
||||
static readonly HashSet<ParticleSystem> s_SubEmitterCollector = new HashSet<ParticleSystem>();
|
||||
|
||||
/// <summary>
|
||||
/// GameObject in the scene to control, or the parent of the instantiated prefab.
|
||||
/// </summary>
|
||||
[SerializeField] public ExposedReference<GameObject> sourceGameObject;
|
||||
|
||||
/// <summary>
|
||||
/// Prefab object that will be instantiated.
|
||||
/// </summary>
|
||||
[SerializeField] public GameObject prefabGameObject;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether Particle Systems will be controlled.
|
||||
/// </summary>
|
||||
[SerializeField] public bool updateParticle = true;
|
||||
|
||||
/// <summary>
|
||||
/// Random seed to supply particle systems that are set to use autoRandomSeed
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used to maintain determinism when playing back in timeline. Sub emitters will be assigned incrementing random seeds to maintain determinism and distinction.
|
||||
/// </remarks>
|
||||
[SerializeField] public uint particleRandomSeed;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether playableDirectors are controlled.
|
||||
/// </summary>
|
||||
[SerializeField] public bool updateDirector = true;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether Monobehaviours implementing ITimeControl will be controlled.
|
||||
/// </summary>
|
||||
[SerializeField] public bool updateITimeControl = true;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether to search the entire hierarchy for controllable components.
|
||||
/// </summary>
|
||||
[SerializeField] public bool searchHierarchy;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate whether GameObject activation is controlled
|
||||
/// </summary>
|
||||
[SerializeField] public bool active = true;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the active state of the GameObject when Timeline is stopped.
|
||||
/// </summary>
|
||||
[SerializeField] public ActivationControlPlayable.PostPlaybackState postPlayback = ActivationControlPlayable.PostPlaybackState.Revert;
|
||||
|
||||
PlayableAsset m_ControlDirectorAsset;
|
||||
double m_Duration = PlayableBinding.DefaultDuration;
|
||||
bool m_SupportLoop;
|
||||
|
||||
private static HashSet<PlayableDirector> s_ProcessedDirectors = new HashSet<PlayableDirector>();
|
||||
private static HashSet<GameObject> s_CreatedPrefabs = new HashSet<GameObject>();
|
||||
|
||||
// does the last instance created control directors and/or particles
|
||||
internal bool controllingDirectors { get; private set; }
|
||||
internal bool controllingParticles { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This function is called when the object is loaded.
|
||||
/// </summary>
|
||||
public void OnEnable()
|
||||
{
|
||||
// can't be set in a constructor
|
||||
if (particleRandomSeed == 0)
|
||||
particleRandomSeed = (uint)Random.Range(1, k_MaxRandInt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the duration in seconds needed to play the underlying director or particle system exactly once.
|
||||
/// </summary>
|
||||
public override double duration { get { return m_Duration; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the capabilities of TimelineClips that contain a ControlPlayableAsset
|
||||
/// </summary>
|
||||
public ClipCaps clipCaps
|
||||
{
|
||||
get { return ClipCaps.ClipIn | ClipCaps.SpeedMultiplier | (m_SupportLoop ? ClipCaps.Looping : ClipCaps.None); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root of a Playable subgraph to control the contents of the game object.
|
||||
/// </summary>
|
||||
/// <param name="graph">PlayableGraph that will own the playable</param>
|
||||
/// <param name="go">The GameObject that triggered the graph build</param>
|
||||
/// <returns>The root playable of the subgraph</returns>
|
||||
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
|
||||
{
|
||||
// case 989856
|
||||
if (prefabGameObject != null)
|
||||
{
|
||||
if (s_CreatedPrefabs.Contains(prefabGameObject))
|
||||
{
|
||||
Debug.LogWarningFormat("Control Track Clip ({0}) is causing a prefab to instantiate itself recursively. Aborting further instances.", name);
|
||||
return Playable.Create(graph);
|
||||
}
|
||||
s_CreatedPrefabs.Add(prefabGameObject);
|
||||
}
|
||||
|
||||
Playable root = Playable.Null;
|
||||
var playables = new List<Playable>();
|
||||
|
||||
GameObject sourceObject = sourceGameObject.Resolve(graph.GetResolver());
|
||||
if (prefabGameObject != null)
|
||||
{
|
||||
Transform parenTransform = sourceObject != null ? sourceObject.transform : null;
|
||||
var controlPlayable = PrefabControlPlayable.Create(graph, prefabGameObject, parenTransform);
|
||||
|
||||
sourceObject = controlPlayable.GetBehaviour().prefabInstance;
|
||||
playables.Add(controlPlayable);
|
||||
}
|
||||
|
||||
m_Duration = PlayableBinding.DefaultDuration;
|
||||
m_SupportLoop = false;
|
||||
|
||||
controllingParticles = false;
|
||||
controllingDirectors = false;
|
||||
|
||||
if (sourceObject != null)
|
||||
{
|
||||
var directors = updateDirector ? GetComponent<PlayableDirector>(sourceObject) : k_EmptyDirectorsList;
|
||||
var particleSystems = updateParticle ? GetControllableParticleSystems(sourceObject) : k_EmptyParticlesList;
|
||||
|
||||
// update the duration and loop values (used for UI purposes) here
|
||||
// so they are tied to the latest gameObject bound
|
||||
UpdateDurationAndLoopFlag(directors, particleSystems);
|
||||
|
||||
var director = go.GetComponent<PlayableDirector>();
|
||||
if (director != null)
|
||||
m_ControlDirectorAsset = director.playableAsset;
|
||||
|
||||
if (go == sourceObject && prefabGameObject == null)
|
||||
{
|
||||
Debug.LogWarningFormat("Control Playable ({0}) is referencing the same PlayableDirector component than the one in which it is playing.", name);
|
||||
active = false;
|
||||
if (!searchHierarchy)
|
||||
updateDirector = false;
|
||||
}
|
||||
|
||||
if (active)
|
||||
CreateActivationPlayable(sourceObject, graph, playables);
|
||||
|
||||
if (updateDirector)
|
||||
SearchHierarchyAndConnectDirector(directors, graph, playables, prefabGameObject != null);
|
||||
|
||||
if (updateParticle)
|
||||
SearchHierarchyAndConnectParticleSystem(particleSystems, graph, playables);
|
||||
|
||||
if (updateITimeControl)
|
||||
SearchHierarchyAndConnectControlableScripts(GetControlableScripts(sourceObject), graph, playables);
|
||||
|
||||
// Connect Playables to Generic to Mixer
|
||||
root = ConnectPlayablesToMixer(graph, playables);
|
||||
}
|
||||
|
||||
if (prefabGameObject != null)
|
||||
s_CreatedPrefabs.Remove(prefabGameObject);
|
||||
|
||||
if (!root.IsValid())
|
||||
root = Playable.Create(graph);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static Playable ConnectPlayablesToMixer(PlayableGraph graph, List<Playable> playables)
|
||||
{
|
||||
var mixer = Playable.Create(graph, playables.Count);
|
||||
|
||||
for (int i = 0; i != playables.Count; ++i)
|
||||
{
|
||||
ConnectMixerAndPlayable(graph, mixer, playables[i], i);
|
||||
}
|
||||
|
||||
mixer.SetPropagateSetTime(true);
|
||||
|
||||
return mixer;
|
||||
}
|
||||
|
||||
void CreateActivationPlayable(GameObject root, PlayableGraph graph,
|
||||
List<Playable> outplayables)
|
||||
{
|
||||
var activation = ActivationControlPlayable.Create(graph, root, postPlayback);
|
||||
if (activation.IsValid())
|
||||
outplayables.Add(activation);
|
||||
}
|
||||
|
||||
void SearchHierarchyAndConnectParticleSystem(IEnumerable<ParticleSystem> particleSystems, PlayableGraph graph,
|
||||
List<Playable> outplayables)
|
||||
{
|
||||
foreach (var particleSystem in particleSystems)
|
||||
{
|
||||
if (particleSystem != null)
|
||||
{
|
||||
controllingParticles = true;
|
||||
outplayables.Add(ParticleControlPlayable.Create(graph, particleSystem, particleRandomSeed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchHierarchyAndConnectDirector(IEnumerable<PlayableDirector> directors, PlayableGraph graph,
|
||||
List<Playable> outplayables, bool disableSelfReferences)
|
||||
{
|
||||
foreach (var director in directors)
|
||||
{
|
||||
if (director != null)
|
||||
{
|
||||
if (director.playableAsset != m_ControlDirectorAsset)
|
||||
{
|
||||
outplayables.Add(DirectorControlPlayable.Create(graph, director));
|
||||
controllingDirectors = true;
|
||||
}
|
||||
// if this self references, disable the director.
|
||||
else if (disableSelfReferences)
|
||||
{
|
||||
director.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SearchHierarchyAndConnectControlableScripts(IEnumerable<MonoBehaviour> controlableScripts, PlayableGraph graph, List<Playable> outplayables)
|
||||
{
|
||||
foreach (var script in controlableScripts)
|
||||
{
|
||||
outplayables.Add(TimeControlPlayable.Create(graph, (ITimeControl)script));
|
||||
}
|
||||
}
|
||||
|
||||
static void ConnectMixerAndPlayable(PlayableGraph graph, Playable mixer, Playable playable,
|
||||
int portIndex)
|
||||
{
|
||||
graph.Connect(playable, 0, mixer, portIndex);
|
||||
mixer.SetInputWeight(playable, 1.0f);
|
||||
}
|
||||
|
||||
internal IList<T> GetComponent<T>(GameObject gameObject)
|
||||
{
|
||||
var components = new List<T>();
|
||||
if (gameObject != null)
|
||||
{
|
||||
if (searchHierarchy)
|
||||
{
|
||||
gameObject.GetComponentsInChildren<T>(true, components);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject.GetComponents<T>(components);
|
||||
}
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
internal static IEnumerable<MonoBehaviour> GetControlableScripts(GameObject root)
|
||||
{
|
||||
if (root == null)
|
||||
yield break;
|
||||
|
||||
foreach (var script in root.GetComponentsInChildren<MonoBehaviour>())
|
||||
{
|
||||
if (script is ITimeControl)
|
||||
yield return script;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateDurationAndLoopFlag(IList<PlayableDirector> directors, IList<ParticleSystem> particleSystems)
|
||||
{
|
||||
if (directors.Count == 0 && particleSystems.Count == 0)
|
||||
return;
|
||||
|
||||
const double invalidDuration = double.NegativeInfinity;
|
||||
|
||||
var maxDuration = invalidDuration;
|
||||
var supportsLoop = false;
|
||||
|
||||
foreach (var director in directors)
|
||||
{
|
||||
if (director.playableAsset != null)
|
||||
{
|
||||
var assetDuration = director.playableAsset.duration;
|
||||
|
||||
if (director.playableAsset is TimelineAsset && assetDuration > 0.0)
|
||||
// Timeline assets report being one tick shorter than they actually are, unless they are empty
|
||||
assetDuration = (double)((DiscreteTime)assetDuration).OneTickAfter();
|
||||
|
||||
maxDuration = Math.Max(maxDuration, assetDuration);
|
||||
supportsLoop = supportsLoop || director.extrapolationMode == DirectorWrapMode.Loop;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var particleSystem in particleSystems)
|
||||
{
|
||||
maxDuration = Math.Max(maxDuration, particleSystem.main.duration);
|
||||
supportsLoop = supportsLoop || particleSystem.main.loop;
|
||||
}
|
||||
|
||||
m_Duration = double.IsNegativeInfinity(maxDuration) ? PlayableBinding.DefaultDuration : maxDuration;
|
||||
m_SupportLoop = supportsLoop;
|
||||
}
|
||||
|
||||
IList<ParticleSystem> GetControllableParticleSystems(GameObject go)
|
||||
{
|
||||
var roots = new List<ParticleSystem>();
|
||||
|
||||
// searchHierarchy will look for particle systems on child objects.
|
||||
// once a particle system is found, all child particle systems are controlled with playables
|
||||
// unless they are subemitters
|
||||
|
||||
if (searchHierarchy || go.GetComponent<ParticleSystem>() != null)
|
||||
{
|
||||
GetControllableParticleSystems(go.transform, roots, s_SubEmitterCollector);
|
||||
s_SubEmitterCollector.Clear();
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
static void GetControllableParticleSystems(Transform t, ICollection<ParticleSystem> roots, HashSet<ParticleSystem> subEmitters)
|
||||
{
|
||||
var ps = t.GetComponent<ParticleSystem>();
|
||||
if (ps != null)
|
||||
{
|
||||
if (!subEmitters.Contains(ps))
|
||||
{
|
||||
roots.Add(ps);
|
||||
CacheSubEmitters(ps, subEmitters);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < t.childCount; ++i)
|
||||
{
|
||||
GetControllableParticleSystems(t.GetChild(i), roots, subEmitters);
|
||||
}
|
||||
}
|
||||
|
||||
static void CacheSubEmitters(ParticleSystem ps, HashSet<ParticleSystem> subEmitters)
|
||||
{
|
||||
if (ps == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < ps.subEmitters.subEmittersCount; i++)
|
||||
{
|
||||
subEmitters.Add(ps.subEmitters.GetSubEmitterSystem(i));
|
||||
// don't call this recursively. subEmitters are only simulated one level deep.
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void GatherProperties(PlayableDirector director, IPropertyCollector driver)
|
||||
{
|
||||
// This method is no longer called by Control Tracks.
|
||||
if (director == null)
|
||||
return;
|
||||
|
||||
// prevent infinite recursion
|
||||
if (s_ProcessedDirectors.Contains(director))
|
||||
return;
|
||||
s_ProcessedDirectors.Add(director);
|
||||
|
||||
var gameObject = sourceGameObject.Resolve(director);
|
||||
if (gameObject != null)
|
||||
{
|
||||
if (updateParticle)// case 1076850 -- drive all emitters, not just roots.
|
||||
PreviewParticles(driver, gameObject.GetComponentsInChildren<ParticleSystem>(true));
|
||||
|
||||
if (active)
|
||||
PreviewActivation(driver, new[] { gameObject });
|
||||
|
||||
if (updateITimeControl)
|
||||
PreviewTimeControl(driver, director, GetControlableScripts(gameObject));
|
||||
|
||||
if (updateDirector)
|
||||
PreviewDirectors(driver, GetComponent<PlayableDirector>(gameObject));
|
||||
}
|
||||
s_ProcessedDirectors.Remove(director);
|
||||
}
|
||||
|
||||
internal static void PreviewParticles(IPropertyCollector driver, IEnumerable<ParticleSystem> particles)
|
||||
{
|
||||
foreach (var ps in particles)
|
||||
{
|
||||
driver.AddFromName<ParticleSystem>(ps.gameObject, "randomSeed");
|
||||
driver.AddFromName<ParticleSystem>(ps.gameObject, "autoRandomSeed");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void PreviewActivation(IPropertyCollector driver, IEnumerable<GameObject> objects)
|
||||
{
|
||||
foreach (var gameObject in objects)
|
||||
driver.AddFromName(gameObject, "m_IsActive");
|
||||
}
|
||||
|
||||
internal static void PreviewTimeControl(IPropertyCollector driver, PlayableDirector director, IEnumerable<MonoBehaviour> scripts)
|
||||
{
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
var propertyPreview = script as IPropertyPreview;
|
||||
if (propertyPreview != null)
|
||||
propertyPreview.GatherProperties(director, driver);
|
||||
else
|
||||
driver.AddFromComponent(script.gameObject, script);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void PreviewDirectors(IPropertyCollector driver, IEnumerable<PlayableDirector> directors)
|
||||
{
|
||||
foreach (var childDirector in directors)
|
||||
{
|
||||
if (childDirector == null)
|
||||
continue;
|
||||
|
||||
var timeline = childDirector.playableAsset as TimelineAsset;
|
||||
if (timeline == null)
|
||||
continue;
|
||||
|
||||
timeline.GatherProperties(childDirector, driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48853ae485fa386428341ac1ea122570
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEngine.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// A Track whose clips control time-related elements on a GameObject.
|
||||
/// </summary>
|
||||
[TrackClipType(typeof(ControlPlayableAsset), false)]
|
||||
[ExcludeFromPreset]
|
||||
[TimelineHelpURL(typeof(ControlTrack))]
|
||||
public class ControlTrack : TrackAsset
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
private static readonly HashSet<PlayableDirector> s_ProcessedDirectors = new HashSet<PlayableDirector>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GatherProperties(PlayableDirector director, IPropertyCollector driver)
|
||||
{
|
||||
if (director == null)
|
||||
return;
|
||||
|
||||
// avoid recursion
|
||||
if (s_ProcessedDirectors.Contains(director))
|
||||
return;
|
||||
|
||||
s_ProcessedDirectors.Add(director);
|
||||
|
||||
var particlesToPreview = new HashSet<ParticleSystem>();
|
||||
var activationToPreview = new HashSet<GameObject>();
|
||||
var timeControlToPreview = new HashSet<MonoBehaviour>();
|
||||
var subDirectorsToPreview = new HashSet<PlayableDirector>();
|
||||
|
||||
foreach (var clip in GetClips())
|
||||
{
|
||||
var controlPlayableAsset = clip.asset as ControlPlayableAsset;
|
||||
if (controlPlayableAsset == null)
|
||||
continue;
|
||||
|
||||
var gameObject = controlPlayableAsset.sourceGameObject.Resolve(director);
|
||||
if (gameObject == null)
|
||||
continue;
|
||||
|
||||
if (controlPlayableAsset.updateParticle)
|
||||
particlesToPreview.UnionWith(gameObject.GetComponentsInChildren<ParticleSystem>(true));
|
||||
if (controlPlayableAsset.active)
|
||||
activationToPreview.Add(gameObject);
|
||||
if (controlPlayableAsset.updateITimeControl)
|
||||
timeControlToPreview.UnionWith(ControlPlayableAsset.GetControlableScripts(gameObject));
|
||||
if (controlPlayableAsset.updateDirector)
|
||||
subDirectorsToPreview.UnionWith(controlPlayableAsset.GetComponent<PlayableDirector>(gameObject));
|
||||
}
|
||||
|
||||
ControlPlayableAsset.PreviewParticles(driver, particlesToPreview);
|
||||
ControlPlayableAsset.PreviewActivation(driver, activationToPreview);
|
||||
ControlPlayableAsset.PreviewTimeControl(driver, director, timeControlToPreview);
|
||||
ControlPlayableAsset.PreviewDirectors(driver, subDirectorsToPreview);
|
||||
|
||||
s_ProcessedDirectors.Remove(director);
|
||||
|
||||
particlesToPreview.Clear();
|
||||
activationToPreview.Clear();
|
||||
timeControlToPreview.Clear();
|
||||
subDirectorsToPreview.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15e0374501f39d54eb30235764636e0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user