fix: float conversion breaks sub-state-machine entry transitions, including FaceEmo (#674)

* fix: float conversion breaks substatemachine entry transitions

Closes: #669

* fix: unnecessary type conversions
This commit is contained in:
bd_ 2024-02-17 19:19:35 +09:00 committed by GitHub
parent 54d4e974db
commit 27f0557367
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 231 additions and 38 deletions

View File

@ -189,20 +189,18 @@ namespace nadena.dev.modular_avatar.animation
|| path.StartsWith("Packages/com.vrchat.");
}
/// <summary>
/// Enumerates all states in an animator controller
/// Enumerates all state machines and sub-state machines starting from a specific starting ASM
/// </summary>
/// <param name="ac"></param>
/// <returns></returns>
internal static IEnumerable<AnimatorState> States(AnimatorController ac)
internal static IEnumerable<AnimatorStateMachine> ReachableStateMachines(this AnimatorStateMachine asm)
{
HashSet<AnimatorStateMachine> visitedStateMachines = new HashSet<AnimatorStateMachine>();
Queue<AnimatorStateMachine> pending = new Queue<AnimatorStateMachine>();
foreach (var layer in ac.layers)
{
if (layer.stateMachine != null) pending.Enqueue(layer.stateMachine);
}
pending.Enqueue(asm);
while (pending.Count > 0)
{
@ -215,10 +213,7 @@ namespace nadena.dev.modular_avatar.animation
if (child.stateMachine != null) pending.Enqueue(child.stateMachine);
}
foreach (var state in next.states)
{
yield return state.state;
}
yield return next;
}
}
}

View File

@ -130,18 +130,18 @@ namespace nadena.dev.modular_avatar.animation
{
foreach (var layer in _layers)
{
foreach (var asset in layer.stateMachine.ReferencedAssets(includeScene: false))
foreach (var asm in layer.stateMachine.ReachableStateMachines())
{
if (asset is AnimatorState s)
foreach (ChildAnimatorState s in asm.states)
{
s.transitions = s.transitions.SelectMany(FixupTransition).ToArray();
s.state.transitions = s.state.transitions.SelectMany(FixupTransition).ToArray();
}
asm.entryTransitions = asm.entryTransitions
.SelectMany(FixupTransition).ToArray();
asm.anyStateTransitions = asm.anyStateTransitions
.SelectMany(FixupTransition).ToArray();
}
layer.stateMachine.entryTransitions = layer.stateMachine.entryTransitions
.SelectMany(FixupTransition).ToArray();
layer.stateMachine.anyStateTransitions = layer.stateMachine.anyStateTransitions
.SelectMany(FixupTransition).ToArray();
}
}
@ -376,6 +376,8 @@ namespace nadena.dev.modular_avatar.animation
{
if (_parameters.TryGetValue(param.name, out var acp))
{
if (acp.type == param.type) continue;
if (acp.type != param.type &&
(acp.type == AnimatorControllerParameterType.Trigger ||
param.type == AnimatorControllerParameterType.Trigger))
@ -403,7 +405,7 @@ namespace nadena.dev.modular_avatar.animation
defaultInt = param.defaultInt
};
_parameters.Add(param.name, param);
_parameters.Add(param.name, clonedParameter);
_parameterSource.Add(param.name, controller);
}

View File

