Unity-jump-proj

This commit is contained in:
2024-09-09 11:07:16 +03:00
parent 2c29906bbf
commit fd96a5627d
13707 changed files with 866380 additions and 0 deletions

View File

@ -0,0 +1,341 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(AnimationPlayableAsset)), CanEditMultipleObjects]
class AnimationPlayableAssetInspector : Editor
{
static class Styles
{
public static readonly GUIContent RotationText = L10n.TextContent("Rotation");
public static readonly GUIContent AnimClipText = L10n.TextContent("Animation Clip");
public static readonly GUIContent TransformOffsetTitle = L10n.TextContent("Clip Transform Offsets", "Use this to offset the root transform position and rotation relative to the track when playing this clip");
public static readonly GUIContent AnimationClipName = L10n.TextContent("Animation Clip Name");
public static readonly GUIContent MatchTargetFieldsTitle = L10n.TextContent("Offsets Match Fields", "Fields to apply when matching offsets on clips. The defaults can be set on the track.");
public static readonly GUIContent UseDefaults = L10n.TextContent("Use defaults");
public static readonly GUIContent RemoveStartOffset = L10n.TextContent("Remove Start Offset", "Makes playback of the clip play relative to first key of the root transform");
public static readonly GUIContent ApplyFootIK = L10n.TextContent("Foot IK", "Enable to apply foot IK to the AnimationClip when the target is humanoid.");
public static readonly GUIContent Loop = L10n.TextContent("Loop", "Whether the source Animation Clip loops during playback.");
}
TimelineWindow m_TimelineWindow;
GameObject m_Binding;
TimelineAnimationUtilities.OffsetEditMode m_OffsetEditMode = TimelineAnimationUtilities.OffsetEditMode.None;
EditorClip m_EditorClip;
EditorClip[] m_EditorClips;
SerializedProperty m_PositionProperty;
SerializedProperty m_RotationProperty;
SerializedProperty m_AnimClipProperty;
SerializedProperty m_UseTrackMatchFieldsProperty;
SerializedProperty m_MatchTargetFieldsProperty;
SerializedObject m_SerializedAnimClip;
SerializedProperty m_SerializedAnimClipName;
SerializedProperty m_RemoveStartOffsetProperty;
SerializedProperty m_ApplyFootIK;
SerializedProperty m_Loop;
Vector3 m_LastPosition;
Vector3 m_LastRotation;
public override void OnInspectorGUI()
{
if (target == null)
return;
serializedObject.Update();
if (!m_TimelineWindow) m_TimelineWindow = TimelineWindow.instance;
ShowAnimationClipField();
ShowRecordableClipRename();
ShowAnimationClipWarnings();
EditorGUI.BeginChangeCheck();
TransformOffsetsGUI();
// extra checks are because the context menu may need to cause a re-evaluate
bool changed = EditorGUI.EndChangeCheck() ||
m_LastPosition != m_PositionProperty.vector3Value ||
m_LastRotation != m_RotationProperty.vector3Value;
m_LastPosition = m_PositionProperty.vector3Value;
m_LastRotation = m_RotationProperty.vector3Value;
if (changed)
{
// updates the changed properties and pushes them to the active playable
serializedObject.ApplyModifiedProperties();
((AnimationPlayableAsset)target).LiveLink();
// force an evaluate to happen next frame
if (TimelineWindow.instance != null && TimelineWindow.instance.state != null)
{
TimelineWindow.instance.state.Evaluate();
}
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_ApplyFootIK, Styles.ApplyFootIK);
EditorGUILayout.PropertyField(m_Loop, Styles.Loop);
if (EditorGUI.EndChangeCheck())
TimelineEditor.Refresh(RefreshReason.ContentsModified);
serializedObject.ApplyModifiedProperties();
}
void ShowAnimationClipField()
{
bool disabled = m_EditorClips == null || m_EditorClips.Any(c => c.clip == null || c.clip.recordable);
using (new EditorGUI.DisabledScope(disabled))
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_AnimClipProperty, Styles.AnimClipText);
if (EditorGUI.EndChangeCheck())
{
// rename the timeline clips to match the animation name if it did previously
if (m_AnimClipProperty.objectReferenceValue != null && m_EditorClips != null)
{
var newName = m_AnimClipProperty.objectReferenceValue.name;
foreach (var c in m_EditorClips)
{
if (c == null || c.clip == null || c.clip.asset == null)
continue;
var apa = c.clip.asset as AnimationPlayableAsset;
if (apa != null && apa.clip != null && c.clip.displayName == apa.clip.name)
{
if (c.clip.GetParentTrack() != null)
Undo.RegisterCompleteObjectUndo(c.clip.GetParentTrack(), L10n.Tr("Inspector"));
c.clip.displayName = newName;
}
}
}
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
}
}
void TransformOffsetsMatchFieldsGUI()
{
var rect = EditorGUILayout.GetControlRect(true);
EditorGUI.BeginProperty(rect, Styles.MatchTargetFieldsTitle, m_UseTrackMatchFieldsProperty);
rect = EditorGUI.PrefixLabel(rect, Styles.MatchTargetFieldsTitle);
int oldIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUI.BeginChangeCheck();
bool val = m_UseTrackMatchFieldsProperty.boolValue;
val = EditorGUI.ToggleLeft(rect, Styles.UseDefaults, val);
if (EditorGUI.EndChangeCheck())
m_UseTrackMatchFieldsProperty.boolValue = val;
EditorGUI.indentLevel = oldIndent;
EditorGUI.EndProperty();
if (!val || m_UseTrackMatchFieldsProperty.hasMultipleDifferentValues)
{
EditorGUI.indentLevel++;
AnimationTrackInspector.MatchTargetsFieldGUI(m_MatchTargetFieldsProperty);
EditorGUI.indentLevel--;
}
}
void TransformOffsetsGUI()
{
if (ShouldShowOffsets())
{
EditorGUILayout.Space();
EditorGUILayout.LabelField(Styles.TransformOffsetTitle);
EditorGUI.indentLevel++;
using (new EditorGUI.DisabledScope(targets.Length > 1))
{
var previousOffsetMode = m_OffsetEditMode;
AnimationTrackInspector.ShowMotionOffsetEditModeToolbar(ref m_OffsetEditMode);
if (previousOffsetMode != m_OffsetEditMode)
{
SetTimeToClip();
SceneView.RepaintAll();
}
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_PositionProperty);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_RotationProperty, Styles.RotationText);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUI.indentLevel--;
TransformOffsetsMatchFieldsGUI();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_RemoveStartOffsetProperty, Styles.RemoveStartOffset);
if (EditorGUI.EndChangeCheck())
{
TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
Repaint();
}
}
}
void Reevaluate()
{
if (m_TimelineWindow != null && m_TimelineWindow.state != null)
{
m_TimelineWindow.state.Refresh();
m_TimelineWindow.state.EvaluateImmediate();
}
}
// Make sure the director time is within the bounds of the clip
void SetTimeToClip()
{
if (m_TimelineWindow != null && m_TimelineWindow.state != null)
{
m_TimelineWindow.state.editSequence.time = Math.Min(m_EditorClip.clip.end, Math.Max(m_EditorClip.clip.start, m_TimelineWindow.state.editSequence.time));
}
}
public void OnEnable()
{
if (target == null) // case 946080
return;
m_EditorClip = UnityEditor.Selection.activeObject as EditorClip;
m_EditorClips = UnityEditor.Selection.objects.OfType<EditorClip>().ToArray();
SceneView.duringSceneGui += OnSceneGUI;
m_PositionProperty = serializedObject.FindProperty("m_Position");
m_PositionProperty.isExpanded = true;
m_RotationProperty = serializedObject.FindProperty("m_EulerAngles");
m_AnimClipProperty = serializedObject.FindProperty("m_Clip");
m_UseTrackMatchFieldsProperty = serializedObject.FindProperty("m_UseTrackMatchFields");
m_MatchTargetFieldsProperty = serializedObject.FindProperty("m_MatchTargetFields");
m_RemoveStartOffsetProperty = serializedObject.FindProperty("m_RemoveStartOffset");
m_ApplyFootIK = serializedObject.FindProperty("m_ApplyFootIK");
m_Loop = serializedObject.FindProperty("m_Loop");
m_LastPosition = m_PositionProperty.vector3Value;
m_LastRotation = m_RotationProperty.vector3Value;
}
void OnDestroy()
{
SceneView.duringSceneGui -= OnSceneGUI;
}
void OnSceneGUI(SceneView sceneView)
{
DoManipulators();
}
Transform GetTransform()
{
if (m_Binding != null)
return m_Binding.transform;
if (m_TimelineWindow != null && m_TimelineWindow.state != null &&
m_TimelineWindow.state.editSequence.director != null &&
m_EditorClip != null && m_EditorClip.clip != null)
{
var obj = TimelineUtility.GetSceneGameObject(m_TimelineWindow.state.editSequence.director,
m_EditorClip.clip.GetParentTrack());
m_Binding = obj;
if (obj != null)
return obj.transform;
}
return null;
}
void DoManipulators()
{
if (m_EditorClip == null || m_EditorClip.clip == null)
return;
AnimationPlayableAsset animationPlayable = m_EditorClip.clip.asset as AnimationPlayableAsset;
AnimationTrack track = m_EditorClip.clip.GetParentTrack() as AnimationTrack;
Transform transform = GetTransform();
if (transform != null && animationPlayable != null && m_OffsetEditMode != TimelineAnimationUtilities.OffsetEditMode.None && track != null)
{
UndoExtensions.RegisterPlayableAsset(animationPlayable, L10n.Tr("Inspector"));
Vector3 position = transform.position;
Quaternion rotation = transform.rotation;
EditorGUI.BeginChangeCheck();
if (m_OffsetEditMode == TimelineAnimationUtilities.OffsetEditMode.Translation)
{
position = Handles.PositionHandle(position, Tools.pivotRotation == PivotRotation.Global ? Quaternion.identity : rotation);
}
else if (m_OffsetEditMode == TimelineAnimationUtilities.OffsetEditMode.Rotation)
{
rotation = Handles.RotationHandle(rotation, position);
}
if (EditorGUI.EndChangeCheck())
{
var res = TimelineAnimationUtilities.UpdateClipOffsets(animationPlayable, track, transform, position, rotation);
animationPlayable.position = res.position;
animationPlayable.eulerAngles = AnimationUtility.GetClosestEuler(res.rotation, animationPlayable.eulerAngles, RotationOrder.OrderZXY);
Reevaluate();
Repaint();
}
}
}
void ShowAnimationClipWarnings()
{
AnimationClip clip = m_AnimClipProperty.objectReferenceValue as AnimationClip;
if (clip == null)
{
EditorGUILayout.HelpBox(AnimationPlayableAssetEditor.k_NoClipAssignedError, MessageType.Warning);
}
else if (clip.legacy)
{
EditorGUILayout.HelpBox(AnimationPlayableAssetEditor.k_LegacyClipError, MessageType.Warning);
}
}
bool ShouldShowOffsets()
{
return targets.OfType<AnimationPlayableAsset>().All(x => x.hasRootTransforms);
}
void ShowRecordableClipRename()
{
if (targets.Length > 1 || m_EditorClip == null || m_EditorClip.clip == null || !m_EditorClip.clip.recordable)
return;
AnimationClip clip = m_AnimClipProperty.objectReferenceValue as AnimationClip;
if (clip == null || !AssetDatabase.IsSubAsset(clip))
return;
if (m_SerializedAnimClip == null)
{
m_SerializedAnimClip = new SerializedObject(clip);
m_SerializedAnimClipName = m_SerializedAnimClip.FindProperty("m_Name");
}
if (m_SerializedAnimClipName != null)
{
m_SerializedAnimClip.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.DelayedTextField(m_SerializedAnimClipName, Styles.AnimationClipName);
if (EditorGUI.EndChangeCheck())
m_SerializedAnimClip.ApplyModifiedProperties();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7852b99951997645ae7adaac5f0b083
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,516 @@
//#define PERF_PROFILE
using System;
using System.ComponentModel;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(AnimationTrack)), CanEditMultipleObjects]
class AnimationTrackInspector : TrackAssetInspector
{
static class Styles
{
public static GUIContent MatchTargetFieldsTitle = L10n.TextContent("Default Offset Match Fields", "Fields to apply when matching offsets on clips. These are the defaults, and can be overridden for each clip.");
public static readonly GUIContent PositionIcon = EditorGUIUtility.IconContent("MoveTool");
public static readonly GUIContent RotationIcon = EditorGUIUtility.IconContent("RotateTool");
public static GUIContent XTitle = EditorGUIUtility.TextContent("X");
public static GUIContent YTitle = EditorGUIUtility.TextContent("Y");
public static GUIContent ZTitle = EditorGUIUtility.TextContent("Z");
public static GUIContent PositionTitle = L10n.TextContent("Position");
public static GUIContent RotationTitle = L10n.TextContent("Rotation");
public static readonly GUIContent OffsetModeTitle = L10n.TextContent("Track Offsets");
public static readonly string TransformOffsetInfo = L10n.Tr("Transform offsets are applied to the entire track. Use this mode to play the animation track at a fixed position and rotation.");
public static readonly string SceneOffsetInfo = L10n.Tr("Scene offsets will use the existing transform as initial offsets. Use this to play the track from the gameObjects current position and rotation.");
public static readonly string AutoOffsetInfo = L10n.Tr("Auto will apply scene offsets if there is a controller attached to the animator and transform offsets otherwise.");
public static readonly string AutoOffsetWarning = L10n.Tr("This mode is deprecated may be removed in a future release.");
public static readonly string InheritedFromParent = L10n.Tr("Inherited");
public static readonly string InheritedToolTip = L10n.Tr("This value is inherited from it's parent track.");
public static readonly string AvatarMaskWarning = L10n.Tr("Applying an Avatar Mask to the base track may not properly mask Root Motion or Humanoid bones from an Animator Controller or other Timeline track.");
public static readonly GUIContent RecordingOffsets = L10n.TextContent("Recorded Offsets", "Offsets applied to recorded position and rotation keys");
public static readonly GUIContent RecordingIkApplied = L10n.TextContent("Apply Foot IK", "Applies Foot IK to recorded Animation.");
public static readonly GUIContent[] OffsetContents;
public static readonly GUIContent[] OffsetInheritContents;
static Styles()
{
var values = Enum.GetValues(typeof(TrackOffset));
OffsetContents = new GUIContent[values.Length];
OffsetInheritContents = new GUIContent[values.Length];
for (var index = 0; index < values.Length; index++)
{
var offset = (TrackOffset)index;
var name = ObjectNames.NicifyVariableName(L10n.Tr(offset.ToString()));
var memInfo = typeof(TrackOffset).GetMember(offset.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
name = ((DescriptionAttribute)attributes[0]).Description;
}
OffsetContents[index] = new GUIContent(name);
OffsetInheritContents[index] = new GUIContent(string.Format("{0} ({1})", InheritedFromParent, name));
}
}
}
TimelineAnimationUtilities.OffsetEditMode m_OffsetEditMode = TimelineAnimationUtilities.OffsetEditMode.None;
SerializedProperty m_MatchFieldsProperty;
SerializedProperty m_TrackPositionProperty;
SerializedProperty m_TrackRotationProperty;
SerializedProperty m_AvatarMaskProperty;
SerializedProperty m_ApplyAvatarMaskProperty;
SerializedProperty m_TrackOffsetProperty;
SerializedProperty m_RecordedOffsetPositionProperty;
SerializedProperty m_RecordedOffsetEulerProperty;
SerializedProperty m_RecordedApplyFootIK;
Vector3 m_lastPosition;
Vector3 m_lastRotation;
GUIContent m_TempContent = new GUIContent();
void Evaluate()
{
if (timelineWindow.state != null && timelineWindow.state.editSequence.director != null)
{
// force the update immediately, the deferred doesn't always work with the inspector
timelineWindow.state.editSequence.director.Evaluate();
}
}
void RebuildGraph()
{
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
public override void OnInspectorGUI()
{
using (new EditorGUI.DisabledScope(IsTrackLocked()))
{
serializedObject.Update();
DrawRootTransformOffset();
EditorGUI.BeginChangeCheck();
DrawRecordedProperties();
DrawAvatarProperties();
if (EditorGUI.EndChangeCheck())
RebuildGraph();
DrawMatchFieldsGUI();
serializedObject.ApplyModifiedProperties();
}
}
bool AnimatesRootTransform()
{
return targets.OfType<AnimationTrack>().All(t => t.AnimatesRootTransform());
}
bool ShouldDrawOffsets()
{
bool hasMultiple;
var offsetMode = GetOffsetMode(out hasMultiple);
if (hasMultiple)
return false;
if (offsetMode == TrackOffset.ApplySceneOffsets)
return false;
if (offsetMode == TrackOffset.ApplyTransformOffsets)
return true;
// Auto mode.
PlayableDirector director = this.m_Context as PlayableDirector;
if (director == null)
return false;
// If any bound animators have controllers don't show
foreach (var track in targets.OfType<AnimationTrack>())
{
var animator = track.GetBinding(director);
if (animator != null && animator.runtimeAnimatorController != null)
return false;
}
return true;
}
void DrawRootTransformOffset()
{
if (!AnimatesRootTransform())
return;
bool showWarning = SetupOffsetTooltip();
DrawRootTransformDropDown();
if (ShouldDrawOffsets())
{
EditorGUI.indentLevel++;
DrawRootMotionToolBar();
DrawRootMotionOffsetFields();
EditorGUI.indentLevel--;
}
if (showWarning)
{
EditorGUI.indentLevel++;
EditorGUILayout.HelpBox(Styles.AutoOffsetWarning, MessageType.Warning, true);
EditorGUI.indentLevel--;
}
}
bool SetupOffsetTooltip()
{
Styles.OffsetModeTitle.tooltip = string.Empty;
bool hasMultiple;
var offsetMode = GetOffsetMode(out hasMultiple);
bool showWarning = false;
if (!hasMultiple)
{
if (offsetMode == TrackOffset.ApplyTransformOffsets)
Styles.OffsetModeTitle.tooltip = Styles.TransformOffsetInfo;
else if (offsetMode == TrackOffset.ApplySceneOffsets)
Styles.OffsetModeTitle.tooltip = Styles.SceneOffsetInfo;
else if (offsetMode == TrackOffset.Auto)
{
Styles.OffsetModeTitle.tooltip = Styles.AutoOffsetInfo;
showWarning = true;
}
}
return showWarning;
}
void DrawRootTransformDropDown()
{
bool anySubTracks = targets.OfType<AnimationTrack>().Any(t => t.isSubTrack);
bool allSubTracks = targets.OfType<AnimationTrack>().All(t => t.isSubTrack);
bool mixed;
var rootOffsetMode = GetOffsetMode(out mixed);
// if we are showing subtracks, we need to show the current mode from the parent
// BUT keep it disabled
if (anySubTracks)
{
m_TempContent.tooltip = string.Empty;
if (mixed)
m_TempContent.text = EditorGUI.mixedValueContent.text;
else if (!allSubTracks)
m_TempContent.text = Styles.OffsetContents[(int)rootOffsetMode].text;
else
{
m_TempContent.text = Styles.OffsetInheritContents[(int)rootOffsetMode].text;
m_TempContent.tooltip = Styles.InheritedToolTip;
}
using (new EditorGUI.DisabledScope(true))
EditorGUILayout.LabelField(Styles.OffsetModeTitle, m_TempContent, EditorStyles.popup);
}
else
{
// We use an enum popup explicitly because it will handle the description attribute on the enum
using (new GUIMixedValueScope(mixed))
{
var rect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight);
EditorGUI.BeginProperty(rect, Styles.OffsetModeTitle, m_TrackOffsetProperty);
EditorGUI.BeginChangeCheck();
var result = (TrackOffset)EditorGUI.EnumPopup(rect, Styles.OffsetModeTitle, (TrackOffset)m_TrackOffsetProperty.intValue);
if (EditorGUI.EndChangeCheck())
{
m_TrackOffsetProperty.enumValueIndex = (int)result;
// this property changes the recordable state of the objects, so auto disable recording
if (TimelineWindow.instance != null)
{
if (TimelineWindow.instance.state != null)
TimelineWindow.instance.state.recording = false;
RebuildGraph();
}
}
EditorGUI.EndProperty();
}
}
}
void DrawMatchFieldsGUI()
{
if (!AnimatesRootTransform())
return;
m_MatchFieldsProperty.isExpanded = EditorGUILayout.Foldout(m_MatchFieldsProperty.isExpanded, Styles.MatchTargetFieldsTitle, true);
if (m_MatchFieldsProperty.isExpanded)
{
EditorGUI.indentLevel++;
MatchTargetsFieldGUI(m_MatchFieldsProperty);
EditorGUI.indentLevel--;
}
}
void DrawRootMotionOffsetFields()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_TrackPositionProperty);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_TrackRotationProperty, Styles.RotationTitle);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUILayout.Space();
if (EditorGUI.EndChangeCheck())
{
UpdateOffsets();
}
}
void DrawRootMotionToolBar()
{
bool disable = targets.Length > 1;
bool changed = false;
if (!disable)
{
// detects external changes
changed |= m_lastPosition != m_TrackPositionProperty.vector3Value || m_lastRotation != m_TrackRotationProperty.vector3Value;
m_lastPosition = m_TrackPositionProperty.vector3Value;
m_lastRotation = m_TrackRotationProperty.vector3Value;
SceneView.RepaintAll();
}
EditorGUI.BeginChangeCheck();
using (new EditorGUI.DisabledScope(disable))
ShowMotionOffsetEditModeToolbar(ref m_OffsetEditMode);
changed |= EditorGUI.EndChangeCheck();
if (changed)
{
UpdateOffsets();
}
}
void UpdateOffsets()
{
foreach (var track in targets.OfType<AnimationTrack>())
track.UpdateClipOffsets();
Evaluate();
}
void DrawAvatarProperties()
{
EditorGUILayout.PropertyField(m_ApplyAvatarMaskProperty);
if (m_ApplyAvatarMaskProperty.hasMultipleDifferentValues || m_ApplyAvatarMaskProperty.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_AvatarMaskProperty);
EditorGUI.indentLevel--;
}
if (targets.OfType<AnimationTrack>().Any(x => !x.isSubTrack))
EditorGUILayout.HelpBox(Styles.AvatarMaskWarning, MessageType.Warning);
EditorGUILayout.Space();
}
public static void ShowMotionOffsetEditModeToolbar(ref TimelineAnimationUtilities.OffsetEditMode motionOffset)
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.FlexibleSpace();
int newMotionOffsetMode = GUILayout.Toolbar((int)motionOffset, new[] { Styles.PositionIcon, Styles.RotationIcon });
if (GUI.changed)
{
if ((int)motionOffset == newMotionOffsetMode) //untoggle the button
motionOffset = TimelineAnimationUtilities.OffsetEditMode.None;
else
motionOffset = (TimelineAnimationUtilities.OffsetEditMode)newMotionOffsetMode;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(3);
}
public override void OnEnable()
{
base.OnEnable();
SceneView.duringSceneGui += OnSceneGUI;
m_MatchFieldsProperty = serializedObject.FindProperty("m_MatchTargetFields");
m_TrackPositionProperty = serializedObject.FindProperty("m_Position");
m_TrackRotationProperty = serializedObject.FindProperty("m_EulerAngles");
m_TrackOffsetProperty = serializedObject.FindProperty("m_TrackOffset");
m_AvatarMaskProperty = serializedObject.FindProperty("m_AvatarMask");
m_ApplyAvatarMaskProperty = serializedObject.FindProperty("m_ApplyAvatarMask");
m_RecordedOffsetPositionProperty = serializedObject.FindProperty("m_InfiniteClipOffsetPosition");
m_RecordedOffsetEulerProperty = serializedObject.FindProperty("m_InfiniteClipOffsetEulerAngles");
m_RecordedApplyFootIK = serializedObject.FindProperty("m_InfiniteClipApplyFootIK");
m_lastPosition = m_TrackPositionProperty.vector3Value;
m_lastRotation = m_TrackRotationProperty.vector3Value;
}
public void OnDestroy()
{
SceneView.duringSceneGui -= OnSceneGUI;
}
void OnSceneGUI(SceneView sceneView)
{
DoOffsetManipulator();
}
void DoOffsetManipulator()
{
if (targets.Length > 1) //do not edit the track offset on a multiple selection
return;
if (timelineWindow == null || timelineWindow.state == null || timelineWindow.state.editSequence.director == null)
return;
AnimationTrack animationTrack = target as AnimationTrack;
if (animationTrack != null && (animationTrack.trackOffset == TrackOffset.ApplyTransformOffsets) && m_OffsetEditMode != TimelineAnimationUtilities.OffsetEditMode.None)
{
var boundObject = TimelineUtility.GetSceneGameObject(timelineWindow.state.editSequence.director, animationTrack);
var boundObjectTransform = boundObject != null ? boundObject.transform : null;
var offsets = TimelineAnimationUtilities.GetTrackOffsets(animationTrack, boundObjectTransform);
EditorGUI.BeginChangeCheck();
switch (m_OffsetEditMode)
{
case TimelineAnimationUtilities.OffsetEditMode.Translation:
offsets.position = Handles.PositionHandle(offsets.position, (Tools.pivotRotation == PivotRotation.Global)
? Quaternion.identity
: offsets.rotation);
break;
case TimelineAnimationUtilities.OffsetEditMode.Rotation:
offsets.rotation = Handles.RotationHandle(offsets.rotation, offsets.position);
break;
}
if (EditorGUI.EndChangeCheck())
{
UndoExtensions.RegisterTrack(animationTrack, L10n.Tr("Inspector"));
TimelineAnimationUtilities.UpdateTrackOffset(animationTrack, boundObjectTransform, offsets);
Evaluate();
Repaint();
}
}
}
public void DrawRecordedProperties()
{
// only show if this applies to all targets
foreach (var track in targets)
{
var animationTrack = track as AnimationTrack;
if (animationTrack == null || animationTrack.inClipMode || animationTrack.infiniteClip == null || animationTrack.infiniteClip.empty)
return;
}
GUILayout.Label(Styles.RecordingOffsets);
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_RecordedOffsetPositionProperty, Styles.PositionTitle);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_RecordedOffsetEulerProperty, Styles.RotationTitle);
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_RecordedApplyFootIK, Styles.RecordingIkApplied);
EditorGUILayout.Space();
}
public static void MatchTargetsFieldGUI(SerializedProperty property)
{
const float ToggleWidth = 20;
int value = 0;
MatchTargetFields enumValue = (MatchTargetFields)property.intValue;
EditorGUI.BeginChangeCheck();
Rect rect = EditorGUILayout.GetControlRect(false, kLineHeight * 2);
Rect itemRect = new Rect(rect.x, rect.y, rect.width, kLineHeight);
EditorGUI.BeginProperty(rect, Styles.MatchTargetFieldsTitle, property);
float minWidth = 0, maxWidth = 0;
EditorStyles.label.CalcMinMaxWidth(Styles.XTitle, out minWidth, out maxWidth);
float width = minWidth + ToggleWidth;
GUILayout.BeginHorizontal();
Rect r = EditorGUI.PrefixLabel(itemRect, Styles.PositionTitle);
int oldIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
r.width = width;
value |= EditorGUI.ToggleLeft(r, Styles.XTitle, enumValue.HasAny(MatchTargetFields.PositionX)) ? (int)MatchTargetFields.PositionX : 0;
r.x += width;
value |= EditorGUI.ToggleLeft(r, Styles.YTitle, enumValue.HasAny(MatchTargetFields.PositionY)) ? (int)MatchTargetFields.PositionY : 0;
r.x += width;
value |= EditorGUI.ToggleLeft(r, Styles.ZTitle, enumValue.HasAny(MatchTargetFields.PositionZ)) ? (int)MatchTargetFields.PositionZ : 0;
EditorGUI.indentLevel = oldIndent;
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
itemRect.y += kLineHeight;
r = EditorGUI.PrefixLabel(itemRect, Styles.RotationTitle);
EditorGUI.indentLevel = 0;
r.width = width;
value |= EditorGUI.ToggleLeft(r, Styles.XTitle, enumValue.HasAny(MatchTargetFields.RotationX)) ? (int)MatchTargetFields.RotationX : 0;
r.x += width;
value |= EditorGUI.ToggleLeft(r, Styles.YTitle, enumValue.HasAny(MatchTargetFields.RotationY)) ? (int)MatchTargetFields.RotationY : 0;
r.x += width;
value |= EditorGUI.ToggleLeft(r, Styles.ZTitle, enumValue.HasAny(MatchTargetFields.RotationZ)) ? (int)MatchTargetFields.RotationZ : 0;
EditorGUI.indentLevel = oldIndent;
GUILayout.EndHorizontal();
EditorGUI.EndProperty();
if (EditorGUI.EndChangeCheck())
{
property.intValue = value;
}
}
static TrackOffset GetOffsetMode(AnimationTrack track)
{
if (track.isSubTrack)
{
var parent = track.parent as AnimationTrack;
if (parent != null) // fallback to the current track if there is an error
track = parent;
}
return track.trackOffset;
}
// gets the current mode,
TrackOffset GetOffsetMode(out bool hasMultiple)
{
var rootOffsetMode = GetOffsetMode(target as AnimationTrack);
hasMultiple = targets.OfType<AnimationTrack>().Any(t => GetOffsetMode(t) != rootOffsetMode);
return rootOffsetMode;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: edc553b1b3c8a25438b62783410b26ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,47 @@
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
// Simple inspector used by built in assets
// that only need to hide the script field
class BasicAssetInspector : Editor, IInspectorChangeHandler
{
bool m_ShouldRebuild;
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
serializedObject.Update();
SerializedProperty property = serializedObject.GetIterator();
bool expanded = true;
while (property.NextVisible(expanded))
{
expanded = false;
if (SkipField(property.propertyPath))
continue;
EditorGUILayout.PropertyField(property, true);
}
m_ShouldRebuild = serializedObject.ApplyModifiedProperties();
EditorGUI.EndChangeCheck();
}
public virtual void OnPlayableAssetChangedInInspector()
{
if (m_ShouldRebuild)
{
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
m_ShouldRebuild = false;
}
static bool SkipField(string fieldName)
{
return fieldName == "m_Script";
}
}
[CustomEditor(typeof(ActivationPlayableAsset))]
class ActivationPlayableAssetInspector : BasicAssetInspector { }
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e086b15460c228c4f9b116f0e3e2f175
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
// Preset libraries
static class BuiltInPresets
{
static CurvePresetLibrary s_BlendInPresets;
static CurvePresetLibrary s_BlendOutPresets;
internal static CurvePresetLibrary blendInPresets
{
get
{
if (s_BlendInPresets == null)
{
s_BlendInPresets = ScriptableObject.CreateInstance<CurvePresetLibrary>();
s_BlendInPresets.Add(new AnimationCurve(CurveEditorWindow.GetConstantKeys(1f)), "None");
s_BlendInPresets.Add(new AnimationCurve(CurveEditorWindow.GetLinearKeys()), "Linear");
s_BlendInPresets.Add(new AnimationCurve(CurveEditorWindow.GetEaseInKeys()), "EaseIn");
s_BlendInPresets.Add(new AnimationCurve(CurveEditorWindow.GetEaseOutKeys()), "EaseOut");
s_BlendInPresets.Add(new AnimationCurve(CurveEditorWindow.GetEaseInOutKeys()), "EaseInOut");
}
return s_BlendInPresets;
}
}
internal static CurvePresetLibrary blendOutPresets
{
get
{
if (s_BlendOutPresets == null)
{
s_BlendOutPresets = ScriptableObject.CreateInstance<CurvePresetLibrary>();
s_BlendOutPresets.Add(new AnimationCurve(CurveEditorWindow.GetConstantKeys(1f)), "None");
s_BlendOutPresets.Add(ReverseCurve(new AnimationCurve(CurveEditorWindow.GetLinearKeys())), "Linear");
s_BlendOutPresets.Add(ReverseCurve(new AnimationCurve(CurveEditorWindow.GetEaseInKeys())), "EaseIn");
s_BlendOutPresets.Add(ReverseCurve(new AnimationCurve(CurveEditorWindow.GetEaseOutKeys())), "EaseOut");
s_BlendOutPresets.Add(ReverseCurve(new AnimationCurve(CurveEditorWindow.GetEaseInOutKeys())), "EaseInOut");
}
return s_BlendOutPresets;
}
}
static AnimationCurve ReverseCurve(AnimationCurve curve)
{
Keyframe[] keys = curve.keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].value = 1 - keys[i].value;
keys[i].inTangent *= -1;
keys[i].outTangent *= -1;
}
curve.keys = keys;
return curve;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae05f0dd1cf145e4e8e905c7971ee433
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f68c7f7359094f045930a108c444e7a4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,855 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(EditorClip)), CanEditMultipleObjects]
class ClipInspector : Editor
{
internal static class Styles
{
public static readonly GUIContent StartName = L10n.TextContent("Start", "The start time of the clip");
public static readonly GUIContent DurationName = L10n.TextContent("Duration", "The length of the clip");
public static readonly GUIContent EndName = L10n.TextContent("End", "The end time of the clip");
public static readonly GUIContent EaseInDurationName = L10n.TextContent("Ease In Duration", "The length of the ease in");
public static readonly GUIContent BlendInDurationName = L10n.TextContent("Blend In Duration", "The length of the blend in");
public static readonly GUIContent EaseOutDurationName = L10n.TextContent("Ease Out Duration", "The length of the ease out");
public static readonly GUIContent BlendOutDurationName = L10n.TextContent("Blend Out Duration", "The length of the blend out");
public static readonly GUIContent ClipInName = L10n.TextContent("Clip In", "Start the clip at this local time");
public static readonly GUIContent TimeScaleName = L10n.TextContent("Speed Multiplier", "Time scale of the playback speed");
public static readonly GUIContent PreExtrapolateLabel = L10n.TextContent("Pre-Extrapolate", "Extrapolation used prior to the first clip");
public static readonly GUIContent PostExtrapolateLabel = L10n.TextContent("Post-Extrapolate", "Extrapolation used after a clip ends");
public static readonly GUIContent BlendInCurveName = L10n.TextContent("In", "Blend In Curve");
public static readonly GUIContent BlendOutCurveName = L10n.TextContent("Out", "Blend Out Curve");
public static readonly GUIContent PreviewTitle = L10n.TextContent("Curve Editor");
public static readonly GUIContent ClipTimingTitle = L10n.TextContent("Clip Timing");
public static readonly GUIContent AnimationExtrapolationTitle = L10n.TextContent("Animation Extrapolation");
public static readonly GUIContent BlendCurvesTitle = L10n.TextContent("Blend Curves");
public static readonly GUIContent GroupTimingTitle = L10n.TextContent("Multiple Clip Timing");
public static readonly GUIContent MultipleClipsSelectedIncompatibleCapabilitiesWarning = L10n.TextContent("Multiple clips selected. Only common properties are shown.");
public static readonly GUIContent MultipleSelectionTitle = L10n.TextContent("Timeline Clips");
public static readonly GUIContent MultipleClipStartName = L10n.TextContent("Start", "The start time of the clip group");
public static readonly GUIContent MultipleClipEndName = L10n.TextContent("End", "The end time of the clip group");
public static readonly GUIContent TimelineClipFG = DirectorStyles.IconContent("TimelineClipFG");
public static readonly GUIContent TimelineClipBG = DirectorStyles.IconContent("TimelineClipBG");
}
class EditorClipSelection : ICurvesOwnerInspectorWrapper
{
public EditorClip editorClip { get; }
public TimelineClip clip
{
get { return editorClip == null ? null : editorClip.clip; }
}
public SerializedObject serializedPlayableAsset { get; }
public ICurvesOwner curvesOwner
{
get { return clip; }
}
public int lastCurveVersion { get; set; }
public double lastEvalTime { get; set; }
public EditorClipSelection(EditorClip anEditorClip)
{
editorClip = anEditorClip;
lastCurveVersion = -1;
lastEvalTime = -1;
var so = new SerializedObject(editorClip);
var playableAssetProperty = so.FindProperty("m_Clip.m_Asset");
if (playableAssetProperty != null)
{
var asset = playableAssetProperty.objectReferenceValue as UnityEngine.Playables.PlayableAsset;
if (asset != null)
serializedPlayableAsset = new SerializedObject(asset);
}
}
public double ToLocalTime(double time)
{
return clip == null ? time : clip.ToLocalTime(time);
}
}
enum PreviewCurveState
{
None = 0,
MixIn = 1,
MixOut = 2
}
SerializedProperty m_DisplayNameProperty;
SerializedProperty m_BlendInDurationProperty;
SerializedProperty m_BlendOutDurationProperty;
SerializedProperty m_EaseInDurationProperty;
SerializedProperty m_EaseOutDurationProperty;
SerializedProperty m_ClipInProperty;
SerializedProperty m_TimeScaleProperty;
SerializedProperty m_PostExtrapolationModeProperty;
SerializedProperty m_PreExtrapolationModeProperty;
SerializedProperty m_PostExtrapolationTimeProperty;
SerializedProperty m_PreExtrapolationTimeProperty;
SerializedProperty m_MixInCurveProperty;
SerializedProperty m_MixOutCurveProperty;
SerializedProperty m_BlendInCurveModeProperty;
SerializedProperty m_BlendOutCurveModeProperty;
void InitializeProperties()
{
m_DisplayNameProperty = serializedObject.FindProperty("m_Clip.m_DisplayName");
m_BlendInDurationProperty = serializedObject.FindProperty("m_Clip.m_BlendInDuration");
m_BlendOutDurationProperty = serializedObject.FindProperty("m_Clip.m_BlendOutDuration");
m_EaseInDurationProperty = serializedObject.FindProperty("m_Clip.m_EaseInDuration");
m_EaseOutDurationProperty = serializedObject.FindProperty("m_Clip.m_EaseOutDuration");
m_ClipInProperty = serializedObject.FindProperty("m_Clip.m_ClipIn");
m_TimeScaleProperty = serializedObject.FindProperty("m_Clip.m_TimeScale");
m_PostExtrapolationModeProperty = serializedObject.FindProperty("m_Clip.m_PostExtrapolationMode");
m_PreExtrapolationModeProperty = serializedObject.FindProperty("m_Clip.m_PreExtrapolationMode");
m_PostExtrapolationTimeProperty = serializedObject.FindProperty("m_Clip.m_PostExtrapolationTime");
m_PreExtrapolationTimeProperty = serializedObject.FindProperty("m_Clip.m_PreExtrapolationTime");
m_MixInCurveProperty = serializedObject.FindProperty("m_Clip.m_MixInCurve");
m_MixOutCurveProperty = serializedObject.FindProperty("m_Clip.m_MixOutCurve");
m_BlendInCurveModeProperty = serializedObject.FindProperty("m_Clip.m_BlendInCurveMode");
m_BlendOutCurveModeProperty = serializedObject.FindProperty("m_Clip.m_BlendOutCurveMode");
}
TimelineAsset m_TimelineAsset;
List<EditorClipSelection> m_SelectionCache;
Editor m_SelectedPlayableAssetsInspector;
ClipInspectorCurveEditor m_ClipCurveEditor;
CurvePresetLibrary m_CurvePresets;
bool m_IsClipAssetInspectorExpanded = true;
GUIContent m_ClipAssetTitle = new GUIContent();
string m_MultiselectionHeaderTitle;
ClipInspectorSelectionInfo m_SelectionInfo;
// the state of the mixin curve preview
PreviewCurveState m_PreviewCurveState;
const double k_TimeScaleSensitivity = 0.003;
bool hasMultipleSelection
{
get { return targets.Length > 1; }
}
float currentFrameRate
{
get { return m_TimelineAsset != null ? (float)m_TimelineAsset.editorSettings.frameRate : (float)TimelineAsset.EditorSettings.kDefaultFrameRate; }
}
bool selectionHasIncompatibleCapabilities
{
get
{
return !(m_SelectionInfo.supportsBlending
&& m_SelectionInfo.supportsClipIn
&& m_SelectionInfo.supportsExtrapolation
&& m_SelectionInfo.supportsSpeedMultiplier);
}
}
public override bool RequiresConstantRepaint()
{
return base.RequiresConstantRepaint() || (m_SelectedPlayableAssetsInspector != null && m_SelectedPlayableAssetsInspector.RequiresConstantRepaint());
}
internal override void OnHeaderTitleGUI(Rect titleRect, string header)
{
if (hasMultipleSelection)
{
base.OnHeaderTitleGUI(titleRect, m_MultiselectionHeaderTitle);
return;
}
if (m_DisplayNameProperty != null)
{
using (new EditorGUI.DisabledScope(!IsEnabled()))
{
serializedObject.Update();
if (IsLocked())
{
base.OnHeaderTitleGUI(titleRect, m_DisplayNameProperty.stringValue);
}
else
{
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedTextField(titleRect, m_DisplayNameProperty, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
ApplyModifiedProperties();
TimelineWindow.RepaintIfEditingTimelineAsset(m_TimelineAsset);
}
}
}
}
}
internal override Rect DrawHeaderHelpAndSettingsGUI(Rect r)
{
using (new EditorGUI.DisabledScope(IsLocked()))
{
var helpSize = EditorStyles.iconButton.CalcSize(EditorGUI.GUIContents.helpIcon);
const int kTopMargin = 5;
// Show Editor Header Items.
return EditorGUIUtility.DrawEditorHeaderItems(new Rect(r.xMax - helpSize.x, r.y + kTopMargin, helpSize.x, helpSize.y), targets);
}
}
internal override void OnHeaderIconGUI(Rect iconRect)
{
using (new EditorGUI.DisabledScope(IsLocked()))
{
var bgColor = Color.white;
if (!EditorGUIUtility.isProSkin)
bgColor.a = 0.55f;
using (new GUIColorOverride(bgColor))
{
GUI.Label(iconRect, Styles.TimelineClipBG);
}
var fgColor = Color.white;
if (m_SelectionInfo != null && m_SelectionInfo.uniqueParentTracks.Count == 1)
fgColor = TrackResourceCache.GetTrackColor(m_SelectionInfo.uniqueParentTracks.First());
using (new GUIColorOverride(fgColor))
{
GUI.Label(iconRect, Styles.TimelineClipFG);
}
}
}
public void OnEnable()
{
Undo.undoRedoPerformed += OnUndoRedoPerformed;
m_ClipCurveEditor = new ClipInspectorCurveEditor();
m_SelectionCache = new List<EditorClipSelection>();
var selectedClips = new List<TimelineClip>();
foreach (var editorClipObject in targets)
{
var editorClip = editorClipObject as EditorClip;
if (editorClip != null)
{
//all selected clips should have the same TimelineAsset
if (!IsTimelineAssetValidForEditorClip(editorClip))
{
m_SelectionCache.Clear();
return;
}
m_SelectionCache.Add(new EditorClipSelection(editorClip));
selectedClips.Add(editorClip.clip);
}
}
InitializeProperties();
m_SelectionInfo = new ClipInspectorSelectionInfo(selectedClips);
if (m_SelectionInfo.selectedAssetTypesAreHomogeneous)
{
var selectedAssets = m_SelectionCache.Select(e => e.clip.asset).ToArray();
m_SelectedPlayableAssetsInspector = TimelineInspectorUtility.GetInspectorForObjects(selectedAssets, m_SelectedPlayableAssetsInspector);
}
m_MultiselectionHeaderTitle = m_SelectionCache.Count + " " + Styles.MultipleSelectionTitle.text;
m_ClipAssetTitle.text = PlayableAssetSectionTitle();
}
void OnDisable()
{
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
DestroyImmediate(m_SelectedPlayableAssetsInspector);
}
void DrawClipProperties()
{
var dirtyEditorClipSelection = m_SelectionCache.Where(s => s.editorClip.GetHashCode() != s.editorClip.lastHash);
UnselectCurves();
EditorGUI.BeginChangeCheck();
//Group Selection
if (hasMultipleSelection)
{
GUILayout.Label(Styles.GroupTimingTitle);
EditorGUI.indentLevel++;
DrawGroupSelectionProperties();
EditorGUI.indentLevel--;
EditorGUILayout.Space();
}
//Draw clip timing
GUILayout.Label(Styles.ClipTimingTitle);
if (hasMultipleSelection && selectionHasIncompatibleCapabilities)
{
GUILayout.Label(Styles.MultipleClipsSelectedIncompatibleCapabilitiesWarning, EditorStyles.helpBox);
}
EditorGUI.indentLevel++;
if (!m_SelectionInfo.containsAtLeastTwoClipsOnTheSameTrack)
{
DrawStartTimeField();
DrawEndTimeField();
}
if (!hasMultipleSelection)
{
DrawDurationProperty();
}
if (m_SelectionInfo.supportsBlending)
{
EditorGUILayout.Space();
DrawBlendingProperties();
}
if (m_SelectionInfo.supportsClipIn)
{
EditorGUILayout.Space();
DrawClipInProperty();
}
if (!hasMultipleSelection && m_SelectionInfo.supportsSpeedMultiplier)
{
EditorGUILayout.Space();
DrawTimeScale();
}
EditorGUI.indentLevel--;
bool hasDirtyEditorClips = false;
foreach (var editorClipSelection in dirtyEditorClipSelection)
{
EditorUtility.SetDirty(editorClipSelection.editorClip);
hasDirtyEditorClips = true;
}
//Re-evaluate the graph in case of a change in properties
bool propertiesHaveChanged = false;
if (EditorGUI.EndChangeCheck() || hasDirtyEditorClips)
{
if (TimelineWindow.IsEditingTimelineAsset(m_TimelineAsset) && TimelineWindow.instance.state != null)
{
TimelineWindow.instance.state.Evaluate();
TimelineWindow.instance.Repaint();
}
propertiesHaveChanged = true;
}
//Draw Animation Extrapolation
if (m_SelectionInfo.supportsExtrapolation)
{
EditorGUILayout.Space();
GUILayout.Label(Styles.AnimationExtrapolationTitle);
EditorGUI.indentLevel++;
DrawExtrapolationOptions();
EditorGUI.indentLevel--;
}
//Blend curves
if (m_SelectionInfo.supportsBlending)
{
EditorGUILayout.Space();
GUILayout.Label(Styles.BlendCurvesTitle);
EditorGUI.indentLevel++;
DrawBlendOptions();
EditorGUI.indentLevel--;
}
EditorGUILayout.Space();
if (CanShowPlayableAssetInspector())
{
DrawClipAssetGui();
}
if (propertiesHaveChanged)
{
foreach (var item in m_SelectionCache)
item.editorClip.lastHash = item.editorClip.GetHashCode();
m_SelectionInfo.Update();
}
}
public override void OnInspectorGUI()
{
if (TimelineWindow.instance == null || m_TimelineAsset == null)
return;
using (new EditorGUI.DisabledScope(IsLocked()))
{
EditMode.HandleModeClutch();
serializedObject.Update();
DrawClipProperties();
ApplyModifiedProperties();
}
}
internal override bool IsEnabled()
{
if (!TimelineUtility.IsCurrentSequenceValid() || IsCurrentSequenceReadOnly())
return false;
if (m_TimelineAsset != TimelineWindow.instance.state.editSequence.asset)
return false;
return base.IsEnabled();
}
void DrawTimeScale()
{
var inputEvent = InputEvent.None;
var newEndTime = m_SelectionInfo.end;
var oldTimeScale = m_TimeScaleProperty.doubleValue;
EditorGUI.BeginChangeCheck();
var newTimeScale = TimelineInspectorUtility.DelayedAndDraggableDoubleField(Styles.TimeScaleName, oldTimeScale, ref inputEvent, k_TimeScaleSensitivity);
if (EditorGUI.EndChangeCheck())
{
newTimeScale = newTimeScale.Clamp(TimelineClip.kTimeScaleMin, TimelineClip.kTimeScaleMax);
newEndTime = m_SelectionInfo.start + (m_SelectionInfo.duration * oldTimeScale / newTimeScale);
}
EditMode.inputHandler.ProcessTrim(inputEvent, newEndTime, true);
}
void DrawStartTimeField()
{
var inputEvent = InputEvent.None;
var newStart = TimelineInspectorUtility.TimeFieldUsingTimeReference(Styles.StartName, m_SelectionInfo.multipleClipStart, false, m_SelectionInfo.hasMultipleStartValues, currentFrameRate, 0.0, TimelineClip.kMaxTimeValue, ref inputEvent);
if (inputEvent.InputHasBegun() && m_SelectionInfo.hasMultipleStartValues)
{
var items = ItemsUtils.ToItems(m_SelectionInfo.clips);
EditMode.inputHandler.SetValueForEdge(items, AttractedEdge.Left, newStart); //if the field has multiple values, set the same start on all selected clips
m_SelectionInfo.Update(); //clips could have moved relative to each other, recalculate
}
EditMode.inputHandler.ProcessMove(inputEvent, newStart);
}
void DrawEndTimeField()
{
var inputEvent = InputEvent.None;
var newEndTime = TimelineInspectorUtility.TimeFieldUsingTimeReference(Styles.EndName, m_SelectionInfo.multipleClipEnd, false, m_SelectionInfo.hasMultipleEndValues, currentFrameRate, 0, TimelineClip.kMaxTimeValue, ref inputEvent);
if (inputEvent.InputHasBegun() && m_SelectionInfo.hasMultipleEndValues)
{
var items = ItemsUtils.ToItems(m_SelectionInfo.clips);
EditMode.inputHandler.SetValueForEdge(items, AttractedEdge.Right, newEndTime); //if the field has multiple value, set the same end on all selected clips
m_SelectionInfo.Update(); //clips could have moved relative to each other, recalculate
}
var newStartValue = m_SelectionInfo.multipleClipStart + (newEndTime - m_SelectionInfo.multipleClipEnd);
EditMode.inputHandler.ProcessMove(inputEvent, newStartValue);
}
void DrawClipAssetGui()
{
const float labelIndent = 34;
if (m_SelectedPlayableAssetsInspector == null)
return;
var rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebar);
var oldWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = rect.width - labelIndent;
m_IsClipAssetInspectorExpanded = EditorGUI.FoldoutTitlebar(rect, m_ClipAssetTitle, m_IsClipAssetInspectorExpanded, false);
EditorGUIUtility.labelWidth = oldWidth;
if (m_IsClipAssetInspectorExpanded)
{
EditorGUILayout.Space();
EditorGUI.indentLevel++;
ShowPlayableAssetInspector();
EditorGUI.indentLevel--;
}
}
void DrawExtrapolationOptions()
{
// PreExtrapolation
var preExtrapolationTime = m_PreExtrapolationTimeProperty.doubleValue;
bool hasPreExtrap = preExtrapolationTime > 0.0;
if (hasPreExtrap)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(m_PreExtrapolationModeProperty, Styles.PreExtrapolateLabel);
using (new GUIMixedValueScope(m_PreExtrapolationTimeProperty.hasMultipleDifferentValues))
EditorGUILayout.DoubleField(preExtrapolationTime, EditorStyles.label);
EditorGUILayout.EndHorizontal();
}
// PostExtrapolation
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_PostExtrapolationModeProperty, Styles.PostExtrapolateLabel);
if (EditorGUI.EndChangeCheck())
{
ApplyModifiedProperties();
//recalculate Extrapolation times to update next clip pre-extrapolation
foreach (var track in m_SelectionInfo.uniqueParentTracks)
Extrapolation.CalculateExtrapolationTimes(track);
TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
}
using (new GUIMixedValueScope(m_PostExtrapolationTimeProperty.hasMultipleDifferentValues))
EditorGUILayout.DoubleField(m_PostExtrapolationTimeProperty.doubleValue, EditorStyles.label);
EditorGUILayout.EndHorizontal();
}
}
void OnDestroy()
{
DestroyImmediate(m_SelectedPlayableAssetsInspector);
}
public override GUIContent GetPreviewTitle()
{
return Styles.PreviewTitle;
}
public override bool HasPreviewGUI()
{
return m_PreviewCurveState != PreviewCurveState.None;
}
public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
{
if (m_PreviewCurveState != PreviewCurveState.None && m_ClipCurveEditor != null)
{
SetCurveEditorTrackHead();
m_ClipCurveEditor.OnGUI(r, m_CurvePresets);
}
}
void SetCurveEditorTrackHead()
{
if (TimelineWindow.instance == null || TimelineWindow.instance.state == null)
return;
if (hasMultipleSelection)
return;
var editorClip = target as EditorClip;
if (editorClip == null)
return;
var director = TimelineWindow.instance.state.editSequence.director;
if (director == null)
return;
m_ClipCurveEditor.trackTime = ClipInspectorCurveEditor.kDisableTrackTime;
}
void UnselectCurves()
{
if (Event.current.type == EventType.MouseDown)
{
if (m_ClipCurveEditor != null)
m_ClipCurveEditor.SetUpdateCurveCallback(null);
m_PreviewCurveState = PreviewCurveState.None;
}
}
// Callback when the mixin/mixout properties are clicked on
void OnMixCurveSelected(string title, CurvePresetLibrary library, SerializedProperty curveSelected, bool easeIn)
{
m_PreviewCurveState = easeIn ? PreviewCurveState.MixIn : PreviewCurveState.MixOut;
m_CurvePresets = library;
var animationCurve = curveSelected.animationCurveValue;
m_ClipCurveEditor.headerString = title;
m_ClipCurveEditor.SetCurve(animationCurve);
m_ClipCurveEditor.SetSelected(animationCurve);
if (easeIn)
m_ClipCurveEditor.SetUpdateCurveCallback(MixInCurveUpdated);
else
m_ClipCurveEditor.SetUpdateCurveCallback(MixOutCurveUpdated);
Repaint();
}
// callback when the mix property is updated
void MixInCurveUpdated(AnimationCurve curve, EditorCurveBinding binding)
{
curve.keys = CurveEditUtility.SanitizeCurveKeys(curve.keys, true);
m_MixInCurveProperty.animationCurveValue = curve;
ApplyModifiedProperties();
var editorClip = target as EditorClip;
if (editorClip != null)
editorClip.lastHash = editorClip.GetHashCode();
RefreshCurves();
}
void MixOutCurveUpdated(AnimationCurve curve, EditorCurveBinding binding)
{
curve.keys = CurveEditUtility.SanitizeCurveKeys(curve.keys, false);
m_MixOutCurveProperty.animationCurveValue = curve;
ApplyModifiedProperties();
var editorClip = target as EditorClip;
if (editorClip != null)
editorClip.lastHash = editorClip.GetHashCode();
RefreshCurves();
}
void RefreshCurves()
{
AnimationCurvePreviewCache.ClearCache();
TimelineWindow.RepaintIfEditingTimelineAsset(m_TimelineAsset);
Repaint();
}
void DrawBlendCurve(GUIContent title, SerializedProperty modeProperty, SerializedProperty curveProperty, Action<SerializedProperty> onCurveClick)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(modeProperty, title);
if (hasMultipleSelection)
{
GUILayout.FlexibleSpace();
}
else
{
using (new EditorGUI.DisabledScope(modeProperty.intValue != (int)TimelineClip.BlendCurveMode.Manual))
{
ClipInspectorCurveEditor.CurveField(GUIContent.none, curveProperty, onCurveClick);
}
}
EditorGUILayout.EndHorizontal();
}
void ShowPlayableAssetInspector()
{
if (!m_SelectionInfo.selectedAssetTypesAreHomogeneous)
return;
if (m_SelectedPlayableAssetsInspector != null)
{
if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout)
{
foreach (var selectedItem in m_SelectionCache)
CurvesOwnerInspectorHelper.PreparePlayableAsset(selectedItem);
}
EditorGUI.BeginChangeCheck();
using (new EditorGUI.DisabledScope(IsLocked()))
{
m_SelectedPlayableAssetsInspector.OnInspectorGUI();
}
if (EditorGUI.EndChangeCheck())
{
MarkClipsDirty();
if (TimelineWindow.IsEditingTimelineAsset(m_TimelineAsset) && TimelineWindow.instance.state != null)
{
var inspectorChangeHandler = m_SelectedPlayableAssetsInspector as IInspectorChangeHandler;
if (inspectorChangeHandler != null)
inspectorChangeHandler.OnPlayableAssetChangedInInspector();
else
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
}
}
}
void ApplyModifiedProperties()
{
// case 926861 - we need to force the track to be dirty since modifying the clip does not
// automatically mark the track asset as dirty
if (serializedObject.ApplyModifiedProperties())
{
foreach (var obj in serializedObject.targetObjects)
{
var editorClip = obj as EditorClip;
if (editorClip != null && editorClip.clip != null && editorClip.clip.GetParentTrack() != null)
{
editorClip.clip.MarkDirty();
EditorUtility.SetDirty(editorClip.clip.GetParentTrack());
}
}
}
}
void MarkClipsDirty()
{
foreach (var obj in targets)
{
var editorClip = obj as EditorClip;
if (editorClip != null && editorClip.clip != null)
{
editorClip.clip.MarkDirty();
}
}
}
string PlayableAssetSectionTitle()
{
var firstSelectedClipAsset = m_SelectionCache.Any() ? m_SelectionCache.First().clip.asset : null;
return firstSelectedClipAsset != null
? ObjectNames.NicifyVariableName(firstSelectedClipAsset.GetType().Name)
: string.Empty;
}
bool IsTimelineAssetValidForEditorClip(EditorClip editorClip)
{
var trackAsset = editorClip.clip.GetParentTrack();
if (trackAsset == null)
return false;
var clipTimelineAsset = trackAsset.timelineAsset;
if (m_TimelineAsset == null)
m_TimelineAsset = clipTimelineAsset;
else if (clipTimelineAsset != m_TimelineAsset)
{
m_TimelineAsset = null;
return false;
}
return true;
}
bool CanShowPlayableAssetInspector()
{
if (hasMultipleSelection)
return m_SelectedPlayableAssetsInspector != null &&
m_SelectedPlayableAssetsInspector.canEditMultipleObjects &&
m_SelectionInfo.selectedAssetTypesAreHomogeneous;
else
return true;
}
void DrawDurationProperty()
{
var minDuration = 1.0 / 30.0;
if (currentFrameRate > float.Epsilon)
{
minDuration = 1.0 / currentFrameRate;
}
var inputEvent = InputEvent.None;
var newDuration = TimelineInspectorUtility.DurationFieldUsingTimeReference(
Styles.DurationName, m_SelectionInfo.start, m_SelectionInfo.end, false, m_SelectionInfo.hasMultipleDurationValues, currentFrameRate, minDuration, TimelineClip.kMaxTimeValue, ref inputEvent);
EditMode.inputHandler.ProcessTrim(inputEvent, m_SelectionInfo.start + newDuration, false);
}
void DrawBlendingProperties()
{
const double mixMinimum = 0.0;
var inputEvent = InputEvent.None;
double blendMax;
GUIContent label;
var useBlendIn = m_SelectionInfo.hasBlendIn;
SerializedProperty currentMixInProperty;
if (!useBlendIn)
{
currentMixInProperty = m_EaseInDurationProperty;
var blendOutStart = m_SelectionInfo.duration - m_BlendOutDurationProperty.doubleValue;
blendMax = Math.Min(Math.Max(mixMinimum, m_SelectionInfo.maxMixIn), blendOutStart);
label = Styles.EaseInDurationName;
}
else
{
currentMixInProperty = m_BlendInDurationProperty;
blendMax = TimelineClip.kMaxTimeValue;
label = Styles.BlendInDurationName;
}
if (blendMax > TimeUtility.kTimeEpsilon)
TimelineInspectorUtility.TimeField(currentMixInProperty, label, useBlendIn, currentFrameRate, mixMinimum,
blendMax, ref inputEvent);
var useBlendOut = m_SelectionInfo.hasBlendOut;
SerializedProperty currentMixOutProperty;
if (!useBlendOut)
{
currentMixOutProperty = m_EaseOutDurationProperty;
var blendInEnd = m_SelectionInfo.duration - m_BlendInDurationProperty.doubleValue;
blendMax = Math.Min(Math.Max(mixMinimum, m_SelectionInfo.maxMixOut), blendInEnd);
label = Styles.EaseOutDurationName;
}
else
{
currentMixOutProperty = m_BlendOutDurationProperty;
blendMax = TimelineClip.kMaxTimeValue;
label = Styles.BlendOutDurationName;
}
if (blendMax > TimeUtility.kTimeEpsilon)
TimelineInspectorUtility.TimeField(currentMixOutProperty, label, useBlendOut, currentFrameRate,
mixMinimum, blendMax, ref inputEvent);
}
void DrawClipInProperty()
{
var action = InputEvent.None;
TimelineInspectorUtility.TimeField(m_ClipInProperty, Styles.ClipInName, false, currentFrameRate, 0, TimelineClip.kMaxTimeValue, ref action);
}
void DrawBlendOptions()
{
EditorGUI.BeginChangeCheck();
DrawBlendCurve(Styles.BlendInCurveName, m_BlendInCurveModeProperty, m_MixInCurveProperty, x => OnMixCurveSelected("Blend In", BuiltInPresets.blendInPresets, x, true));
DrawBlendCurve(Styles.BlendOutCurveName, m_BlendOutCurveModeProperty, m_MixOutCurveProperty, x => OnMixCurveSelected("Blend Out", BuiltInPresets.blendOutPresets, x, false));
if (EditorGUI.EndChangeCheck())
TimelineWindow.RepaintIfEditingTimelineAsset(m_TimelineAsset);
}
void DrawGroupSelectionProperties()
{
var inputEvent = InputEvent.None;
var newStartTime = TimelineInspectorUtility.TimeField(Styles.MultipleClipStartName, m_SelectionInfo.multipleClipStart, false, false, currentFrameRate, 0, TimelineClip.kMaxTimeValue, ref inputEvent);
EditMode.inputHandler.ProcessMove(inputEvent, newStartTime);
inputEvent = InputEvent.None;
var newEndTime = TimelineInspectorUtility.TimeField(Styles.MultipleClipEndName, m_SelectionInfo.multipleClipEnd, false, false, currentFrameRate, 0, TimelineClip.kMaxTimeValue, ref inputEvent);
var newStartValue = newStartTime + (newEndTime - m_SelectionInfo.multipleClipEnd);
EditMode.inputHandler.ProcessMove(inputEvent, newStartValue);
}
bool IsLocked()
{
if (!TimelineUtility.IsCurrentSequenceValid() || IsCurrentSequenceReadOnly())
return true;
return targets.OfType<EditorClip>().Any(t => t.clip.GetParentTrack() != null && t.clip.GetParentTrack().lockedInHierarchy);
}
static bool IsCurrentSequenceReadOnly()
{
return TimelineWindow.instance.state.editSequence.isReadOnly;
}
void OnUndoRedoPerformed()
{
if (m_PreviewCurveState == PreviewCurveState.None)
return;
// if an undo is performed the curves need to be updated in the curve editor, as the reference to them is no longer valid
// case 978673
if (m_ClipCurveEditor != null)
{
serializedObject.Update();
m_ClipCurveEditor.SetCurve(m_PreviewCurveState == PreviewCurveState.MixIn ? m_MixInCurveProperty.animationCurveValue : m_MixOutCurveProperty.animationCurveValue);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dff73c4907c95264c8fc095a81f9d51e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,353 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
namespace UnityEditor.Timeline
{
class ClipInspectorCurveEditor
{
CurveEditor m_CurveEditor;
CurveWrapper[] m_CurveWrappers;
const float k_HeaderHeight = 30;
const float k_PresetHeight = 30;
Action<AnimationCurve, EditorCurveBinding> m_CurveUpdatedCallback;
GUIContent m_TextContent = new GUIContent();
GUIStyle m_LabelStyle;
GUIStyle m_LegendStyle;
// Track time. controls the position of the track head
public static readonly double kDisableTrackTime = double.NaN;
double m_trackTime = kDisableTrackTime;
public double trackTime { get { return m_trackTime; } set { m_trackTime = value; } }
public string headerString { get; set; }
public ClipInspectorCurveEditor()
{
var curveEditorSettings = new CurveEditorSettings
{
allowDeleteLastKeyInCurve = false,
allowDraggingCurvesAndRegions = true,
hTickLabelOffset = 0.1f,
showAxisLabels = true,
useFocusColors = false,
wrapColor = new EditorGUIUtility.SkinnedColor(Color.black),
hSlider = false,
hRangeMin = 0.0f,
vRangeMin = 0.0F,
vRangeMax = 1.0f,
hRangeMax = 1.0F,
vSlider = false,
hRangeLocked = false,
vRangeLocked = false,
undoRedoSelection = true,
hTickStyle = new TickStyle
{
tickColor = new EditorGUIUtility.SkinnedColor(new Color(0.0f, 0.0f, 0.0f, 0.2f)),
distLabel = 30,
stubs = false,
centerLabel = true
},
vTickStyle = new TickStyle
{
tickColor = new EditorGUIUtility.SkinnedColor(new Color(1.0f, 0.0f, 0.0f, 0.2f)),
distLabel = 20,
stubs = false,
centerLabel = true
}
};
m_CurveEditor = new CurveEditor(new Rect(0, 0, 1000, 100), new CurveWrapper[0], true)
{
settings = curveEditorSettings,
ignoreScrollWheelUntilClicked = true
};
}
internal bool InitStyles()
{
if (EditorStyles.s_Current == null)
return false;
if (m_LabelStyle == null)
{
m_LabelStyle = new GUIStyle(EditorStyles.whiteLargeLabel);
m_LegendStyle = new GUIStyle(EditorStyles.miniBoldLabel);
m_LabelStyle.alignment = TextAnchor.MiddleCenter;
m_LegendStyle.alignment = TextAnchor.MiddleCenter;
}
return true;
}
internal void OnGUI(Rect clientRect, CurvePresetLibrary presets)
{
const float presetPad = 30.0f;
if (!InitStyles())
return;
if (m_CurveWrappers == null || m_CurveWrappers.Length == 0)
return;
// regions
var headerRect = new Rect(clientRect.x, clientRect.y, clientRect.width, k_HeaderHeight);
var curveRect = new Rect(clientRect.x, clientRect.y + headerRect.height, clientRect.width, clientRect.height - k_HeaderHeight - k_PresetHeight);
var presetRect = new Rect(clientRect.x + presetPad, clientRect.y + curveRect.height + k_HeaderHeight, clientRect.width - presetPad, k_PresetHeight);
GUI.Box(headerRect, headerString, m_LabelStyle);
//Case 1201474 : Force to update only when Repaint event is called as the new rect provided on other event create a wrong curve editor computation.
if (Event.current.type == EventType.Repaint)
{
m_CurveEditor.rect = curveRect;
m_CurveEditor.shownAreaInsideMargins = new Rect(0, 0, 1, 1);
}
m_CurveEditor.animationCurves = m_CurveWrappers;
UpdateSelectionColors();
DrawTrackHead(curveRect);
EditorGUI.BeginChangeCheck();
m_CurveEditor.OnGUI();
DrawPresets(presetRect, presets);
bool hasChanged = EditorGUI.EndChangeCheck();
if (presets == null)
DrawLegend(presetRect);
if (hasChanged)
ProcessUpdates();
ConsumeMouseEvents(clientRect);
}
static void ConsumeMouseEvents(Rect rect)
{
var isMouseEvent = Event.current.type == EventType.MouseUp || Event.current.type == EventType.MouseDown;
if (isMouseEvent && rect.Contains(Event.current.mousePosition))
Event.current.Use();
}
void DrawPresets(Rect position, PresetLibrary curveLibrary)
{
if (curveLibrary == null || curveLibrary.Count() == 0)
return;
const int maxNumPresets = 9;
int numPresets = curveLibrary.Count();
int showNumPresets = Mathf.Min(numPresets, maxNumPresets);
const float swatchWidth = 30;
const float swatchHeight = 15;
const float spaceBetweenSwatches = 10;
float presetButtonsWidth = showNumPresets * swatchWidth + (showNumPresets - 1) * spaceBetweenSwatches;
float flexWidth = (position.width - presetButtonsWidth) * 0.5f;
// Preset swatch area
float curY = (position.height - swatchHeight) * 0.5f;
float curX = 3.0f;
if (flexWidth > 0)
curX = flexWidth;
GUI.BeginGroup(position);
for (int i = 0; i < showNumPresets; i++)
{
if (i > 0)
curX += spaceBetweenSwatches;
var swatchRect = new Rect(curX, curY, swatchWidth, swatchHeight);
m_TextContent.tooltip = curveLibrary.GetName(i);
if (GUI.Button(swatchRect, m_TextContent, GUIStyle.none))
{
// if there is only 1, no need to specify
IEnumerable<CurveWrapper> wrappers = m_CurveWrappers;
if (m_CurveWrappers.Length > 1)
wrappers = m_CurveWrappers.Where(x => x.selected == CurveWrapper.SelectionMode.Selected);
foreach (var wrapper in wrappers)
{
var presetCurve = (AnimationCurve)curveLibrary.GetPreset(i);
wrapper.curve.keys = (Keyframe[])presetCurve.keys.Clone();
wrapper.changed = true;
}
// case 1259902 - flushes internal selection caches preventing index out of range exceptions
m_CurveEditor.SelectNone();
foreach (var wrapper in wrappers)
wrapper.selected = CurveWrapper.SelectionMode.Selected;
}
if (Event.current.type == EventType.Repaint)
curveLibrary.Draw(swatchRect, i);
curX += swatchWidth;
}
GUI.EndGroup();
}
// draw a line representing where in the current clip we are
void DrawTrackHead(Rect clientRect)
{
DirectorStyles styles = TimelineWindow.styles;
if (styles == null)
return;
if (!double.IsNaN(m_trackTime))
{
float x = m_CurveEditor.TimeToPixel((float)m_trackTime, clientRect);
x = Mathf.Clamp(x, clientRect.xMin, clientRect.xMax);
var p1 = new Vector2(x, clientRect.yMin);
var p2 = new Vector2(x, clientRect.yMax);
Graphics.DrawLine(p1, p2, DirectorStyles.Instance.customSkin.colorPlayhead);
}
}
// Draws a legend for the displayed curves
void DrawLegend(Rect r)
{
if (m_CurveWrappers == null || m_CurveWrappers.Length == 0)
return;
Color c = GUI.color;
float boxWidth = r.width / m_CurveWrappers.Length;
for (int i = 0; i < m_CurveWrappers.Length; i++)
{
CurveWrapper cw = m_CurveWrappers[i];
if (cw != null)
{
var pos = new Rect(r.x + i * boxWidth, r.y, boxWidth, r.height);
var textColor = cw.color;
textColor.a = 1;
GUI.color = textColor;
string name = LabelName(cw.binding.propertyName);
EditorGUI.LabelField(pos, name, m_LegendStyle);
}
}
GUI.color = c;
}
// Helper for making label name appropriately small
static char[] s_LabelMarkers = { '_' };
static string LabelName(string propertyName)
{
propertyName = AnimationWindowUtility.GetPropertyDisplayName(propertyName);
int index = propertyName.LastIndexOfAny(s_LabelMarkers);
if (index >= 0)
propertyName = propertyName.Substring(index);
return propertyName;
}
public void SetCurve(AnimationCurve curve)
{
if (m_CurveWrappers == null || m_CurveWrappers.Length != 1)
{
m_CurveWrappers = new CurveWrapper[1];
var cw = new CurveWrapper
{
renderer = new NormalCurveRenderer(curve),
readOnly = false,
color = EditorGUI.kCurveColor,
id = 0xFEED,
hidden = false,
regionId = -1
};
cw.renderer.SetWrap(WrapMode.Clamp, WrapMode.Clamp);
cw.renderer.SetCustomRange(0, 1);
m_CurveWrappers[0] = cw;
UpdateSelectionColors();
m_CurveEditor.animationCurves = m_CurveWrappers;
}
else
{
m_CurveWrappers[0].renderer = new NormalCurveRenderer(curve);
}
}
internal void SetUpdateCurveCallback(Action<AnimationCurve, EditorCurveBinding> callback)
{
m_CurveUpdatedCallback = callback;
}
void ProcessUpdates()
{
foreach (var cw in m_CurveWrappers)
{
if (cw.changed)
{
cw.changed = false;
if (m_CurveUpdatedCallback != null)
m_CurveUpdatedCallback(cw.curve, cw.binding);
}
}
}
public void SetSelected(AnimationCurve curve)
{
m_CurveEditor.SelectNone();
if (m_CurveWrappers != null && m_CurveWrappers.Length > 0)
{
if (m_CurveWrappers[0].renderer.GetCurve() == curve)
{
m_CurveWrappers[0].selected = CurveWrapper.SelectionMode.Selected;
m_CurveEditor.AddSelection(new CurveSelection(m_CurveWrappers[0].id, 0));
}
}
UpdateSelectionColors();
}
void UpdateSelectionColors()
{
if (m_CurveWrappers == null)
return;
// manually manage selection colors
foreach (var cw in m_CurveWrappers)
{
Color c = cw.color;
if (cw.readOnly)
c.a = 0.75f;
else if (cw.selected != CurveWrapper.SelectionMode.None)
c.a = 1.0f;
else
c.a = 0.5f;
cw.color = c;
}
}
public static void CurveField(GUIContent title, SerializedProperty property, Action<SerializedProperty> onClick)
{
Rect controlRect = EditorGUILayout.GetControlRect(GUILayout.MinWidth(20));
EditorGUI.BeginProperty(controlRect, title, property);
DrawCurve(controlRect, property, onClick, EditorGUI.kCurveColor, EditorGUI.kCurveBGColor);
EditorGUI.EndProperty();
}
static Rect DrawCurve(Rect controlRect, SerializedProperty property, Action<SerializedProperty> onClick, Color fgColor, Color bgColor)
{
if (GUI.Button(controlRect, GUIContent.none))
{
if (onClick != null)
onClick(property);
}
EditorGUIUtility.DrawCurveSwatch(controlRect, null, property, fgColor, bgColor);
return controlRect;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3d14fa8f6934e14d92e37279e40e89b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
class ClipInspectorSelectionInfo
{
public double start, end, duration;
public double multipleClipStart, multipleClipEnd;
public double smallestDuration;
public bool hasMultipleStartValues, hasMultipleEndValues, hasMultipleDurationValues;
public bool supportsExtrapolation, supportsClipIn, supportsSpeedMultiplier, supportsBlending;
public bool hasBlendIn, hasBlendOut;
public double maxMixIn, maxMixOut;
public bool selectedAssetTypesAreHomogeneous;
public bool containsAtLeastTwoClipsOnTheSameTrack;
public HashSet<TrackAsset> uniqueParentTracks = new HashSet<TrackAsset>();
public ICollection<TimelineClip> clips { get; private set; }
public ClipInspectorSelectionInfo(ICollection<TimelineClip> selectedClips)
{
supportsBlending = supportsClipIn = supportsExtrapolation = supportsSpeedMultiplier = true;
hasBlendIn = hasBlendOut = true;
maxMixIn = maxMixOut = TimelineClip.kMaxTimeValue;
selectedAssetTypesAreHomogeneous = true;
smallestDuration = TimelineClip.kMaxTimeValue;
start = end = duration = 0;
multipleClipStart = multipleClipEnd = 0;
hasMultipleStartValues = hasMultipleEndValues = hasMultipleDurationValues = false;
containsAtLeastTwoClipsOnTheSameTrack = false;
clips = selectedClips;
Build();
}
void Build()
{
if (!clips.Any()) return;
var firstSelectedClip = clips.First();
if (firstSelectedClip == null) return;
var firstSelectedClipAssetType = firstSelectedClip.asset != null ? firstSelectedClip.asset.GetType() : null;
smallestDuration = TimelineClip.kMaxTimeValue;
InitSelectionBounds(firstSelectedClip);
InitMultipleClipBounds(firstSelectedClip);
foreach (var clip in clips)
{
if (clip == null) continue;
uniqueParentTracks.Add(clip.GetParentTrack());
selectedAssetTypesAreHomogeneous &= clip.asset.GetType() == firstSelectedClipAssetType;
UpdateClipCaps(clip);
UpdateBlends(clip);
UpdateMixMaximums(clip);
UpdateSmallestDuration(clip);
UpdateMultipleValues(clip);
UpdateMultipleValues(clip);
}
containsAtLeastTwoClipsOnTheSameTrack = uniqueParentTracks.Count != clips.Count;
}
public void Update()
{
var firstSelectedClip = clips.First();
if (firstSelectedClip == null) return;
hasBlendIn = hasBlendOut = true;
maxMixIn = maxMixOut = TimelineClip.kMaxTimeValue;
hasMultipleStartValues = hasMultipleDurationValues = hasMultipleEndValues = false;
smallestDuration = TimelineClip.kMaxTimeValue;
InitSelectionBounds(firstSelectedClip);
InitMultipleClipBounds(firstSelectedClip);
foreach (var clip in clips)
{
if (clip == null) continue;
UpdateBlends(clip);
UpdateMixMaximums(clip);
UpdateSmallestDuration(clip);
UpdateMultipleValues(clip);
}
}
void InitSelectionBounds(TimelineClip clip)
{
start = clip.start;
duration = clip.duration;
end = clip.start + clip.duration;
}
void InitMultipleClipBounds(TimelineClip firstSelectedClip)
{
multipleClipStart = firstSelectedClip.start;
multipleClipEnd = end;
}
void UpdateSmallestDuration(TimelineClip clip)
{
smallestDuration = Math.Min(smallestDuration, clip.duration);
}
void UpdateClipCaps(TimelineClip clip)
{
supportsBlending &= clip.SupportsBlending();
supportsClipIn &= clip.SupportsClipIn();
supportsExtrapolation &= clip.SupportsExtrapolation();
supportsSpeedMultiplier &= clip.SupportsSpeedMultiplier();
}
void UpdateMultipleValues(TimelineClip clip)
{
hasMultipleStartValues |= !Mathf.Approximately((float)clip.start, (float)start);
hasMultipleDurationValues |= !Mathf.Approximately((float)clip.duration, (float)duration);
var clipEnd = clip.start + clip.duration;
hasMultipleEndValues |= !Mathf.Approximately((float)clipEnd, (float)end);
multipleClipStart = Math.Min(multipleClipStart, clip.start);
multipleClipEnd = Math.Max(multipleClipEnd, clip.end);
}
void UpdateBlends(TimelineClip clip)
{
hasBlendIn &= clip.hasBlendIn;
hasBlendOut &= clip.hasBlendOut;
}
void UpdateMixMaximums(TimelineClip clip)
{
var clipMaxMixIn = Math.Max(0.0, clip.duration - clip.mixOutDuration);
var clipMaxMixOut = Math.Max(0.0, clip.duration - clip.mixInDuration);
maxMixIn = Math.Min(maxMixIn, clipMaxMixIn);
maxMixOut = Math.Min(maxMixOut, clipMaxMixOut);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57a39be2178cca94ab21e15c082e3ab6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 86cacab070a0a46e99aedb596a32c4fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
using System;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class CurvesOwnerInspectorHelper
{
// Because what is animated is not the asset, but the instanced playable,
// we apply the animation clip here to preview what is being shown
// This could be improved doing something more inline with animation mode,
// and reverting values that aren't be recorded later to avoid dirtying the asset
public static void PreparePlayableAsset(ICurvesOwnerInspectorWrapper wrapper)
{
if (wrapper.serializedPlayableAsset == null)
return;
var curvesOwner = wrapper.curvesOwner;
if (curvesOwner == null || curvesOwner.curves == null)
return;
var timelineWindow = TimelineWindow.instance;
if (timelineWindow == null || timelineWindow.state == null)
return;
// requires preview mode. reset the eval time so previous value is correct value is displayed while toggling
if (!timelineWindow.state.previewMode)
{
wrapper.lastEvalTime = -1;
return;
}
var time = wrapper.ToLocalTime(timelineWindow.state.editSequence.time);
// detect if the time has changed, or if the curves have changed
if (Math.Abs(wrapper.lastEvalTime - time) < TimeUtility.kTimeEpsilon)
{
int curveVersion = AnimationClipCurveCache.Instance.GetCurveInfo(curvesOwner.curves).version;
if (curveVersion == wrapper.lastCurveVersion)
return;
wrapper.lastCurveVersion = curveVersion;
}
wrapper.lastEvalTime = time;
var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(curvesOwner.curves);
int count = clipInfo.bindings.Length;
if (count == 0)
return;
wrapper.serializedPlayableAsset.Update();
var prop = wrapper.serializedPlayableAsset.GetIterator();
while (prop.NextVisible(true))
{
if (curvesOwner.IsParameterAnimated(prop.propertyPath))
{
var curve = curvesOwner.GetAnimatedParameter(prop.propertyPath);
switch (prop.propertyType)
{
case SerializedPropertyType.Boolean:
prop.boolValue = curve.Evaluate((float)time) > 0;
break;
case SerializedPropertyType.Float:
prop.floatValue = curve.Evaluate((float)time);
break;
case SerializedPropertyType.Integer:
prop.intValue = Mathf.FloorToInt(curve.Evaluate((float)time));
break;
case SerializedPropertyType.Color:
SetAnimatedValue(curvesOwner, prop, "r", time);
SetAnimatedValue(curvesOwner, prop, "g", time);
SetAnimatedValue(curvesOwner, prop, "b", time);
SetAnimatedValue(curvesOwner, prop, "a", time);
break;
case SerializedPropertyType.Quaternion:
case SerializedPropertyType.Vector4:
SetAnimatedValue(curvesOwner, prop, "w", time);
goto case SerializedPropertyType.Vector3;
case SerializedPropertyType.Vector3:
SetAnimatedValue(curvesOwner, prop, "z", time);
goto case SerializedPropertyType.Vector2;
case SerializedPropertyType.Vector2:
SetAnimatedValue(curvesOwner, prop, "x", time);
SetAnimatedValue(curvesOwner, prop, "y", time);
break;
}
}
}
wrapper.serializedPlayableAsset.ApplyModifiedPropertiesWithoutUndo();
}
static void SetAnimatedValue(ICurvesOwner clip, SerializedProperty property, string path, double localTime)
{
var prop = property.FindPropertyRelative(path);
if (prop != null)
{
var curve = clip.GetAnimatedParameter(prop.propertyPath);
if (curve != null)
prop.floatValue = curve.Evaluate((float)localTime);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a371bcbba2084dd0a8ebc6826aa8794
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
interface ICurvesOwnerInspectorWrapper
{
ICurvesOwner curvesOwner { get; }
SerializedObject serializedPlayableAsset { get; }
int lastCurveVersion { get; set; }
double lastEvalTime { get; set; }
double ToLocalTime(double time);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 926a61ff0dec44a5aab649acb411e9ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
/// <summary>
/// Internally used Inspector
/// </summary>
[CustomEditor(typeof(DirectorNamedColor))]
class DirectorNamedColorInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("ToTextAsset"))
{
DirectorStyles.Instance.ExportSkinToFile();
}
if (GUILayout.Button("Reload From File"))
{
DirectorStyles.Instance.ReloadSkin();
UnityEditor.Selection.activeObject = DirectorStyles.Instance.customSkin;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c8ceb9efacb4974bb3b7e2a87137b07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[ExcludeFromPreset]
[TimelineHelpURL(typeof(TimelineClip))]
class EditorClip : ScriptableObject
{
[SerializeField] TimelineClip m_Clip;
public TimelineClip clip
{
get { return m_Clip; }
set { m_Clip = value; }
}
public int lastHash { get; set; }
public override int GetHashCode()
{
return clip.Hash();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 197c1114eb793d24c8ef31120a134e88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline
{
static class EditorClipFactory
{
static Dictionary<TimelineClip, EditorClip> s_EditorCache = new Dictionary<TimelineClip, EditorClip>();
public static EditorClip GetEditorClip(TimelineClip clip)
{
if (clip == null)
throw new ArgumentException("parameter cannot be null");
if (s_EditorCache.ContainsKey(clip))
{
var editorClip = s_EditorCache[clip];
if (editorClip != null)
return editorClip;
}
var editor = ScriptableObject.CreateInstance<EditorClip>();
editor.hideFlags |= HideFlags.HideInHierarchy | HideFlags.DontSaveInEditor;
editor.clip = clip;
editor.lastHash = editor.GetHashCode();
s_EditorCache[clip] = editor;
return editor;
}
public static void RemoveEditorClip(TimelineClip clip)
{
if (clip == null)
return;
if (s_EditorCache.ContainsKey(clip))
{
var obj = s_EditorCache[clip];
if (obj != null)
UnityObject.DestroyImmediate(obj);
s_EditorCache.Remove(clip);
}
}
public static bool Contains(TimelineClip clip)
{
return clip != null && s_EditorCache.ContainsKey(clip);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6ba30c492ac73742bc0cfee6817045a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
using System;
using UnityEngine;
using UnityEngine.Timeline;
using TimelineEditorSettings = UnityEngine.Timeline.TimelineAsset.EditorSettings;
#if TIMELINE_FRAMEACCURATE
using UnityEngine.Playables;
#endif
namespace UnityEditor.Timeline
{
[CustomPropertyDrawer(typeof(FrameRateFieldAttribute), true)]
class FrameRateDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var frameRateAttribute = attribute as FrameRateFieldAttribute;
if (frameRateAttribute == null)
return;
EditorGUI.BeginProperty(position, label, property);
property.doubleValue = FrameRateField(property.doubleValue, label, position, out bool frameRateIsValid);
EditorGUI.EndProperty();
#if TIMELINE_FRAMEACCURATE
if (!frameRateIsValid && TimelinePreferences.instance.playbackLockedToFrame)
EditorGUILayout.HelpBox(
L10n.Tr("Locking playback cannot be enabled for this frame rate."),
MessageType.Warning);
#endif
}
public static double FrameRateField(double frameRate, GUIContent label, Rect position, out bool isValid)
{
double frameRateDouble = FrameRateDisplayUtility.RoundFrameRate(frameRate);
FrameRate frameRateObj = TimeUtility.GetClosestFrameRate(frameRateDouble);
isValid = frameRateObj.IsValid();
TimeUtility.ToStandardFrameRate(frameRateObj, out StandardFrameRates option);
position = EditorGUI.PrefixLabel(position, label);
Rect posPopup = new Rect(position.x, position.y, position.width / 2, position.height);
Rect posFloatField = new Rect(posPopup.xMax, position.y, position.width / 2, position.height);
using (var checkOption = new EditorGUI.ChangeCheckScope())
{
option = (StandardFrameRates)EditorGUI.Popup(posPopup, (int)option,
FrameRateDisplayUtility.GetDefaultFrameRatesLabels(option));
if (checkOption.changed)
{
isValid = true;
return TimeUtility.ToFrameRate(option).rate;
}
}
using (var checkFrame = new EditorGUI.ChangeCheckScope())
{
frameRateDouble = Math.Abs(EditorGUI.DoubleField(posFloatField, frameRateDouble));
frameRateObj = TimeUtility.GetClosestFrameRate(frameRateDouble);
if (checkFrame.changed)
{
isValid = frameRateObj.IsValid();
return isValid ? frameRateObj.rate : frameRateDouble;
}
}
return frameRateDouble;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c503dbc4e0a4e9db2d004409138d5cc
timeCreated: 1612886047

View File

@ -0,0 +1,107 @@
//#define PERF_PROFILE
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(GroupTrack)), CanEditMultipleObjects]
class GroupTrackInspector : TrackAssetInspector
{
static class Styles
{
public static readonly GUIContent GroupSubTrackHeaderName = L10n.TextContent("Name");
public static readonly GUIContent GroupSubTrackHeaderType = L10n.TextContent("Type");
public static readonly GUIContent GroupSubTrackHeaderDuration = L10n.TextContent("Duration");
public static readonly GUIContent GroupSubTrackHeaderFrames = L10n.TextContent("Frames");
public static readonly GUIContent GroupInvalidTrack = L10n.TextContent("Invalid Track");
}
ReorderableList m_SubTracks;
public override void OnInspectorGUI()
{
foreach (var group in targets)
{
var groupTrack = group as GroupTrack;
if (groupTrack == null) return;
var childrenTracks = groupTrack.GetChildTracks();
var groupTrackName = groupTrack.name;
GUILayout.Label(childrenTracks.Count() > 0
? groupTrackName + " (" + childrenTracks.Count() + ")"
: groupTrackName, EditorStyles.boldLabel);
GUILayout.Space(3.0f);
// the subTrackObjects is used because it's the internal list
m_SubTracks.list = groupTrack.subTracksObjects;
m_SubTracks.DoLayoutList();
m_SubTracks.index = -1;
}
}
public override void OnEnable()
{
base.OnEnable();
m_SubTracks = new ReorderableList(new string[] { }, typeof(string), false, true, false, false)
{
drawElementCallback = OnDrawSubTrack,
drawHeaderCallback = OnDrawHeader,
showDefaultBackground = true,
index = 0,
elementHeight = 20
};
}
static void OnDrawHeader(Rect rect)
{
int sections = 4;
float sectionWidth = rect.width / sections;
rect.width = sectionWidth;
GUI.Label(rect, Styles.GroupSubTrackHeaderName, EditorStyles.label);
rect.x += sectionWidth;
GUI.Label(rect, Styles.GroupSubTrackHeaderType, EditorStyles.label);
rect.x += sectionWidth;
GUI.Label(rect, Styles.GroupSubTrackHeaderDuration, EditorStyles.label);
rect.x += sectionWidth;
GUI.Label(rect, Styles.GroupSubTrackHeaderFrames, EditorStyles.label);
}
void OnDrawSubTrack(Rect rect, int index, bool selected, bool focused)
{
int sections = 4;
float sectionWidth = rect.width / sections;
var childrenTrack = m_SubTracks.list[index] as TrackAsset;
if (childrenTrack == null)
{
object o = m_SubTracks.list[index];
rect.width = sectionWidth;
if (o != null) // track is loaded, but has broken script
{
string name = ((UnityEngine.Object)m_SubTracks.list[index]).name;
GUI.Label(rect, name, EditorStyles.label);
}
rect.x += sectionWidth;
using (new GUIColorOverride(DirectorStyles.kClipErrorColor))
GUI.Label(rect, Styles.GroupInvalidTrack.text, EditorStyles.label);
return;
}
rect.width = sectionWidth;
GUI.Label(rect, childrenTrack.name, EditorStyles.label);
rect.x += sectionWidth;
GUI.Label(rect, childrenTrack.GetType().Name, EditorStyles.label);
rect.x += sectionWidth;
GUI.Label(rect, childrenTrack.duration.ToString(), EditorStyles.label);
rect.x += sectionWidth;
double exactFrames = TimeUtility.ToExactFrames(childrenTrack.duration, TimelineWindow.instance.state.referenceSequence.frameRate);
GUI.Label(rect, exactFrames.ToString(), EditorStyles.label);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41dcdc094b311464c8d6cb614548d89b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
namespace UnityEditor.Timeline
{
/// <summary>
/// Implement this interface in your PlayableAsset inspector to change what happens when a UI component in the inspector is modified
/// </summary>
/// <remarks>The default PlayableAsset inspector will cause any UI change to force a PlayableGraph rebuild</remarks>
public interface IInspectorChangeHandler
{
/// <summary>
/// This method will be called when a Playable Asset inspector is modified.
/// </summary>
void OnPlayableAssetChangedInInspector();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b41db653d2547dca9eb5c43b6c5aeb0
timeCreated: 1613416310

View File

@ -0,0 +1,69 @@
using System;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(Marker), true)]
[CanEditMultipleObjects]
class MarkerInspector : BasicAssetInspector
{
static class Styles
{
public static readonly string MultipleMarkerSelectionTitle = L10n.Tr("{0} Markers");
public static readonly string UndoCommand = L10n.Tr("Rename marker");
}
internal override bool IsEnabled()
{
if (!TimelineUtility.IsCurrentSequenceValid() || IsCurrentSequenceReadOnly())
return false;
var marker = target as Marker;
if (marker != null)
{
if (!marker.parent.GetShowMarkers())
return false;
}
return base.IsEnabled();
}
internal override void OnHeaderTitleGUI(Rect titleRect, string header)
{
if (targets.Length > 1)
{
var multiSelectTitle = string.Format(Styles.MultipleMarkerSelectionTitle, targets.Length);
base.OnHeaderTitleGUI(titleRect, multiSelectTitle);
return;
}
var marker = target as Marker;
if (marker != null)
{
if (marker.parent.GetShowMarkers() && TimelineUtility.IsCurrentSequenceValid() && !IsCurrentSequenceReadOnly())
{
EditorGUI.BeginChangeCheck();
var newName = EditorGUI.DelayedTextField(titleRect, marker.name);
if (EditorGUI.EndChangeCheck())
{
UndoExtensions.RegisterMarker(marker, Styles.UndoCommand);
marker.name = newName;
}
}
else
{
base.OnHeaderTitleGUI(titleRect, marker.name);
}
}
else
{
var typeName = TypeUtility.GetDisplayName(target.GetType());
EditorGUILayout.LabelField(typeName);
}
}
static bool IsCurrentSequenceReadOnly()
{
return TimelineWindow.instance.state.editSequence.isReadOnly;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb461734117c80c43ab595d699f801eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,69 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomPropertyDrawer(typeof(TimeFieldAttribute), true)]
class TimeFieldDrawer : PropertyDrawer
{
static WindowState state
{
get { return TimelineWindow.instance != null ? TimelineWindow.instance.state : null; }
}
static double currentFrameRate
{
get { return state != null ? TimelineWindow.instance.state.referenceSequence.frameRate : 0.0; }
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType != SerializedPropertyType.Float)
{
GUILayout.Label("TimeField only works on floating point types");
return;
}
var timeFieldAttribute = attribute as TimeFieldAttribute;
if (timeFieldAttribute == null)
return;
var rect = EditorGUILayout.s_LastRect;
EditorGUI.BeginChangeCheck();
if (timeFieldAttribute.useEditMode == TimeFieldAttribute.UseEditMode.ApplyEditMode)
TimeFieldWithEditMode(rect, property, label);
else
TimeField(rect, property, label);
if (EditorGUI.EndChangeCheck())
{
if (state != null)
state.Refresh();
}
}
static void TimeField(Rect rect, SerializedProperty property, GUIContent label)
{
var evt1 = InputEvent.None;
TimelineInspectorUtility.TimeField(rect, property, label, false, currentFrameRate, 0, float.MaxValue, ref evt1);
}
static void TimeFieldWithEditMode(Rect rect, SerializedProperty property, GUIContent label)
{
double minStartTime;
if (property.hasMultipleDifferentValues)
minStartTime = SelectionManager.SelectedItems().Min(i => i.start);
else
minStartTime = property.doubleValue;
var evt = InputEvent.None;
var newValue = TimelineInspectorUtility.TimeField(
rect, label, minStartTime, false, property.hasMultipleDifferentValues, currentFrameRate, 0.0, float.MaxValue, ref evt);
EditMode.inputHandler.ProcessMove(evt, newValue);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0c73ea1c5ff95e43806e9002c155070
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,139 @@
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(TimelineAsset)), CanEditMultipleObjects]
class TimelineAssetInspector : Editor
{
const int k_MinWidth = 140;
internal static class Styles
{
public static readonly GUIContent FrameRate = L10n.TextContent("Frame Rate", "The frame rate at which this sequence updates");
public static readonly GUIContent DurationMode = L10n.TextContent("Duration Mode", "Specified how the duration of the sequence is calculated");
public static readonly GUIContent Duration = L10n.TextContent("Duration", "The length of the sequence");
public static readonly GUIContent HeaderTitleMultiselection = L10n.TextContent("Timeline Assets");
public static readonly GUIContent IgnorePreviewWarning = L10n.TextContent("When ignoring preview, the Timeline window will modify the scene when this timeline is opened.");
public static readonly GUIContent ScenePreviewLabel = L10n.TextContent("Scene Preview");
}
SerializedProperty m_FrameRateProperty;
SerializedProperty m_DurationModeProperty;
SerializedProperty m_FixedDurationProperty;
SerializedProperty m_ScenePreviewProperty;
void InitializeProperties()
{
var editorSettings = serializedObject.FindProperty("m_EditorSettings");
m_FrameRateProperty = editorSettings.FindPropertyRelative("m_Framerate");
m_DurationModeProperty = serializedObject.FindProperty("m_DurationMode");
m_FixedDurationProperty = serializedObject.FindProperty("m_FixedDuration");
m_ScenePreviewProperty = editorSettings.FindPropertyRelative("m_ScenePreview");
}
public void OnEnable()
{
InitializeProperties();
}
internal override bool IsEnabled()
{
return !FileUtil.HasReadOnly(targets) && base.IsEnabled();
}
protected override void OnHeaderGUI()
{
string headerTitle;
if (targets.Length == 1)
headerTitle = target.name;
else
headerTitle = targets.Length + " " + Styles.HeaderTitleMultiselection.text;
DrawHeaderGUI(this, headerTitle, 0);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_FrameRateProperty, Styles.FrameRate);
#if TIMELINE_FRAMEACCURATE
if (EditorGUI.EndChangeCheck())
{
ResetFrameLockedPlayback(targets);
}
#else
EditorGUI.EndChangeCheck();
#endif
var frameRate = m_FrameRateProperty.doubleValue;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_DurationModeProperty, Styles.DurationMode, GUILayout.MinWidth(k_MinWidth));
var durationMode = (TimelineAsset.DurationMode)m_DurationModeProperty.enumValueIndex;
var inputEvent = InputEvent.None;
if (durationMode == TimelineAsset.DurationMode.FixedLength)
TimelineInspectorUtility.TimeField(m_FixedDurationProperty, Styles.Duration, false, frameRate, double.Epsilon, TimelineClip.kMaxTimeValue * 2, ref inputEvent);
else
{
var isMixed = targets.Length > 1;
TimelineInspectorUtility.TimeField(Styles.Duration, ((TimelineAsset)target).duration, true, isMixed, frameRate, double.MinValue, double.MaxValue, ref inputEvent);
}
DrawIgnorePreviewProperty();
var changed = EditorGUI.EndChangeCheck();
serializedObject.ApplyModifiedProperties();
if (changed)
TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
}
void DrawIgnorePreviewProperty()
{
EditorGUI.BeginChangeCheck();
{
EditorGUILayout.PropertyField(m_ScenePreviewProperty, Styles.ScenePreviewLabel, GUILayout.MinWidth(k_MinWidth));
}
var changed = EditorGUI.EndChangeCheck();
if (changed && TimelineWindow.instance && TimelineWindow.instance.state != null && ContainsMasterAsset(targets))
ResetWindowState(TimelineWindow.instance.state);
if (!m_ScenePreviewProperty.boolValue || m_ScenePreviewProperty.hasMultipleDifferentValues)
EditorGUILayout.HelpBox(Styles.IgnorePreviewWarning.text, MessageType.Warning);
}
static void ResetWindowState(WindowState state)
{
state.Reset();
state.Stop();
state.masterSequence.viewModel.windowTime = 0.0;
if (state.masterSequence.director != null) state.masterSequence.director.time = 0.0;
}
static bool ContainsMasterAsset(Object[] asset)
{
return asset != null && asset.Any(tl => tl == TimelineWindow.instance.state.masterSequence.asset);
}
#if TIMELINE_FRAMEACCURATE
static void ResetFrameLockedPlayback(Object[] asset)
{
if (TimelineWindow.instance != null
&& TimelineWindow.instance.state != null
&& TimelinePreferences.instance.playbackLockedToFrame
&& ContainsMasterAsset(asset))
{
TimelineEditor.RefreshPreviewPlay();
}
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e00a5dea786950546a21b0e2d817e466
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,347 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[Flags]
enum InputEvent
{
None = 0,
DragEnter = 1,
DragExit = 2,
Drag = 4,
KeyboardInput = 8
}
static class InputEventMethods
{
public static bool InputHasBegun(this InputEvent evt)
{
return evt == InputEvent.DragEnter || evt == InputEvent.KeyboardInput;
}
}
static class TimelineInspectorUtility
{
internal static class Styles
{
public static readonly GUIContent SecondsPrefix = L10n.TextContent("s", "Seconds");
public static readonly GUIContent FramesPrefix = L10n.TextContent("f", "Frames");
}
public static void TimeField(SerializedProperty property, GUIContent label, bool readOnly, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent)
{
var rect = EditorGUILayout.GetControlRect();
TimeField(rect, property, label, readOnly, frameRate, minValue, maxValue, ref inputEvent);
}
// Display Time related properties in frames and seconds
public static void TimeField(Rect rect, SerializedProperty property, GUIContent label, bool readOnly, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent)
{
using (var propertyScope = new PropertyScope(rect, label, property))
{
GUIContent title = propertyScope.content;
rect = EditorGUI.PrefixLabel(rect, title);
using (new IndentLevelScope(0))
using (new LabelWidthScope((int)EditorGUI.kMiniLabelW))
using (new GUIMixedValueScope(property.hasMultipleDifferentValues))
{
var secondsRect = new Rect(rect.xMin, rect.yMin, rect.width / 2 - EditorGUI.kSpacingSubLabel, rect.height);
var framesRect = new Rect(rect.xMin + rect.width / 2, rect.yMin, rect.width / 2, rect.height);
if (readOnly)
{
EditorGUI.FloatField(secondsRect, Styles.SecondsPrefix, (float)property.doubleValue, EditorStyles.label);
}
else
{
EditorGUI.BeginChangeCheck();
DelayedAndDraggableDoubleField(secondsRect, Styles.SecondsPrefix, property, ref inputEvent);
if (EditorGUI.EndChangeCheck())
{
property.doubleValue = Clamp(property.doubleValue, minValue, maxValue);
}
}
if (frameRate > TimeUtility.kTimeEpsilon)
{
EditorGUI.BeginChangeCheck();
double time = property.doubleValue;
int frames = TimeUtility.ToFrames(time, frameRate);
double exactFrames = TimeUtility.ToExactFrames(time, frameRate);
bool useIntField = TimeUtility.OnFrameBoundary(time, frameRate);
if (readOnly)
{
if (useIntField)
EditorGUI.IntField(framesRect, Styles.FramesPrefix, frames, EditorStyles.label);
else
EditorGUI.DoubleField(framesRect, Styles.FramesPrefix, exactFrames, EditorStyles.label);
}
else
{
if (useIntField)
{
int newFrames = DelayedAndDraggableIntField(framesRect, Styles.FramesPrefix, frames, ref inputEvent);
time = Math.Max(0, TimeUtility.FromFrames(newFrames, frameRate));
}
else
{
double newExactFrames = DelayedAndDraggableDoubleField(framesRect, Styles.FramesPrefix, exactFrames, ref inputEvent);
time = Math.Max(0, TimeUtility.FromFrames((int)Math.Floor(newExactFrames), frameRate));
}
}
if (EditorGUI.EndChangeCheck())
{
property.doubleValue = Clamp(time, minValue, maxValue);
}
}
}
}
}
public static double TimeFieldUsingTimeReference(
GUIContent label, double time, bool readOnly, bool showMixed, double frameRate, double minValue,
double maxValue, ref InputEvent inputEvent)
{
var state = TimelineWindow.instance.state;
var needsTimeConversion = state != null && state.timeReferenceMode == TimeReferenceMode.Global;
if (needsTimeConversion)
time = state.editSequence.ToGlobalTime(time);
var t = TimeField(label, time, readOnly, showMixed, frameRate, minValue, maxValue, ref inputEvent);
if (needsTimeConversion)
t = state.editSequence.ToLocalTime(t);
return t;
}
public static double DurationFieldUsingTimeReference(
GUIContent label, double start, double end, bool readOnly, bool showMixed, double frameRate,
double minValue, double maxValue, ref InputEvent inputEvent)
{
var state = TimelineWindow.instance.state;
var needsTimeConversion = state != null && state.timeReferenceMode == TimeReferenceMode.Global;
if (needsTimeConversion)
{
start = state.editSequence.ToGlobalTime(start);
end = state.editSequence.ToGlobalTime(end);
}
var duration = end - start;
var t = TimeField(label, duration, readOnly, showMixed, frameRate, minValue, maxValue, ref inputEvent);
end = start + t;
if (needsTimeConversion)
{
start = state.editSequence.ToLocalTime(start);
end = state.editSequence.ToLocalTime(end);
}
return end - start;
}
public static double TimeField(Rect rect, GUIContent label, double time, bool readOnly, bool showMixed, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent)
{
using (new HorizontalScope(label, GUIStyle.none))
{
rect = EditorGUI.PrefixLabel(rect, label);
using (new IndentLevelScope(0))
using (new LabelWidthScope((int)EditorGUI.kMiniLabelW))
using (new GUIMixedValueScope(showMixed))
{
var secondsRect = new Rect(rect.xMin, rect.yMin, rect.width / 2 - EditorGUI.kSpacingSubLabel, rect.height);
var framesRect = new Rect(rect.xMin + rect.width / 2, rect.yMin, rect.width / 2, rect.height);
if (readOnly)
{
EditorGUI.FloatField(secondsRect, Styles.SecondsPrefix, (float)time, EditorStyles.label);
}
else
{
time = DelayedAndDraggableDoubleField(secondsRect, Styles.SecondsPrefix, time, ref inputEvent);
}
if (frameRate > TimeUtility.kTimeEpsilon)
{
int frames = TimeUtility.ToFrames(time, frameRate);
double exactFrames = TimeUtility.ToExactFrames(time, frameRate);
bool useIntField = TimeUtility.OnFrameBoundary(time, frameRate);
if (readOnly)
{
if (useIntField)
EditorGUI.IntField(framesRect, Styles.FramesPrefix, frames, EditorStyles.label);
else
EditorGUI.FloatField(framesRect, Styles.FramesPrefix, (float)exactFrames, EditorStyles.label);
}
else
{
double newTime;
EditorGUI.BeginChangeCheck();
if (useIntField)
{
int newFrames = DelayedAndDraggableIntField(framesRect, Styles.FramesPrefix, frames, ref inputEvent);
newTime = Math.Max(0, TimeUtility.FromFrames(newFrames, frameRate));
}
else
{
double newExactFrames = DelayedAndDraggableDoubleField(framesRect, Styles.FramesPrefix, exactFrames, ref inputEvent);
newTime = Math.Max(0, TimeUtility.FromFrames((int)Math.Floor(newExactFrames), frameRate));
}
if (EditorGUI.EndChangeCheck())
{
time = newTime;
}
}
}
}
}
return Clamp(time, minValue, maxValue);
}
public static double TimeField(GUIContent label, double time, bool readOnly, bool showMixed, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent)
{
var rect = EditorGUILayout.GetControlRect();
return TimeField(rect, label, time, readOnly, showMixed, frameRate, minValue, maxValue, ref inputEvent);
}
static InputEvent InputEventType(Rect rect, int id)
{
var evt = Event.current;
switch (evt.GetTypeForControl(id))
{
case EventType.MouseDown:
if (rect.Contains(evt.mousePosition) && evt.button == 0)
{
return InputEvent.DragEnter;
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
return InputEvent.DragExit;
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
return InputEvent.Drag;
}
break;
case EventType.KeyDown:
if (GUIUtility.hotControl == id && evt.keyCode == KeyCode.Escape)
{
return InputEvent.DragExit;
}
break;
}
return InputEvent.None;
}
static double DelayedAndDraggableDoubleField(Rect rect, GUIContent label, double value, ref InputEvent inputEvent, double dragSensitivity)
{
var id = GUIUtility.GetControlID(FocusType.Keyboard);
var fieldRect = EditorGUI.PrefixLabel(rect, id, label);
rect.xMax = fieldRect.x;
double refValue = value;
long dummy = 0;
inputEvent |= InputEventType(rect, id);
EditorGUI.DragNumberValue(rect, id, true, ref refValue, ref dummy, dragSensitivity);
EditorGUI.BeginChangeCheck();
var result = EditorGUI.DelayedDoubleFieldInternal(fieldRect, GUIContent.none, refValue, EditorStyles.numberField);
if (EditorGUI.EndChangeCheck())
inputEvent |= InputEvent.KeyboardInput;
return result;
}
static int DelayedAndDraggableIntField(Rect rect, GUIContent label, int value, ref InputEvent inputEvent, long dragSensitivity)
{
var id = GUIUtility.GetControlID(FocusType.Keyboard);
var fieldRect = EditorGUI.PrefixLabel(rect, id, label);
rect.xMax = fieldRect.x;
double dummy = 0.0;
long refValue = value;
inputEvent |= InputEventType(rect, id);
EditorGUI.DragNumberValue(rect, id, false, ref dummy, ref refValue, dragSensitivity);
EditorGUI.BeginChangeCheck();
var result = EditorGUI.DelayedIntFieldInternal(fieldRect, GUIContent.none, (int)refValue, EditorStyles.numberField);
if (EditorGUI.EndChangeCheck())
inputEvent |= InputEvent.KeyboardInput;
return result;
}
internal static double DelayedAndDraggableDoubleField(GUIContent label, double value, ref InputEvent action, double dragSensitivity)
{
var r = EditorGUILayout.s_LastRect = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight);
return DelayedAndDraggableDoubleField(r, label, value, ref action, dragSensitivity);
}
static void DelayedAndDraggableDoubleField(Rect rect, GUIContent label, SerializedProperty property, ref InputEvent inputEvent)
{
EditorGUI.BeginChangeCheck();
var newValue = DelayedAndDraggableDoubleField(rect, label, property.doubleValue, ref inputEvent);
if (EditorGUI.EndChangeCheck())
property.doubleValue = newValue;
}
static double DelayedAndDraggableDoubleField(Rect rect, GUIContent label, double value, ref InputEvent inputEvent)
{
var dragSensitivity = NumericFieldDraggerUtility.CalculateFloatDragSensitivity(value);
return DelayedAndDraggableDoubleField(rect, label, value, ref inputEvent, dragSensitivity);
}
static int DelayedAndDraggableIntField(Rect rect, GUIContent label, int value, ref InputEvent inputEvent)
{
var dragSensitivity = NumericFieldDraggerUtility.CalculateIntDragSensitivity(value);
return DelayedAndDraggableIntField(rect, label, value, ref inputEvent, dragSensitivity);
}
internal static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0) return min;
if (val.CompareTo(max) > 0) return max;
return val;
}
public static Editor GetInspectorForObjects(UnityEngine.Object[] objects, Editor previousEditor)
{
// create cached editor throws on assembly reload...
try
{
if (objects.Any(x => x != null))
{
var director = TimelineWindow.instance.state.editSequence.director;
Editor.CreateCachedEditorWithContext(objects, director, null, ref previousEditor);
return previousEditor;
}
}
catch (Exception)
{ }
return null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3387717991705ce4e8ef033a0e543a06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Timeline;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.UIElements;
#if !UNITY_2020_2_OR_NEWER
using L10n = UnityEditor.Timeline.L10n;
#endif
/// <summary>
/// Store the editor preferences for Timeline.
/// </summary>
[FilePath("TimelinePreferences.asset", FilePathAttribute.Location.PreferencesFolder)]
public class TimelinePreferences : ScriptableSingleton<TimelinePreferences>
{
/// <summary>
/// The time unit used by the Timeline Editor when displaying time values.
/// </summary>
[SerializeField]
public TimeFormat timeFormat;
/// <summary>
/// Define the time unit for the timeline window.
/// true : frame unit.
/// false : timecode unit.
/// </summary>
[NonSerialized, Obsolete("timeUnitInFrame is deprecated. Use timeFormat instead", false)]
public bool timeUnitInFrame;
/// <summary>
/// Draw the waveforms for all audio clips.
/// </summary>
[SerializeField]
public bool showAudioWaveform = true;
/// <summary>
/// Allow the users to hear audio while scrubbing on audio clip.
/// </summary>
[SerializeField]
bool m_AudioScrubbing;
/// <summary>
/// Enables audio scrubbing when moving the playhead.
/// </summary>
public bool audioScrubbing
{
get { return m_AudioScrubbing; }
set
{
if (m_AudioScrubbing != value)
{
m_AudioScrubbing = value;
TimelinePlayable.muteAudioScrubbing = !value;
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
}
}
/// <summary>
/// Enable Snap to Frame to manipulate clips and align them on frames.
/// </summary>
[SerializeField]
public bool snapToFrame = true;
#if TIMELINE_FRAMEACCURATE
[SerializeField] bool m_PlaybackLockedToFrame;
#endif
/// <summary>
/// Enable Timelines to be evaluated on frame during editor preview.
/// </summary>
public bool playbackLockedToFrame
{
get
{
#if TIMELINE_FRAMEACCURATE
return m_PlaybackLockedToFrame;
#else
Debug.LogWarning($"PlaybackLockedToFrame is not available for this Unity version");
return false;
#endif
}
set
{
#if TIMELINE_FRAMEACCURATE
m_PlaybackLockedToFrame = value;
TimelineEditor.RefreshPreviewPlay();
#else
Debug.LogWarning($"PlaybackLockedToFrame is not available for this Unity version");
#endif
}
}
/// <summary>
/// Enable the ability to snap clips on the edge of another clip.
/// </summary>
[SerializeField]
public bool edgeSnap = true;
/// <summary>
/// Behavior of the timeline window during playback.
/// </summary>
[SerializeField]
public PlaybackScrollMode playbackScrollMode;
void OnDisable()
{
Save();
}
/// <summary>
/// Save the timeline preferences settings file.
/// </summary>
public void Save()
{
Save(true);
}
internal SerializedObject GetSerializedObject()
{
return new SerializedObject(this);
}
}
class TimelinePreferencesProvider : SettingsProvider
{
SerializedObject m_SerializedObject;
SerializedProperty m_ShowAudioWaveform;
SerializedProperty m_TimeFormat;
SerializedProperty m_SnapToFrame;
SerializedProperty m_EdgeSnap;
SerializedProperty m_PlaybackScrollMode;
SerializedProperty m_PlaybackLockedToFrame;
internal class Styles
{
public static readonly GUIContent TimeUnitLabel = L10n.TextContent("Time Unit", "Define the time unit for the timeline window (Frames, Timecode or Seconds).");
public static readonly GUIContent ShowAudioWaveformLabel = L10n.TextContent("Show Audio Waveforms", "Draw the waveforms for all audio clips.");
public static readonly GUIContent AudioScrubbingLabel = L10n.TextContent("Allow Audio Scrubbing", "Allow the users to hear audio while scrubbing on audio clip.");
public static readonly GUIContent SnapToFrameLabel = L10n.TextContent("Snap To Frame", "Enable Snap to Frame to manipulate clips and align them on frames.");
public static readonly GUIContent EdgeSnapLabel = L10n.TextContent("Edge Snap", "Enable the ability to snap clips on the edge of another clip.");
public static readonly GUIContent PlaybackScrollModeLabel = L10n.TextContent("Playback Scrolling Mode", "Define scrolling behavior during playback.");
public static readonly GUIContent EditorSettingLabel = L10n.TextContent("Timeline Editor Settings", "");
#if TIMELINE_FRAMEACCURATE
public static readonly GUIContent PlaybackLockedToFrame = L10n.TextContent("Playback Locked To Frame", "Enable Frame Accurate Preview.");
#endif
}
public TimelinePreferencesProvider(string path, SettingsScope scopes, IEnumerable<string> keywords = null)
: base(path, scopes, keywords)
{
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
TimelinePreferences.instance.Save();
m_SerializedObject = TimelinePreferences.instance.GetSerializedObject();
m_ShowAudioWaveform = m_SerializedObject.FindProperty("showAudioWaveform");
m_TimeFormat = m_SerializedObject.FindProperty("timeFormat");
m_SnapToFrame = m_SerializedObject.FindProperty("snapToFrame");
m_EdgeSnap = m_SerializedObject.FindProperty("edgeSnap");
m_PlaybackScrollMode = m_SerializedObject.FindProperty("playbackScrollMode");
#if TIMELINE_FRAMEACCURATE
m_PlaybackLockedToFrame = m_SerializedObject.FindProperty("m_PlaybackLockedToFrame");
#endif
}
public override void OnGUI(string searchContext)
{
m_SerializedObject.Update();
EditorGUI.BeginChangeCheck();
using (new SettingsWindow.GUIScope())
{
EditorGUILayout.LabelField(Styles.EditorSettingLabel, EditorStyles.boldLabel);
m_TimeFormat.enumValueIndex = EditorGUILayout.Popup(Styles.TimeUnitLabel, m_TimeFormat.enumValueIndex, m_TimeFormat.enumDisplayNames);
m_PlaybackScrollMode.enumValueIndex = EditorGUILayout.Popup(Styles.PlaybackScrollModeLabel, m_PlaybackScrollMode.enumValueIndex, m_PlaybackScrollMode.enumNames);
m_ShowAudioWaveform.boolValue = EditorGUILayout.Toggle(Styles.ShowAudioWaveformLabel, m_ShowAudioWaveform.boolValue);
TimelinePreferences.instance.audioScrubbing = EditorGUILayout.Toggle(Styles.AudioScrubbingLabel, TimelinePreferences.instance.audioScrubbing);
m_SnapToFrame.boolValue = EditorGUILayout.Toggle(Styles.SnapToFrameLabel, m_SnapToFrame.boolValue);
m_EdgeSnap.boolValue = EditorGUILayout.Toggle(Styles.EdgeSnapLabel, m_EdgeSnap.boolValue);
#if TIMELINE_FRAMEACCURATE
m_PlaybackLockedToFrame.boolValue = EditorGUILayout.Toggle(Styles.PlaybackLockedToFrame, m_PlaybackLockedToFrame.boolValue);
#endif
}
if (EditorGUI.EndChangeCheck())
{
m_SerializedObject.ApplyModifiedProperties();
TimelinePreferences.instance.Save();
TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
TimelineEditor.RefreshPreviewPlay();
}
}
[SettingsProvider]
public static SettingsProvider CreateTimelineProjectSettingProvider()
{
var provider = new TimelinePreferencesProvider("Preferences/Timeline", SettingsScope.User, GetSearchKeywordsFromGUIContentProperties<Styles>());
return provider;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 227613ec5b3943443a284e30a6d6b91e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Timeline;
using UnityEngine.Serialization;
using UnityEngine.Timeline;
using UnityEngine.UIElements;
#if !UNITY_2020_2_OR_NEWER
using L10n = UnityEditor.Timeline.L10n;
#endif
/// <summary>
/// Store the settings for Timeline that will be stored with the Unity Project.
/// </summary>
[FilePath("ProjectSettings/TimelineSettings.asset", FilePathAttribute.Location.ProjectFolder)]
public class TimelineProjectSettings : ScriptableSingleton<TimelineProjectSettings>
{
/// <summary>
/// Define the default framerate when a Timeline asset is created.
/// </summary>
[HideInInspector, Obsolete("assetDefaultFramerate has been deprecated. Use defaultFrameRate instead.")]
public float assetDefaultFramerate = (float)TimelineAsset.EditorSettings.kDefaultFrameRate;
[SerializeField, FrameRateField, FormerlySerializedAs("assetDefaultFramerate")]
private double m_DefaultFrameRate = TimelineAsset.EditorSettings.kDefaultFrameRate;
/// <summary>
/// Defines the default frame rate when a Timeline asset is created from the project window.
/// </summary>
public double defaultFrameRate
{
#pragma warning disable 0618
get
{
if (m_DefaultFrameRate != assetDefaultFramerate)
{
return assetDefaultFramerate;
}
return m_DefaultFrameRate;
}
set
{
m_DefaultFrameRate = value;
assetDefaultFramerate = (float)value;
}
#pragma warning restore 0618
}
void OnDisable()
{
Save();
}
/// <summary>
/// Save the timeline project settings file in the project directory.
/// </summary>
public void Save()
{
Save(true);
}
internal SerializedObject GetSerializedObject()
{
return new SerializedObject(this);
}
private void OnValidate()
{
#pragma warning disable 0618
assetDefaultFramerate = (float)m_DefaultFrameRate;
#pragma warning restore 0618
}
}
class TimelineProjectSettingsProvider : SettingsProvider
{
SerializedObject m_SerializedObject;
SerializedProperty m_Framerate;
private class Styles
{
public static readonly GUIContent DefaultFramerateLabel = L10n.TextContent("Default frame rate", "The default frame rate for new Timeline assets.");
public static readonly GUIContent TimelineAssetLabel = L10n.TextContent("Timeline Asset", "");
public static readonly string WarningString = L10n.Tr("Locking playback cannot be enabled for this frame rate.");
}
public TimelineProjectSettingsProvider(string path, SettingsScope scopes, IEnumerable<string> keywords = null)
: base(path, scopes, keywords) { }
public override void OnActivate(string searchContext, VisualElement rootElement)
{
TimelineProjectSettings.instance.Save();
m_SerializedObject = TimelineProjectSettings.instance.GetSerializedObject();
m_Framerate = m_SerializedObject.FindProperty("m_DefaultFrameRate");
}
public override void OnGUI(string searchContext)
{
using (new SettingsWindow.GUIScope())
{
m_SerializedObject.Update();
EditorGUILayout.LabelField(Styles.TimelineAssetLabel, EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
m_Framerate.doubleValue = FrameRateDrawer.FrameRateField(m_Framerate.doubleValue, Styles.DefaultFramerateLabel,
EditorGUILayout.GetControlRect(), out bool frameRateIsValid);
if (EditorGUI.EndChangeCheck())
{
m_SerializedObject.ApplyModifiedProperties();
TimelineProjectSettings.instance.Save();
}
#if TIMELINE_FRAMEACCURATE
if (!frameRateIsValid && TimelinePreferences.instance.playbackLockedToFrame)
EditorGUILayout.HelpBox(Styles.WarningString, MessageType.Warning);
#endif
}
}
[SettingsProvider]
public static SettingsProvider CreateTimelineProjectSettingProvider()
{
var provider = new TimelineProjectSettingsProvider("Project/Timeline", SettingsScope.Project, GetSearchKeywordsFromGUIContentProperties<Styles>());
return provider;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a287be6c49135cd4f9b2b8666c39d999
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,217 @@
//#define PERF_PROFILE
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomEditor(typeof(TrackAsset), true, isFallback = true)]
[CanEditMultipleObjects]
class TrackAssetInspector : Editor
{
class TrackCurvesWrapper : ICurvesOwnerInspectorWrapper
{
public ICurvesOwner curvesOwner { get; }
public SerializedObject serializedPlayableAsset { get; }
public int lastCurveVersion { get; set; }
public double lastEvalTime { get; set; }
public TrackCurvesWrapper(TrackAsset track)
{
lastCurveVersion = -1;
lastEvalTime = -1;
if (track != null)
{
curvesOwner = track;
serializedPlayableAsset = new SerializedObject(track);
}
}
public double ToLocalTime(double time)
{
return time;
}
}
TrackCurvesWrapper m_TrackCurvesWrapper;
SerializedProperty m_Name;
bool m_IsBuiltInType;
Texture m_HeaderIcon;
protected TimelineWindow timelineWindow
{
get
{
return TimelineWindow.instance;
}
}
protected bool IsTrackLocked()
{
if (!TimelineUtility.IsCurrentSequenceValid() || IsCurrentSequenceReadOnly())
return true;
return targets.Any(track => ((TrackAsset)track).lockedInHierarchy);
}
public override void OnInspectorGUI()
{
using (new EditorGUI.DisabledScope(IsTrackLocked()))
{
DrawInspector();
}
}
internal override bool IsEnabled()
{
return TimelineUtility.IsCurrentSequenceValid() && !IsCurrentSequenceReadOnly() && base.IsEnabled();
}
internal override void OnHeaderTitleGUI(Rect titleRect, string header)
{
serializedObject.Update();
var textFieldRect = titleRect;
using (new GUIMixedValueScope(m_Name.hasMultipleDifferentValues))
{
var seqWindow = TimelineWindow.instance;
if (IsTrackLocked())
{
base.OnHeaderTitleGUI(titleRect, m_Name.stringValue);
}
else
{
EditorGUI.BeginChangeCheck();
string newName = EditorGUI.DelayedTextField(textFieldRect, m_Name.stringValue, EditorStyles.textField);
if (EditorGUI.EndChangeCheck() && !string.IsNullOrEmpty(newName))
{
for (int c = 0; c < targets.Length; c++)
{
ObjectNames.SetNameSmart(targets[c], newName);
}
if (seqWindow != null)
seqWindow.Repaint();
}
serializedObject.ApplyModifiedProperties();
}
}
}
internal override void OnHeaderIconGUI(Rect iconRect)
{
if (TimelineWindow.instance == null)
return;
using (new EditorGUI.DisabledScope(IsTrackLocked()))
{
if (m_HeaderIcon != null)
GUI.Label(iconRect, GUIContent.Temp(m_HeaderIcon));
}
}
internal override Rect DrawHeaderHelpAndSettingsGUI(Rect r)
{
using (new EditorGUI.DisabledScope(IsTrackLocked()))
{
var helpSize = EditorStyles.iconButton.CalcSize(EditorGUI.GUIContents.helpIcon);
const int kTopMargin = 5;
// Show Editor Header Items.
return EditorGUIUtility.DrawEditorHeaderItems(new Rect(r.xMax - helpSize.x, r.y + kTopMargin, helpSize.x, helpSize.y), targets);
}
}
public virtual void OnEnable()
{
m_IsBuiltInType = target != null && target.GetType().Assembly == typeof(TrackAsset).Assembly;
m_Name = serializedObject.FindProperty("m_Name");
m_TrackCurvesWrapper = new TrackCurvesWrapper(target as TrackAsset);
m_HeaderIcon = TrackResourceCache.s_DefaultIcon.image;
// only worry about the first track. if types are different, a different inspector is used.
var track = target as TrackAsset;
if (track != null)
{
var drawer = CustomTimelineEditorCache.GetTrackEditor(track);
UnityEngine.Object binding = null;
var director = m_Context as PlayableDirector;
if (director != null)
binding = director.GetGenericBinding(track);
var options = drawer.GetTrackOptions(track, binding);
if (options.icon != null)
m_HeaderIcon = options.icon;
else
m_HeaderIcon = TrackResourceCache.GetTrackIcon(track).image;
}
}
void DrawInspector()
{
if (serializedObject == null)
return;
if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout)
{
CurvesOwnerInspectorHelper.PreparePlayableAsset(m_TrackCurvesWrapper);
}
serializedObject.Update();
using (var changeScope = new EditorGUI.ChangeCheckScope())
{
DrawTrackProperties();
if (changeScope.changed)
{
serializedObject.ApplyModifiedProperties();
ApplyChanges();
}
}
}
protected virtual void DrawTrackProperties()
{
var property = serializedObject.GetIterator();
var expanded = true;
while (property.NextVisible(expanded))
{
if ("m_Script" == property.propertyPath)
{
// Don't draw script field for built-in types
if (m_IsBuiltInType)
continue;
// Disable the script field, as it will break your Timeline if you change it.
EditorGUI.BeginDisabled(true);
EditorGUILayout.PropertyField(property, !expanded);
EditorGUI.EndDisabled();
continue;
}
EditorGUILayout.PropertyField(property, !expanded);
expanded = false;
}
}
protected virtual void ApplyChanges()
{
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
static bool IsCurrentSequenceReadOnly()
{
return TimelineWindow.instance.state.editSequence.isReadOnly;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8795e0dd0041d2f44b1fe1959fc9fb53
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: