// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2023 Kybernetik // using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; namespace Animancer { /// /// Enforces various rules throughout the system, most of which are compiled out if UNITY_ASSERTIONS is not defined /// (by default, it is only defined in the Unity Editor and in Development Builds). /// /// https://kybernetik.com.au/animancer/api/Animancer/Validate /// public static partial class Validate { /************************************************************************************************************************/ /// [Assert-Conditional] Throws if the `clip` is marked as . /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void AssertNotLegacy(AnimationClip clip) { #if UNITY_ASSERTIONS if (clip.legacy) throw new ArgumentException( $"Legacy clip '{clip.name}' cannot be used by Animancer." + " Set the legacy property to false before using this clip." + " If it was imported as part of a model then the model's Rig type must be changed to Humanoid or Generic." + " Otherwise you can use the 'Toggle Legacy' function in the clip's context menu" + " (via the cog icon in the top right of its Inspector)."); #endif } /************************************************************************************************************************/ /// [Assert-Conditional] Throws if the is not the `root`. /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void AssertRoot(AnimancerNode node, AnimancerPlayable root) { #if UNITY_ASSERTIONS if (node.Root != root) throw new ArgumentException( $"{nameof(AnimancerNode)}.{nameof(AnimancerNode.Root)} mismatch:" + $" cannot use a node in an {nameof(AnimancerPlayable)} that is not its {nameof(AnimancerNode.Root)}: " + node.GetDescription()); #endif } /************************************************************************************************************************/ /// [Assert-Conditional] Throws if the `node`'s is invalid. /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void AssertPlayable(AnimancerNode node) { #if UNITY_ASSERTIONS if (node._Playable.IsValid()) return; var description = node.ToString(); var stackTrace = AnimancerNode.GetConstructorStackTrace(node); if (stackTrace != null) description += "\n\n" + stackTrace; if (node is AnimancerState state) state.Destroy(); if (node.Root == null) throw new InvalidOperationException( $"{nameof(AnimancerNode)}.{nameof(AnimancerNode.Root)} hasn't been set so its" + $" {nameof(Playable)} hasn't been created. It can be set by playing the state" + $" or calling {nameof(AnimancerState.SetRoot)} on it directly." + $" {nameof(AnimancerState.SetParent)} would also work if the parent has a {nameof(AnimancerNode.Root)}." + $"\n• State: {description}"); else throw new InvalidOperationException( $"{nameof(AnimancerNode)}.{nameof(IPlayableWrapper.Playable)}" + $" has either been destroyed or was never created." + $" {nameof(AnimancerNode.CreatePlayable)} likely needs" + $" to be called on it before performing this operation." + $"\n• State: {description}"); #endif } /************************************************************************************************************************/ /// [Assert-Conditional] /// Throws if the `state` was not actually assigned to its specified in /// the `states`. /// /// /// /// The is larger than the number of `states`. /// [System.Diagnostics.Conditional(Strings.Assertions)] public static void AssertCanRemoveChild(AnimancerState state, IList childStates, int childCount) { #if UNITY_ASSERTIONS var index = state.Index; if (index < 0) throw new InvalidOperationException( $"Cannot remove a child state that did not have an {nameof(state.Index)} assigned"); if (index > childCount) throw new IndexOutOfRangeException( $"{nameof(AnimancerState)}.{nameof(state.Index)} ({state.Index})" + $" is outside the collection of states (count {childCount})"); if (childStates[state.Index] != state) throw new InvalidOperationException( $"Cannot remove a child state that was not actually connected to its port on {state.Parent}:" + $"\n• Port: {state.Index}" + $"\n• Connected Child: {AnimancerUtilities.ToStringOrNull(childStates[state.Index])}" + $"\n• Disconnecting Child: {AnimancerUtilities.ToStringOrNull(state)}"); #endif } /************************************************************************************************************************/ } }