Skip to content

Commit

Permalink
Implement animator state machine transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
bdunderscore committed Nov 17, 2024
1 parent 9dea510 commit a8c04fb
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Editor/API/AnimatorServices/AnimatorServicesContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public VirtualAnimatorController this[object key]

if (state.virtualController == null)
{
state.virtualController = VirtualAnimatorController.Clone(_cloneContext, state.originalController);
state.virtualController = _cloneContext.Clone(state.originalController);
}

return state.virtualController;
Expand Down
3 changes: 2 additions & 1 deletion Editor/API/AnimatorServices/CloneContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Immutable;
using UnityEditor.Animations;
using UnityEngine;
using Object = UnityEngine.Object;

namespace nadena.dev.ndmf.animator
{
Expand Down Expand Up @@ -82,7 +83,7 @@ private U GetOrClone<T, U>(T key, Func<CloneContext, T, U> clone) where U : clas
{
_cloneDepth++;

if (key == null) return null;
if (key == null || (key is Object obj && obj == null)) return null;
if (TryGetValue(key, out U value)) return value;
value = clone(this, key);
_clones[key] = value;
Expand Down
24 changes: 24 additions & 0 deletions Editor/API/AnimatorServices/VirtualObjects/VirtualStateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public static VirtualStateMachine Clone(CloneContext context, AnimatorStateMachi
State = context.Clone(s.state),
Position = s.position
}).ToImmutableList();

vsm.StateMachineTransitions = stateMachine.stateMachines
.ToImmutableDictionary(
sm => context.Clone(sm.stateMachine),
sm => stateMachine.GetStateMachineTransitions(sm.stateMachine)
.Select(context.Clone).ToImmutableList()
);
});

return vsm;
Expand All @@ -72,6 +79,7 @@ private VirtualStateMachine(CloneContext context, string name = "")
Behaviours = ImmutableList<StateMachineBehaviour>.Empty;
StateMachines = ImmutableList<VirtualChildStateMachine>.Empty;
States = ImmutableList<VirtualChildState>.Empty;
StateMachineTransitions = ImmutableDictionary<VirtualStateMachine, ImmutableList<VirtualTransition>>.Empty;
}

AnimatorStateMachine ICommitable<AnimatorStateMachine>.Prepare(CommitContext context)
Expand Down Expand Up @@ -108,6 +116,14 @@ void ICommitable<AnimatorStateMachine>.Commit(CommitContext context, AnimatorSta
.ToArray();
// DefaultState will be overwritten if we set it too soon; set it last.
obj.defaultState = context.CommitObject(DefaultState);

foreach (var (sm, transitions) in StateMachineTransitions)
{
obj.SetStateMachineTransitions(
context.CommitObject(sm),
transitions.Select(t => (AnimatorTransition)context.CommitObject(t)).ToArray()
);
}
}

private string _name;
Expand Down Expand Up @@ -190,6 +206,14 @@ public ImmutableList<VirtualChildState> States
set => _states = I(value);
}

private ImmutableDictionary<VirtualStateMachine, ImmutableList<VirtualTransition>> _stateMachineTransitions;

public ImmutableDictionary<VirtualStateMachine, ImmutableList<VirtualTransition>> StateMachineTransitions
{
get => _stateMachineTransitions;
set => _stateMachineTransitions = I(value);
}

private VirtualState _defaultState;

public VirtualState DefaultState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ internal VirtualStateTransition(CloneContext context, AnimatorStateTransition cl
_stateTransition = cloned;
}

public VirtualStateTransition() : base(null, new AnimatorStateTransition())
{
_stateTransition = (AnimatorStateTransition)_transition;
}

