From ae975506d78829de4080d8ca4ed85b9ed5c19c26 Mon Sep 17 00:00:00 2001 From: bd_ Date: Sat, 19 Oct 2024 17:15:14 -0700 Subject: [PATCH] fix: occasionally objects controlling reactive components "flicker" (#1298) Reported-by: @whipnice --- .../Animation/GameObjectDisableDelayPass.cs | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/Editor/Animation/GameObjectDisableDelayPass.cs b/Editor/Animation/GameObjectDisableDelayPass.cs index 8e8bca4c..fb453429 100644 --- a/Editor/Animation/GameObjectDisableDelayPass.cs +++ b/Editor/Animation/GameObjectDisableDelayPass.cs @@ -1,9 +1,11 @@ using System.Linq; +using nadena.dev.modular_avatar.core.editor; using nadena.dev.ndmf; using UnityEditor; using UnityEditor.Animations; using UnityEngine; using VRC.SDK3.Avatars.Components; +using BuildContext = nadena.dev.ndmf.BuildContext; namespace nadena.dev.modular_avatar.animation { @@ -23,11 +25,16 @@ namespace nadena.dev.modular_avatar.animation if (fx == null) return; + var nullMotion = new AnimationClip(); + nullMotion.name = "NullMotion"; + var blendTree = new BlendTree(); blendTree.blendType = BlendTreeType.Direct; blendTree.useAutomaticThresholds = false; - blendTree.children = asc.BoundReadableProperties.Select(GenerateDelayChild).ToArray(); + blendTree.children = asc.BoundReadableProperties + .Select(prop => GenerateDelayChild(nullMotion, prop)) + .ToArray(); var asm = new AnimatorStateMachine(); var state = new AnimatorState(); @@ -54,7 +61,7 @@ namespace nadena.dev.modular_avatar.animation }).ToArray(); } - private ChildMotion GenerateDelayChild((EditorCurveBinding, string) binding) + private ChildMotion GenerateDelayChild(Motion nullMotion, (EditorCurveBinding, string) binding) { var ecb = binding.Item1; var prop = binding.Item2; @@ -64,10 +71,40 @@ namespace nadena.dev.modular_avatar.animation curve.AddKey(0, 1); AnimationUtility.SetEditorCurve(motion, ecb, curve); + // Occasionally, we'll have a very small value pop up, probably due to FP errors. + // To correct for this, instead of directly using the property in the direct blend tree, + // we'll use a 1D blend tree to give ourselves a buffer. + + var bufferBlendTree = new BlendTree(); + bufferBlendTree.blendType = BlendTreeType.Simple1D; + bufferBlendTree.useAutomaticThresholds = false; + bufferBlendTree.blendParameter = prop; + bufferBlendTree.children = new[] + { + new ChildMotion + { + motion = nullMotion, + timeScale = 1, + threshold = 0 + }, + new ChildMotion + { + motion = nullMotion, + timeScale = 1, + threshold = 0.01f + }, + new ChildMotion + { + motion = motion, + timeScale = 1, + threshold = 1 + } + }; + return new ChildMotion { - motion = motion, - directBlendParameter = prop, + motion = bufferBlendTree, + directBlendParameter = MergeBlendTreePass.ALWAYS_ONE, timeScale = 1 }; }