Files
av3-animation-as-crab/Framework/Editor/V1/Aac.cs
T
Haï~ d22d73cc82 (BREAKING) Add AnimatorAsCode V1:
- Breaking changes:
  - Remove dependency to VRChat in the AnimatorAsCode.V1 namespace. The namespace should now be usable in non-VRChat projects.
  - All VRChat-specific methods are now extension functions in a separate namespace, AnimatorAsCode.V1.VRC.
  - VRChat methods that modify existing assets are split into a separate namespace, AnimatorAsCode.V1.VRCDestructiveWorkflow.
- Breaking fixes:
  - Fix incorrect type signature on AacFlFloatParameterGroup.ToList() and AacFlIntParameterGroup.ToList().
- Add AacFlBase.NewBlendTree():
  - This allows creating blend trees as code.
  - Add AacFlState.WithAnimation(AacFlBlendTree).
- Add AacFlBase.NoAnimator():
  - This can be used to obtain AacFlParameter objects for use in the generation of BlendTrees and other systems, without requiring the existence of a backing animator controller.
  - This can also be used in some cases where there are type casts (Bool to Float, Float to Bool).
- Add AacFlBase.NewAnimatorController():
  - This is meant to be used for non-destructive workflows.
  - This creates an animator controller inside the asset container. This animator controller will be reaped in the same way as other assets are reaped.
2023-10-02 04:51:07 +02:00

