Validate.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Playables;
  6. namespace Animancer
  7. {
  8. /// <summary>
  9. /// Enforces various rules throughout the system, most of which are compiled out if UNITY_ASSERTIONS is not defined
  10. /// (by default, it is only defined in the Unity Editor and in Development Builds).
  11. /// </summary>
  12. /// https://kybernetik.com.au/animancer/api/Animancer/Validate
  13. ///
  14. public static partial class Validate
  15. {
  16. /************************************************************************************************************************/
  17. /// <summary>[Assert-Conditional] Throws if the `clip` is marked as <see cref="AnimationClip.legacy"/>.</summary>
  18. /// <exception cref="ArgumentException"/>
  19. [System.Diagnostics.Conditional(Strings.Assertions)]
  20. public static void AssertNotLegacy(AnimationClip clip)
  21. {
  22. #if UNITY_ASSERTIONS
  23. if (clip.legacy)
  24. throw new ArgumentException(
  25. $"Legacy clip '{clip.name}' cannot be used by Animancer." +
  26. " Set the legacy property to false before using this clip." +
  27. " If it was imported as part of a model then the model's Rig type must be changed to Humanoid or Generic." +
  28. " Otherwise you can use the 'Toggle Legacy' function in the clip's context menu" +
  29. " (via the cog icon in the top right of its Inspector).");
  30. #endif
  31. }
  32. /************************************************************************************************************************/
  33. /// <summary>[Assert-Conditional] Throws if the <see cref="AnimancerNode.Root"/> is not the `root`.</summary>
  34. /// <exception cref="ArgumentException"/>
  35. [System.Diagnostics.Conditional(Strings.Assertions)]
  36. public static void AssertRoot(AnimancerNode node, AnimancerPlayable root)
  37. {
  38. #if UNITY_ASSERTIONS
  39. if (node.Root != root)
  40. throw new ArgumentException(
  41. $"{nameof(AnimancerNode)}.{nameof(AnimancerNode.Root)} mismatch:" +
  42. $" cannot use a node in an {nameof(AnimancerPlayable)} that is not its {nameof(AnimancerNode.Root)}: " +
  43. node.GetDescription());
  44. #endif
  45. }
  46. /************************************************************************************************************************/
  47. /// <summary>[Assert-Conditional] Throws if the `node`'s <see cref="Playable"/> is invalid.</summary>
  48. /// <exception cref="InvalidOperationException"/>
  49. [System.Diagnostics.Conditional(Strings.Assertions)]
  50. public static void AssertPlayable(AnimancerNode node)
  51. {
  52. #if UNITY_ASSERTIONS
  53. if (node._Playable.IsValid())
  54. return;
  55. var description = node.ToString();
  56. if (node is AnimancerState state)
  57. state.Destroy();
  58. if (node.Root == null)
  59. throw new InvalidOperationException(
  60. $"{nameof(AnimancerNode)}.{nameof(AnimancerNode.Root)} hasn't been set so its" +
  61. $" {nameof(Playable)} hasn't been created. It can be set by playing the state" +
  62. $" or calling {nameof(AnimancerState.SetRoot)} on it directly." +
  63. $" {nameof(AnimancerState.SetParent)} would also work if the parent has a {nameof(AnimancerNode.Root)}." +
  64. $"\n• State: {description}");
  65. else
  66. throw new InvalidOperationException(
  67. $"{nameof(AnimancerNode)}.{nameof(IPlayableWrapper.Playable)} has not been created." +
  68. $" {nameof(AnimancerNode.CreatePlayable)} likely needs to be called on it before performing this operation." +
  69. $"\n• State: {description}");
  70. #endif
  71. }
  72. /************************************************************************************************************************/
  73. /// <summary>[Assert-Conditional]
  74. /// Throws if the `state` was not actually assigned to its specified <see cref="AnimancerNode.Index"/> in
  75. /// the `states`.
  76. /// </summary>
  77. /// <exception cref="InvalidOperationException"/>
  78. /// <exception cref="IndexOutOfRangeException">
  79. /// The <see cref="AnimancerNode.Index"/> is larger than the number of `states`.
  80. /// </exception>
  81. [System.Diagnostics.Conditional(Strings.Assertions)]
  82. public static void AssertCanRemoveChild(AnimancerState state, IList<AnimancerState> states)
  83. {
  84. #if UNITY_ASSERTIONS
  85. var index = state.Index;
  86. if (index < 0)
  87. throw new InvalidOperationException(
  88. $"Cannot remove a child state that did not have an {nameof(state.Index)} assigned");
  89. if (index > states.Count)
  90. throw new IndexOutOfRangeException(
  91. $"{nameof(AnimancerState)}.{nameof(state.Index)} ({state.Index})" +
  92. $" is outside the collection of states (count {states.Count})");
  93. if (states[state.Index] != state)
  94. throw new InvalidOperationException(
  95. $"Cannot remove a child state that was not actually connected to its port on {state.Parent}:" +
  96. $"\n• Port: {state.Index}" +
  97. $"\n• Connected Child: {AnimancerUtilities.ToStringOrNull(states[state.Index])}" +
  98. $"\n• Disconnecting Child: {AnimancerUtilities.ToStringOrNull(state)}");
  99. #endif
  100. }
  101. /************************************************************************************************************************/
  102. }
  103. }