AnimancerStateDrawer.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR && UNITY_IMGUI
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using static Animancer.Editor.AnimancerGraphDrawer;
  7. using static Animancer.Editor.AnimancerGUI;
  8. using static Animancer.Editor.AnimancerStateDrawerColors;
  9. using Object = UnityEngine.Object;
  10. namespace Animancer.Editor
  11. {
  12. /// <summary>[Editor-Only] Draws the Inspector GUI for an <see cref="AnimancerState"/>.</summary>
  13. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerStateDrawer_1
  14. [CustomGUI(typeof(AnimancerState))]
  15. public class AnimancerStateDrawer<T> : AnimancerNodeDrawer<T>
  16. where T : AnimancerState
  17. {
  18. /************************************************************************************************************************/
  19. /// <inheritdoc/>
  20. protected override bool AutoNormalizeSiblingWeights
  21. => AutoNormalizeWeights
  22. && typeof(T) != typeof(ManualMixerState);
  23. /************************************************************************************************************************/
  24. private FastObjectField _NameField;
  25. private FastObjectField _MainObjectField;
  26. /// <summary>Draws the state's main label with a bar to indicate its current time.</summary>
  27. protected override void DoLabelGUI(Rect area)
  28. {
  29. area = area.Expand(StandardSpacing, 0);
  30. var wholeArea = area;
  31. var effectiveWeight = Value.EffectiveWeight;
  32. var highlightArea = default(Rect);
  33. var isRepaint = Event.current.type == EventType.Repaint;
  34. if (isRepaint)
  35. {
  36. EditorGUI.DrawRect(wholeArea, HeaderBackgroundColor);
  37. highlightArea = DoTimeHighlightBarGUI(wholeArea, effectiveWeight);
  38. DoEventsGUI(wholeArea);
  39. ObjectHighlightGUI.Draw(wholeArea, Value);
  40. }
  41. DoWeightLabel(ref area, Value.Weight, effectiveWeight);
  42. AnimationBindings.DoBindingMatchGUI(ref area, Value);
  43. HandleLabelClick(wholeArea);
  44. area = EditorGUI.IndentedRect(area);
  45. var name = Value.DebugName ?? Value.Key;
  46. var mainObject = Value.MainObject;
  47. if (mainObject == null)
  48. {
  49. var value = name ?? Value;
  50. var drawPing = value != Value;
  51. _NameField.Draw(area, value, drawPing);
  52. }
  53. else if (ReferenceEquals(name, mainObject) ||
  54. (name is Object nameObject && nameObject == mainObject) ||
  55. (name is ITransition && Current != null && !Current.IsMainObjectUsedMultipleTimes(mainObject)))
  56. {
  57. _MainObjectField.Draw(area, mainObject, false);
  58. }
  59. else
  60. {
  61. if (name != null)
  62. {
  63. var nameArea = StealFromLeft(ref area, EditorGUIUtility.labelWidth - IndentSize);
  64. _NameField.Draw(nameArea, name, true);
  65. }
  66. _MainObjectField.Draw(area, mainObject, false);
  67. }
  68. if (isRepaint)
  69. DoDetailLinesGUI(wholeArea, highlightArea, effectiveWeight);
  70. }
  71. /************************************************************************************************************************/
  72. /// <summary>Draws a progress bar to show the animation time.</summary>
  73. public Rect DoTimeHighlightBarGUI(Rect area, float effectiveWeight)
  74. => DoTimeHighlightBarGUI(
  75. area,
  76. Value.IsPlaying,
  77. effectiveWeight,
  78. Value.Time,
  79. Value.EffectiveSpeed,
  80. Value.Length,
  81. Value.IsLooping);
  82. /// <summary>Draws a progress bar to show the animation time.</summary>
  83. public static Rect DoTimeHighlightBarGUI(
  84. Rect area,
  85. bool isPlaying,
  86. float effectiveWeight,
  87. float time,
  88. float speed,
  89. float length,
  90. bool isLooping)
  91. {
  92. if (ScaleTimeBarByWeight)
  93. {
  94. var height = area.height;
  95. area.height *= Mathf.Clamp01(effectiveWeight);
  96. area.y += height - area.height;
  97. }
  98. var color = isPlaying ? PlayingBarColor : PausedBarColor;
  99. var wrappedTime = GetWrappedTime(time, length, isLooping);
  100. if (length == 0)
  101. {
  102. if (time == 0)
  103. return area;
  104. }
  105. else
  106. {
  107. if (speed >= 0 || time == 0)
  108. {
  109. area.width *= Mathf.Clamp01(wrappedTime / length);
  110. }
  111. else
  112. {
  113. var xMax = area.xMax;
  114. area.x += area.width * Mathf.Clamp01(wrappedTime / length);
  115. area.x = Mathf.Floor(area.x);
  116. area.xMax = xMax;
  117. }
  118. }
  119. EditorGUI.DrawRect(area, color);
  120. return area;
  121. }
  122. /************************************************************************************************************************/
  123. /// <summary>Draws lines for the current weight, time, and fade destination.</summary>
  124. public void DoDetailLinesGUI(
  125. Rect totalArea,
  126. Rect highlightArea,
  127. float effectiveWeight)
  128. {
  129. var length = Value.Length;
  130. var speed = Value.Speed;
  131. var speedSign = speed >= 0 ? 1 : -1;
  132. var currentX = speed >= 0 ? highlightArea.xMax : highlightArea.xMin - 1;
  133. var forwardEdge = speed >= 0 ? totalArea.xMax : totalArea.xMin - 1;
  134. var color = FadeLineColor;
  135. color.a = color.a * effectiveWeight * 0.75f + 0.25f;
  136. if (Value.Time != 0 || Value.IsPlaying || Value.Weight != 0)
  137. {
  138. EditorGUI.DrawRect(
  139. new(highlightArea.x, highlightArea.yMin, highlightArea.width, 1),
  140. color);
  141. if (length == 0)
  142. return;
  143. EditorGUI.DrawRect(
  144. new(currentX - speedSign, totalArea.y, 1, totalArea.height),
  145. color);
  146. }
  147. else if (length == 0)
  148. {
  149. return;
  150. }
  151. if (!Value.IsPlaying)
  152. return;
  153. var fade = Value.FadeGroup;
  154. if (fade == null || !fade.IsValid)
  155. return;
  156. var currentCorner = new Vector2(currentX, highlightArea.yMin);
  157. var targetWeight = Value.TargetWeight;
  158. var remainingFadeDuration = fade.RemainingFadeDuration;
  159. var targetCorner = new Vector2(
  160. currentCorner.x + speed * remainingFadeDuration / Value.Length * totalArea.width,
  161. Mathf.Lerp(totalArea.yMax, totalArea.yMin, targetWeight));
  162. var intersect = Mathf.InverseLerp(currentCorner.x, targetCorner.x, forwardEdge);
  163. var end = Vector2.LerpUnclamped(currentCorner, targetCorner, intersect);
  164. BeginTriangles(color);
  165. DrawLineBatched(
  166. currentCorner,
  167. end,
  168. 1);
  169. if (intersect < 1 && Value.IsLooping)
  170. {
  171. end.x -= speedSign * totalArea.width;
  172. targetCorner.x -= speedSign * totalArea.width;
  173. DrawLineBatched(
  174. end,
  175. targetCorner,
  176. 1);
  177. }
  178. EndTriangles();
  179. }
  180. /************************************************************************************************************************/
  181. /// <summary>Draws marks on the timeline for each event.</summary>
  182. private void DoEventsGUI(Rect area)
  183. {
  184. if (!ShowEvents)
  185. return;
  186. DoAnimancerEventsGUI(area);
  187. DoAnimationEventsGUI(area);
  188. }
  189. /// <summary>Draws marks on the timeline for each Animancer Event.</summary>
  190. private void DoAnimancerEventsGUI(Rect area)
  191. {
  192. var events = Value.SharedEvents;
  193. if (events == null)
  194. return;
  195. for (int i = 0; i < events.Count; i++)
  196. DoEventTick(area, events[i].normalizedTime);
  197. if (events.OnEnd != null)
  198. DoEventTick(area, events.GetRealNormalizedEndTime(Value.Speed));
  199. }
  200. /// <summary>Draws marks on the timeline for each Animation Event.</summary>
  201. private void DoAnimationEventsGUI(Rect area)
  202. {
  203. var clip = Value.MainObject as AnimationClip;
  204. if (clip == null)
  205. return;
  206. var inverseLength = 1f / Value.Length;
  207. var events = clip.GetCachedEvents();
  208. for (int i = 0; i < events.Length; i++)
  209. DoEventTick(area, events[i].time * inverseLength);
  210. }
  211. /// <summary>Draws a mark on the timeline for an event.</summary>
  212. private static void DoEventTick(Rect area, float normalizedTime)
  213. {
  214. if (normalizedTime >= 0 && normalizedTime <= 1)
  215. {
  216. var x = area.x + area.width * normalizedTime;
  217. var eventArea = new Rect(x - 1, area.y, 2, area.height * 0.3f);
  218. EditorGUI.DrawRect(eventArea, EventTickColor);
  219. }
  220. }
  221. /************************************************************************************************************************/
  222. /// <summary>Handles clicks on the label area.</summary>
  223. private void HandleLabelClick(Rect area)
  224. {
  225. var currentEvent = Event.current;
  226. if (currentEvent.type != EventType.MouseUp ||
  227. currentEvent.button != 0 ||
  228. !area.Contains(currentEvent.mousePosition))
  229. return;
  230. currentEvent.Use(0);
  231. if (currentEvent.control)
  232. FadeInTarget();
  233. else
  234. ToggleExpanded(currentEvent.alt);
  235. }
  236. /// <summary>Fades in the target state (or its parent state if not directly attached to a layer).</summary>
  237. private void FadeInTarget()
  238. {
  239. Value.Graph.UnpauseGraph();
  240. AnimancerState target = Value;
  241. while (target != null)
  242. {
  243. var parent = target.Parent;
  244. if (parent is AnimancerLayer layer)
  245. {
  246. var fadeDuration = target.CalculateEditorFadeDuration(
  247. AnimancerGraph.DefaultFadeDuration);
  248. layer.Play(target, fadeDuration);
  249. return;
  250. }
  251. target = parent as AnimancerState;
  252. }
  253. }
  254. /// <summary>Toggles the target's details between expanded and collapsed.</summary>
  255. private void ToggleExpanded(bool toggleSiblings)
  256. {
  257. IsExpanded = !IsExpanded;
  258. if (toggleSiblings)
  259. {
  260. var parent = Value.Parent;
  261. var childCount = parent.ChildCount;
  262. for (int i = 0; i < childCount; i++)
  263. parent.GetChildNode(i)._IsInspectorExpanded = IsExpanded;
  264. }
  265. }
  266. /************************************************************************************************************************/
  267. /// <inheritdoc/>
  268. protected override void DoFoldoutGUI(Rect area)
  269. {
  270. var hierarchyMode = EditorGUIUtility.hierarchyMode;
  271. EditorGUIUtility.hierarchyMode = true;
  272. IsExpanded = EditorGUI.Foldout(area, IsExpanded, GUIContent.none, true);
  273. EditorGUIUtility.hierarchyMode = hierarchyMode;
  274. }
  275. /************************************************************************************************************************/
  276. /// <summary>
  277. /// Gets the current <see cref="AnimancerState.Time"/>.
  278. /// If the state is looping, the value is modulo by the <see cref="AnimancerState.Length"/>.
  279. /// </summary>
  280. private float GetWrappedTime(out float length)
  281. => GetWrappedTime(Value.Time, length = Value.Length, Value.IsLooping);
  282. /// <summary>
  283. /// Gets the current <see cref="AnimancerState.Time"/>.
  284. /// If the state is looping, the value is modulo by the <see cref="AnimancerState.Length"/>.
  285. /// </summary>
  286. private static float GetWrappedTime(float time, float length, bool isLooping)
  287. {
  288. var wrappedTime = time;
  289. if (isLooping)
  290. {
  291. wrappedTime = AnimancerUtilities.Wrap(wrappedTime, length);
  292. if (wrappedTime == 0 && time != 0)
  293. wrappedTime = length;
  294. }
  295. return wrappedTime;
  296. }
  297. /************************************************************************************************************************/
  298. private FastObjectField _KeyField;
  299. private FastObjectField _OwnerField;
  300. /************************************************************************************************************************/
  301. /// <summary>The display name of the <see cref="AnimancerState.MainObject"/> field.</summary>
  302. public virtual string MainObjectName
  303. => "Main Object";
  304. /************************************************************************************************************************/
  305. /// <inheritdoc/>
  306. protected override void DoDetailsGUI()
  307. {
  308. base.DoDetailsGUI();
  309. if (!IsExpanded)
  310. return;
  311. EditorGUI.indentLevel++;
  312. DoOptionalReferenceGUI(ref _KeyField, "Key", Value.Key);
  313. DoOptionalReferenceGUI(ref _OwnerField, "Owner", Value.Owner);
  314. var mainObject = Value.MainObject;
  315. if (mainObject != null)
  316. {
  317. var mainObjectType = Value.MainObjectType ?? typeof(Object);
  318. EditorGUI.BeginChangeCheck();
  319. var area = LayoutSingleLineRect(SpacingMode.Before);
  320. mainObject = EditorGUI.ObjectField(
  321. area,
  322. MainObjectName,
  323. mainObject,
  324. mainObjectType,
  325. true);
  326. if (EditorGUI.EndChangeCheck())
  327. Value.MainObject = mainObject;
  328. }
  329. DoTimeSliderGUI();
  330. DoNodeDetailsGUI();
  331. DoOnEndGUI();
  332. EditorGUI.indentLevel--;
  333. }
  334. /************************************************************************************************************************/
  335. /// <summary>Draws a `reference` if it isn't <c>null</c>.</summary>
  336. private static void DoOptionalReferenceGUI(ref FastObjectField field, string label, object reference)
  337. {
  338. if (reference != null)
  339. field.Draw(LayoutSingleLineRect(SpacingMode.Before), label, reference);
  340. }
  341. /************************************************************************************************************************/
  342. /// <summary>Draws a slider for controlling the current <see cref="AnimancerState.Time"/>.</summary>
  343. private void DoTimeSliderGUI()
  344. {
  345. if (Value.Length <= 0)
  346. return;
  347. var time = GetWrappedTime(out var length);
  348. if (length == 0)
  349. return;
  350. var area = LayoutSingleLineRect(SpacingMode.Before);
  351. var normalized = DoNormalizedTimeToggle(ref area);
  352. string label;
  353. float max;
  354. if (normalized)
  355. {
  356. label = "Normalized Time";
  357. time /= length;
  358. max = 1;
  359. }
  360. else
  361. {
  362. label = "Time";
  363. max = length;
  364. }
  365. DoLoopCounterGUI(ref area, length);
  366. EditorGUI.BeginChangeCheck();
  367. label = BeginTightLabel(label);
  368. time = EditorGUI.Slider(area, label, time, 0, max);
  369. EndTightLabel();
  370. if (TryUseClickEvent(area, 2))
  371. time = 0;
  372. if (EditorGUI.EndChangeCheck())
  373. {
  374. if (normalized)
  375. Value.NormalizedTime = time;
  376. else
  377. Value.Time = time;
  378. }
  379. }
  380. /************************************************************************************************************************/
  381. private static bool DoNormalizedTimeToggle(ref Rect area)
  382. {
  383. using (var label = PooledGUIContent.Acquire("N"))
  384. {
  385. var style = MiniButtonStyle;
  386. var width = style.CalculateWidth(label);
  387. var toggleArea = StealFromRight(ref area, width);
  388. UseNormalizedTimeSliders.Value = GUI.Toggle(toggleArea, UseNormalizedTimeSliders, label, style);
  389. }
  390. return UseNormalizedTimeSliders;
  391. }
  392. /************************************************************************************************************************/
  393. private static ConversionCache<int, string> _LoopCounterCache;
  394. private void DoLoopCounterGUI(ref Rect area, float length)
  395. {
  396. _LoopCounterCache ??= new(x => $"x{x}");
  397. string label;
  398. var normalizedTime = Value.Time / length;
  399. if (float.IsNaN(normalizedTime))
  400. {
  401. label = "NaN";
  402. }
  403. else
  404. {
  405. var loops = Mathf.FloorToInt(Value.Time / length);
  406. label = _LoopCounterCache.Convert(loops);
  407. }
  408. var width = CalculateLabelWidth(label);
  409. var labelArea = StealFromRight(ref area, width);
  410. GUI.Label(labelArea, label);
  411. }
  412. /************************************************************************************************************************/
  413. private void DoOnEndGUI()
  414. {
  415. var events = Value.SharedEvents;
  416. if (events == null)
  417. return;
  418. var drawer = EventSequenceDrawer.Get(events);
  419. var area = LayoutRect(drawer.CalculateHeight(events), SpacingMode.Before);
  420. using (var label = PooledGUIContent.Acquire("Events"))
  421. drawer.DoGUI(ref area, events, label);
  422. }
  423. /************************************************************************************************************************/
  424. #region Context Menu
  425. /************************************************************************************************************************/
  426. /// <inheritdoc/>
  427. protected override void PopulateContextMenu(GenericMenu menu)
  428. {
  429. AddContextMenuFunctions(menu);
  430. menu.AddFunction("Play",
  431. !Value.IsPlaying || Value.Weight != 1,
  432. () =>
  433. {
  434. AnimancerState.SkipNextExpectFade();
  435. Value.Graph.UnpauseGraph();
  436. Value.Graph.Layers[0].Play(Value);
  437. });
  438. AnimancerEditorUtilities.AddFadeFunction(menu, "Cross Fade (Ctrl + Click)",
  439. Value.Weight != 1,
  440. Value,
  441. duration =>
  442. {
  443. AnimancerState.SkipNextExpectFade();
  444. Value.Graph.UnpauseGraph();
  445. Value.Graph.Layers[0].Play(Value, duration);
  446. });
  447. menu.AddSeparator("");
  448. menu.AddItem(new("Destroy State"),
  449. false,
  450. () => Value.Destroy());
  451. menu.AddSeparator("");
  452. AddDisplayOptions(menu);
  453. AnimancerEditorUtilities.AddDocumentationLink(
  454. menu,
  455. "State Documentation",
  456. Strings.DocsURLs.States);
  457. }
  458. /************************************************************************************************************************/
  459. /// <summary>Adds the details of this state to the `menu`.</summary>
  460. protected virtual void AddContextMenuFunctions(GenericMenu menu)
  461. {
  462. menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.Key)}: {AnimancerUtilities.ToStringOrNull(Value.Key)}"));
  463. menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.Owner)}: {AnimancerUtilities.ToStringOrNull(Value.Owner)}"));
  464. var length = Value.Length;
  465. if (!float.IsNaN(length))
  466. menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.Length)}: {length}"));
  467. menu.AddDisabledItem(new($"{DetailsPrefix}Playable Path: {Value.GetPath()}"));
  468. var mainAsset = Value.MainObject;
  469. if (mainAsset != null)
  470. {
  471. var assetPath = AssetDatabase.GetAssetPath(mainAsset);
  472. if (assetPath != null)
  473. menu.AddDisabledItem(new($"{DetailsPrefix}Asset Path: {assetPath.Replace("/", "->")}"));
  474. }
  475. var events = Value.SharedEvents;
  476. if (events != null)
  477. {
  478. for (int i = 0; i < events.Count; i++)
  479. {
  480. var index = i;
  481. var name = events.GetName(i);
  482. AddEventFunctions(
  483. menu,
  484. name.IsNullOrEmpty() ? "Event " + index : name,
  485. name,
  486. events[index],
  487. () => events.SetCallback(index, AnimancerEvent.InvokeBoundCallback),
  488. () => events.Remove(index));
  489. }
  490. AddEventFunctions(
  491. menu,
  492. "End Event",
  493. default,
  494. events.EndEvent,
  495. () => events.EndEvent = new(float.NaN, null), null);
  496. }
  497. }
  498. /************************************************************************************************************************/
  499. private void AddEventFunctions(
  500. GenericMenu menu,
  501. string displayName,
  502. StringReference name,
  503. AnimancerEvent animancerEvent,
  504. GenericMenu.MenuFunction clearEvent,
  505. GenericMenu.MenuFunction removeEvent)
  506. {
  507. displayName = $"Events/{displayName}/";
  508. menu.AddDisabledItem(new($"{displayName}{nameof(AnimancerState.NormalizedTime)}: {animancerEvent.normalizedTime}"));
  509. bool canInvoke;
  510. if (animancerEvent.callback == null)
  511. {
  512. menu.AddDisabledItem(new(displayName + "Callback: null"));
  513. canInvoke = false;
  514. }
  515. else if (animancerEvent.callback == AnimancerEvent.DummyCallback)
  516. {
  517. menu.AddDisabledItem(new(displayName + "Callback: Dummy"));
  518. canInvoke = false;
  519. }
  520. else
  521. {
  522. var label = displayName +
  523. (animancerEvent.callback.Target != null
  524. ? ("Target: " + animancerEvent.callback.Target)
  525. : "Target: null");
  526. var targetObject = animancerEvent.callback.Target as Object;
  527. menu.AddFunction(label,
  528. targetObject != null,
  529. () => Selection.activeObject = targetObject);
  530. menu.AddDisabledItem(new(
  531. $"{displayName}Declaring Type: {animancerEvent.callback.Method.DeclaringType.GetNameCS()}"));
  532. menu.AddDisabledItem(new(
  533. $"{displayName}Method: {animancerEvent.callback.Method}"));
  534. canInvoke = true;
  535. }
  536. if (clearEvent != null)
  537. menu.AddFunction(displayName + "Clear", canInvoke || !float.IsNaN(animancerEvent.normalizedTime), clearEvent);
  538. if (removeEvent != null)
  539. menu.AddFunction(displayName + "Remove", true, removeEvent);
  540. menu.AddFunction(displayName + "Invoke", canInvoke, () => animancerEvent.DelayInvoke(name, Value));
  541. }
  542. /************************************************************************************************************************/
  543. #endregion
  544. /************************************************************************************************************************/
  545. }
  546. /// <summary>[Editor-Only] Colors used by <see cref="AnimancerStateDrawer{T}"/>.</summary>
  547. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerStateDrawerColors
  548. public static class AnimancerStateDrawerColors
  549. {
  550. /************************************************************************************************************************/
  551. /// <summary>Colors used by this system.</summary>
  552. public static readonly Color
  553. HeaderBackgroundColor = Grey(0.35f, 0.35f),
  554. PlayingBarColor = new(0.15f, 0.7f, 0.15f, 0.4f),// Green = Playing.
  555. PausedBarColor = new(0.7f, 0.7f, 0.15f, 0.4f),// Yelow = Paused.
  556. FadeLineColor = new(0.3f, 1, 0.3f, 1);
  557. /// <summary>Colors used by this system.</summary>
  558. public static Color EventTickColor
  559. => Grey(EditorGUIUtility.isProSkin ? 0.8f : 0.2f, 0.8f);
  560. /************************************************************************************************************************/
  561. }
  562. }
  563. #endif