793 lines
31 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using Random = UnityEngine.Random;
// ReSharper disable once CheckNamespace
namespace AnimatorAsCode.V1
{
public static class AacV1
{
public static AacFlBase Create(AacConfiguration configuration)
{
return new AacFlBase(configuration);
}
internal static AnimatorController NewAnimatorController(AacConfiguration component, string suffix)
{
return RegisterAnimatorController(component, suffix, new AnimatorController());
}
internal static AnimatorController RegisterAnimatorController(AacConfiguration component, string suffix, AnimatorController animatorController)
{
animatorController.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
animatorController.hideFlags = HideFlags.None;
AssetDatabase.AddObjectToAsset(animatorController, component.AssetContainer);
return animatorController;
}
internal static AnimationClip NewClip(AacConfiguration component, string suffix)
{
return RegisterClip(component, suffix, new AnimationClip());
}
internal static AnimationClip RegisterClip(AacConfiguration component, string suffix, AnimationClip clip)
{
clip.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
clip.hideFlags = HideFlags.None;
AssetDatabase.AddObjectToAsset(clip, component.AssetContainer);
return clip;
}
internal static BlendTree NewBlendTreeAsRaw(AacConfiguration component, string suffix)
{
var clip = new BlendTree();
clip.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
clip.hideFlags = HideFlags.None;
AssetDatabase.AddObjectToAsset(clip, component.AssetContainer);
return clip;
}
internal static EditorCurveBinding Binding(AacConfiguration component, Type type, Transform transform, string propertyName)
{
return new EditorCurveBinding
{
path = ResolveRelativePath(component.AnimatorRoot, transform),
type = type,
propertyName = propertyName
};
}
internal static AnimationCurve OneFrame(float desiredValue)
{
return AnimationCurve.Constant(0f, 1 / 60f, desiredValue);
}
internal static AnimationCurve ConstantSeconds(float seconds, float desiredValue)
{
return AnimationCurve.Constant(0f, seconds, desiredValue);
}
internal static string ResolveRelativePath(Transform avatar, Transform item)
{
if (item.parent != avatar && item.parent != null)
{
return ResolveRelativePath(avatar, item.parent) + "/" + item.name;
}
return item.name;
}
internal static EditorCurveBinding ToSubBinding(EditorCurveBinding binding, string suffix)
{
return new EditorCurveBinding {path = binding.path, type = binding.type, propertyName = binding.propertyName + "." + suffix};
}
}
public struct AacConfiguration
{
public string SystemName;
// Please consult "MigratingFromV0ToV1.md" on how to migrate this property
// public VRCAvatarDescriptor AvatarDescriptor;
public Transform AnimatorRoot;
public Transform DefaultValueRoot;
public AnimatorController AssetContainer;
public string AssetKey;
public IAacDefaultsProvider DefaultsProvider;
public Dictionary<Type, object> AdditionalData; // Nullable
public AacConfiguration WithAdditonalData(Type key, object value)
{
var conf = this;
if (AdditionalData == null) conf.AdditionalData = new Dictionary<Type, object>();
conf.AdditionalData[key] = value;
return conf;
}
public bool TryGetAdditionalData(Type key, out object value)
{
if (AdditionalData != null && AdditionalData.TryGetValue(key, out var result))
{
value = result;
return true;
}
value = null;
return false;
}
}
public struct AacFlLayer
{
private readonly AnimatorController _animatorController;
private readonly AacConfiguration _configuration;
private readonly string _fullLayerName;
private readonly AacFlStateMachine _stateMachine;
internal AacFlLayer(AnimatorController animatorController, AacConfiguration configuration, AacFlStateMachine stateMachine, string fullLayerName)
{
_animatorController = animatorController;
_configuration = configuration;
_fullLayerName = fullLayerName;
_stateMachine = stateMachine;
}
public AacFlState NewState(string name)
{
var lastState = _stateMachine.LastNodePosition();
var state = _stateMachine.NewState(name, 0, 0).Shift(lastState, 0, 1);
return state;
}
public AacFlState NewState(string name, int x, int y)
{
return _stateMachine.NewState(name, x, y);
}
public AacFlStateMachine NewSubStateMachine(string name)
{
return _stateMachine.NewSubStateMachine(name);
}
public AacFlStateMachine NewSubStateMachine(string name, int x, int y)
{
return _stateMachine.NewSubStateMachine(name, x, y);
}
public AacFlTransition AnyTransitionsTo(AacFlState destination)
{
return _stateMachine.AnyTransitionsTo(destination);
}
public AacFlEntryTransition EntryTransitionsTo(AacFlState destination)
{
return _stateMachine.EntryTransitionsTo(destination);
}
public AacFlEntryTransition EntryTransitionsTo(AacFlStateMachine destination)
{
return _stateMachine.EntryTransitionsTo(destination);
}
public AacFlBoolParameter BoolParameter(string parameterName) => _stateMachine.InternalBackingAnimator().BoolParameter(parameterName);
public AacFlBoolParameter TriggerParameterAsBool(string parameterName) => _stateMachine.InternalBackingAnimator().TriggerParameter(parameterName);
public AacFlFloatParameter FloatParameter(string parameterName) => _stateMachine.InternalBackingAnimator().FloatParameter(parameterName);
public AacFlIntParameter IntParameter(string parameterName) => _stateMachine.InternalBackingAnimator().IntParameter(parameterName);
public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().BoolParameters(parameterNames);
public AacFlBoolParameterGroup TriggerParametersAsBools(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().TriggerParameters(parameterNames);
public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().FloatParameters(parameterNames);
public AacFlIntParameterGroup IntParameters(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().IntParameters(parameterNames);
public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters) => _stateMachine.InternalBackingAnimator().BoolParameters(parameters);
public AacFlBoolParameterGroup TriggerParametersAsBools(params AacFlBoolParameter[] parameters) => _stateMachine.InternalBackingAnimator().TriggerParameters(parameters);
public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters) => _stateMachine.InternalBackingAnimator().FloatParameters(parameters);
public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters) => _stateMachine.InternalBackingAnimator().IntParameters(parameters);
public AacFlLayer OverrideValue(AacFlBoolParameter toBeForced, bool value)
{
var parameters = _animatorController.parameters;
foreach (var param in parameters)
{
if (param.name == toBeForced.Name)
{
param.defaultBool = value;
}
}
_animatorController.parameters = parameters;
return this;
}
public AacFlLayer OverrideValue(AacFlFloatParameter toBeForced, float value)
{
var parameters = _animatorController.parameters;
foreach (var param in parameters)
{
if (param.name == toBeForced.Name)
{
param.defaultFloat = value;
}
}
_animatorController.parameters = parameters;
return this;
}
public AacFlLayer OverrideValue(AacFlIntParameter toBeForced, int value)
{
var parameters = _animatorController.parameters;
foreach (var param in parameters)
{
if (param.name == toBeForced.Name)
{
param.defaultInt = value;
}
}
_animatorController.parameters = parameters;
return this;
}
public AacFlLayer WithAvatarMask(AvatarMask avatarMask)
{
var finalFullLayerName = _fullLayerName;
_animatorController.layers = _animatorController.layers
.Select(layer =>
{
if (layer.name == finalFullLayerName)
{
layer.avatarMask = avatarMask;
}
return layer;
})
.ToArray();
return this;
}
public AacFlLayer WithAvatarMaskNoTransforms()
{
ResolveAvatarMask(new Transform[0]);
return this;
}
public AacFlLayer ResolveAvatarMask(Transform[] paths)
{
// FIXME: Fragile
var avatarMask = new AvatarMask();
avatarMask.name = "zAutogenerated__" + _configuration.AssetKey + "_" + _fullLayerName + "__AvatarMask";
avatarMask.hideFlags = HideFlags.None;
if (paths.Length == 0)
{
avatarMask.transformCount = 1;
avatarMask.SetTransformActive(0, false);
avatarMask.SetTransformPath(0, "_ignored");
}
else
{
avatarMask.transformCount = paths.Length;
for (var index = 0; index < paths.Length; index++)
{
var transform = paths[index];
avatarMask.SetTransformActive(index, true);
avatarMask.SetTransformPath(index, AacV1.ResolveRelativePath(_configuration.AnimatorRoot, transform));
}
}
for (int i = 0; i < (int) AvatarMaskBodyPart.LastBodyPart; i++)
{
avatarMask.SetHumanoidBodyPartActive((AvatarMaskBodyPart) i, false);
}
AssetDatabase.AddObjectToAsset(avatarMask, _animatorController);
WithAvatarMask(avatarMask);
return this;
}
public AacFlLayer WithDefaultState(AacFlState newDefaultState)
{
_stateMachine.WithDefaultState(newDefaultState);
return this;
}
public AacFlStateMachine InternalStateMachine()
{
return _stateMachine;
}
}
public class AacFlBase
{
private readonly AacConfiguration _configuration;
public AacConfiguration InternalConfiguration()
{
return _configuration;
}
internal AacFlBase(AacConfiguration configuration)
{
_configuration = configuration;
}
public AacFlClip NewClip()
{
var clip = AacV1.NewClip(_configuration, Guid.NewGuid().ToString());
return new AacFlClip(_configuration, clip);
}
public AacFlClip CopyClip(AnimationClip originalClip)
{
var newClip = UnityEngine.Object.Instantiate(originalClip);
var clip = AacV1.RegisterClip(_configuration, Guid.NewGuid().ToString(), newClip);
return new AacFlClip(_configuration, clip);
}
public AacFlNonInitializedBlendTree NewBlendTree()
{
return new AacFlNonInitializedBlendTree(AacV1.NewBlendTreeAsRaw(_configuration, Guid.NewGuid().ToString()));
}
public BlendTree NewBlendTreeAsRaw()
{
return AacV1.NewBlendTreeAsRaw(_configuration, Guid.NewGuid().ToString());
}
public AacFlClip NewClip(string name)
{
var clip = AacV1.NewClip(_configuration, name);
return new AacFlClip(_configuration, clip);
}
public AacFlClip DummyClipLasting(float numberOf, AacFlUnit unit)
{
var dummyClip = AacV1.NewClip(_configuration, $"D({numberOf} {Enum.GetName(typeof(AacFlUnit), unit)})");
return new AacFlClip(_configuration, dummyClip)
.Animating(clip => clip.Animates("_ignored", typeof(GameObject), "m_IsActive")
.WithUnit(unit, keyframes => keyframes.Constant(0, 0f).Constant(numberOf, 0f)));
}
// For backwards compatibility, generates an animation with 2 keyframes that are at a distance of 1 / 60 of 1 frame,
// that is 1 / 3600 seconds.
// This was due to DummyClipLasting returning a clip with unit conversion applied twice,
// resulting in an "undesired" animation which was functional anyways.
// Since this dummy clip is used internally on all uninitalized states,
// preserve this behaviour so that existing animators don't break due to this change.
protected AacFlClip AnomalousSingleKeyframeClip()
{
var numberOf = 1f;
var unit = AacFlUnit.Frames;
var dummyClip = AacV1.NewClip(_configuration, $"D({numberOf} {Enum.GetName(typeof(AacFlUnit), unit)})");
var duration = unit == AacFlUnit.Frames ? numberOf / 60f : numberOf;
return new AacFlClip(_configuration, dummyClip)
.Animating(clip => clip.Animates("_ignored", typeof(GameObject), "m_IsActive")
.WithUnit(unit, keyframes => keyframes.Constant(0, 0f).Constant(duration, 0f)));
}
public AacFlController NewAnimatorController()
{
var animatorController = AacV1.NewAnimatorController(_configuration, Guid.NewGuid().ToString());
return new AacFlController(_configuration, animatorController, this);
}
public AacFlController NewAnimatorController(string name)
{
var animatorController = AacV1.NewAnimatorController(_configuration, name);
return new AacFlController(_configuration, animatorController, this);
}
public AacFlLayer CreateMainArbitraryControllerLayer(AnimatorController controller) => InternalDoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerName(_configuration.SystemName));
public AacFlLayer CreateSupportingArbitraryControllerLayer(AnimatorController controller, string suffix) => InternalDoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix));
public AacFlLayer CreateFirstArbitraryControllerLayer(AnimatorController controller) => InternalDoCreateLayer(controller, controller.layers[0].name);
public AacFlLayer InternalDoCreateLayer(AnimatorController animator, string layerName)
{
var ag = new AacAnimatorGenerator(animator, CreateEmptyClip().Clip, _configuration.DefaultsProvider);
var machine = ag.CreateOrClearLayerAtSameIndex(layerName, 1f);
return new AacFlLayer(animator, _configuration, machine, layerName);
}
internal AacFlLayer DoCreateLayerWithoutDeleting(AnimatorController animator, string layerName)
{
var ag = new AacAnimatorGenerator(animator, CreateEmptyClip().Clip, _configuration.DefaultsProvider);
var machine = ag.CreateOrClearLayerAtSameIndex(layerName, 1f, null, false);
return new AacFlLayer(animator, _configuration, machine, layerName);
}
private AacFlClip CreateEmptyClip()
{
var emptyClip = AnomalousSingleKeyframeClip();
return emptyClip;
}
public void ClearPreviousAssets()
{
var allSubAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(_configuration.AssetContainer));
foreach (var subAsset in allSubAssets)
{
if (subAsset.name.StartsWith($"zAutogenerated__{_configuration.AssetKey}__")
&& (subAsset is AnimationClip || subAsset is BlendTree || subAsset is AvatarMask))
{
AssetDatabase.RemoveObjectFromAsset(subAsset);
}
}
}
public AacFlNoAnimator NoAnimator()
{
return new AacFlNoAnimator();
}
}
public class AacFlNoAnimator
{
public AacFlFloatParameter FloatParameter(string parameterName) => AacFlFloatParameter.Internally(parameterName);
public AacFlIntParameter IntParameter(string parameterName) => AacFlIntParameter.Internally(parameterName);
public AacFlBoolParameter BoolParameter(string parameterName) => AacFlBoolParameter.Internally(parameterName);
}
public class AacFlBlendTree
{
protected AacFlBlendTree(BlendTree blendTree)
{
BlendTree = blendTree;
}
public BlendTree BlendTree { get; }
}
public class AacFlNonInitializedBlendTree : AacFlBlendTree
{
public AacFlNonInitializedBlendTree(BlendTree blendTree) : base(blendTree)
{
}
public AacFlBlendTree2D FreeformCartesian(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY)
{
BlendTree.blendType = BlendTreeType.FreeformCartesian2D;
BlendTree.blendParameter = parameterX.Name;
BlendTree.blendParameterY = parameterY.Name;
return new AacFlBlendTree2D(BlendTree);
}
public AacFlBlendTree2D FreeformDirectional(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY)
{
BlendTree.blendType = BlendTreeType.FreeformDirectional2D;
BlendTree.blendParameter = parameterX.Name;
BlendTree.blendParameterY = parameterY.Name;
return new AacFlBlendTree2D(BlendTree);
}
public AacFlBlendTree2D SimpleDirectional(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY)
{
BlendTree.blendType = BlendTreeType.SimpleDirectional2D;
BlendTree.blendParameter = parameterX.Name;
BlendTree.blendParameterY = parameterY.Name;
return new AacFlBlendTree2D(BlendTree);
}
public AacFlBlendTree1D Simple(AacFlFloatParameter parameter)
{
BlendTree.blendType = BlendTreeType.Simple1D;
BlendTree.blendParameter = parameter.Name;
BlendTree.useAutomaticThresholds = false;
return new AacFlBlendTree1D(BlendTree);
}
public AacFlBlendTreeDirect Direct()
{
BlendTree.blendType = BlendTreeType.Direct;
return new AacFlBlendTreeDirect(BlendTree);
}
}
public class AacFlBlendTree2D : AacFlBlendTree
{
public AacFlBlendTree2D(BlendTree blendTree) : base(blendTree)
{
}
public AacFlBlendTree2D WithAnimation(AacFlBlendTree blendTree, Vector2 pos)
{
return WithAnimation(blendTree.BlendTree, pos);
}
public AacFlBlendTree2D WithAnimation(AacFlBlendTree blendTree, float x, float y)
{
return WithAnimation(blendTree.BlendTree, x, y);
}
public AacFlBlendTree2D WithAnimation(AacFlClip clip, Vector2 pos)
{
return WithAnimation(clip.Clip, pos);
}
public AacFlBlendTree2D WithAnimation(AacFlClip clip, float x, float y)
{
return WithAnimation(clip.Clip, x, y);
}
public AacFlBlendTree2D WithAnimation(Motion motion, Vector2 pos)
{
return WithAnimation(motion, pos.x, pos.y);
}
public AacFlBlendTree2D WithAnimation(Motion motion, float x, float y)
{
var children = BlendTree.children ?? new ChildMotion[0];
var childrenList = children.ToList();
childrenList.Add(new ChildMotion
{
motion = motion,
position = new Vector2(x, y),
timeScale = 1f
});
BlendTree.children = childrenList.ToArray();
return this;
}
}
public class AacFlBlendTree1D : AacFlBlendTree
{
public AacFlBlendTree1D(BlendTree blendTree) : base(blendTree)
{
}
public AacFlBlendTree1D WithAnimation(AacFlClip clip, float threshold)
{
return WithAnimation(clip.Clip, threshold);
}
public AacFlBlendTree1D WithAnimation(AacFlBlendTree blendTree, float threshold)
{
return WithAnimation(blendTree.BlendTree, threshold);
}
public AacFlBlendTree1D WithAnimation(Motion motion, float threshold)
{
var children = BlendTree.children ?? new ChildMotion[0];
var childrenList = children.ToList();
childrenList.Add(new ChildMotion
{
motion = motion,
threshold = threshold,
timeScale = 1f
});
BlendTree.children = childrenList.ToArray();
return this;
}
}
public class AacFlBlendTreeDirect : AacFlBlendTree
{
public AacFlBlendTreeDirect(BlendTree blendTree) : base(blendTree)
{
}
public AacFlBlendTreeDirect WithAnimation(AacFlClip clip, AacFlFloatParameter parameter)
{
return WithAnimation(clip.Clip, parameter);
}
public AacFlBlendTreeDirect WithAnimation(AacFlBlendTree blendTree, AacFlFloatParameter parameter)
{
return WithAnimation(blendTree.BlendTree, parameter);
}
public AacFlBlendTreeDirect WithAnimation(Motion motion, AacFlFloatParameter parameter)
{
var children = BlendTree.children ?? new ChildMotion[0];
var childrenList = children.ToList();
childrenList.Add(new ChildMotion
{
motion = motion,
directBlendParameter = parameter.Name,
timeScale = 1f
});
BlendTree.children = childrenList.ToArray();
return this;
}
}
public class AacFlController
{
public AnimatorController AnimatorController;
private readonly AacConfiguration _configuration;
private readonly AacFlBase _base;
public AacFlController(AacConfiguration configuration, AnimatorController animatorController, AacFlBase originalBase)
{
AnimatorController = animatorController;
_configuration = configuration;
_base = originalBase;
}
public AacFlLayer CreateLayer(string suffix) => _base.DoCreateLayerWithoutDeleting(AnimatorController, _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix));
public AacFlLayer CreateLayer() => _base.DoCreateLayerWithoutDeleting(AnimatorController, _configuration.SystemName);
}
public class AacAnimatorRemoval
{
private readonly AnimatorController _animatorController;
public AacAnimatorRemoval(AnimatorController animatorController)
{
_animatorController = animatorController;
}
public void RemoveLayer(string layerName)
{
var index = FindIndexOf(layerName);
if (index == -1) return;
_animatorController.RemoveLayer(index);
}
private int FindIndexOf(string layerName)
{
return _animatorController.layers.ToList().FindIndex(layer => layer.name == layerName);
}
}
public class AacAnimatorGenerator
{
private readonly AnimatorController _animatorController;
private readonly AnimationClip _emptyClip;
private readonly IAacDefaultsProvider _defaultsProvider;
internal AacAnimatorGenerator(AnimatorController animatorController, AnimationClip emptyClip, IAacDefaultsProvider defaultsProvider)
{
_animatorController = animatorController;
_emptyClip = emptyClip;
_defaultsProvider = defaultsProvider;
}
internal void CreateParamsAsNeeded(params AacFlParameter[] parameters)
{
foreach (var parameter in parameters)
{
switch (parameter)
{
case AacFlIntParameter _:
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Int);
break;
case AacFlFloatParameter _:
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Float);
break;
case AacFlBoolParameter _:
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Bool);
break;
}
}
}
internal void CreateTriggerParamsAsNeeded(params AacFlBoolParameter[] parameters)
{
foreach (var parameter in parameters)
{
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Trigger);
}
}
private void CreateParamIfNotExists(string paramName, AnimatorControllerParameterType type)
{
if (_animatorController.parameters.FirstOrDefault(param => param.name == paramName) == null)
{
_animatorController.AddParameter(paramName, type);
}
}
// DEPRECATED: This causes the editor window to glitch by deselecting, which is jarring for experimentation
internal AacFlStateMachine CreateOrRemakeLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null)
{
var originalIndexToPreserveOrdering = FindIndexOf(layerName);
if (originalIndexToPreserveOrdering != -1)
{
_animatorController.RemoveLayer(originalIndexToPreserveOrdering);
}
AddLayerWithWeight(layerName, weightWhenCreating, maskWhenCreating);
if (originalIndexToPreserveOrdering != -1)
{
var items = _animatorController.layers.ToList();
var last = items[items.Count - 1];
items.RemoveAt(items.Count - 1);
items.Insert(originalIndexToPreserveOrdering, last);
_animatorController.layers = items.ToArray();
}
var layer = TryGetLayer(layerName);
var machinist = new AacFlStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider);
return machinist
.WithAnyStatePosition(0, 7)
.WithEntryPosition(0, -1)
.WithExitPosition(7, -1);
}
internal AacFlStateMachine CreateOrClearLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null, bool allowDeletion = true)
{
var originalIndexToPreserveOrdering = FindIndexOf(layerName);
if (originalIndexToPreserveOrdering != -1)
{
if (!allowDeletion)
{
throw new InvalidOperationException($"Cannot create layer with name {layerName} as it already exists. When creating layers using NewAnimatorController, you may only use unique names.");
}
RecursivelyClearChildrenMachines(_animatorController.layers[originalIndexToPreserveOrdering].stateMachine);
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.stateMachines = new ChildAnimatorStateMachine[0];
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.states = new ChildAnimatorState[0];
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.entryTransitions = new AnimatorTransition[0];
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.anyStateTransitions = new AnimatorStateTransition[0];
}
else
{
_animatorController.AddLayer(_animatorController.MakeUniqueLayerName(layerName));
originalIndexToPreserveOrdering = _animatorController.layers.Length - 1;
}
var layers = _animatorController.layers;
layers[originalIndexToPreserveOrdering].avatarMask = maskWhenCreating;
layers[originalIndexToPreserveOrdering].defaultWeight = weightWhenCreating;
_animatorController.layers = layers;
var layer = TryGetLayer(layerName);
var machinist = new AacFlStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider);
_defaultsProvider.ConfigureStateMachine(layer.stateMachine);
return machinist;
}
private void RecursivelyClearChildrenMachines(AnimatorStateMachine parentMachine)
{
// TODO: RemoveStateMachine might already be recursive
foreach (var childStateMachineHolder in parentMachine.stateMachines)
{
RecursivelyClearChildrenMachines(childStateMachineHolder.stateMachine);
parentMachine.RemoveStateMachine(childStateMachineHolder.stateMachine);
}
}
private int FindIndexOf(string layerName)
{
return _animatorController.layers.ToList().FindIndex(layer1 => layer1.name == layerName);
}
private AnimatorControllerLayer TryGetLayer(string layerName)
{
return _animatorController.layers.FirstOrDefault(it => it.name == layerName);
}
private void AddLayerWithWeight(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating)
{
_animatorController.AddLayer(_animatorController.MakeUniqueLayerName(layerName));
var mutatedLayers = _animatorController.layers;
mutatedLayers[mutatedLayers.Length - 1].defaultWeight = weightWhenCreating;
mutatedLayers[mutatedLayers.Length - 1].avatarMask = maskWhenCreating;
_animatorController.layers = mutatedLayers;
}
}
}