/Library/PackageCache/com.unity.timeline@1.2.18/Editor/inspectors/AnimationPlayableAssetInspector.cs
C# | 341 lines | 283 code | 53 blank | 5 comment | 100 complexity | 6cf0dec07e103ac9127ad35e4dbb4d0d MD5 | raw file
1using System;
2using System.Linq;
3using UnityEngine;
4using UnityEngine.Timeline;
5
6namespace UnityEditor.Timeline
7{
8 [CustomEditor(typeof(AnimationPlayableAsset)), CanEditMultipleObjects]
9 class AnimationPlayableAssetInspector : Editor
10 {
11 static class Styles
12 {
13 public static readonly GUIContent RotationText = EditorGUIUtility.TrTextContent("Rotation");
14 public static readonly GUIContent AnimClipText = EditorGUIUtility.TrTextContent("Animation Clip");
15 public static readonly GUIContent TransformOffsetTitle = EditorGUIUtility.TrTextContent("Clip Transform Offsets", "Use this to offset the root transform position and rotation relative to the track when playing this clip");
16 public static readonly GUIContent AnimationClipName = EditorGUIUtility.TrTextContent("Animation Clip Name");
17 public static readonly GUIContent MatchTargetFieldsTitle = EditorGUIUtility.TrTextContent("Offsets Match Fields", "Fields to apply when matching offsets on clips. The defaults can be set on the track.");
18 public static readonly GUIContent UseDefaults = EditorGUIUtility.TrTextContent("Use defaults");
19 public static readonly GUIContent RemoveStartOffset = EditorGUIUtility.TrTextContent("Remove Start Offset", "Makes playback of the clip play relative to first key of the root transform");
20 public static readonly GUIContent ApplyFootIK = EditorGUIUtility.TrTextContent("Foot IK", "Enable to apply foot IK to the AnimationClip when the target is humanoid.");
21 public static readonly GUIContent Loop = EditorGUIUtility.TrTextContent("Loop", "Whether the source Animation Clip loops during playback.");
22 }
23
24 TimelineWindow m_TimelineWindow;
25 GameObject m_Binding;
26
27 TimelineAnimationUtilities.OffsetEditMode m_OffsetEditMode = TimelineAnimationUtilities.OffsetEditMode.None;
28 EditorClip m_EditorClip;
29 EditorClip[] m_EditorClips;
30
31 SerializedProperty m_PositionProperty;
32 SerializedProperty m_RotationProperty;
33 SerializedProperty m_AnimClipProperty;
34 SerializedProperty m_UseTrackMatchFieldsProperty;
35 SerializedProperty m_MatchTargetFieldsProperty;
36 SerializedObject m_SerializedAnimClip;
37 SerializedProperty m_SerializedAnimClipName;
38 SerializedProperty m_RemoveStartOffsetProperty;
39 SerializedProperty m_ApplyFootIK;
40 SerializedProperty m_Loop;
41
42 Vector3 m_LastPosition;
43 Vector3 m_LastRotation;
44
45 public override void OnInspectorGUI()
46 {
47 if (target == null)
48 return;
49
50 serializedObject.Update();
51
52 if (!m_TimelineWindow) m_TimelineWindow = TimelineWindow.instance;
53
54 ShowAnimationClipField();
55 ShowRecordableClipRename();
56 ShowAnimationClipWarnings();
57
58 EditorGUI.BeginChangeCheck();
59
60 TransformOffsetsGUI();
61
62 // extra checks are because the context menu may need to cause a re-evaluate
63 bool changed = EditorGUI.EndChangeCheck() ||
64 m_LastPosition != m_PositionProperty.vector3Value ||
65 m_LastRotation != m_RotationProperty.vector3Value;
66 m_LastPosition = m_PositionProperty.vector3Value;
67 m_LastRotation = m_RotationProperty.vector3Value;
68
69 if (changed)
70 {
71 // updates the changed properties and pushes them to the active playable
72 serializedObject.ApplyModifiedProperties();
73 ((AnimationPlayableAsset)target).LiveLink();
74
75 // force an evaluate to happen next frame
76 if (TimelineWindow.instance != null && TimelineWindow.instance.state != null)
77 {
78 TimelineWindow.instance.state.Evaluate();
79 }
80 }
81
82 EditorGUI.BeginChangeCheck();
83 EditorGUILayout.PropertyField(m_ApplyFootIK, Styles.ApplyFootIK);
84 EditorGUILayout.PropertyField(m_Loop, Styles.Loop);
85 if (EditorGUI.EndChangeCheck())
86 TimelineEditor.Refresh(RefreshReason.ContentsModified);
87
88 serializedObject.ApplyModifiedProperties();
89 }
90
91 void ShowAnimationClipField()
92 {
93 bool disabled = m_EditorClips == null || m_EditorClips.Any(c => c.clip == null || c.clip.recordable);
94 using (new EditorGUI.DisabledScope(disabled))
95 {
96 EditorGUI.BeginChangeCheck();
97 EditorGUILayout.PropertyField(m_AnimClipProperty, Styles.AnimClipText);
98 if (EditorGUI.EndChangeCheck())
99 {
100 // rename the timeline clips to match the animation name if it did previously
101 if (m_AnimClipProperty.objectReferenceValue != null && m_EditorClips != null)
102 {
103 var newName = m_AnimClipProperty.objectReferenceValue.name;
104 foreach (var c in m_EditorClips)
105 {
106 if (c == null || c.clip == null || c.clip.asset == null)
107 continue;
108
109 var apa = c.clip.asset as AnimationPlayableAsset;
110 if (apa != null && apa.clip != null && c.clip.displayName == apa.clip.name)
111 {
112 if (c.clip.parentTrack != null)
113 Undo.RegisterCompleteObjectUndo(c.clip.parentTrack, "Inspector");
114 c.clip.displayName = newName;
115 }
116 }
117 }
118
119 TimelineEditor.Refresh(RefreshReason.ContentsModified);
120 }
121 }
122 }
123
124 void TransformOffsetsMatchFieldsGUI()
125 {
126 var rect = EditorGUILayout.GetControlRect(true);
127 EditorGUI.BeginProperty(rect, Styles.MatchTargetFieldsTitle, m_UseTrackMatchFieldsProperty);
128
129 rect = EditorGUI.PrefixLabel(rect, Styles.MatchTargetFieldsTitle);
130 int oldIndent = EditorGUI.indentLevel;
131 EditorGUI.indentLevel = 0;
132 EditorGUI.BeginChangeCheck();
133 bool val = m_UseTrackMatchFieldsProperty.boolValue;
134 val = EditorGUI.ToggleLeft(rect, Styles.UseDefaults, val);
135 if (EditorGUI.EndChangeCheck())
136 m_UseTrackMatchFieldsProperty.boolValue = val;
137
138 EditorGUI.indentLevel = oldIndent;
139 EditorGUI.EndProperty();
140
141
142 if (!val || m_UseTrackMatchFieldsProperty.hasMultipleDifferentValues)
143 {
144 EditorGUI.indentLevel++;
145 AnimationTrackInspector.MatchTargetsFieldGUI(m_MatchTargetFieldsProperty);
146 EditorGUI.indentLevel--;
147 }
148 }
149
150 void TransformOffsetsGUI()
151 {
152 if (ShouldShowOffsets())
153 {
154 EditorGUILayout.Space();
155 EditorGUILayout.LabelField(Styles.TransformOffsetTitle);
156 EditorGUI.indentLevel++;
157
158 using (new EditorGUI.DisabledScope(targets.Length > 1))
159 {
160 var previousOffsetMode = m_OffsetEditMode;
161 AnimationTrackInspector.ShowMotionOffsetEditModeToolbar(ref m_OffsetEditMode);
162 if (previousOffsetMode != m_OffsetEditMode)
163 {
164 SetTimeToClip();
165 SceneView.RepaintAll();
166 }
167 }
168
169 EditorGUILayout.BeginHorizontal();
170 EditorGUILayout.PropertyField(m_PositionProperty);
171 EditorGUILayout.EndHorizontal();
172
173 EditorGUILayout.BeginHorizontal();
174 EditorGUILayout.PropertyField(m_RotationProperty, Styles.RotationText);
175 EditorGUILayout.EndHorizontal();
176
177 EditorGUILayout.Space();
178
179 EditorGUI.indentLevel--;
180
181 TransformOffsetsMatchFieldsGUI();
182
183 EditorGUI.BeginChangeCheck();
184 EditorGUILayout.PropertyField(m_RemoveStartOffsetProperty, Styles.RemoveStartOffset);
185 if (EditorGUI.EndChangeCheck())
186 {
187 TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
188 Repaint();
189 }
190 }
191 }
192
193 void Reevaluate()
194 {
195 if (m_TimelineWindow != null && m_TimelineWindow.state != null)
196 {
197 m_TimelineWindow.state.Refresh();
198 m_TimelineWindow.state.EvaluateImmediate();
199 }
200 }
201
202 // Make sure the director time is within the bounds of the clip
203 void SetTimeToClip()
204 {
205 if (m_TimelineWindow != null && m_TimelineWindow.state != null)
206 {
207 m_TimelineWindow.state.editSequence.time = Math.Min(m_EditorClip.clip.end, Math.Max(m_EditorClip.clip.start, m_TimelineWindow.state.editSequence.time));
208 }
209 }
210
211 public void OnEnable()
212 {
213 if (target == null) // case 946080
214 return;
215
216 m_EditorClip = UnityEditor.Selection.activeObject as EditorClip;
217 m_EditorClips = UnityEditor.Selection.objects.OfType<EditorClip>().ToArray();
218 SceneView.duringSceneGui += OnSceneGUI;
219
220 m_PositionProperty = serializedObject.FindProperty("m_Position");
221 m_PositionProperty.isExpanded = true;
222 m_RotationProperty = serializedObject.FindProperty("m_EulerAngles");
223 m_AnimClipProperty = serializedObject.FindProperty("m_Clip");
224 m_UseTrackMatchFieldsProperty = serializedObject.FindProperty("m_UseTrackMatchFields");
225 m_MatchTargetFieldsProperty = serializedObject.FindProperty("m_MatchTargetFields");
226 m_RemoveStartOffsetProperty = serializedObject.FindProperty("m_RemoveStartOffset");
227 m_ApplyFootIK = serializedObject.FindProperty("m_ApplyFootIK");
228 m_Loop = serializedObject.FindProperty("m_Loop");
229
230 m_LastPosition = m_PositionProperty.vector3Value;
231 m_LastRotation = m_RotationProperty.vector3Value;
232 }
233
234 void OnDestroy()
235 {
236 SceneView.duringSceneGui -= OnSceneGUI;
237 }
238
239 void OnSceneGUI(SceneView sceneView)
240 {
241 DoManipulators();
242 }
243
244 Transform GetTransform()
245 {
246 if (m_Binding != null)
247 return m_Binding.transform;
248
249 if (m_TimelineWindow != null && m_TimelineWindow.state != null &&
250 m_TimelineWindow.state.editSequence.director != null &&
251 m_EditorClip != null && m_EditorClip.clip != null)
252 {
253 var obj = TimelineUtility.GetSceneGameObject(m_TimelineWindow.state.editSequence.director,
254 m_EditorClip.clip.parentTrack);
255 m_Binding = obj;
256 if (obj != null)
257 return obj.transform;
258 }
259 return null;
260 }
261
262 void DoManipulators()
263 {
264 if (m_EditorClip == null || m_EditorClip.clip == null)
265 return;
266
267 AnimationPlayableAsset animationPlayable = m_EditorClip.clip.asset as AnimationPlayableAsset;
268 AnimationTrack track = m_EditorClip.clip.parentTrack as AnimationTrack;
269 Transform transform = GetTransform();
270
271 if (transform != null && animationPlayable != null && m_OffsetEditMode != TimelineAnimationUtilities.OffsetEditMode.None && track != null)
272 {
273 TimelineUndo.PushUndo(animationPlayable, "Inspector");
274 Vector3 position = transform.position;
275 Quaternion rotation = transform.rotation;
276
277 EditorGUI.BeginChangeCheck();
278 if (m_OffsetEditMode == TimelineAnimationUtilities.OffsetEditMode.Translation)
279 {
280 position = Handles.PositionHandle(position, Tools.pivotRotation == PivotRotation.Global ? Quaternion.identity : rotation);
281 }
282 else if (m_OffsetEditMode == TimelineAnimationUtilities.OffsetEditMode.Rotation)
283 {
284 rotation = Handles.RotationHandle(rotation, position);
285 }
286
287 if (EditorGUI.EndChangeCheck())
288 {
289 var res = TimelineAnimationUtilities.UpdateClipOffsets(animationPlayable, track, transform, position, rotation);
290 animationPlayable.position = res.position;
291 animationPlayable.eulerAngles = AnimationUtility.GetClosestEuler(res.rotation, animationPlayable.eulerAngles, RotationOrder.OrderZXY);
292 Reevaluate();
293 Repaint();
294 }
295 }
296 }
297
298 void ShowAnimationClipWarnings()
299 {
300 AnimationClip clip = m_AnimClipProperty.objectReferenceValue as AnimationClip;
301 if (clip == null)
302 {
303 EditorGUILayout.HelpBox(AnimationPlayableAssetEditor.k_NoClipAssignedError, MessageType.Warning);
304 }
305 else if (clip.legacy)
306 {
307 EditorGUILayout.HelpBox(AnimationPlayableAssetEditor.k_LegacyClipError, MessageType.Warning);
308 }
309 }
310
311 bool ShouldShowOffsets()
312 {
313 return targets.OfType<AnimationPlayableAsset>().All(x => x.hasRootTransforms);
314 }
315
316 void ShowRecordableClipRename()
317 {
318 if (targets.Length > 1 || m_EditorClip == null || m_EditorClip.clip == null || !m_EditorClip.clip.recordable)
319 return;
320
321 AnimationClip clip = m_AnimClipProperty.objectReferenceValue as AnimationClip;
322 if (clip == null || !AssetDatabase.IsSubAsset(clip))
323 return;
324
325 if (m_SerializedAnimClip == null)
326 {
327 m_SerializedAnimClip = new SerializedObject(clip);
328 m_SerializedAnimClipName = m_SerializedAnimClip.FindProperty("m_Name");
329 }
330
331 if (m_SerializedAnimClipName != null)
332 {
333 m_SerializedAnimClip.Update();
334 EditorGUI.BeginChangeCheck();
335 EditorGUILayout.DelayedTextField(m_SerializedAnimClipName, Styles.AnimationClipName);
336 if (EditorGUI.EndChangeCheck())
337 m_SerializedAnimClip.ApplyModifiedProperties();
338 }
339 }
340 }
341}