// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer
{
/// A generic set of objects corresponding to up/right/down/left.
///
/// Documentation:
///
/// Directional Animation Sets
///
/// https://kybernetik.com.au/animancer/api/Animancer/DirectionalSet_1
///
[AnimancerHelpUrl(typeof(DirectionalSet<>))]
public abstract class DirectionalSet : ScriptableObject
{
/************************************************************************************************************************/
#region Read-Only
/************************************************************************************************************************/
#if UNITY_ASSERTIONS
private bool _AllowChanges;
#endif
/// [Assert-Only] Sets a debug flag to enable or disable the ability to modify this set.
[System.Diagnostics.Conditional(Strings.Assertions)]
public void AllowChanges(bool allow = true)
{
#if UNITY_ASSERTIONS
_AllowChanges = allow;
#endif
}
/// [Assert-Only]
/// Throws an if wasn't called.
///
[System.Diagnostics.Conditional(Strings.Assertions)]
public void AssertAllowChanges()
{
#if UNITY_ASSERTIONS
if (!_AllowChanges)
throw new InvalidOperationException(
$"Unable to modify {this} because it is read-only." +
$" Call {nameof(AllowChanges)}() before making any changes.");
#endif
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Directions
/************************************************************************************************************************/
/// The number of directions in this set.
public abstract int DirectionCount { get; }
/************************************************************************************************************************/
/// Returns the name of the specified `direction`.
protected abstract string GetDirectionName(int direction);
/************************************************************************************************************************/
/// Returns the object associated with the specified `direction`.
public abstract T Get(int direction);
/************************************************************************************************************************/
/// Returns the object closest to the specified `direction`.
public abstract T Get(Vector2 direction);
/************************************************************************************************************************/
/// Sets the object associated with the specified `direction`.
public abstract void Set(int direction, T value);
/************************************************************************************************************************/
/// [Editor-Only]
/// Attempts to assign the `value` to one of this set's fields based on its `name` and
/// returns the direction index of that field (or -1 if it was unable to determine the direction).
///
public int SetByName(string name, T value)
{
var direction = GetDirection(name);
if (direction >= 0)
Set(direction, value);
return direction;
}
/// [Editor-Only]
/// Attempts to assign the `value` to one of this set's fields based on its name and
/// returns the direction index of that field (or -1 if it was unable to determine the direction).
///
public int SetByName(U value)
where U : Object, T
=> SetByName(value.name, value);
/************************************************************************************************************************/
#region Conversion
/************************************************************************************************************************/
/// Returns a vector representing the specified `direction`.
public abstract Vector2 GetDirection(int direction);
/************************************************************************************************************************/
/// Returns the index of the direction which best matches the given `name`.
public abstract int GetDirection(string name);
/************************************************************************************************************************/
/// Returns a copy of the `vector` pointing in the closest direction this set has an object for.
public abstract Vector2 Snap(Vector2 vector);
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Gathering
/************************************************************************************************************************/
/// Adds all objects from this set to the `values`, starting from the specified `index`.
public void AddTo(T[] values, int index)
{
var count = DirectionCount;
for (int i = 0; i < count; i++)
values[index + i] = Get(i);
}
/// Adds all objects from this set to the `values`.
public void AddTo(List values)
{
var count = DirectionCount;
for (int i = 0; i < count; i++)
values.Add(Get(i));
}
/************************************************************************************************************************/
///
/// Adds unit vectors corresponding to each of the objects in this set to the `directions`,
/// starting from the specified `index`.
///
public void AddTo(Vector2[] directions, int index)
{
var count = DirectionCount;
for (int i = 0; i < count; i++)
directions[index + i] = GetDirection(i);
}
/************************************************************************************************************************/
/// Calls and .
public void AddTo(T[] values, Vector2[] directions, int index)
{
AddTo(values, index);
AddTo(directions, index);
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}