diff --git a/Framework/Editor/V1/Aac.cs b/Framework/Editor/V1/Aac.cs index 8ade46a..c1ab547 100644 --- a/Framework/Editor/V1/Aac.cs +++ b/Framework/Editor/V1/Aac.cs @@ -11,6 +11,7 @@ namespace AnimatorAsCode.V1 { public static class AacV1 { + /// Create an Animator As Code (AAC) base. public static AacFlBase Create(AacConfiguration configuration) { return new AacFlBase(configuration); @@ -21,7 +22,7 @@ namespace AnimatorAsCode.V1 return RegisterAnimatorController(component, suffix, new AnimatorController()); } - internal static AnimatorController RegisterAnimatorController(AacConfiguration component, string suffix, AnimatorController animatorController) + private 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; @@ -135,6 +136,7 @@ namespace AnimatorAsCode.V1 _stateMachine = stateMachine; } + /// Create a new state, initially positioned below the last generated state of this layer. public AacFlState NewState(string name) { var lastState = _stateMachine.LastNodePosition(); @@ -142,49 +144,85 @@ namespace AnimatorAsCode.V1 return state; } + /// Create a new state at a specific position x and y, in grid units. The grid size is defined in the DefaultsProvider of the AacConfiguration of AAC. x positive goes right, y positive goes down. public AacFlState NewState(string name, int x, int y) { return _stateMachine.NewState(name, x, y); } + /// Create a new SSM, initially positioned below the last generated state of this layer. public AacFlStateMachine NewSubStateMachine(string name) { return _stateMachine.NewSubStateMachine(name); } + /// Create a new SSM at a specific position `x` and `y`, in grid units. The grid size is defined in the DefaultsProvider of the AacConfiguration of AAC. `x` positive goes right, `y` positive goes down. public AacFlStateMachine NewSubStateMachine(string name, int x, int y) { return _stateMachine.NewSubStateMachine(name, x, y); } + /// Create a transition from Any to the `destination` state. public AacFlTransition AnyTransitionsTo(AacFlState destination) { return _stateMachine.AnyTransitionsTo(destination); } + /// Create a transition from Any to the `destination` SSM. + public AacFlTransition AnyTransitionsTo(AacFlStateMachine destination) + { + return _stateMachine.AnyTransitionsTo(destination); + } + + // Create a transition from the Entry to the `destination` state. public AacFlEntryTransition EntryTransitionsTo(AacFlState destination) { return _stateMachine.EntryTransitionsTo(destination); } + // Create a transition from the Entry to the `destination` state machine. public AacFlEntryTransition EntryTransitionsTo(AacFlStateMachine destination) { return _stateMachine.EntryTransitionsTo(destination); } + /// Create a Bool parameter in the animator. public AacFlBoolParameter BoolParameter(string parameterName) => _stateMachine.InternalBackingAnimator().BoolParameter(parameterName); + + /// Create a Trigger parameter in the animator, but returns a Bool parameter for use in AAC. public AacFlBoolParameter TriggerParameterAsBool(string parameterName) => _stateMachine.InternalBackingAnimator().TriggerParameter(parameterName); + + /// Create a Float parameter in the animator. public AacFlFloatParameter FloatParameter(string parameterName) => _stateMachine.InternalBackingAnimator().FloatParameter(parameterName); + + /// Create an Int parameter in the animator. public AacFlIntParameter IntParameter(string parameterName) => _stateMachine.InternalBackingAnimator().IntParameter(parameterName); + + /// Create multiple Bool parameters in the animator, and returns a group of multiple Bools. public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().BoolParameters(parameterNames); + + /// Create multiple Trigger parameters in the animator, but returns a group of multiple Bools for use in AAC. public AacFlBoolParameterGroup TriggerParametersAsBools(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().TriggerParameters(parameterNames); + + /// Create multiple Float parameters in the animator, and returns a group of multiple Floats. public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().FloatParameters(parameterNames); + + /// Create multiple Int parameters in the animator, and returns a group of multiple Ints. public AacFlIntParameterGroup IntParameters(params string[] parameterNames) => _stateMachine.InternalBackingAnimator().IntParameters(parameterNames); + + /// Combine multiple Bool parameters into a group. public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters) => _stateMachine.InternalBackingAnimator().BoolParameters(parameters); + + /// Combine multiple Bool parameters into a group. There is no difference with BoolParameters(...) and is provided only for semantic purposes. public AacFlBoolParameterGroup TriggerParametersAsBools(params AacFlBoolParameter[] parameters) => _stateMachine.InternalBackingAnimator().TriggerParameters(parameters); + + /// Combine multiple Float parameters into a group. public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters) => _stateMachine.InternalBackingAnimator().FloatParameters(parameters); + + /// Combine multiple Int parameters into a group. public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters) => _stateMachine.InternalBackingAnimator().IntParameters(parameters); + /// Set the Bool value of `toBeForced` parameter to `value` in the animator. public AacFlLayer OverrideValue(AacFlBoolParameter toBeForced, bool value) { var parameters = _animatorController.parameters; @@ -201,6 +239,7 @@ namespace AnimatorAsCode.V1 return this; } + /// Set the Float value of `toBeForced` parameter to `value` in the animator. public AacFlLayer OverrideValue(AacFlFloatParameter toBeForced, float value) { var parameters = _animatorController.parameters; @@ -217,6 +256,7 @@ namespace AnimatorAsCode.V1 return this; } + /// Set the Int value of `toBeForced` parameter to `value` in the animator. public AacFlLayer OverrideValue(AacFlIntParameter toBeForced, int value) { var parameters = _animatorController.parameters; @@ -233,6 +273,7 @@ namespace AnimatorAsCode.V1 return this; } + /// Set the Avatar Mask of the layer. public AacFlLayer WithAvatarMask(AvatarMask avatarMask) { var finalFullLayerName = _fullLayerName; @@ -251,6 +292,7 @@ namespace AnimatorAsCode.V1 return this; } + /// Set the Avatar Mask of the layer to be an Avatar Mask which denies all transforms. The asset is generated into the container. public AacFlLayer WithAvatarMaskNoTransforms() { ResolveAvatarMask(new Transform[0]); @@ -258,6 +300,7 @@ namespace AnimatorAsCode.V1 return this; } + /// Set the Avatar Mask of the layer to be an Avatar Mask that allows the specified transforms. If `paths` is an empty array, all transforms are denied, which is effectively the same as calling `.WithAvatarMaskNoTransforms()`. The asset is generated into the container. public AacFlLayer ResolveAvatarMask(Transform[] paths) { // FIXME: Fragile @@ -294,12 +337,14 @@ namespace AnimatorAsCode.V1 return this; } + /// Set the Default State of the layer. public AacFlLayer WithDefaultState(AacFlState newDefaultState) { _stateMachine.WithDefaultState(newDefaultState); return this; } + /// NON-PUBLIC: Internal use only so that extensions can access this. Maybe this can be improved public AacFlStateMachine InternalStateMachine() { return _stateMachine; @@ -310,6 +355,7 @@ namespace AnimatorAsCode.V1 { private readonly AacConfiguration _configuration; + /// NON-PUBLIC: Internal use only so that destructive workflow can access this. Maybe this can be improved public AacConfiguration InternalConfiguration() { return _configuration; @@ -320,12 +366,14 @@ namespace AnimatorAsCode.V1 _configuration = configuration; } + /// Create a new clip. The asset is generated into the container. public AacFlClip NewClip() { var clip = AacV1.NewClip(_configuration, Guid.NewGuid().ToString()); return new AacFlClip(_configuration, clip); } + /// Create a new clip that is a copy of `originalClip`. The asset is generated into the container. public AacFlClip CopyClip(AnimationClip originalClip) { var newClip = UnityEngine.Object.Instantiate(originalClip); @@ -333,22 +381,26 @@ namespace AnimatorAsCode.V1 return new AacFlClip(_configuration, clip); } + /// Create a new BlendTree asset. The asset is generated into the container. public AacFlNonInitializedBlendTree NewBlendTree() { return new AacFlNonInitializedBlendTree(AacV1.NewBlendTreeAsRaw(_configuration, Guid.NewGuid().ToString())); } + /// Create a new BlendTree asset and returns a native BlendTree object. The asset is generated into the container. You may use NewBlendTree() instead to obtain a fluent interface. public BlendTree NewBlendTreeAsRaw() { return AacV1.NewBlendTreeAsRaw(_configuration, Guid.NewGuid().ToString()); } + /// Create a new clip with a name. However, the name is only used as a suffix for the asset. The asset is generated into the container. public AacFlClip NewClip(string name) { var clip = AacV1.NewClip(_configuration, name); return new AacFlClip(_configuration, clip); } + /// Create a new clip which animates a dummy transform for a specific duration specified in an unit (Frames or Seconds). public AacFlClip DummyClipLasting(float numberOf, AacFlUnit unit) { var dummyClip = AacV1.NewClip(_configuration, $"D({numberOf} {Enum.GetName(typeof(AacFlUnit), unit)})"); @@ -364,7 +416,7 @@ namespace AnimatorAsCode.V1 // 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() + private AacFlClip AnomalousSingleKeyframeClip() { var numberOf = 1f; var unit = AacFlUnit.Frames; @@ -376,23 +428,33 @@ namespace AnimatorAsCode.V1 .Animating(clip => clip.Animates("_ignored", typeof(GameObject), "m_IsActive") .WithUnit(unit, keyframes => keyframes.Constant(0, 0f).Constant(duration, 0f))); } - + + /// Create a new animator controller. The asset is generated into the container. public AacFlController NewAnimatorController() { var animatorController = AacV1.NewAnimatorController(_configuration, Guid.NewGuid().ToString()); return new AacFlController(_configuration, animatorController, this); } + /// Create a new animator controller with a name. However, the name is only used as a suffix for the asset. The asset is generated into the container. public AacFlController NewAnimatorController(string name) { var animatorController = AacV1.NewAnimatorController(_configuration, name); return new AacFlController(_configuration, animatorController, this); } + + //---- ## Destructive workflow + /// Destructive workflow: Create a main layer for an arbitrary AnimatorController, clearing the previous one of the same system. You are not obligated to have a main layer. public AacFlLayer CreateMainArbitraryControllerLayer(AnimatorController controller) => InternalDoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerName(_configuration.SystemName)); + + /// Destructive workflow: Create a supporting layer for an arbitrary AnimatorController, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public AacFlLayer CreateSupportingArbitraryControllerLayer(AnimatorController controller, string suffix) => InternalDoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix)); + + /// Destructive workflow: Clears the topmost layer of an arbitrary AnimatorController, and returns it. public AacFlLayer CreateFirstArbitraryControllerLayer(AnimatorController controller) => InternalDoCreateLayer(controller, controller.layers[0].name); + /// NON-PUBLIC: Internal use only so that destructive workflow can access this. Maybe this can be improved public AacFlLayer InternalDoCreateLayer(AnimatorController animator, string layerName) { var ag = new AacAnimatorGenerator(animator, CreateEmptyClip().Clip, _configuration.DefaultsProvider); @@ -415,6 +477,7 @@ namespace AnimatorAsCode.V1 return emptyClip; } + /// Removes all assets from the asset container matching the specified asset key. public void ClearPreviousAssets() { var allSubAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(_configuration.AssetContainer)); @@ -428,6 +491,7 @@ namespace AnimatorAsCode.V1 } } + /// If you are not creating an animator, this returns an object from which you can obtain animator parameter objects. You should use this class if you are creating BlendTree assets without any animator controllers to back it. Otherwise, it is strongly recommended to obtain animator parameter objects directly from the layer objects instead of using NoAnimator(), as the use of NoAnimator() will not result in the registration of any parameters inside the animator controller. public AacFlNoAnimator NoAnimator() { return new AacFlNoAnimator(); @@ -436,182 +500,16 @@ namespace AnimatorAsCode.V1 public class AacFlNoAnimator { + /// Create a Float parameter, for use without a backing animator. public AacFlFloatParameter FloatParameter(string parameterName) => AacFlFloatParameter.Internally(parameterName); + + /// Create a Int parameter, for use without a backing animator. public AacFlIntParameter IntParameter(string parameterName) => AacFlIntParameter.Internally(parameterName); + + /// Create a Bool parameter, for use without a backing animator. 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; diff --git a/Framework/Editor/V1/AacFlBlendTrees.cs b/Framework/Editor/V1/AacFlBlendTrees.cs new file mode 100644 index 0000000..155135f --- /dev/null +++ b/Framework/Editor/V1/AacFlBlendTrees.cs @@ -0,0 +1,239 @@ +using System; +using System.Linq; +using UnityEditor.Animations; +using UnityEngine; + +// ReSharper disable once CheckNamespace +namespace AnimatorAsCode.V1 +{ + public class AacFlBlendTree + { + protected AacFlBlendTree(BlendTree blendTree) + { + BlendTree = blendTree; + } + + public BlendTree BlendTree { get; } + } + + public class AacFlNonInitializedBlendTree : AacFlBlendTree + { + public AacFlNonInitializedBlendTree(BlendTree blendTree) : base(blendTree) + { + } + + // Define this BlendTree as being FreeformCartesian2D. + public AacFlBlendTree2D FreeformCartesian2D(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY) + { + return New2DBlendTree(parameterX, parameterY, BlendTreeType.FreeformCartesian2D); + } + + // Define this BlendTree as being FreeformDirectional2D. + public AacFlBlendTree2D FreeformDirectional2D(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY) + { + return New2DBlendTree(parameterX, parameterY, BlendTreeType.FreeformDirectional2D); + } + + // Define this BlendTree as being SimpleDirectional2D. + public AacFlBlendTree2D SimpleDirectional2D(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY) + { + return New2DBlendTree(parameterX, parameterY, BlendTreeType.SimpleDirectional2D); + } + + // Define this BlendTree as being Simple1D. + public AacFlBlendTree1D Simple1D(AacFlFloatParameter parameter) + { + BlendTree.blendType = BlendTreeType.Simple1D; + BlendTree.blendParameter = parameter.Name; + BlendTree.useAutomaticThresholds = false; + + return new AacFlBlendTree1D(BlendTree); + } + + // Define this BlendTree as being Direct. + public AacFlBlendTreeDirect Direct() + { + BlendTree.blendType = BlendTreeType.Direct; + + return new AacFlBlendTreeDirect(BlendTree); + } + + private AacFlBlendTree2D New2DBlendTree(AacFlFloatParameter parameterX, AacFlFloatParameter parameterY, BlendTreeType blendTreeType) + { + BlendTree.blendType = blendTreeType; + BlendTree.blendParameter = parameterX.Name; + BlendTree.blendParameterY = parameterY.Name; + + return new AacFlBlendTree2D(BlendTree); + } + } + + public class AacFlBlendTree2D : AacFlBlendTree + { + public AacFlBlendTree2D(BlendTree blendTree) : base(blendTree) + { + } + + // Add a BlendTree in the specified coordinates. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree2D WithAnimation(AacFlBlendTree blendTree, Vector2 pos, Action furtherDefiningChild = null) + { + return WithAnimation(blendTree.BlendTree, pos, furtherDefiningChild); + } + + // Add a BlendTree in the specified `x` and `y` coordinates. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree2D WithAnimation(AacFlBlendTree blendTree, float x, float y, Action furtherDefiningChild = null) + { + return WithAnimation(blendTree.BlendTree, x, y, furtherDefiningChild); + } + + // Add a Clip in the specified coordinates. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree2D WithAnimation(AacFlClip clip, Vector2 pos, Action furtherDefiningChild = null) + { + return WithAnimation(clip.Clip, pos, furtherDefiningChild); + } + + // Add a Clip in the specified `x` and `y` coordinates. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree2D WithAnimation(AacFlClip clip, float x, float y, Action furtherDefiningChild = null) + { + return WithAnimation(clip.Clip, x, y, furtherDefiningChild); + } + + // Add a raw motion in the specified coordinates. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree2D WithAnimation(Motion motion, Vector2 pos, Action furtherDefiningChild = null) + { + return WithAnimation(motion, pos.x, pos.y, furtherDefiningChild); + } + + // Add a raw motion in the specified `x` and `y` coordinates. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree2D WithAnimation(Motion motion, float x, float y, Action furtherDefiningChild = null) + { + var children = BlendTree.children ?? new ChildMotion[0]; + var childrenList = children.ToList(); + + + var childMotionModifier = new AacFlBlendTreeChildMotion(); + furtherDefiningChild?.Invoke(childMotionModifier); + var newChildMotion = new ChildMotion + { + motion = motion, + position = new Vector2(x, y), + timeScale = childMotionModifier.TimeScale, + mirror = childMotionModifier.Mirror, + cycleOffset = childMotionModifier.CycleOffset + }; + + childrenList.Add(newChildMotion); + BlendTree.children = childrenList.ToArray(); + + return this; + } + } + + public class AacFlBlendTree1D : AacFlBlendTree + { + public AacFlBlendTree1D(BlendTree blendTree) : base(blendTree) + { + } + + // Add a BlendTree in the specified threshold. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree1D WithAnimation(AacFlBlendTree blendTree, float threshold, Action furtherDefiningChild = null) + { + return WithAnimation(blendTree.BlendTree, threshold, furtherDefiningChild); + } + + // Add a Clip in the specified threshold. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree1D WithAnimation(AacFlClip clip, float threshold, Action furtherDefiningChild = null) + { + return WithAnimation(clip.Clip, threshold, furtherDefiningChild); + } + + // Add a raw motion in the specified threshold. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTree1D WithAnimation(Motion motion, float threshold, Action furtherDefiningChild = null) + { + var children = BlendTree.children ?? new ChildMotion[0]; + var childrenList = children.ToList(); + + var childMotionModifier = new AacFlBlendTreeChildMotion(); + furtherDefiningChild?.Invoke(childMotionModifier); + childrenList.Add(new ChildMotion + { + motion = motion, + threshold = threshold, + timeScale = childMotionModifier.TimeScale, + mirror = childMotionModifier.Mirror, + cycleOffset = childMotionModifier.CycleOffset + }); + BlendTree.children = childrenList.ToArray(); + + return this; + } + } + + public class AacFlBlendTreeDirect : AacFlBlendTree + { + public AacFlBlendTreeDirect(BlendTree blendTree) : base(blendTree) + { + } + + // Add a BlendTree driven by the specified parameter. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTreeDirect WithAnimation(AacFlBlendTree blendTree, AacFlFloatParameter parameter, Action furtherDefiningChild = null) + { + return WithAnimation(blendTree.BlendTree, parameter, furtherDefiningChild); + } + + // Add a Clip driven by the specified parameter. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTreeDirect WithAnimation(AacFlClip clip, AacFlFloatParameter parameter, Action furtherDefiningChild = null) + { + return WithAnimation(clip.Clip, parameter, furtherDefiningChild); + } + + // Add a raw motion driven by the specified parameter. The last parameter overload is optional: by default, the timeScale is 1, cycle offset is 0, mirror is false. + public AacFlBlendTreeDirect WithAnimation(Motion motion, AacFlFloatParameter parameter, Action furtherDefiningChild = null) + { + var children = BlendTree.children ?? new ChildMotion[0]; + var childrenList = children.ToList(); + + var childMotionModifier = new AacFlBlendTreeChildMotion(); + furtherDefiningChild?.Invoke(childMotionModifier); + childrenList.Add(new ChildMotion + { + motion = motion, + directBlendParameter = parameter.Name, + timeScale = childMotionModifier.TimeScale, + mirror = childMotionModifier.Mirror, + cycleOffset = childMotionModifier.CycleOffset + }); + BlendTree.children = childrenList.ToArray(); + + return this; + } + } + + public class AacFlBlendTreeChildMotion + { + internal float TimeScale { get; set; } = 1f; + internal bool Mirror { get; set; } + internal float CycleOffset { get; set; } + + /// Set the time scale. The time scale value is 1 by default. + public AacFlBlendTreeChildMotion WithTimeScaleSetTo(float timeScale) + { + TimeScale = timeScale; + return this; + } + + /// Set the mirror option. The mirror option value is false by default. + public AacFlBlendTreeChildMotion WithMirrorSetTo(bool mirror) + { + Mirror = mirror; + return this; + } + + /// Set the cycle offset. The cycle offset value is 0 by default. + public AacFlBlendTreeChildMotion WithCycleOffsetSetTo(float cycleOffset) + { + CycleOffset = cycleOffset; + return this; + } + } +} \ No newline at end of file diff --git a/Framework/Editor/V1/AacFlBlendTrees.cs.meta b/Framework/Editor/V1/AacFlBlendTrees.cs.meta new file mode 100644 index 0000000..c5ef520 --- /dev/null +++ b/Framework/Editor/V1/AacFlBlendTrees.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 80c6ca79d93446f088d6e7b1bfee5daa +timeCreated: 1694177157 \ No newline at end of file diff --git a/Framework/Editor/V1/AacFlStates.cs b/Framework/Editor/V1/AacFlStates.cs index dd554e5..5ae454a 100644 --- a/Framework/Editor/V1/AacFlStates.cs +++ b/Framework/Editor/V1/AacFlStates.cs @@ -198,6 +198,11 @@ namespace AnimatorAsCode.V1 return AnyTransition(destination, Machine); } + public AacFlTransition AnyTransitionsTo(AacFlStateMachine destination) + { + return AnyTransition(destination, Machine); + } + public AacFlEntryTransition EntryTransitionsTo(AacFlState destination) { return EntryTransition(destination, Machine); @@ -238,6 +243,11 @@ namespace AnimatorAsCode.V1 return new AacFlTransition(ConfigureTransition(animatorStateMachine.AddAnyStateTransition(destination.State)), animatorStateMachine, null, destination.State); } + private AacFlTransition AnyTransition(AacFlStateMachine destination, AnimatorStateMachine animatorStateMachine) + { + return new AacFlTransition(ConfigureTransition(animatorStateMachine.AddAnyStateTransition(destination.Machine)), animatorStateMachine, null, destination.Machine); + } + private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { DefaultsProvider.ConfigureTransition(transition); diff --git a/Framework/Editor/V1VRC/AacVRCExtensions.cs b/Framework/Editor/V1VRC/AacVRCExtensions.cs index e9d72ed..fbd2e55 100644 --- a/Framework/Editor/V1VRC/AacVRCExtensions.cs +++ b/Framework/Editor/V1VRC/AacVRCExtensions.cs @@ -9,6 +9,7 @@ namespace AnimatorAsCode.V1.VRC { public static class AacVRCExtensions { + /// Return an AacVrcAssetLibrary, which lets you select various assets from VRChat. public static AacVrcAssetLibrary VrcAssets(this AacFlBase that) { return new AacVrcAssetLibrary(); diff --git a/Framework/Editor/V1VRCDestructiveWorkflow/AacV1VRCDestructiveWorkflowExtensions.cs b/Framework/Editor/V1VRCDestructiveWorkflow/AacV1VRCDestructiveWorkflowExtensions.cs index c1f487a..e1eb977 100644 --- a/Framework/Editor/V1VRCDestructiveWorkflow/AacV1VRCDestructiveWorkflowExtensions.cs +++ b/Framework/Editor/V1VRCDestructiveWorkflow/AacV1VRCDestructiveWorkflowExtensions.cs @@ -16,18 +16,40 @@ namespace AnimatorAsCode.V1.VRCDestructiveWorkflow return that.WithAdditonalData(typeof(IAdditionalDataAvatarDescriptor), avatarDescriptor); } + /// Create the main Fx layer of that system, clearing the previous one of the same system. You are not obligated to have a main layer. public static AacFlLayer CreateMainFxLayer(this AacFlBase that) => DoCreateMainLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.FX); + + /// Create the main Gesture layer of that system, clearing the previous one of the same system. You are not obligated to have a main layer. public static AacFlLayer CreateMainGestureLayer(this AacFlBase that) => DoCreateMainLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Gesture); + + /// Create the main Action layer of that system, clearing the previous one of the same system. You are not obligated to have a main layer. public static AacFlLayer CreateMainActionLayer(this AacFlBase that) => DoCreateMainLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Action); + + /// Create the main Idle layer of that system, clearing the previous one of the same system. You are not obligated to have a main layer. public static AacFlLayer CreateMainIdleLayer(this AacFlBase that) => DoCreateMainLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Additive); + + /// Create the main Locomotion layer of that system, clearing the previous one of the same system. You are not obligated to have a main layer. public static AacFlLayer CreateMainLocomotionLayer(this AacFlBase that) => DoCreateMainLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Base); + + /// Create the main layer of that system for a specific type of layer, clearing the previous one of the same system. You are not obligated to have a main layer. public static AacFlLayer CreateMainAv3Layer(this AacFlBase that, VRCAvatarDescriptor.AnimLayerType animLayerType) => DoCreateMainLayerOnController(that, animLayerType); + /// Create a supporting Fx layer for that system and suffix, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public static AacFlLayer CreateSupportingFxLayer(this AacFlBase that, string suffix) => DoCreateSupportingLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.FX, suffix); + + /// Create a supporting Gesture layer for that system and suffix, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public static AacFlLayer CreateSupportingGestureLayer(this AacFlBase that, string suffix) => DoCreateSupportingLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Gesture, suffix); + + /// Create a supporting Action layer for that system and suffix, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public static AacFlLayer CreateSupportingActionLayer(this AacFlBase that, string suffix) => DoCreateSupportingLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Action, suffix); + + /// Create a supporting Idle layer for that system and suffix, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public static AacFlLayer CreateSupportingIdleLayer(this AacFlBase that, string suffix) => DoCreateSupportingLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Additive, suffix); + + /// Create a supporting Locomotion layer for that system and suffix, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public static AacFlLayer CreateSupportingLocomotionLayer(this AacFlBase that, string suffix) => DoCreateSupportingLayerOnController(that, VRCAvatarDescriptor.AnimLayerType.Base, suffix); + + /// Create a supporting layer for that system and suffix, clearing the previous one of the same system and suffix. You can create multiple supporting layers with different suffixes, and you are not obligated to have a main layer to create a supporting layer. public static AacFlLayer CreateSupportingAv3Layer(this AacFlBase that, VRCAvatarDescriptor.AnimLayerType animLayerType, string suffix) => DoCreateSupportingLayerOnController(that, animLayerType, suffix); private static AacFlLayer DoCreateMainLayerOnController(AacFlBase that, VRCAvatarDescriptor.AnimLayerType animType) @@ -51,12 +73,14 @@ namespace AnimatorAsCode.V1.VRCDestructiveWorkflow return (AnimatorController) ad.baseAnimationLayers.First(it => it.type == animLayerType).animatorController; } + /// Remove all main layers matching that system from all animators of the Avatar descriptor. public static void RemoveAllMainLayers(this AacFlBase that) { var layerName = that.InternalConfiguration().SystemName; RemoveLayerOnAllControllers(that, that.InternalConfiguration().DefaultsProvider.ConvertLayerName(layerName)); } + /// Remove all supporting layers matching that system and suffix from all animators of the Avatar descriptor. public static void RemoveAllSupportingLayers(this AacFlBase that, string suffix) { var layerName = that.InternalConfiguration().SystemName; diff --git a/MigratingFromV0ToV1.md b/MigratingFromV0ToV1.md index cb21266..537ea55 100644 --- a/MigratingFromV0ToV1.md +++ b/MigratingFromV0ToV1.md @@ -1,6 +1,9 @@ -Migrating from V0 to V1 +Migrating AnimatorAsCode from V0 to V1 ====== +TODO: Fix mixed Linear/Easing curve generator??? +TODO: Fix undo slow operations (ask bd?) + AnimatorAsCode V1 introduces the following main breaking changes: - VRChat Avatars is now an optional dependency. AnimatorAsCode can now be used in non-VRChat projects. - All VRChat-related functions have been split between two classes of extension methods.