Since it’s early (before 8AM), I wanted to start off with an easy win, which was to format all the scripts in my project with CSharpier. Really, I was just tired of seeing yellow warning lines under every ending semicolon in my project. According the VSCode CSharpier terminal output, having multiple csproj files in my project was preventing it from running properly, and if I changed my terminal directory to another folder and ran CSharpier there, it’d bypass the error. The solution was twofold:
- Have CSharpier ignore
csprojfiles entirely with a.csharpierignore. - Properly assign the CSharpier VSCode plugin as the default C# code formatter in the editor.
Now, time to fix that bug from yesterday.
It took me a hot sec to properly articulate the closed hand sphere momentum bug in typed words to Claude; to be safe, I drew a diagram in photoshop and passed it into the prompt for extra context.
The original place I suspected the error to be coming from, the hand midpoint calculation in GetClampedMetaballPosition(), turned out to be a false flag, but it also turned out to be obsolete! As detailed at the end of 2026-01-13, I was shooting a ray from the center of the player’s hands to the position of the sphere and positioning the metaball based on where that ray intersected the grid. However, that logic was implemented before the drag-based slowdown of the player sphere introduced in 2026-01-19. Since the sphere now stays near its clamped metaball, that complex position raycasting clamping logic wasn’t needed! Claude replaced it with a simple axis-aligned clamping system:
return new Vector3(
Mathf.Clamp(spherePos.x, gridMin.x, gridMax.x),
Mathf.Clamp(spherePos.y, gridMin.y, gridMax.y),
Mathf.Clamp(spherePos.z, gridMin.z, gridMax.z)
);The real cause of the bug was the extra call I was making to AlignAndCalculateVectors() in HandForce when both hands were closed. Disabling the extra push fixed the bug, but also disabled the extra push. I might be on a time crunch today, but that doesn’t mean I’m ok with a cop-out.
The fix was to add a method that tracks which hand is open when only one hand is open, and when that state started, and use that to calculate the “midpoint” when doing the final push.
I’m breaking all of my NUMBER 1 CODING RULE by not doing a code freeze now that everything is working. Instead, I asked Claude to refactor code 🥶
Thankfully, it did a good job. Besides, hand state tracking truly belonged in the PlayerConstructor, and “midpoint” was not a semantically accurate name for the push target. My OCD won. Unfortunately, I’m still doing extra hand state tracking calculations in HandEffects, but I won’t dare refactor that file today. Adding that to my todos.
Retesting the dynamics between changing hands states is especially salient given what I’ve worked on the last two days, and after doing so, I noticed behavior bugs. The last single open hand position was being used when both hands became open, causing the push target to get set to the last single open hand rather than the midpoint between the hands. Also, when closing both hands at the same time, the momentum of the sphere was being pushed towards the last single open hand. The fix was to reset lastSingleOpenHand to None whenever both hands are open. Lastly, I noticed that transitioning between a single hand open to both hands being open seemed to give the sphere an extra push, even though that logic was only being applied when moving into a state where both hands were closed. This, it turned out, was being caused by the abrupt switching off of my singleHandOpenForceDamper when entering the both open state. I decided to lerp between the original and dampened values over a singleHandForceLerpDuration. Now, all is smooth and I’m ready to freeze!
One more todo, before signing off: organize all the new global settings I’ve added into their proper sections.
Tags: unity vfx gamedev programming debugging refactoring metaballs