In effort to learn more about Unity and become rich making games I started building a new project with the intent of having a third person camera and player controller.
Since I didn’t want to re-invent the wheel I browsed the various demo assets Unity has and found one called Starter Assets - Third Person Character Controller. How convenient!
I imported the asset into my project and started playing around with it.
After a little while I started noticing something wrong with my camera, as I was running around in my playground the camera was stuttering, seemingly teleporting when orbitting around the player model.
At first I blamed my code as I tweaked and added stuff on top of the starter assets but I could not really pinpoint the cause of this behavior.
Input Actions and Events
This project was a deep dive for me into the Unity Input System (the new one) and I have to admit I really like the way they did things. It’s both intuitive and really powerful.
My Input Action set was as follow, fairly straight forward. Note that the use of pointer
instead of mouse
in the Look
action doesn’t make a difference as the latter directly inherits from the former.
By default the camera controller shipping with the asset pack uses the Send Message
method to notify the code when an input is received. I decided instead to use native C# Events as an alternative after reading an extensive article by Daniel Ilett on messaging within Unity.
private void OnLook(InputAction.CallbackContext context) {
look = context.ReadValue<Vector2>();
}
Haunted mouses
I kept testing things to cross potential culprits off my list : trying different mouses, updating drivers and tweaking the DPI settings in the Logitech app but no dice. I then plugged a game controller and.. it just worked.. flawlessly. So clearly at that point I knew that was something wrong specifically with mouse input.
It was time to acquire data. I started by attaching a Trail Renderer to my camera to more easily see the path it was following and we can immediately see as I rotate around the player the camera will “jump” ahead as shown here with the straight lines.
I also imported the handy Squiggle package and graphed the Vector2
delta input from the mouse and the result was quite interesting.
During this test I was simply moving the mouse left to right as consistently as possible and yet we can see a huge spike on the X axis that should not be there. This definitely explains the behavior of the camera.. but why ?
Profiler to the rescue
The answer ? more data! I fired up the standalone profiler (after patching the cinemachine package) and a few frames later the lag spike of doom appeared on the profiler as well.
The vast majority of this frame time was spend doing something called EditorLoop
which basically means the Unity Editor itself was executing code pausing the game execution for a few milliseconds. It is important to keep in mind the game in play mode shares the editor’s thread.
Of course this spike appears on the mouse graph as well but surprisingly It is offset by a few frames. In addition, note the little gap at the peak of the reading.
The spike on the profiler occurs at frame 2801 while the spike on the mouse input graph occurs at frame 2803 and when looking at the scale of the amplitude it appears the spike is roughly three times as big as the average reading and this is not a coincidence.
I stumbled upon a forum post by a Unity employee talking about this behavior. Basically the way the new Input System works causes delta events to add up until they are consumed which should not be an issue as you would usually consume every input event as soon as they arrive. However, since in this case the Unity Editor is introducing a pause in some frames, all the events produced within those frames will accumulate and be returned as one sum the next time your event listener is executed.
Coming back to the profiler data, the slow frame 2801 likely recorded 3 delta events each with a value of around -28 on the X axis causing the sum of about -85 on frame 2803. I’m not exactly sure why the behavior doesn’t occur on the first frame following the slow frame but It is possible the frame counter of my mouse graph is slightly inaccurate or the event was simply not triggered because I’m using Dynamic Updates
in my project’s Input settings.
Now what ?
First the bad news : I never bothered digging deeper to fix the issue because when asking around It was clear this was not the widespread issue I thought it was. There was definitely something wrong in my project. In fact I ended up making a new project from scratch, imported the starter assets once again and never encountered the issue again.
The good news : This issue does not occur in the final product. Since the issue is caused by the Unity Editor slowing down the game it only occurs during play mode. If you’re struggling with the issue you can try it yourself : compile your game and run it, input stutter should disappear.
Obviously having broken controls in play mode isn’t great but at least since we now know the root cause is editor overhead we can try hunting down editor features that might be computationally expensive, one easy target is the scene view. Start by closing all the editor views you don’t need in play mode and work you way up from here.
Notes
Versions used :
Unity 2021.3.0f1 on Windows 10
Cinemachine 2.8.4
Starter Assets - Third Person Character Controller 1.0