// 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();
}
}
}