// MIT License - Copyright (c) 2023 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE namespace WallstopStudios.UnityHelpers.Utils { using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.Serialization; using System.Text.Json.Serialization; using Core.Extension; using UnityEngine; /// /// Maps an enum value to a set of boolean parameters so that only one /// state flag is enabled at a time. Helps drive complex state machines with strongly typed enums. /// /// Specific enum type used to drive the animator parameters. [DataContract] public sealed class AnimatorEnumStateMachine where T : struct, IConvertible, IComparable, IFormattable { private static readonly T[] Values = InitializeValues(); private static T[] InitializeValues() { Array enumValues = Enum.GetValues(typeof(T)); T[] result = new T[enumValues.Length]; for (int i = 0; i < enumValues.Length; i++) { result[i] = (T)enumValues.GetValue(i); } return result; } [JsonIgnore] [IgnoreDataMember] private readonly HashSet _availableBools = new(); /// /// Backing animator that exposes the boolean parameters backing the enum state. /// [JsonIgnore] [IgnoreDataMember] public readonly Animator Animator; [JsonIgnore] [IgnoreDataMember] private T _value; /// /// Gets or sets the currently active enum value. Setting the value toggles the underlying /// boolean parameters so that only the matching state remains true. /// [DataMember] [JsonInclude] public T Value { get => _value; set { foreach (T possibleValue in Values) { string valueName = possibleValue.ToString(CultureInfo.InvariantCulture); if (_availableBools.Contains(valueName)) { Animator.SetBool(valueName, Equals(value, possibleValue)); } } _value = value; } } /// /// Serialized helper used to record the enum name for persistence. /// [DataMember] [JsonInclude] private string Type => typeof(T).Name; /// /// Creates a state machine wrapper around the provided and optionally /// initializes it with a default enum value. /// /// Animator whose boolean parameters correspond to the enum entries. /// Initial enum value to apply to the animator. /// Thrown when is null. public AnimatorEnumStateMachine(Animator animator, T defaultValue = default) { if (animator == null) { throw new ArgumentNullException(nameof(animator)); } Animator = animator; foreach (AnimatorControllerParameter parameter in Animator.parameters) { switch (parameter.type) { case AnimatorControllerParameterType.Bool: case AnimatorControllerParameterType.Trigger: _availableBools.Add(parameter.name); continue; default: continue; } } _value = defaultValue; } /// /// Serializes the state machine into JSON format for debugging and tooling scenarios. /// /// JSON representation of the current state. public override string ToString() { return this.ToJson(); } } }