using System; using System.Collections.Generic; using System.Linq; using UnityEditor.Animations; using UnityEngine; // ReSharper disable once CheckNamespace namespace AnimatorAsCode.V1 { public class AacBackingAnimator { private readonly AacAnimatorGenerator _generator; public AacBackingAnimator(AacAnimatorGenerator animatorGenerator) { _generator = animatorGenerator; } public AacFlBoolParameter BoolParameter(string parameterName) { var result = AacFlBoolParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlBoolParameter TriggerParameter(string parameterName) { var result = AacFlBoolParameter.Internally(parameterName); _generator.CreateTriggerParamsAsNeeded(result); return result; } public AacFlFloatParameter FloatParameter(string parameterName) { var result = AacFlFloatParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlIntParameter IntParameter(string parameterName) { var result = AacFlIntParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlEnumIntParameter EnumParameter(string parameterName) where TEnum : Enum { var result = AacFlEnumIntParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames) { var result = AacFlBoolParameterGroup.Internally(parameterNames); _generator.CreateParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlBoolParameterGroup TriggerParameters(params string[] parameterNames) { var result = AacFlBoolParameterGroup.Internally(parameterNames); _generator.CreateTriggerParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames) { var result = AacFlFloatParameterGroup.Internally(parameterNames); _generator.CreateParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlIntParameterGroup IntParameters(params string[] parameterNames) { var result = AacFlIntParameterGroup.Internally(parameterNames); _generator.CreateParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters) { var result = AacFlBoolParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateParamsAsNeeded(parameters); return result; } public AacFlBoolParameterGroup TriggerParameters(params AacFlBoolParameter[] parameters) { var result = AacFlBoolParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateTriggerParamsAsNeeded(parameters); return result; } public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters) { var result = AacFlFloatParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateParamsAsNeeded(parameters); return result; } public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters) { var result = AacFlIntParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateParamsAsNeeded(parameters); return result; } } public class AacFlStateMachine : AacAnimatorNode { /// Exposes the underlying Unity AnimatorStateMachine object of this state 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; private readonly List _childNodes; private readonly HashSet _stateNames; internal AacFlStateMachine(AnimatorStateMachine machine, AnimationClip emptyClip, AacBackingAnimator backingAnimator, IAacDefaultsProvider defaultsProvider, Transform animatorRoot, AacFlStateMachine parent = null) : base(parent, defaultsProvider, animatorRoot) { Machine = machine; _emptyClip = emptyClip; _backingAnimator = backingAnimator; _defaultsProvider = defaultsProvider; _stateNames = new HashSet(); var grid = defaultsProvider.Grid(); _gridShiftX = grid.x; _gridShiftY = grid.y; _childNodes = new List(); } /// FOR USE ONLY BY EXTENSION FUNCTIONS:
/// Exposes the internal backing animator. public AacBackingAnimator InternalBackingAnimator() { return _backingAnimator; } /// Create a new state machine, initially positioned below the last generated state of this layer.
/// 🔺 If the name is already used, a number will be appended at the end. public AacFlStateMachine NewSubStateMachine(string name) { var lastState = LastNodePosition(); return NewSubStateMachine(name, 0, 0).Shift(lastState, 0, 1); } /// Create a new state machine 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.
/// 🔺 If the name is already used, a number will be appended at the end. public AacFlStateMachine NewSubStateMachine(string name, int x, int y) { var stateMachine = AacInternals.NoUndo(Machine, () => Machine.AddStateMachine(EnsureNameIsDeduplicated(name), GridPosition(x, y))); var aacMachine = new AacFlStateMachine(stateMachine, _emptyClip, _backingAnimator, DefaultsProvider, AnimatorRoot, this); _defaultsProvider.ConfigureStateMachine(stateMachine); _childNodes.Add(aacMachine); return aacMachine; } public AacFlStateMachine WithEntryPosition(int x, int y) { Machine.entryPosition = GridPosition(x, y); return this; } public AacFlStateMachine WithExitPosition(int x, int y) { Machine.exitPosition = GridPosition(x, y); return this; } public AacFlStateMachine WithAnyStatePosition(int x, int y) { Machine.anyStatePosition = GridPosition(x, y); return this; } public AacFlStateMachine WithParentStateMachinePosition(int x, int y) { Machine.parentStateMachinePosition = GridPosition(x, y); return this; } /// Create a new state, initially positioned below the last generated state of this layer.
/// 🔺 If the name is already used, a number will be appended at the end. public AacFlState NewState(string name) { var lastState = LastNodePosition(); return NewState(name, 0, 0).Shift(lastState, 0, 1); } /// 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.
/// 🔺 If the name is already used, a number will be appended at the end. public AacFlState NewState(string name, int x, int y) { var state = AacInternals.NoUndo(Machine, () => Machine.AddState(EnsureNameIsDeduplicated(name), GridPosition(x, y))); DefaultsProvider.ConfigureState(state, _emptyClip); var aacState = new AacFlState(state, this, DefaultsProvider, AnimatorRoot); _childNodes.Add(aacState); return aacState; } private string EnsureNameIsDeduplicated(string name) { var suffix = 0; var stateName = name; while (_stateNames.Contains(stateName)) { stateName = name + " " + (++suffix); } _stateNames.Add(stateName); return stateName; } /// Create a transition from Any to the `destination` state. public AacFlTransition AnyTransitionsTo(AacFlState destination) { return AnyTransition(destination, Machine); } /// Create a transition from Any to the `destination` state machine. public AacFlTransition AnyTransitionsTo(AacFlStateMachine destination) { return AnyTransition(destination, Machine); } /// Create a transition from the Entry to the `destination` state. public AacFlEntryTransition EntryTransitionsTo(AacFlState destination) { return EntryTransition(destination, Machine); } /// Create a transition from the Entry to the `destination` state 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(AacInternals.NoUndo(ParentMachine.Machine, () => ParentMachine.Machine.AddStateMachineTransition(Machine, destination.State)), ParentMachine.Machine, Machine, destination.State); } public AacFlNewTransitionContinuation TransitionsTo(AacFlStateMachine destination) { return new AacFlNewTransitionContinuation(AacInternals.NoUndo(ParentMachine.Machine, () => ParentMachine.Machine.AddStateMachineTransition(Machine, destination.Machine)), ParentMachine.Machine, Machine, destination.Machine); } public AacFlNewTransitionContinuation Restarts() { return new AacFlNewTransitionContinuation(AacInternals.NoUndo(ParentMachine.Machine, () => ParentMachine.Machine.AddStateMachineTransition(Machine, Machine)), ParentMachine.Machine, Machine, Machine); } public AacFlNewTransitionContinuation Exits() { return new AacFlNewTransitionContinuation(AacInternals.NoUndo(ParentMachine.Machine, () => ParentMachine.Machine.AddStateMachineExitTransition(Machine)), ParentMachine.Machine, Machine, null); } private AacFlTransition AnyTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine) { return new AacFlTransition(ConfigureTransition(AacInternals.NoUndo(animatorStateMachine, () => animatorStateMachine.AddAnyStateTransition(destination.State))), animatorStateMachine, null, destination.State); } private AacFlTransition AnyTransition(AacFlStateMachine destination, AnimatorStateMachine animatorStateMachine) { return new AacFlTransition(ConfigureTransition(AacInternals.NoUndo(animatorStateMachine, () => animatorStateMachine.AddAnyStateTransition(destination.Machine))), animatorStateMachine, null, destination.Machine); } private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { DefaultsProvider.ConfigureTransition(transition); return transition; } private AacFlEntryTransition EntryTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine) { return new AacFlEntryTransition(AacInternals.NoUndo(animatorStateMachine, () => animatorStateMachine.AddEntryTransition(destination.State)), animatorStateMachine, null, destination.State); } private AacFlEntryTransition EntryTransition(AacFlStateMachine destination, AnimatorStateMachine animatorStateMachine) { return new AacFlEntryTransition(AacInternals.NoUndo(animatorStateMachine, () => animatorStateMachine.AddEntryTransition(destination.Machine)), animatorStateMachine, null, destination.Machine); } internal Vector3 LastNodePosition() { return _childNodes.LastOrDefault()?.GetPosition() ?? Vector3.right * _gridShiftX * 2; } private Vector3 GridPosition(int x, int y) { return new Vector3(x * _gridShiftX, y * _gridShiftY, 0); } internal IReadOnlyList GetChildNodes() { 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 override TBehaviour EnsureBehaviour() { foreach (var behaviour in Machine.behaviours) if (behaviour is TBehaviour myBehaviour) return myBehaviour; return AacInternals.NoUndo(Machine, () => Machine.AddStateMachineBehaviour()); } public override TBehaviour CreateNewBehaviour() { return AacInternals.NoUndo(Machine, () => Machine.AddStateMachineBehaviour()); } } public class AacFlState : AacAnimatorNode { public readonly AnimatorState State; private readonly AnimatorStateMachine _machine; public AacFlState(AnimatorState state, AacFlStateMachine parentMachine, IAacDefaultsProvider defaultsProvider, Transform animatorRoot) : base(parentMachine, defaultsProvider, animatorRoot) { State = state; _machine = parentMachine.Machine; } public AacFlState WithAnimation(Motion clip) { State.motion = clip; return this; } public AacFlState WithAnimation(AacFlClip clip) { State.motion = clip.Clip; return this; } public AacFlState WithAnimation(AacFlBlendTree blendTree) { State.motion = blendTree.BlendTree; return this; } /// Create a new transition from this state to the `destination` state. public AacFlTransition TransitionsTo(AacFlState destination) { return new AacFlTransition(ConfigureTransition(AacInternals.NoUndo(State, () => State.AddTransition(destination.State))), _machine, State, destination.State); } /// Create a new transition from this state to the `destination` state machine. public AacFlTransition TransitionsTo(AacFlStateMachine destination) { return new AacFlTransition(ConfigureTransition(AacInternals.NoUndo(State, () => State.AddTransition(destination.Machine))), _machine, State, destination.Machine); } /// Create a new transition from Any to this state. public AacFlTransition TransitionsFromAny() { return new AacFlTransition(ConfigureTransition(AacInternals.NoUndo(State, () => _machine.AddAnyStateTransition(State))), _machine, null, State); } /// Create a new transition from Entry to this state. Note that the first created state is the default state, so generally this function does not need to be invoked onto the first created state.
/// Calling this function will not define this state to be the default state. public AacFlEntryTransition TransitionsFromEntry() { return new AacFlEntryTransition(AacInternals.NoUndo(State, () => _machine.AddEntryTransition(State)), _machine, null, State); } /// Create a transition with no exit time to the `destination` state.
/// Calling this function does not return the transition. public AacFlState AutomaticallyMovesTo(AacFlState destination) { var transition = ConfigureTransition(AacInternals.NoUndo(State, () => State.AddTransition(destination.State))); transition.hasExitTime = true; return this; } /// Create a transition with no exit time to the `destination` state machine.
/// Calling this function does not return the transition. public AacFlState AutomaticallyMovesTo(AacFlStateMachine destination) { var transition = ConfigureTransition(AacInternals.NoUndo(State, () => State.AddTransition(destination.Machine))); transition.hasExitTime = true; return this; } /// Create a transition from this state to the exit. public AacFlTransition Exits() { return new AacFlTransition(ConfigureTransition(AacInternals.NoUndo(State, () => State.AddExitTransition())), _machine, State, null); } private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { DefaultsProvider.ConfigureTransition(transition); return transition; } /// Set Write Defaults. If you need to do this to many states, consider changing the AacConfiguration DefaultsProvider when creating the AnimatorAsCode instance. public AacFlState WithWriteDefaultsSetTo(bool shouldWriteDefaults) { State.writeDefaultValues = shouldWriteDefaults; return this; } /// Obsolete. Use `WithMotionTime()` instead.
/// Set the Motion Time to a parameter. This was formerly known as Normalized Time.
///
/// This function is preserved for compatibility.
/// The behaviour is identical to `WithMotionTime()`. [Obsolete("This function was renamed to WithMotionTime(AacFlFloatParameter)")] public AacFlState MotionTime(AacFlFloatParameter floatParam) { return WithMotionTime(floatParam); } /// Set the Motion Time to a parameter. This was formerly known as Normalized Time. public AacFlState WithMotionTime(AacFlFloatParameter floatParam) { State.timeParameterActive = true; State.timeParameter = floatParam.Name; return this; } /// Set the Cycle Offset to a parameter. public AacFlState WithCycleOffset(AacFlFloatParameter floatParam) { State.cycleOffsetParameterActive = true; State.cycleOffsetParameter = floatParam.Name; return this; } /// Set the Cycle Offset to a specific value. public AacFlState WithCycleOffsetSetTo(float cycleOffset) { State.cycleOffsetParameterActive = false; State.cycleOffset = cycleOffset; return this; } /// Set the Speed to a parameter. public AacFlState WithSpeed(AacFlFloatParameter parameter) { State.speedParameterActive = true; State.speedParameter = parameter.Name; return this; } /// Set the Speed to a specific value. public AacFlState WithSpeedSetTo(float speed) { State.speedParameterActive = false; State.speed = speed; 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 override TBehaviour EnsureBehaviour() { foreach (var behaviour in State.behaviours) if (behaviour is TBehaviour myBehaviour) return myBehaviour; return AacInternals.NoUndo(State, () => State.AddStateMachineBehaviour()); } public override TBehaviour CreateNewBehaviour() { return AacInternals.NoUndo(State, () => State.AddStateMachineBehaviour()); } } public class AacFlTransition : AacFlNewTransitionContinuation { private readonly AnimatorStateTransition _transition; public AacFlTransition(AnimatorStateTransition transition, AnimatorStateMachine machine, AacTransitionEndpoint sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { _transition = transition; } /// Set interruption source to be Source. public AacFlTransition WithSourceInterruption() { _transition.interruptionSource = TransitionInterruptionSource.Source; return this; } /// Set interruption source set to that value. public AacFlTransition WithInterruption(TransitionInterruptionSource interruptionSource) { _transition.interruptionSource = interruptionSource; return this; } /// Set a fixed transition duration in seconds. public AacFlTransition WithTransitionDurationSeconds(float transitionDuration) { _transition.duration = transitionDuration; return this; } /// Enable ordered interruption. public AacFlTransition WithOrderedInterruption() { _transition.orderedInterruption = true; return this; } /// Disable ordered interruption. public AacFlTransition WithNoOrderedInterruption() { _transition.orderedInterruption = false; return this; } /// Enable transition to self. public AacFlTransition WithTransitionToSelf() { _transition.canTransitionToSelf = true; return this; } /// Disable transition to self. public AacFlTransition WithNoTransitionToSelf() { _transition.canTransitionToSelf = false; return this; } /// Set an exit time at 1, where the animation finishes. public AacFlTransition AfterAnimationFinishes() { _transition.hasExitTime = true; _transition.exitTime = 1; return this; } /// Set an exit time at 0, so that it may transition almost immediately. public AacFlTransition Automatically() { _transition.hasExitTime = true; _transition.exitTime = 0; return this; } // FIXME API: Percent is misnomer /// Set the exit time at a specific normalized amount. public AacFlTransition AfterAnimationIsAtLeastAtPercent(float exitTimeNormalized) { _transition.hasExitTime = true; _transition.exitTime = exitTimeNormalized; return this; } // FIXME API: Percent is misnomer /// Set a non-fixed transition duration in a normalized amount. public AacFlTransition WithTransitionDurationPercent(float transitionDurationNormalized) { _transition.hasFixedDuration = false; _transition.duration = transitionDurationNormalized; return this; } } public class AacFlEntryTransition : AacFlNewTransitionContinuation { public AacFlEntryTransition(AnimatorTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } } public interface IAacFlCondition { void ApplyTo(AacFlCondition appender); } public interface IAacFlOrCondition { List ApplyTo(AacFlNewTransitionContinuation firstContinuation); } public class AacFlCondition { private readonly AnimatorTransitionBase _transition; public AacFlCondition(AnimatorTransitionBase transition) { _transition = transition; } public AacFlCondition Add(string parameter, AnimatorConditionMode mode, float threshold) { AacInternals.NoUndo(_transition, () => _transition.AddCondition(mode, threshold, parameter)); return this; } } public class AacFlNewTransitionContinuation { public readonly AnimatorTransitionBase Transition; private readonly AnimatorStateMachine _machine; private readonly AacTransitionEndpoint _sourceNullableIfAny; private readonly AacTransitionEndpoint _destinationNullableIfExits; public AacFlNewTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacTransitionEndpoint sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits) { Transition = transition; _machine = machine; _sourceNullableIfAny = sourceNullableIfAny; _destinationNullableIfExits = destinationNullableIfExits; } /// Adds a condition to the transition. /// /// The settings of the transition can no longer be modified after this point. /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlTransitionContinuation When(IAacFlCondition action) { action.ApplyTo(new AacFlCondition(Transition)); return AsContinuationWithOr(); } /// Applies a series of conditions to this transition, but this series of conditions cannot include an Or operator. public AacFlTransitionContinuation When(Action actionsWithoutOr) { actionsWithoutOr(new AacFlTransitionContinuationWithoutOr(Transition)); return AsContinuationWithOr(); } /// Applies a series of conditions, and this series may contain Or operators. However, the result can not be followed by an And operator. It can only be an Or operator. public AacFlTransitionContinuationOnlyOr When(Action actionsWithOr) { actionsWithOr(this); return AsContinuationOnlyOr(); } /// Applies a series of conditions, and this series may contain Or operators. All And operators that follow will apply to all the conditions generated by this series, until the next Or operator. public AacFlMultiTransitionContinuation When(IAacFlOrCondition actionsWithOr) { var pendingContinuations = actionsWithOr.ApplyTo(this); return new AacFlMultiTransitionContinuation(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits, pendingContinuations); } /// Provides a handle to start defining conditions using the And operator, for use in loops. The Or operator may be invoked at any point. public AacFlTransitionContinuation WhenConditions() { return AsContinuationWithOr(); } private AacFlTransitionContinuation AsContinuationWithOr() { return new AacFlTransitionContinuation(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits); } private AacFlTransitionContinuationOnlyOr AsContinuationOnlyOr() { return new AacFlTransitionContinuationOnlyOr(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits); } } public class AacFlTransitionContinuation : AacFlTransitionContinuationAbstractWithOr { public AacFlTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacTransitionEndpoint sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } /// Adds an additional condition to the transition that requires all preceding conditions to be true. /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlTransitionContinuation And(IAacFlCondition action) { action.ApplyTo(new AacFlCondition(Transition)); return this; } /// /// Applies a series of conditions to this transition. The conditions cannot include an Or operator. /// /// /// public AacFlTransitionContinuation And(Action actionsWithoutOr) { actionsWithoutOr(new AacFlTransitionContinuationWithoutOr(Transition)); return this; } } public class AacFlMultiTransitionContinuation : AacFlTransitionContinuationAbstractWithOr { private readonly List _pendingContinuations; public AacFlMultiTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacTransitionEndpoint sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits, List pendingContinuations) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { _pendingContinuations = pendingContinuations; } /// Adds an additional condition to these transitions that requires all preceding conditions to be true. /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlMultiTransitionContinuation And(IAacFlCondition action) { foreach (var pendingContinuation in _pendingContinuations) { pendingContinuation.And(action); } return this; } /// /// Applies a series of conditions to these transitions. The conditions cannot include an Or operator. /// /// /// public AacFlMultiTransitionContinuation And(Action actionsWithoutOr) { foreach (var pendingContinuation in _pendingContinuations) { pendingContinuation.And(actionsWithoutOr); } return this; } } public class AacFlTransitionContinuationOnlyOr : AacFlTransitionContinuationAbstractWithOr { public AacFlTransitionContinuationOnlyOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacTransitionEndpoint sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } } public abstract class AacFlTransitionContinuationAbstractWithOr { protected readonly AnimatorTransitionBase Transition; private readonly AnimatorStateMachine _machine; private readonly AacTransitionEndpoint _sourceNullableIfAny; private readonly AacTransitionEndpoint _destinationNullableIfExits; public AacFlTransitionContinuationAbstractWithOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AacTransitionEndpoint sourceNullableIfAny, AacTransitionEndpoint destinationNullableIfExits) { Transition = transition; _machine = machine; _sourceNullableIfAny = sourceNullableIfAny; _destinationNullableIfExits = destinationNullableIfExits; } /// /// Creates a new transition with identical settings but having no conditions defined yet. /// /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlNewTransitionContinuation Or() { return new AacFlNewTransitionContinuation(NewTransitionFromTemplate(), _machine, _sourceNullableIfAny, _destinationNullableIfExits); } private AnimatorTransitionBase NewTransitionFromTemplate() { AnimatorTransitionBase newTransition; if (Transition is AnimatorStateTransition templateStateTransition) { var stateTransition = NewTransition(); stateTransition.duration = templateStateTransition.duration; stateTransition.offset = templateStateTransition.offset; stateTransition.interruptionSource = templateStateTransition.interruptionSource; stateTransition.orderedInterruption = templateStateTransition.orderedInterruption; stateTransition.exitTime = templateStateTransition.exitTime; stateTransition.hasExitTime = templateStateTransition.hasExitTime; stateTransition.hasFixedDuration = templateStateTransition.hasFixedDuration; stateTransition.canTransitionToSelf = templateStateTransition.canTransitionToSelf; newTransition = stateTransition; } else { if (_sourceNullableIfAny == null) { if (_destinationNullableIfExits.TryGetState(out var state)) newTransition = AacInternals.NoUndo(_machine, () => _machine.AddEntryTransition(state)); else if (_destinationNullableIfExits.TryGetStateMachine(out var stateMachine)) newTransition = AacInternals.NoUndo(_machine, () => _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 = AacInternals.NoUndo(_machine, () => _machine.AddStateMachineExitTransition(stateMachine)); else if (_destinationNullableIfExits.TryGetState(out var destinationState)) newTransition = AacInternals.NoUndo(_machine, () => _machine.AddStateMachineTransition(stateMachine, destinationState)); else if (_destinationNullableIfExits.TryGetStateMachine(out var destinationStateMachine)) newTransition = AacInternals.NoUndo(_machine, () => _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) { if (_destinationNullableIfExits.TryGetState(out state)) return AacInternals.NoUndo(_machine, () => _machine.AddAnyStateTransition(state)); if (_destinationNullableIfExits.TryGetStateMachine(out stateMachine)) return AacInternals.NoUndo(_machine, () => _machine.AddAnyStateTransition(stateMachine)); throw new InvalidOperationException("Transition has no source nor destination."); } // source will never be a state machine if we're cloning an AnimatorStateTransition if (_sourceNullableIfAny.TryGetState(out var sourceState)) { if (_destinationNullableIfExits == null) { return AacInternals.NoUndo(sourceState, () => sourceState.AddExitTransition()); } if (_destinationNullableIfExits.TryGetState(out state)) { return AacInternals.NoUndo(sourceState, () => sourceState.AddTransition(state)); } if (_destinationNullableIfExits.TryGetStateMachine(out stateMachine)) { return AacInternals.NoUndo(sourceState, () => 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"); } } public class AacFlTransitionContinuationWithoutOr { private readonly AnimatorTransitionBase _transition; public AacFlTransitionContinuationWithoutOr(AnimatorTransitionBase transition) { _transition = transition; } public AacFlTransitionContinuationWithoutOr And(IAacFlCondition action) { action.ApplyTo(new AacFlCondition(_transition)); return this; } /// /// Applies a series of conditions to this transition. The conditions cannot include an Or operator. /// /// /// public AacFlTransitionContinuationWithoutOr AndWhenever(Action action) { action(this); return this; } } public class AacTransitionEndpoint { private readonly AnimatorState _state; private readonly AnimatorStateMachine _stateMachine; public AacTransitionEndpoint(AnimatorState state) { _state = state; } public AacTransitionEndpoint(AnimatorStateMachine stateMachine) { _stateMachine = stateMachine; } public static implicit operator AacTransitionEndpoint(AnimatorState state) { return new AacTransitionEndpoint(state); } public static implicit operator AacTransitionEndpoint(AnimatorStateMachine stateMachine) { return new AacTransitionEndpoint(stateMachine); } public bool TryGetState(out AnimatorState state) { state = _state; return _state != null; } public bool TryGetStateMachine(out AnimatorStateMachine stateMachine) { stateMachine = _stateMachine; return _stateMachine != null; } } }