All flavours of SmoothDamp implementation in Udon Graph are completely broken
tracked
Uzer Tekton
All flavours of SmoothDamp implementation in Udon Graph are completely broken
What Unity SmoothDamp does
- In Unity source, Mathf.SmoothDamp has a parameter ref float currentVelocitywhich updates itself every frame. Note theref.
The Udon Graph situation
- Currently in Udon Graph, the Mathf.SmoothDamp node has a currentVelocityinput and output. (Should also apply to similar functions that uses SmoothDamp, like Vector3.SmoothDamp and Mathf.SmoothDampAngle.)
- You can apparently plug in a variable for the input, and set the same variable with the output. However, because you cannot set more than one variable at the same time, once you have updated the currentVelocitydummy variable, when you try to get thecurrentvalue float output, it will count as a second calculation using the updatedcurrentVelocitymeant for the next frame. So this cannot work. (See image)
- If you try to set variable of the currentvalue first, and calculate thecurrentVelocitysecond, it will calculate using the newly updatedcurrentmeant for next frame. Again, it cannot work.
- Trying to manually jiggle variables around for this frame and next frame does not work, the output currentwill somehow overshoot the target. Looking at the Unity source code again, this should not be possible. Something spooky is happening, and the only thing spooky is what Udon Graph might be doing with a ref parameter. (Idk what is going on) (See image)
- If the currentVelocityinput and output is left unplugged, the node works. However,currentVelocitydoes not get updated every frame in-game, and will remain 0.
- The effect is that, the movement becomes hugely framerate dependant in a weird way, when it shouldn't. In Unity's original implementation, the overall time to reach a certain point should be about the same for different framerates, regardless of how many steps it takes.
Testing
- I have confirmed these using some custom python code to simulate the duration to reach 0.95 when going from 0 to 1.
Test 1 - measurements using Unity source code (This is what should happen)
- By simulating various framerates, the overall duration using Unity's original code (and making sure the currentVelocityis updated as it should). Notice it is largely framerate independent, except some small errors probably attributed to step sizes in the last frame:
Time to reach 95% of the target at 15 FPS: 0.2667 seconds
Time to reach 95% of the target at 30 FPS: 0.2667 seconds
Time to reach 95% of the target at 60 FPS: 0.2500 seconds
Time to reach 95% of the target at 120 FPS: 0.2417 seconds
Time to reach 95% of the target at 150 FPS: 0.2400 seconds
Time to reach 95% of the target at 200 FPS: 0.2400 seconds
Time to reach 95% of the target at 240 FPS: 0.2417 seconds
Time to reach 95% of the target at 280 FPS: 0.2393 seconds
Time to reach 95% of the target at 300 FPS: 0.2400 seconds
Test 2 - measurements using a modified source matching in-game results (This is what happens in-game if the currentVelocity input and output are left unplugged)
- Another simulation, this time the currentVelocityis manually sabotaged to not be update. The results matches the in-game obeservations.
Simulated results:
Time to reach 95% of the target at 15 FPS (without updating velocity): 0.4667 seconds
Time to reach 95% of the target at 30 FPS (without updating velocity): 0.6667 seconds
Time to reach 95% of the target at 60 FPS (without updating velocity): 1.1000 seconds
Time to reach 95% of the target at 90 FPS (without updating velocity): 1.5667 seconds
Time to reach 95% of the target at 120 FPS (without updating velocity): 2.0333 seconds
Observed results in-game:
15 FPS, ~466 ms
30 FPS, ~666 ms
60 FPS, ~1100 ms
90 FPS, ~1567 ms
120 FPS, ~1.9 seconds
Comments
- In my opinion, if Udon Graph implemented this correctly, currentVelocityshould not be exposed (as input and output) at all, but should be run under the hood of Udon Graph, which is out of my hands.
- I guess, under the hood, Udon Graph just needs to make sure there is a variable put aside for each individual instance of these nodes to be used for saving and readingcurrentVelocity. But I don't know how it works under the hood so I will let the devs figure this out.
- I don't knowif Udon Sharp has the same problem.
References and how to replicate
The world I used for testing is here: https://vrchat.com/home/world/wrld_8df1ce44-319d-4c67-b253-90f5bd1894b5
Unity source for reference: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Mathf.cs
I also attached the two pieces of simulator codes, in Python.
I ran the codes using Jupyter Notebook: https://jupyter.org/try-jupyter/notebooks/?path=notebooks/Intro.ipynb
Log In
This post was marked as
tracked
Uzer Tekton
Apparently it also doesn't work in Udon