fix: play audio absolute paths in a relative controller break (#1555)

In MA 1.11.x, while we corrected relative play audio paths where possible,
we would leave them unchanged if no corresponding object was found. This
meant that we maintained compatibility with older assets that uses absolute
addressing for their Play Audio paths. This change restores that behavior.
This commit is contained in:
bd_ 2025-04-12 18:55:07 -07:00 committed by GitHub
parent 59c256704b
commit 7086b35b3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 216 additions and 2 deletions

View File

@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1552] Merge Blend Treeにて、メインアバターFXレイヤーと同じ名前のintやboolパラメーターがBlend Treeに含まれている場合、
パラメーター型が修正されない問題を修正
- [#1553] リアクティブコンポーネントが生成するステートに、WD設定が正しくない問題を修正
- [#1555] VRC Animator Play Audioが、Audio Sourceまでの絶対パスで設定されている場合に、相対パスのMerge Animator
コンポーネントとマージされた場合、指定されたオブジェクトが存在しないことを検出し、参照を絶対パスとして扱うように修正
- 対象のパスにオブジェクトがある場合は、相対パスとして扱われます。安定性向上のためMerge Animatorコンポーネントと同じ
 指定方法を使用することをお勧めします。
### Changed
- [#1551] Merge Animatorは、遷移のない単一のstateを持つブレンドツリーのレイヤーに対して常にWDをONに設定します。

View File

@ -25,6 +25,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1552] Merge Blend Tree failed to correct parameter types when the main avatar FX layer contained an int or bool
parameter with the same name as one used in the blend tree.
- [#1553] Reactive components might generate states with incorrect write default settings
- [#1555] Fixed compatibility regression from 1.11.x: VRC Animator Play Audio, when configured with an absolute path
but merged with a relative-path merge animator component, will now detect that the indicated object does not
exist, and treat the reference as an absolute path.
- Note that if there is an object in the target path, then it will be treated as a relative path. Using
addressing for Play Audio behaviors consistent with Merge Animator settings is therefore recommended as it will be
more robust.
### Changed
- [#1551] Merge Animator will always set WD ON for single-state blendtree layers with no any state transitions.

View File

@ -12,6 +12,10 @@ Modular Avatarの主な変更点をこのファイルで記録しています。
- (実験的機能) VRC以外のプラットフォームのサポートを有効化
### Fixed
- [#1555] VRC Animator Play Audioが、Audio Sourceまでの絶対パスで設定されている場合に、相対パスのMerge Animator
コンポーネントとマージされた場合、指定されたオブジェクトが存在しないことを検出し、参照を絶対パスとして扱うように修正
- 対象のパスにオブジェクトがある場合は、相対パスとして扱われます。安定性向上のためMerge Animatorコンポーネントと同じ
 指定方法を使用することをお勧めします。
### Changed

View File

@ -14,6 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- (Experimental feature) Enabled support for non-VRC platforms
### Fixed
- [#1555] Fixed compatibility regression from 1.11.x: VRC Animator Play Audio, when configured with an absolute path
but merged with a relative-path merge animator component, will now detect that the indicated object does not
exist, and treat the reference as an absolute path.
- Note that if there is an object in the target path, then it will be treated as a relative path. Using
addressing for Play Audio behaviors consistent with Merge Animator settings is therefore recommended as it will be
more robust.
### Changed

View File

@ -0,0 +1,46 @@
#if MA_VRCSDK3_AVATARS
using System.Linq;
using nadena.dev.modular_avatar.core;
using nadena.dev.ndmf;
using nadena.dev.ndmf.animator;
using VRC.SDK3.Avatars.Components;
namespace nadena.dev.modular_avatar.animation
{
[RunsOnPlatforms(WellKnownPlatforms.VRChatAvatar30)]
public class FixupAbsolutePlayAudioPass : Pass<FixupAbsolutePlayAudioPass>
{
protected override void Execute(BuildContext context)
{
// Older versions of modular avatar did not adjust Animator Play Audio paths when they were absolute paths.
// Replicate this behavior here.
// Note that this runs before any object movement.
var asc = context.Extension<AnimatorServicesContext>();
foreach (var mama in context.AvatarRootTransform.GetComponentsInChildren<ModularAvatarMergeAnimator>(true))
{
if (!mama._wasRelative) continue;
var pathPrefix = asc.ObjectPathRemapper.GetVirtualPathForObject(mama.gameObject) + "/";
foreach (var state in asc.ControllerContext.Controllers[mama].AllReachableNodes()
.OfType<VirtualState>())
{
foreach (var behavior in state.Behaviours.OfType<VRCAnimatorPlayAudio>())
{
if (asc.ObjectPathRemapper.GetObjectForPath(behavior.SourcePath) != null) continue;
if (behavior.SourcePath.StartsWith(pathPrefix))
{
behavior.SourcePath = behavior.SourcePath.Substring(pathPrefix.Length);
}
}
}
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a763cbc4bac94063b6b084ea3f4d8206
timeCreated: 1744422528

View File

@ -7,7 +7,6 @@ using nadena.dev.modular_avatar.editor.ErrorReporting;
using nadena.dev.ndmf;
using nadena.dev.ndmf.animator;
using nadena.dev.ndmf.fluent;
using nadena.dev.ndmf.model;
using nadena.dev.ndmf.util;
using UnityEngine;
using Object = UnityEngine.Object;
@ -64,6 +63,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
seq.WithRequiredExtension(typeof(AnimatorServicesContext), _s2 =>
{
#if MA_VRCSDK3_AVATARS
seq.Run(FixupAbsolutePlayAudioPass.Instance);
seq.Run(MMDRelayEarlyPass.Instance);
seq.Run(RenameParametersPluginPass.Instance);
seq.Run(ParameterAssignerPass.Instance);

View File

@ -58,6 +58,8 @@ namespace nadena.dev.modular_avatar.core
public AvatarObjectReference relativePathRoot = new AvatarObjectReference();
public int layerPriority = 0;
public MergeAnimatorMode mergeAnimatorMode = MergeAnimatorMode.Append;
internal bool _wasRelative;
public override void ResolveReferences()
{
@ -88,7 +90,11 @@ namespace nadena.dev.modular_avatar.core
string IVirtualizeAnimatorController.GetMotionBasePath(object ndmfBuildContext, bool clearPath)
{
var path = GetMotionBasePathCallback(this, ndmfBuildContext);
if (clearPath) pathMode = MergeAnimatorPathMode.Absolute;
if (clearPath)
{
_wasRelative = _wasRelative || pathMode == MergeAnimatorPathMode.Relative;
pathMode = MergeAnimatorPathMode.Absolute;
}
return path;
}

View File

@ -0,0 +1,103 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1107 &-6982002469074649382
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: absolute
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: 5394444500086494115}
m_Position: {x: 330, y: 60, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions: []
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: 5394444500086494115}
--- !u!91 &9100000
AnimatorController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AbsoluteReference
serializedVersion: 5
m_AnimatorParameters: []
m_AnimatorLayers:
- serializedVersion: 5
m_Name: absolute
m_StateMachine: {fileID: -6982002469074649382}
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!114 &2414404072947734855
MonoBehaviour:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1859411423, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
m_Name:
m_EditorClassIdentifier:
Source: {fileID: 0}
SourcePath: Bone Proxy/Audio Source
PlaybackOrder: 0
ParameterName:
Volume: {x: 1, y: 1}
VolumeApplySettings: 1
Pitch: {x: 1, y: 1}
PitchApplySettings: 1
Clips: []
ClipsApplySettings: 1
Loop: 0
LoopApplySettings: 1
DelayInSeconds: 0
PlayOnEnter: 1
StopOnEnter: 1
PlayOnExit: 0
StopOnExit: 0
playbackIndex: 0
--- !u!1102 &5394444500086494115
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:
- {fileID: 2414404072947734855}
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:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 025d88351bb99eb4f8b38dbe071eaf2a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -26,6 +26,11 @@ namespace modular_avatar_tests
var subState = layer.stateMachine.stateMachines[0].stateMachine.states[0].state;
var playAudio2 = (VRCAnimatorPlayAudio) subState.behaviours[0];
Assert.AreEqual("New Parent/Bone Proxy/Audio Source", playAudio2.SourcePath);
var absLayer = findFxLayer(prefab, "absolute");
state = absLayer.stateMachine.states[0].state;
playAudio = (VRCAnimatorPlayAudio) state.behaviours[0];
Assert.AreEqual("New Parent/Bone Proxy/Audio Source", playAudio.SourcePath);
}
}
}

View File

@ -335,6 +335,7 @@ GameObject:
m_Component:
- component: {fileID: 2374223349072045661}
- component: {fileID: 2173147159387031478}
- component: {fileID: 4651177154340700720}
m_Layer: 0
m_Name: Bone Proxy
m_TagString: Untagged
@ -373,6 +374,28 @@ MonoBehaviour:
boneReference: 55
subPath: New Parent
attachmentMode: 1
--- !u!114 &4651177154340700720
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6739982813768973813}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1bb122659f724ebf85fe095ac02dc339, type: 3}
m_Name:
m_EditorClassIdentifier:
animator: {fileID: 9100000, guid: 025d88351bb99eb4f8b38dbe071eaf2a, type: 2}
layerType: 5
deleteAttachedAnimator: 1
pathMode: 0
matchAvatarWriteDefaults: 0
relativePathRoot:
referencePath:
targetObject: {fileID: 0}
layerPriority: 0
mergeAnimatorMode: 0
--- !u!1 &7552762365415873619
GameObject:
m_ObjectHideFlags: 0