public static VirtualStateTransition Clone(
CloneContext context,
AnimatorStateTransition transition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ internal VirtualTransition(CloneContext context, AnimatorTransitionBase cloned)
{
}

public VirtualTransition() : base(null, new AnimatorTransition())
{
}

public static VirtualTransition Clone(
CloneContext context,
AnimatorTransition transition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ namespace nadena.dev.ndmf.animator
{
public class VirtualTransitionBase : VirtualNode, ICommitable<AnimatorTransitionBase>, IDisposable
{
private AnimatorTransitionBase _transition;
protected AnimatorTransitionBase _transition;
private ImmutableList<AnimatorCondition> _conditions;

internal VirtualTransitionBase(CloneContext context, AnimatorTransitionBase cloned)
{
_transition = cloned;

context.DeferCall(() =>
context?.DeferCall(() =>
{
if (cloned.destinationState != null)
{
Expand Down
85 changes: 84 additions & 1 deletion UnitTests~/AnimationServices/StateGraphTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using nadena.dev.ndmf.animator;
using System.Collections.Immutable;
using nadena.dev.ndmf.animator;
using NUnit.Framework;
using UnityEditor.Animations;

Expand Down Expand Up @@ -86,5 +87,87 @@ public void TestStateGraphConvergence()
Assert.AreEqual(committedS3, committedS2.transitions[1].destinationState);
Assert.IsTrue(committedS2.transitions[2].isExit);
}

[Test]
public void TestStateMachineTransitions()
{
var sm1 = new AnimatorStateMachine() { name = "sm1" };
var sm2 = new AnimatorStateMachine() { name = "sm2" };
var sm3 = new AnimatorStateMachine() { name = "sm3" };

var s1 = new AnimatorState() { name = "s1" };
var s2 = new AnimatorState() { name = "s2" };

sm1.stateMachines = new[]
{
new ChildAnimatorStateMachine()
{
stateMachine = sm2
},
new ChildAnimatorStateMachine()
{
stateMachine = sm3
}
};
sm1.states = new[]
{
new ChildAnimatorState()
{
state = s1
},
new ChildAnimatorState()
{
state = s2
}
};

sm1.SetStateMachineTransitions(sm2, new[]
{
new AnimatorTransition()
{
destinationState = s1,
conditions = new AnimatorCondition[0]
}
});

var cloneContext = new CloneContext(new GenericPlatformAnimatorBindings());
var clonedSM1 = cloneContext.Clone(sm1);

Assert.AreEqual(clonedSM1.StateMachines.Count, 2);
var clonedSM2 = clonedSM1.StateMachines[0].StateMachine;
var clonedSM3 = clonedSM1.StateMachines[1].StateMachine;

var clonedS1 = clonedSM1.States[0].State;
var clonedS2 = clonedSM1.States[1].State;

Assert.AreEqual(clonedSM1.StateMachineTransitions.Count, 2);
Assert.AreEqual(clonedS1, clonedSM1.StateMachineTransitions[clonedSM2][0].DestinationState);
Assert.AreEqual(0, clonedSM1.StateMachineTransitions[clonedSM3].Count);

var vt = new VirtualTransition();
vt.SetDestination(clonedS2);

clonedSM1.StateMachineTransitions = clonedSM1.StateMachineTransitions.SetItem(
clonedSM3,
ImmutableList<VirtualTransition>.Empty.Add(vt)
);

var commitContext = new CommitContext();
var outSM1 = commitContext.CommitObject(clonedSM1);

var outSM2 = outSM1.stateMachines[0].stateMachine;
var outSM3 = outSM1.stateMachines[1].stateMachine;

var outS1 = outSM1.states[0].state;
var outS2 = outSM1.states[1].state;

var stateTransitions2 = outSM1.GetStateMachineTransitions(outSM2);
Assert.AreEqual(stateTransitions2.Length, 1);
Assert.AreEqual(outS1, stateTransitions2[0].destinationState);

var stateTransitions3 = outSM1.GetStateMachineTransitions(outSM3);
Assert.AreEqual(stateTransitions3.Length, 1);
Assert.AreEqual(outS2, stateTransitions3[0].destinationState);
}
}
}
2 changes: 0 additions & 2 deletions UnitTests~/AnimationServices/VirtualStateMachineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ public void PreservesParentStateMachinePosition()
virtualState => Assert.AreEqual(new Vector3(1, 2, 3), virtualState.ParentStateMachinePosition)
);
}

// TODO: object graph tests
}


Expand Down

0 comments on commit a8c04fb

Please sign in to comment.