From d2ed56681cb7c8f084f4575a9022420e55a1bd6d Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Sat, 19 Mar 2022 12:03:11 +0100 Subject: [PATCH] add support for sub-state machines --- Framework/Editor/V0/Aac.cs | 29 +- Framework/Editor/V0/AacAnimatorNode.cs | 58 ++++ Framework/Editor/V0/AacAnimatorNode.cs.meta | 11 + Framework/Editor/V0/AacFlStates.cs | 330 ++++++++++++++------ 4 files changed, 324 insertions(+), 104 deletions(-) create mode 100644 Framework/Editor/V0/AacAnimatorNode.cs create mode 100644 Framework/Editor/V0/AacAnimatorNode.cs.meta diff --git a/Framework/Editor/V0/Aac.cs b/Framework/Editor/V0/Aac.cs index 85e60d6..14699fb 100644 --- a/Framework/Editor/V0/Aac.cs +++ b/Framework/Editor/V0/Aac.cs @@ -95,9 +95,9 @@ namespace AnimatorAsCode.V0 private readonly AnimatorController _animatorController; private readonly AacConfiguration _configuration; private readonly string _fullLayerName; - private readonly AacStateMachine _stateMachine; + private readonly AacFlStateMachine _stateMachine; - internal AacFlLayer(AnimatorController animatorController, AacConfiguration configuration, AacStateMachine stateMachine, string fullLayerName) + internal AacFlLayer(AnimatorController animatorController, AacConfiguration configuration, AacFlStateMachine stateMachine, string fullLayerName) { _animatorController = animatorController; _configuration = configuration; @@ -107,7 +107,7 @@ namespace AnimatorAsCode.V0 public AacFlState NewState(string name) { - var lastState = _stateMachine.LastStatePosition(); + var lastState = _stateMachine.LastNodePosition(); var state = _stateMachine.NewState(name, 0, 0).Shift(lastState, 0, 1); return state; } @@ -117,6 +117,16 @@ namespace AnimatorAsCode.V0 return _stateMachine.NewState(name, x, y); } + public AacFlStateMachine NewStateMachine(string name) + { + return _stateMachine.NewStateMachine(name); + } + + public AacFlStateMachine NewStateMachine(string name, int x, int y) + { + return _stateMachine.NewStateMachine(name, x, y); + } + public AacFlTransition AnyTransitionsTo(AacFlState destination) { return _stateMachine.AnyTransitionsTo(destination); @@ -126,6 +136,11 @@ namespace AnimatorAsCode.V0 { return _stateMachine.EntryTransitionsTo(destination); } + + public AacFlEntryTransition EntryTransitionsTo(AacFlStateMachine destination) + { + return _stateMachine.EntryTransitionsTo(destination); + } public AacFlBoolParameter BoolParameter(string parameterName) => _stateMachine.BackingAnimator().BoolParameter(parameterName); public AacFlBoolParameter TriggerParameterAsBool(string parameterName) => _stateMachine.BackingAnimator().TriggerParameter(parameterName); @@ -564,7 +579,7 @@ namespace AnimatorAsCode.V0 } // DEPRECATED: This causes the editor window to glitch by deselecting, which is jarring for experimentation - internal AacStateMachine CreateOrRemakeLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null) + internal AacFlStateMachine CreateOrRemakeLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null) { var originalIndexToPreserveOrdering = FindIndexOf(layerName); if (originalIndexToPreserveOrdering != -1) @@ -583,14 +598,14 @@ namespace AnimatorAsCode.V0 } var layer = TryGetLayer(layerName); - var machinist = new AacStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider); + var machinist = new AacFlStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider); return machinist .WithAnyStatePosition(0, 7) .WithEntryPosition(0, -1) .WithExitPosition(7, -1); } - internal AacStateMachine CreateOrClearLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null) + internal AacFlStateMachine CreateOrClearLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null) { var originalIndexToPreserveOrdering = FindIndexOf(layerName); if (originalIndexToPreserveOrdering != -1) @@ -618,7 +633,7 @@ namespace AnimatorAsCode.V0 _animatorController.layers = layers; var layer = TryGetLayer(layerName); - var machinist = new AacStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider); + var machinist = new AacFlStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider); return machinist .WithAnyStatePosition(0, 7) .WithEntryPosition(0, -1) diff --git a/Framework/Editor/V0/AacAnimatorNode.cs b/Framework/Editor/V0/AacAnimatorNode.cs new file mode 100644 index 0000000..53b6fae --- /dev/null +++ b/Framework/Editor/V0/AacAnimatorNode.cs @@ -0,0 +1,58 @@ +using UnityEngine; +using UnityEditor.Animations; +using System.Linq; + +namespace AnimatorAsCode.V0 +{ + public abstract class AacAnimatorNode + { + protected internal abstract Vector3 GetPosition(); + protected internal abstract void SetPosition(Vector3 position); + } + + public abstract class AacAnimatorNode : AacAnimatorNode where T: AacAnimatorNode + { + protected readonly AacFlStateMachine ParentMachine; + protected readonly IAacDefaultsProvider DefaultsProvider; + + protected AacAnimatorNode(AacFlStateMachine parentMachine, IAacDefaultsProvider defaultsProvider) + { + ParentMachine = parentMachine; + DefaultsProvider = defaultsProvider; + } + + public T LeftOf(T otherNode) => MoveNextTo(otherNode, -1, 0); + public T RightOf(T otherNode) => MoveNextTo(otherNode, 1, 0); + public T Over(T otherNode) => MoveNextTo(otherNode, 0, -1); + public T Under(T otherNode) => MoveNextTo(otherNode, 0, 1); + + public T LeftOf() => MoveNextTo(null, -1, 0); + public T RightOf() => MoveNextTo(null, 1, 0); + public T Over() => MoveNextTo(null, 0, -1); + public T Under() => MoveNextTo(null, 0, 1); + + public T Shift(T otherState, int shiftX, int shiftY) => MoveNextTo(otherState, shiftX, shiftY); + + private T MoveNextTo(T otherStateOrSecondToLastWhenNull, int x, int y) + { + if (otherStateOrSecondToLastWhenNull == null) + { + var siblings = ParentMachine.GetChildNodes(); + var other = siblings[siblings.Count - 2]; + Shift(other.GetPosition(), x, y); + + return (T) this; + } + + Shift(otherStateOrSecondToLastWhenNull.GetPosition(), x, y); + + return (T) this; + } + + public T Shift(Vector3 otherPosition, int shiftX, int shiftY) + { + SetPosition(otherPosition + new Vector3(shiftX * DefaultsProvider.Grid().x, shiftY * DefaultsProvider.Grid().y, 0)); + return (T) this; + } + } +} \ No newline at end of file diff --git a/Framework/Editor/V0/AacAnimatorNode.cs.meta b/Framework/Editor/V0/AacAnimatorNode.cs.meta new file mode 100644 index 0000000..02521c2 --- /dev/null +++ b/Framework/Editor/V0/AacAnimatorNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d998de0b1f138034c9b9237055cd03ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Framework/Editor/V0/AacFlStates.cs b/Framework/Editor/V0/AacFlStates.cs index 56fa50b..3c635a5 100644 --- a/Framework/Editor/V0/AacFlStates.cs +++ b/Framework/Editor/V0/AacFlStates.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using UnityEditor.Animations; using UnityEngine; +using UnityEngine.UIElements; using VRC.SDK3.Avatars.Components; using VRC.SDKBase; @@ -110,25 +112,28 @@ namespace AnimatorAsCode.V0 } } - internal class AacStateMachine + public class AacFlStateMachine : AacAnimatorNode { - private readonly AnimatorStateMachine _machine; + public readonly AnimatorStateMachine Machine; private readonly AnimationClip _emptyClip; private readonly AacBackingAnimator _backingAnimator; - private readonly IAacDefaultsProvider _defaultsProvider; private readonly float _gridShiftX; private readonly float _gridShiftY; - public AacStateMachine(AnimatorStateMachine machine, AnimationClip emptyClip, AacBackingAnimator backingAnimator, IAacDefaultsProvider defaultsProvider) + private readonly List _childNodes; + + internal AacFlStateMachine(AnimatorStateMachine machine, AnimationClip emptyClip, AacBackingAnimator backingAnimator, IAacDefaultsProvider defaultsProvider, AacFlStateMachine parent = null) + : base(parent, defaultsProvider) { - _machine = machine; + Machine = machine; _emptyClip = emptyClip; _backingAnimator = backingAnimator; - _defaultsProvider = defaultsProvider; var grid = defaultsProvider.Grid(); _gridShiftX = grid.x; _gridShiftY = grid.y; + + _childNodes = new List(); } internal AacBackingAnimator BackingAnimator() @@ -136,40 +141,87 @@ namespace AnimatorAsCode.V0 return _backingAnimator; } - public AacStateMachine WithEntryPosition(int x, int y) + public AacFlStateMachine NewStateMachine(string name) { - _machine.entryPosition = GridPosition(x, y); + var lastState = LastNodePosition(); + return NewStateMachine(name, 0, 0).Shift(lastState, 0, 1); + } + + public AacFlStateMachine NewStateMachine(string name, int x, int y) + { + var stateMachine = Machine.AddStateMachine(name, GridPosition(x, y)); + var aacMachine = new AacFlStateMachine(stateMachine, _emptyClip, _backingAnimator, DefaultsProvider, this); + _childNodes.Add(aacMachine); + return aacMachine; + } + + public AacFlStateMachine WithEntryPosition(int x, int y) + { + Machine.entryPosition = GridPosition(x, y); return this; } - public AacStateMachine WithExitPosition(int x, int y) + public AacFlStateMachine WithExitPosition(int x, int y) { - _machine.exitPosition = GridPosition(x, y); + Machine.exitPosition = GridPosition(x, y); return this; } - public AacStateMachine WithAnyStatePosition(int x, int y) + public AacFlStateMachine WithAnyStatePosition(int x, int y) { - _machine.anyStatePosition = GridPosition(x, y); + Machine.anyStatePosition = GridPosition(x, y); return this; } + public AacFlState NewState(string name) + { + var lastState = LastNodePosition(); + return NewState(name, 0, 0).Shift(lastState, 0, 1); + } + public AacFlState NewState(string name, int x, int y) { - var state = _machine.AddState(name, GridPosition(x, y)); - _defaultsProvider.ConfigureState(state, _emptyClip); - - return new AacFlState(state, _machine, _defaultsProvider); + var state = Machine.AddState(name, GridPosition(x, y)); + DefaultsProvider.ConfigureState(state, _emptyClip); + var aacState = new AacFlState(state, this, DefaultsProvider); + _childNodes.Add(aacState); + return aacState; } public AacFlTransition AnyTransitionsTo(AacFlState destination) { - return AnyTransition(destination, _machine); + return AnyTransition(destination, Machine); } public AacFlEntryTransition EntryTransitionsTo(AacFlState destination) { - return EntryTransition(destination, _machine); + return EntryTransition(destination, Machine); + } + + public AacFlEntryTransition EntryTransitionsTo(AacFlStateMachine destination) + { + return EntryTransition(destination, Machine); + } + + public AacFlEntryTransition TransitionsFromEntry() + { + return EntryTransition(this, ParentMachine.Machine); + } + + + public AacFlNewTransitionContinuation TransitionsTo(AacFlState destination) + { + return new AacFlNewTransitionContinuation(ParentMachine.Machine.AddStateMachineTransition(Machine, destination.State), ParentMachine.Machine, Machine, destination.State); + } + + public AacFlNewTransitionContinuation TransitionsTo(AacFlStateMachine destination) + { + return new AacFlNewTransitionContinuation(ParentMachine.Machine.AddStateMachineTransition(Machine, destination.Machine), ParentMachine.Machine, Machine, destination.Machine); + } + + public AacFlNewTransitionContinuation Exits() + { + return new AacFlNewTransitionContinuation(ParentMachine.Machine.AddStateMachineExitTransition(Machine), ParentMachine.Machine, Machine, null); } private AacFlTransition AnyTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine) @@ -179,7 +231,7 @@ namespace AnimatorAsCode.V0 private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { - _defaultsProvider.ConfigureTransition(transition); + DefaultsProvider.ConfigureTransition(transition); return transition; } @@ -188,87 +240,67 @@ namespace AnimatorAsCode.V0 return new AacFlEntryTransition(animatorStateMachine.AddEntryTransition(destination.State), animatorStateMachine, null, destination.State); } - internal Vector3 LastStatePosition() + private AacFlEntryTransition EntryTransition(AacFlStateMachine destination, AnimatorStateMachine animatorStateMachine) { - return _machine.states.Length > 0 ? _machine.states.Last().position : Vector3.zero; + return new AacFlEntryTransition(animatorStateMachine.AddEntryTransition(destination.Machine), animatorStateMachine, null, destination.Machine); + } + + internal Vector3 LastNodePosition() + { + return _childNodes.LastOrDefault()?.GetPosition() ?? Vector3.zero; } private Vector3 GridPosition(int x, int y) { - return new Vector3(x * _gridShiftX , y * _gridShiftY, 0); + return new Vector3(x * _gridShiftX, y * _gridShiftY, 0); } - public AacStateMachine WithDefaultState(AacFlState newDefaultState) + internal IReadOnlyList GetChildNodes() { - _machine.defaultState = newDefaultState.State; + return _childNodes; + } + + protected internal override Vector3 GetPosition() + { + return ParentMachine.Machine.stateMachines.First(x => x.stateMachine == Machine).position; + } + + protected internal override void SetPosition(Vector3 position) + { + var stateMachines = ParentMachine.Machine.stateMachines; + for (var i = 0; i < stateMachines.Length; i++) + { + var m = stateMachines[i]; + if (m.stateMachine == Machine) + { + m.position = position; + stateMachines[i] = m; + break; + } + } + ParentMachine.Machine.stateMachines = stateMachines; + } + + public AacFlStateMachine WithDefaultState(AacFlState newDefaultState) + { + Machine.defaultState = newDefaultState.State; return this; } } - public class AacFlState + public class AacFlState : AacAnimatorNode { public readonly AnimatorState State; private readonly AnimatorStateMachine _machine; - private readonly IAacDefaultsProvider _defaultsProvider; - private readonly AacBackingAnimator _backingAnimator; private VRCAvatarParameterDriver _driver; private VRCAnimatorTrackingControl _tracking; private VRCAnimatorLocomotionControl _locomotionControl; private VRCAnimatorTemporaryPoseSpace _temporaryPoseSpace; - public AacFlState(AnimatorState state, AnimatorStateMachine machine, IAacDefaultsProvider defaultsProvider) + public AacFlState(AnimatorState state, AacFlStateMachine parentMachine, IAacDefaultsProvider defaultsProvider) : base(parentMachine, defaultsProvider) { State = state; - _machine = machine; - _defaultsProvider = defaultsProvider; - } - - public AacFlState LeftOf(AacFlState otherState) => MoveNextTo(otherState, -1, 0); - public AacFlState RightOf(AacFlState otherState) => MoveNextTo(otherState, 1, 0); - public AacFlState Over(AacFlState otherState) => MoveNextTo(otherState, 0, -1); - public AacFlState Under(AacFlState otherState) => MoveNextTo(otherState, 0, 1); - - public AacFlState LeftOf() => MoveNextTo(null, -1, 0); - public AacFlState RightOf() => MoveNextTo(null, 1, 0); - public AacFlState Over() => MoveNextTo(null, 0, -1); - public AacFlState Under() => MoveNextTo(null, 0, 1); - - public AacFlState Shift(AacFlState otherState, int shiftX, int shiftY) => MoveNextTo(otherState, shiftX, shiftY); - - private AacFlState MoveNextTo(AacFlState otherStateOrSecondToLastWhenNull, int x, int y) - { - if (otherStateOrSecondToLastWhenNull == null) - { - var other = _machine.states[_machine.states.Length - 2]; - Shift(other.position, x, y); - - return this; - } - else - { - var other = _machine.states.First(animatorState => animatorState.state == otherStateOrSecondToLastWhenNull.State); - Shift(other.position, x, y); - - return this; - } - } - - public AacFlState Shift(Vector3 otherPosition, int shiftX, int shiftY) - { - var states = _machine.states; - for (var index = 0; index < states.Length; index++) - { - var childAnimatorState = states[index]; - if (childAnimatorState.state == State) - { - var cms = childAnimatorState; - cms.position = otherPosition + new Vector3(shiftX * _defaultsProvider.Grid().x, shiftY * _defaultsProvider.Grid().y, 0); - states[index] = cms; - break; - } - } - _machine.states = states; - return this; + _machine = parentMachine.Machine; } public AacFlState WithAnimation(Motion clip) @@ -288,6 +320,11 @@ namespace AnimatorAsCode.V0 return new AacFlTransition(ConfigureTransition(State.AddTransition(destination.State)), _machine, State, destination.State); } + public AacFlTransition TransitionsTo(AacFlStateMachine destination) + { + return new AacFlTransition(State.AddTransition(destination.Machine), _machine, State, destination.Machine); + } + public AacFlTransition TransitionsFromAny() { return new AacFlTransition(ConfigureTransition(_machine.AddAnyStateTransition(State)), _machine, null, State); @@ -312,7 +349,7 @@ namespace AnimatorAsCode.V0 private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { - _defaultsProvider.ConfigureTransition(transition); + DefaultsProvider.ConfigureTransition(transition); return transition; } @@ -679,13 +716,34 @@ namespace AnimatorAsCode.V0 return this; } + + protected internal override Vector3 GetPosition() + { + return _machine.states.First(x => x.state == State).position; + } + + protected internal override void SetPosition(Vector3 position) + { + var states = _machine.states; + for (var i = 0; i < states.Length; i++) + { + var m = states[i]; + if (m.state == State) + { + m.position = position; + states[i] = m; + break; + } + } + _machine.states = states; + } } public class AacFlTransition : AacFlNewTransitionContinuation { private readonly AnimatorStateTransition _transition; - public AacFlTransition(AnimatorStateTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) + public AacFlTransition(AnimatorStateTransition transition, AnimatorStateMachine machine, AacFlTransitionEndpoint sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { _transition = transition; } @@ -759,7 +817,7 @@ namespace AnimatorAsCode.V0 public class AacFlEntryTransition : AacFlNewTransitionContinuation { - public AacFlEntryTransition(AnimatorTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) + public AacFlEntryTransition(AnimatorTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } } @@ -794,10 +852,10 @@ namespace AnimatorAsCode.V0 { public readonly AnimatorTransitionBase Transition; private readonly AnimatorStateMachine _machine; - private readonly AnimatorState _sourceNullableIfAny; - private readonly AnimatorState _destinationNullableIfExits; + private readonly AacFlTransitionEndpoint _sourceNullableIfAny; + private readonly AacFlTransitionEndpoint _destinationNullableIfExits; - public AacFlNewTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) + public AacFlNewTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacFlTransitionEndpoint sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits) { Transition = transition; _machine = machine; @@ -878,7 +936,7 @@ namespace AnimatorAsCode.V0 public class AacFlTransitionContinuation : AacFlTransitionContinuationAbstractWithOr { - public AacFlTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) + public AacFlTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacFlTransitionEndpoint sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } @@ -918,7 +976,7 @@ namespace AnimatorAsCode.V0 { private readonly List _pendingContinuations; - public AacFlMultiTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits, List pendingContinuations) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) + public AacFlMultiTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacFlTransitionEndpoint sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits, List pendingContinuations) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { _pendingContinuations = pendingContinuations; } @@ -965,7 +1023,7 @@ namespace AnimatorAsCode.V0 public class AacFlTransitionContinuationOnlyOr : AacFlTransitionContinuationAbstractWithOr { - public AacFlTransitionContinuationOnlyOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) + public AacFlTransitionContinuationOnlyOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacFlTransitionEndpoint sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } } @@ -974,10 +1032,10 @@ namespace AnimatorAsCode.V0 { protected readonly AnimatorTransitionBase Transition; private readonly AnimatorStateMachine _machine; - private readonly AnimatorState _sourceNullableIfAny; - private readonly AnimatorState _destinationNullableIfExits; + private readonly AacFlTransitionEndpoint _sourceNullableIfAny; + private readonly AacFlTransitionEndpoint _destinationNullableIfExits; - public AacFlTransitionContinuationAbstractWithOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) + public AacFlTransitionContinuationAbstractWithOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacFlTransitionEndpoint sourceNullableIfAny, AacFlTransitionEndpoint destinationNullableIfExits) { Transition = transition; _machine = machine; @@ -1024,25 +1082,65 @@ namespace AnimatorAsCode.V0 } else { - newTransition = _machine.AddEntryTransition(_destinationNullableIfExits); + if (_sourceNullableIfAny == null) + { + if (_destinationNullableIfExits.TryGetState(out var state)) + newTransition = _machine.AddEntryTransition(state); + else if (_destinationNullableIfExits.TryGetStateMachine(out var stateMachine)) + newTransition = _machine.AddEntryTransition(stateMachine); + else + throw new InvalidOperationException("_destinationNullableIfExits is not null but does not contain an AnimatorState or AnimatorStateMachine"); + } + // source will never be a state if we're cloning an AnimatorTransition + else if (_sourceNullableIfAny.TryGetStateMachine(out var stateMachine)) + { + if (_destinationNullableIfExits == null) + newTransition = _machine.AddStateMachineExitTransition(stateMachine); + else if (_destinationNullableIfExits.TryGetState(out var destinationState)) + newTransition = _machine.AddStateMachineTransition(stateMachine, destinationState); + else if (_destinationNullableIfExits.TryGetStateMachine(out var destinationStateMachine)) + newTransition = _machine.AddStateMachineTransition(stateMachine, destinationStateMachine); + else + throw new InvalidOperationException("_destinationNullableIfExits is not null but does not contain an AnimatorState or AnimatorStateMachine"); + } + else + throw new InvalidOperationException("_sourceNullableIfAny is not null but does not contain an AnimatorStateMachine"); } - return newTransition; } private AnimatorStateTransition NewTransition() { + AnimatorState state; + AnimatorStateMachine stateMachine; + if (_sourceNullableIfAny == null) { - return _machine.AddAnyStateTransition(_destinationNullableIfExits); + if (_destinationNullableIfExits.TryGetState(out state)) + return _machine.AddAnyStateTransition(state); + if (_destinationNullableIfExits.TryGetStateMachine(out stateMachine)) + return _machine.AddAnyStateTransition(stateMachine); + throw new InvalidOperationException("Transition has no source nor destination."); } - if (_destinationNullableIfExits == null) + // source will never be a state machine if we're cloning an AnimatorStateTransition + if (_sourceNullableIfAny.TryGetState(out var sourceState)) { - return _sourceNullableIfAny.AddExitTransition(); - } + if (_destinationNullableIfExits == null) + { + return sourceState.AddExitTransition(); + } - return _sourceNullableIfAny.AddTransition(_destinationNullableIfExits); + if (_destinationNullableIfExits.TryGetState(out state)) + { + return sourceState.AddTransition(state); + } + + if (_destinationNullableIfExits.TryGetStateMachine(out stateMachine)) + return sourceState.AddTransition(stateMachine); + throw new InvalidOperationException("_destinationNullableIfExits is not null but does not contain an AnimatorState or AnimatorStateMachine"); + } + throw new InvalidOperationException("_sourceNullableIfAny is not null but does not contain an AnimatorState"); } } @@ -1072,4 +1170,42 @@ namespace AnimatorAsCode.V0 return this; } } + + public class AacFlTransitionEndpoint + { + private readonly AnimatorState _state; + private readonly AnimatorStateMachine _stateMachine; + + public AacFlTransitionEndpoint([NotNull] AnimatorState state) + { + _state = state; + } + + public AacFlTransitionEndpoint([NotNull] AnimatorStateMachine stateMachine) + { + _stateMachine = stateMachine; + } + + public static implicit operator AacFlTransitionEndpoint([NotNull] AnimatorState state) + { + return new AacFlTransitionEndpoint(state); + } + + public static implicit operator AacFlTransitionEndpoint([NotNull] AnimatorStateMachine stateMachine) + { + return new AacFlTransitionEndpoint(stateMachine); + } + + public bool TryGetState(out AnimatorState state) + { + state = _state; + return _state != null; + } + + public bool TryGetStateMachine(out AnimatorStateMachine stateMachine) + { + stateMachine = _stateMachine; + return _stateMachine != null; + } + } }