@ -1,8 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using modular_avatar_tests;
using nadena.dev.modular_avatar.core;
using nadena.dev.ndmf;
using NUnit.Framework;
using UnityEditor.Animations;
@ -132,13 +132,34 @@ public class ConvertTransitionTypes : TestBase
Assert.AreEqual(AnimatorControllerParameterType.Float, p_types["float"]);
Assert.AreEqual(AnimatorControllerParameterType.Float, p_types["int2"]);
}
[Test]
public void SubStateMachineHandling()
{
var prefab = CreatePrefab("ConvertTransitionTypes.prefab");
AvatarProcessor.ProcessAvatar(prefab);
var layer = findFxLayer(prefab, "sub_state_machine");
AssertSingleTransition(layer.stateMachine.entryTransitions[0], ("bool", AnimatorConditionMode.Greater, 0.5f));
var ssm1 = layer.stateMachine.stateMachines[0].stateMachine;
AssertSingleTransition(ssm1.entryTransitions[0], ("bool", AnimatorConditionMode.Greater, 0.5f));
var ssm2 = ssm1.stateMachines[0].stateMachine;
AssertSingleTransition(ssm2.entryTransitions[0], ("bool", AnimatorConditionMode.Greater, 0.5f));
}
[Test]
public void NoConversionWhenConsistent()
{
var prefab = CreatePrefab("ConvertTransitionTypes.prefab");
UnityEngine.Object.DestroyImmediate(prefab.transform.Find("2").gameObject);
var merge1 = prefab.transform.Find("1").GetComponent<ModularAvatarMergeAnimator>();
var merge2 = prefab.transform.Find("2").GetComponent<ModularAvatarMergeAnimator>();
merge2.animator = merge1.animator;
AvatarProcessor.ProcessAvatar(prefab);
@ -192,15 +213,6 @@ public class ConvertTransitionTypes : TestBase
params (string, AnimatorConditionMode, float)[] conditions)
{
var srcState = FindStateInLayer(layer, src);
foreach (var s in layer.stateMachine.states)
{
Debug.Log("$$$ State: " + s.state.name);
foreach (var t0 in s.state.transitions)
{
Debug.Log("$$$ => " + t0.destinationState.name);
}
}
var transitions = srcState.transitions.Where(t2 => t2.destinationState.name == dest)
.ToArray();

View File

@ -1,5 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1107 &-8678029029566323935
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New StateMachine
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: -2368878104934149449}
m_Position: {x: 340, y: 120, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions:
- {fileID: -6793735410602199853}
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -2368878104934149449}
--- !u!1102 &-8277159016159921547
AnimatorState:
serializedVersion: 6
@ -54,6 +77,23 @@ AnimatorStateMachine:
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -3477782397476088704}
--- !u!1109 &-6793735410602199853
AnimatorTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: bool
m_EventTreshold: 0
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -2368878104934149449}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 1
--- !u!1101 &-6702605715029525197
AnimatorStateTransition:
m_ObjectHideFlags: 1
@ -366,6 +406,55 @@ AnimatorStateTransition:
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-2368878104934149449
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New State
m_Speed: 1
m_CycleOffset: 0
m_Transitions: []
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 0}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1107 &-2155158836370751539
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New StateMachine
m_ChildStates: []
m_ChildStateMachines:
- serializedVersion: 1
m_StateMachine: {fileID: -8678029029566323935}
m_Position: {x: 370, y: 100, z: 0}
m_AnyStateTransitions: []
m_EntryTransitions:
- {fileID: 9064003435054199019}
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -2368878104934149449}
--- !u!1101 &-1545596635161646672
AnimatorStateTransition:
m_ObjectHideFlags: 1
@ -431,37 +520,37 @@ AnimatorController:
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_Controller: {fileID: 9100000}
- m_Name: int
m_Type: 3
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_Controller: {fileID: 9100000}
- m_Name: float
m_Type: 1
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_Controller: {fileID: 9100000}
- m_Name: int2
m_Type: 3
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_Controller: {fileID: 9100000}
- m_Name: int3
m_Type: 3
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_Controller: {fileID: 9100000}
- m_Name: trigger
m_Type: 9
m_DefaultFloat: 0
m_DefaultInt: 0
m_DefaultBool: 0
m_Controller: {fileID: 0}
m_Controller: {fileID: 9100000}
m_AnimatorLayers:
- serializedVersion: 5
m_Name: int transitions
@ -535,6 +624,18 @@ AnimatorController:
m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
- serializedVersion: 5
m_Name: sub_state_machine
m_StateMachine: {fileID: 7068830732163781358}
m_Mask: {fileID: 0}
m_Motions: []
m_Behaviours: []
m_BlendingMode: 0
m_SyncedLayerIndex: -1
m_DefaultWeight: 0
m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
--- !u!1102 &308203126016203391
AnimatorState:
serializedVersion: 6
@ -639,6 +740,31 @@ AnimatorStateMachine:
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: 2331480681644977070}
--- !u!1101 &1712637675519008892
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: bool
m_EventTreshold: 0
m_DstStateMachine: {fileID: -8678029029566323935}
m_DstState: {fileID: 0}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.25
m_TransitionOffset: 0
m_ExitTime: 0.75
m_HasExitTime: 0
m_HasFixedDuration: 1
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1107 &2101233559568525048
AnimatorStateMachine:
serializedVersion: 6
@ -832,6 +958,23 @@ AnimatorState:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1109 &6284330602530841453
AnimatorTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: bool
m_EventTreshold: 0
m_DstStateMachine: {fileID: -2155158836370751539}
m_DstState: {fileID: 0}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 1
--- !u!1109 &6482949225251473226
AnimatorTransition:
m_ObjectHideFlags: 1
@ -874,6 +1017,30 @@ AnimatorStateTransition:
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1107 &7068830732163781358
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: sub_state_machine
m_ChildStates: []
m_ChildStateMachines:
- serializedVersion: 1
m_StateMachine: {fileID: -2155158836370751539}
m_Position: {x: 300, y: 170, z: 0}
m_AnyStateTransitions:
- {fileID: 1712637675519008892}
m_EntryTransitions:
- {fileID: 6284330602530841453}
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -2368878104934149449}
--- !u!1101 &7085359437149011441
AnimatorStateTransition:
m_ObjectHideFlags: 1
@ -1058,3 +1225,20 @@ AnimatorState:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1109 &9064003435054199019
AnimatorTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions:
- m_ConditionMode: 1
m_ConditionEvent: bool
m_EventTreshold: 0
m_DstStateMachine: {fileID: -8678029029566323935}
m_DstState: {fileID: 0}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 1