AnimancerNode.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Runtime.CompilerServices;
  6. using System.Text;
  7. using UnityEngine;
  8. using UnityEngine.Playables;
  9. using Object = UnityEngine.Object;
  10. namespace Animancer
  11. {
  12. /// <summary>Base class for <see cref="Playable"/> wrapper objects in an <see cref="AnimancerGraph"/>.</summary>
  13. /// <remarks>This is the base class of <see cref="AnimancerLayer"/> and <see cref="AnimancerState"/>.</remarks>
  14. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerNode
  15. public abstract class AnimancerNode : AnimancerNodeBase,
  16. ICopyable<AnimancerNode>,
  17. IEnumerable<AnimancerState>,
  18. IEnumerator,
  19. IHasDescription
  20. {
  21. /************************************************************************************************************************/
  22. #region Playable
  23. /************************************************************************************************************************/
  24. #if UNITY_EDITOR
  25. /// <summary>[Editor-Only] [Internal] Indicates whether the Inspector details for this node are expanded.</summary>
  26. internal bool _IsInspectorExpanded;
  27. #endif
  28. /************************************************************************************************************************/
  29. /// <summary>Creates and assigns the <see cref="Playable"/> managed by this node.</summary>
  30. /// <remarks>This method also applies the <see cref="AnimancerNodeBase.Speed"/> if it was set beforehand.</remarks>
  31. protected virtual void CreatePlayable()
  32. {
  33. #if UNITY_ASSERTIONS
  34. if (Graph == null)
  35. {
  36. MarkAsUsed(this);
  37. throw new InvalidOperationException($"{nameof(AnimancerNode)}.{nameof(Graph)}" +
  38. $" is null when attempting to create its {nameof(Playable)}: {this}" +
  39. $"\nThe {nameof(Graph)} is generally set when you first play a state," +
  40. $" so you probably just need to play it before trying to access it.");
  41. }
  42. if (_Playable.IsValid())
  43. Debug.LogWarning($"{nameof(AnimancerNode)}.{nameof(CreatePlayable)}" +
  44. $" was called before destroying the previous {nameof(Playable)}: {this}", Graph?.Component as Object);
  45. #endif
  46. CreatePlayable(out _Playable);
  47. #if UNITY_ASSERTIONS
  48. if (!_Playable.IsValid())
  49. throw new InvalidOperationException(
  50. $"{nameof(AnimancerNode)}.{nameof(CreatePlayable)}" +
  51. $" did not create a valid {nameof(Playable)} for {this}");
  52. #endif
  53. if (Speed != 1)
  54. _Playable.SetSpeed(Speed);
  55. }
  56. /// <summary>Creates and assigns the <see cref="Playable"/> managed by this node.</summary>
  57. protected abstract void CreatePlayable(out Playable playable);
  58. /************************************************************************************************************************/
  59. /// <summary>Destroys the <see cref="Playable"/>.</summary>
  60. public void DestroyPlayable()
  61. {
  62. if (_Playable.IsValid())
  63. Graph._PlayableGraph.DestroyPlayable(_Playable);
  64. }
  65. /************************************************************************************************************************/
  66. /// <summary>Calls <see cref="DestroyPlayable"/> and <see cref="CreatePlayable()"/>.</summary>
  67. public virtual void RecreatePlayable()
  68. {
  69. DestroyPlayable();
  70. CreatePlayable();
  71. }
  72. /// <summary>Calls <see cref="RecreatePlayable"/> on this node and all its children recursively.</summary>
  73. public void RecreatePlayableRecursive()
  74. {
  75. RecreatePlayable();
  76. for (int i = ChildCount - 1; i >= 0; i--)
  77. GetChild(i)?.RecreatePlayableRecursive();
  78. }
  79. /************************************************************************************************************************/
  80. /// <summary>Copies the details of `copyFrom` into this node, replacing its previous contents.</summary>
  81. public virtual void CopyFrom(AnimancerNode copyFrom, CloneContext context)
  82. {
  83. SetWeight(copyFrom._Weight);
  84. FadeGroup = context.WillCloneUpdatables
  85. ? null
  86. : copyFrom.FadeGroup?.CloneForSingleTarget(copyFrom, this);
  87. Speed = copyFrom.Speed;
  88. CopyIKFlags(copyFrom);
  89. #if UNITY_ASSERTIONS
  90. DebugName = context.GetCloneOrOriginal(copyFrom.DebugName);
  91. #endif
  92. }
  93. /************************************************************************************************************************/
  94. #endregion
  95. /************************************************************************************************************************/
  96. #region Graph
  97. /************************************************************************************************************************/
  98. /// <summary>The index of the port this node is connected to on the parent's <see cref="Playable"/>.</summary>
  99. /// <remarks>
  100. /// A negative value indicates that it is not assigned to a port.
  101. /// <para></para>
  102. /// Indices are generally assigned starting from 0, ascending in the order they are connected to their layer.
  103. /// They will not usually change unless the <see cref="AnimancerNodeBase.Parent"/> changes or another state on
  104. /// the same layer is destroyed so the last state is swapped into its place to avoid shuffling everything down
  105. /// to cover the gap.
  106. /// <para></para>
  107. /// The setter is internal so user defined states cannot set it incorrectly. Ideally,
  108. /// <see cref="AnimancerLayer"/> should be able to set the port in its constructor and
  109. /// <see cref="AnimancerState.SetParent"/> should also be able to set it, but classes that further inherit from
  110. /// there should not be able to change it without properly calling that method.
  111. /// </remarks>
  112. public int Index { get; internal set; } = int.MinValue;
  113. /************************************************************************************************************************/
  114. /// <summary>Creates a new <see cref="AnimancerNode"/>.</summary>
  115. protected AnimancerNode()
  116. {
  117. #if UNITY_ASSERTIONS
  118. if (TraceConstructor)
  119. _ConstructorStackTrace = new(true);
  120. #endif
  121. }
  122. /************************************************************************************************************************/
  123. #if UNITY_ASSERTIONS
  124. /************************************************************************************************************************/
  125. /// <summary>[Assert-Only]
  126. /// Should a <see cref="System.Diagnostics.StackTrace"/> be captured in the constructor of all new nodes so
  127. /// <see cref="OptionalWarning.UnusedNode"/> can include it in the warning if that node ends up being unused?
  128. /// </summary>
  129. /// <remarks>This has a notable performance cost so it should only be used when trying to identify a problem.</remarks>
  130. public static bool TraceConstructor { get; set; }
  131. /************************************************************************************************************************/
  132. /// <summary>[Assert-Only]
  133. /// The stack trace of the constructor (or null if <see cref="TraceConstructor"/> was false).
  134. /// </summary>
  135. private System.Diagnostics.StackTrace _ConstructorStackTrace;
  136. /// <summary>[Assert-Only]
  137. /// Returns the stack trace of the constructor (or null if <see cref="TraceConstructor"/> was false).
  138. /// </summary>
  139. public static System.Diagnostics.StackTrace GetConstructorStackTrace(AnimancerNode node)
  140. => node._ConstructorStackTrace;
  141. /************************************************************************************************************************/
  142. /// <summary>[Assert-Only] Checks <see cref="OptionalWarning.UnusedNode"/>.</summary>
  143. ~AnimancerNode()
  144. {
  145. if (Graph != null ||
  146. Parent != null ||
  147. OptionalWarning.UnusedNode.IsDisabled())
  148. return;
  149. // ToString might throw an exception since finalizers arn't run on the main thread.
  150. string name = null;
  151. try { name = ToString(); }
  152. catch { name = GetType().FullName; }
  153. var message = $"The {nameof(Graph)} of '{name}'" +
  154. $" is null during finalization (garbage collection)." +
  155. $" This may have been caused by earlier exceptions, but otherwise it probably means" +
  156. $" that this node was never used for anything and should not have been created.";
  157. if (_ConstructorStackTrace != null)
  158. message += "\n\nThis node was created at:\n" + _ConstructorStackTrace;
  159. else
  160. message += $"\n\nEnable {nameof(AnimancerNode)}.{nameof(TraceConstructor)} on startup" +
  161. $" to allow this warning to include the {nameof(System.Diagnostics.StackTrace)}" +
  162. $" of when the node was constructed.";
  163. OptionalWarning.UnusedNode.Log(message);
  164. }
  165. /************************************************************************************************************************/
  166. #endif
  167. /************************************************************************************************************************/
  168. /// <summary>Connects the `child`'s <see cref="Playable"/> to this node.</summary>
  169. /// <remarks>This method is NOT safe to call if the child was already connected.</remarks>
  170. protected internal void ConnectChildUnsafe(int index, AnimancerNode child)
  171. {
  172. #if UNITY_ASSERTIONS
  173. if (index < 0)
  174. {
  175. MarkAsUsed(this);
  176. throw new InvalidOperationException(
  177. $"Invalid {nameof(index)} when attempting to connect to its parent:" +
  178. "\n• Child: " + child +
  179. "\n• Parent: " + this);
  180. }
  181. Validate.AssertPlayable(child);
  182. #endif
  183. Graph._PlayableGraph.Connect(_Playable, child._Playable, index, child._Weight);
  184. }
  185. /// <summary>Disconnects the <see cref="Playable"/> of the child at the specified `index` from this node.</summary>
  186. /// <remarks>This method is safe to call if the child was already disconnected.</remarks>
  187. protected void DisconnectChildSafe(int index)
  188. {
  189. if (_Playable.GetInput(index).IsValid())
  190. Graph._PlayableGraph.Disconnect(_Playable, index);
  191. }
  192. /************************************************************************************************************************/
  193. // IEnumerator for yielding in a coroutine to wait until animations have stopped.
  194. /************************************************************************************************************************/
  195. /// <summary>Is this node playing and not yet at its end?</summary>
  196. /// <remarks>
  197. /// This method is called by <see cref="IEnumerator.MoveNext"/> so this object can be used as a custom yield
  198. /// instruction to wait until it finishes.
  199. /// </remarks>
  200. public abstract bool IsPlayingAndNotEnding();
  201. bool IEnumerator.MoveNext()
  202. => IsPlayingAndNotEnding();
  203. object IEnumerator.Current
  204. => null;
  205. void IEnumerator.Reset() { }
  206. /************************************************************************************************************************/
  207. #endregion
  208. /************************************************************************************************************************/
  209. #region Children
  210. /************************************************************************************************************************/
  211. /// <inheritdoc/>
  212. protected internal override AnimancerNode GetChildNode(int index)
  213. => GetChild(index);
  214. /// <summary>Returns the state connected to the specified `index` as a child of this node.</summary>
  215. /// <remarks>When overriding, don't call this base method because it throws an exception.</remarks>
  216. /// <exception cref="NotSupportedException">This node can't have children.</exception>
  217. public virtual AnimancerState GetChild(int index)
  218. {
  219. MarkAsUsed(this);
  220. throw new NotSupportedException(this + " can't have children.");
  221. }
  222. /// <summary>Called when a child is connected with this node as its <see cref="AnimancerNodeBase.Parent"/>.</summary>
  223. /// <remarks>When overriding, don't call this base method because it throws an exception.</remarks>
  224. /// <exception cref="NotSupportedException">This node can't have children.</exception>
  225. protected internal virtual void OnAddChild(AnimancerState state)
  226. {
  227. MarkAsUsed(this);
  228. state.SetParentInternal(null);
  229. throw new NotSupportedException(this + " can't have children.");
  230. }
  231. /************************************************************************************************************************/
  232. // IEnumerable for 'foreach' statements.
  233. /************************************************************************************************************************/
  234. /// <summary>Gets an enumerator for all of this node's child states.</summary>
  235. public virtual FastEnumerator<AnimancerState> GetEnumerator()
  236. => default;
  237. IEnumerator<AnimancerState> IEnumerable<AnimancerState>.GetEnumerator()
  238. => GetEnumerator();
  239. IEnumerator IEnumerable.GetEnumerator()
  240. => GetEnumerator();
  241. /************************************************************************************************************************/
  242. #endregion
  243. /************************************************************************************************************************/
  244. #region Weight
  245. /************************************************************************************************************************/
  246. /// <summary>[Internal] The current blend weight of this node. Accessed via <see cref="Weight"/>.</summary>
  247. internal float _Weight;
  248. /************************************************************************************************************************/
  249. /// <summary>The current blend weight of this node which determines how much it affects the final output.</summary>
  250. ///
  251. /// <remarks>
  252. /// 0 has no effect while 1 applies the full effect and values inbetween apply a proportional effect.
  253. /// <para></para>
  254. /// Setting this property cancels any fade currently in progress. If you don't wish to do that, you can use
  255. /// <see cref="SetWeight"/> instead.
  256. /// <para></para>
  257. /// <em>Animancer Lite only allows this value to be set to 0 or 1 in runtime builds.</em>
  258. /// </remarks>
  259. ///
  260. /// <example>
  261. /// Calling <see cref="AnimancerLayer.Play(AnimationClip)"/> immediately sets the weight of all states to 0
  262. /// and the new state to 1. Note that this is separate from other values like
  263. /// <see cref="AnimancerState.IsPlaying"/> so a state can be paused at any point and still show its pose on the
  264. /// character or it could be still playing at 0 weight if you want it to still trigger events (though states
  265. /// are normally stopped when they reach 0 weight so you would need to explicitly set it to playing again).
  266. /// <para></para>
  267. /// Calling <see cref="AnimancerLayer.Play(AnimationClip, float, FadeMode)"/> doesn't immediately change
  268. /// the weights, but instead calls <see cref="StartFade(float, float)"/> on every state to set their
  269. /// <see cref="TargetWeight"/> and <see cref="FadeSpeed"/>. Then every update each state's weight will move
  270. /// towards that target value at that speed.
  271. /// </example>
  272. public float Weight
  273. {
  274. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  275. get => _Weight;
  276. set
  277. {
  278. FadeGroup = null;
  279. SetWeight(value);
  280. }
  281. }
  282. /// <summary>
  283. /// Sets the current blend weight of this node which determines how much it affects the final output.
  284. /// 0 has no effect while 1 applies the full effect of this node.
  285. /// </summary>
  286. /// <remarks>
  287. /// This method allows any fade currently in progress to continue. If you don't wish to do that, you can set
  288. /// the <see cref="Weight"/> property instead.
  289. /// <para></para>
  290. /// <em>Animancer Lite only allows this value to be set to 0 or 1 in runtime builds.</em>
  291. /// </remarks>
  292. public virtual void SetWeight(float value)
  293. => SetWeightInternal(value);
  294. /// <summary>The internal non-<c>virtual</c> implementation of <see cref="SetWeight"/>.</summary>
  295. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  296. private void SetWeightInternal(float value)
  297. {
  298. if (_Weight == value)
  299. return;
  300. Validate.AssertSetWeight(this, value);
  301. _Weight = value;
  302. if (Graph != null)
  303. Parent?.Playable.ApplyChildWeight(this);
  304. }
  305. /************************************************************************************************************************/
  306. /// <inheritdoc/>
  307. protected internal override float BaseWeight
  308. => Weight;
  309. /// <summary>
  310. /// The <see cref="Weight"/> of this state multiplied by the <see cref="Weight"/> of each of its parents down
  311. /// the hierarchy to determine how much this state affects the final output.
  312. /// </summary>
  313. public float EffectiveWeight
  314. {
  315. get
  316. {
  317. var weight = Weight;
  318. var parent = Parent;
  319. while (parent != null)
  320. {
  321. weight *= parent.BaseWeight;
  322. parent = parent.Parent;
  323. }
  324. return weight;
  325. }
  326. }
  327. /************************************************************************************************************************/
  328. #endregion
  329. /************************************************************************************************************************/
  330. #region Fading
  331. /************************************************************************************************************************/
  332. internal FadeGroup _FadeGroup;
  333. /// <summary>The current fade being applied to this node (if any).</summary>
  334. public FadeGroup FadeGroup
  335. {
  336. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  337. get => _FadeGroup;
  338. internal set
  339. {
  340. _FadeGroup?.Remove(this);
  341. _FadeGroup = value;
  342. }
  343. }
  344. /// <summary>
  345. /// The desired <see cref="Weight"/> which this node is fading towards according to the
  346. /// <see cref="FadeSpeed"/>.
  347. /// </summary>
  348. public float TargetWeight
  349. => FadeGroup != null
  350. ? FadeGroup.GetTargetWeight(this)
  351. : Weight;
  352. /// <summary>The speed at which this node is fading towards the <see cref="TargetWeight"/>.</summary>
  353. /// <remarks>
  354. /// This value isn't affected by this node's <see cref="AnimancerNodeBase.Speed"/>,
  355. /// but is affected by its parents.
  356. /// </remarks>
  357. public float FadeSpeed
  358. => FadeGroup != null
  359. ? FadeGroup.FadeSpeed
  360. : 0;
  361. /************************************************************************************************************************/
  362. /// <summary>
  363. /// Calls <see cref="OnStartFade"/> and starts fading the <see cref="Weight"/> over the course
  364. /// of the <see cref="AnimancerGraph.DefaultFadeDuration"/> (in seconds).
  365. /// </summary>
  366. /// <remarks>
  367. /// If the `targetWeight` is 0 then <see cref="Stop"/> will be called when the fade is complete.
  368. /// <para></para>
  369. /// If the <see cref="Weight"/> is already equal to the `targetWeight` then the fade will end
  370. /// immediately.
  371. /// </remarks>
  372. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  373. public void StartFade(float targetWeight)
  374. => StartFade(targetWeight, AnimancerGraph.DefaultFadeDuration);
  375. /// <summary>
  376. /// Calls <see cref="OnStartFade"/> and starts fading the <see cref="Weight"/>
  377. /// over the course of the `fadeDuration` (in seconds).
  378. /// </summary>
  379. /// <remarks>
  380. /// If the `targetWeight` is 0 then <see cref="Stop"/> will be called when the fade is complete.
  381. /// <para></para>
  382. /// If the <see cref="Weight"/> is already equal to the `targetWeight`
  383. /// then the fade will end immediately.
  384. /// <para></para>
  385. /// <em>Animancer Lite only allows a `targetWeight` of 0 or 1
  386. /// and the default `fadeDuration` (0.25 seconds) in runtime builds.</em>
  387. /// </remarks>
  388. public void StartFade(float targetWeight, float fadeDuration)
  389. {
  390. if (Weight == targetWeight && FadeGroup == null)
  391. {
  392. OnStartFade();
  393. }
  394. else if (fadeDuration > 0)
  395. {
  396. var fadeSpeed = Math.Abs(targetWeight - Weight) / fadeDuration;
  397. var fade = FadeGroup.Pool.Instance.Acquire();
  398. fade.SetFadeIn(this);
  399. fade.StartFade(targetWeight, fadeSpeed);
  400. }
  401. else
  402. {
  403. Weight = targetWeight;
  404. }
  405. }
  406. /************************************************************************************************************************/
  407. /// <summary>Called by <see cref="StartFade(float, float)"/>.</summary>
  408. protected internal abstract void OnStartFade();
  409. /************************************************************************************************************************/
  410. /// <summary>Removes this node from the <see cref="FadeGroup"/>.</summary>
  411. public void CancelFade()
  412. => _FadeGroup?.Remove(this);
  413. /// <summary>[Internal] Called by <see cref="FadeGroup.Remove"/>.</summary>
  414. /// <remarks>Not called when a fade fully completes.</remarks>
  415. protected internal virtual void InternalClearFade()
  416. {
  417. _FadeGroup = null;
  418. }
  419. /************************************************************************************************************************/
  420. /// <summary>Stops the animation and makes it inactive immediately so it no longer affects the output.</summary>
  421. /// <remarks>
  422. /// Sets <see cref="Weight"/> = 0 by default unless overridden.
  423. /// <para></para>
  424. /// Note that playing something new will automatically stop the old animation.
  425. /// </remarks>
  426. public void Stop()
  427. {
  428. FadeGroup = null;
  429. SetWeightInternal(0);
  430. StopWithoutWeight();
  431. }
  432. /// <summary>[Internal] Stops this node without setting its <see cref="Weight"/>.</summary>
  433. protected internal virtual void StopWithoutWeight() { }
  434. /************************************************************************************************************************/
  435. #endregion
  436. /************************************************************************************************************************/
  437. #region Inverse Kinematics
  438. /************************************************************************************************************************/
  439. /// <summary>
  440. /// Should setting the <see cref="AnimancerNodeBase.Parent"/>
  441. /// also set this node's <see cref="ApplyAnimatorIK"/> to match it?
  442. /// Default is true.
  443. /// </summary>
  444. public static bool ApplyParentAnimatorIK { get; set; } = true;
  445. /// <summary>
  446. /// Should setting the <see cref="AnimancerNodeBase.Parent"/>
  447. /// also set this node's <see cref="ApplyFootIK"/> to match it?
  448. /// Default is true.
  449. /// </summary>
  450. public static bool ApplyParentFootIK { get; set; } = true;
  451. /************************************************************************************************************************/
  452. /// <summary>
  453. /// Copies the IK settings from `copyFrom` into this node:
  454. /// <list type="bullet">
  455. /// <item>If <see cref="ApplyParentAnimatorIK"/> is true, copy <see cref="ApplyAnimatorIK"/>.</item>
  456. /// <item>If <see cref="ApplyParentFootIK"/> is true, copy <see cref="ApplyFootIK"/>.</item>
  457. /// </list>
  458. /// </summary>
  459. public virtual void CopyIKFlags(AnimancerNodeBase copyFrom)
  460. {
  461. if (Graph == null)
  462. return;
  463. if (ApplyParentAnimatorIK)
  464. ApplyAnimatorIK = copyFrom.ApplyAnimatorIK;
  465. if (ApplyParentFootIK)
  466. ApplyFootIK = copyFrom.ApplyFootIK;
  467. }
  468. /************************************************************************************************************************/
  469. /// <inheritdoc/>
  470. public override bool ApplyAnimatorIK
  471. {
  472. get
  473. {
  474. for (int i = ChildCount - 1; i >= 0; i--)
  475. {
  476. var state = GetChild(i);
  477. if (state.ApplyAnimatorIK)
  478. return true;
  479. }
  480. return false;
  481. }
  482. set
  483. {
  484. for (int i = ChildCount - 1; i >= 0; i--)
  485. {
  486. var state = GetChild(i);
  487. state.ApplyAnimatorIK = value;
  488. }
  489. }
  490. }
  491. /************************************************************************************************************************/
  492. /// <inheritdoc/>
  493. public override bool ApplyFootIK
  494. {
  495. get
  496. {
  497. for (int i = ChildCount - 1; i >= 0; i--)
  498. {
  499. var state = GetChild(i);
  500. if (state.ApplyFootIK)
  501. return true;
  502. }
  503. return false;
  504. }
  505. set
  506. {
  507. for (int i = ChildCount - 1; i >= 0; i--)
  508. {
  509. var state = GetChild(i);
  510. state.ApplyFootIK = value;
  511. }
  512. }
  513. }
  514. /************************************************************************************************************************/
  515. #endregion
  516. /************************************************************************************************************************/
  517. #region Descriptions
  518. /************************************************************************************************************************/
  519. #if UNITY_ASSERTIONS
  520. /// <summary>[Assert-Only] The Inspector display name of this node.</summary>
  521. /// <remarks>Set using <see cref="SetDebugName"/>.</remarks>
  522. public object DebugName { get; private set; }
  523. #endif
  524. /// <summary>[Assert-Conditional] Sets the <see cref="DebugName"/> to display in the Inspector.</summary>
  525. [System.Diagnostics.Conditional(Strings.Assertions)]
  526. public void SetDebugName(object name)
  527. {
  528. #if UNITY_ASSERTIONS
  529. DebugName = name;
  530. #endif
  531. }
  532. /// <summary>The Inspector display name of this node.</summary>
  533. public override string ToString()
  534. {
  535. #if UNITY_ASSERTIONS
  536. if (NameCache.TryToString(DebugName, out var name))
  537. return name;
  538. #endif
  539. return base.ToString();
  540. }
  541. /************************************************************************************************************************/
  542. /// <inheritdoc/>
  543. public void AppendDescription(StringBuilder text, string separator = "\n")
  544. {
  545. text.Append(ToString());
  546. AppendDetails(text, separator);
  547. if (ChildCount > 0)
  548. {
  549. text.AppendField(separator, nameof(ChildCount), ChildCount);
  550. var indentedSeparator = separator + Strings.Indent;
  551. var i = 0;
  552. foreach (var child in this)
  553. {
  554. text.Append(separator)
  555. .Append('[')
  556. .Append(i++)
  557. .Append("] ")
  558. .AppendDescription(child, indentedSeparator, true);
  559. }
  560. }
  561. }
  562. /************************************************************************************************************************/
  563. /// <summary>Called by <see cref="AppendDescription"/> to append the details of this node.</summary>
  564. protected virtual void AppendDetails(StringBuilder text, string separator)
  565. {
  566. text.AppendField(separator, "Playable", _Playable.IsValid()
  567. ? _Playable.GetPlayableType().ToString()
  568. : "Invalid");
  569. var parent = Parent;
  570. var isConnected =
  571. parent != null &&
  572. parent.Playable.GetInput(Index).IsValid();
  573. text.AppendField(separator, "Connected", isConnected);
  574. text.AppendField(separator, nameof(Index), Index);
  575. if (Index < 0)
  576. text.Append(" (No Parent)");
  577. text.AppendField(separator, nameof(Speed), Speed);
  578. var realSpeed = _Playable.IsValid()
  579. ? _Playable.GetSpeed()
  580. : Speed;
  581. if (realSpeed != Speed)
  582. text.Append(" (Real ").Append(realSpeed).Append(')');
  583. text.AppendField(separator, nameof(Weight), Weight);
  584. if (Weight != TargetWeight)
  585. {
  586. text.AppendField(separator, nameof(TargetWeight), TargetWeight);
  587. text.AppendField(separator, nameof(FadeSpeed), FadeSpeed);
  588. }
  589. AppendIKDetails(text, separator, this);
  590. #if UNITY_ASSERTIONS
  591. if (_ConstructorStackTrace != null)
  592. text.AppendField(separator, "ConstructorStackTrace", _ConstructorStackTrace);
  593. #endif
  594. }
  595. /************************************************************************************************************************/
  596. /// <summary>
  597. /// Appends the details of <see cref="AnimancerNodeBase.ApplyAnimatorIK"/> and
  598. /// <see cref="AnimancerNodeBase.ApplyFootIK"/>.
  599. /// </summary>
  600. public static void AppendIKDetails(StringBuilder text, string separator, AnimancerNodeBase node)
  601. {
  602. if (!node.Playable.IsValid())
  603. return;
  604. text.Append(separator)
  605. .Append("InverseKinematics: ");
  606. if (node.ApplyAnimatorIK)
  607. {
  608. text.Append("OnAnimatorIK");
  609. if (node.ApplyFootIK)
  610. text.Append(", FootIK");
  611. }
  612. else if (node.ApplyFootIK)
  613. {
  614. text.Append("FootIK");
  615. }
  616. else
  617. {
  618. text.Append("None");
  619. }
  620. }
  621. /************************************************************************************************************************/
  622. /// <summary>Returns the hierarchy path of this node through its <see cref="AnimancerNodeBase.Parent"/>s.</summary>
  623. public string GetPath()
  624. {
  625. var path = StringBuilderPool.Instance.Acquire();
  626. if (Parent is AnimancerNode parent)
  627. {
  628. AppendPath(path, parent);
  629. AppendPortAndType(path);
  630. }
  631. else
  632. {
  633. AppendPortAndType(path);
  634. }
  635. return path.ReleaseToString();
  636. }
  637. /// <summary>Appends the hierarchy path of this state through its <see cref="AnimancerNodeBase.Parent"/>s.</summary>
  638. private static void AppendPath(StringBuilder path, AnimancerNode parent)
  639. {
  640. if (parent != null)
  641. {
  642. if (parent.Parent is AnimancerNode grandParent)
  643. {
  644. AppendPath(path, grandParent);
  645. }
  646. else
  647. {
  648. var layer = parent.Layer;
  649. if (layer != null)
  650. {
  651. path.Append("Layers[")
  652. .Append(parent.Layer.Index)
  653. .Append("].States");
  654. }
  655. else
  656. {
  657. path.Append("NoLayer -> ")
  658. .Append(parent.ToString());
  659. }
  660. return;
  661. }
  662. }
  663. if (parent is AnimancerState state)
  664. {
  665. state.AppendPortAndType(path);
  666. }
  667. else
  668. {
  669. path.Append(" -> ")
  670. .Append(parent.GetType());
  671. }
  672. }
  673. /// <summary>Appends "[Index] -> ToString()".</summary>
  674. private void AppendPortAndType(StringBuilder path)
  675. {
  676. path.Append('[')
  677. .Append(Index)
  678. .Append("] -> ")
  679. .Append(ToString());
  680. }
  681. /************************************************************************************************************************/
  682. #endregion
  683. /************************************************************************************************************************/
  684. }
  685. }