I have a simple game that is using a Unity animation to move an object around the screen in a 2D game. Think space ship following a path like Galaga. I made a GameObject, animated it moving around the screen, saved that as a prefab. I instantiate that prefab, child my object to it, and play the animation.
So far so good. The animation is smooth as silk.
So next I add players (space ships) and they shoot projectiles at the enemy.
Here's the issue. As soon as the players start shooting I can see the motion of the enemy is no longer smooth. It's janky.
Now, my first thought would be that the problem is the players shooting are calling
Instantiate is slow so I'm not hitting 60fps. Okay fine, I implemented object pools and use
GameObject.SetActive to turn them on and off. I run the game, same problem. Motion of enemy is smooth when no shots are fired. Motion is janky while shots are being fired.
Someone tells me
GameObject.SetActive is slow.
Okay, let's check. I add an FPS graph. The FPS is pretty smooth according to the graph. It seems like I'm at 60fps 100% of the time shooting or not.
So ... what gives? If I'm always hitting 60fps then why does shooting cause the motion of some other unrelated object to become janky and more importantly how do I fix it so it's not janky?
Unfortunately it's hard to show the issue. I tried recording the screen using quicktime with and without shots. The screen recording is 60fps and it shows no jank with or without shots even though I could clearly see the jank while recording when the ships are shooting.
I recorded with my iPhone at 60fps. You should be able to see that this video is mostly smooth. There are 3 or 4 janks early on (which weren't there in reality, it's a sync issue between the phone and the TV), but basically the motion of the thing swinging left and right is smooth
Where as this video with the ships shooting it should be very clear is full of jank. The motion of the object swinging back and forth is not smooth at all and yet if you look at the FPS meter in the top left corner you can see processing time (blue) is almost nothing. It would fill the graph all the way to the green if it was taking 1/60 of a second to compute a frame. The green is the framerate and it's a pretty consistent 60fps.
That kind of makes it seem like a unity bug related to swapping frames or something else happening late.
Note I'm using a Mac Mini but the game is seriously simple. There's about 20 small/medium sprites on the screen and nothing else.
I posted a repo here although I suspect you won't be able to repo the issue unless you also have the same or similar model Mac Mini since I can't repo on my Macbook Pro.
So some more data. I tried turning off vsync (
QualitySettings.vSyncCount = 0). The game runs 310 to 350fps at all times clearly showing the issue is not speed. It should be able to hit 60fps with no problems. Of course with vblank off there is notable rippling (it's tearing but given the content of the game I don't see what I normally consider tears. Still I might end up using this mode or at least making it an option as it looks much better than the jank I'm seeing with
QualitySettings.vSyncCount = 1.) If I set the target FPS to 100 then I see serious tearing. I tried a target FPS of 60 and it looks the same as with vSyncCount set to 1 (in other words, janky). Tried a target of 65 and it looks different but still bad. sigh....
Can repo on my MacBook Pro by connecting it via HDMI to my TV. If I display the game on my TV I get the jank when shooting. I even tried turning off the Macbook Pro main display by connecting an external keyboard and mouse and then closing the lid just to make sure it wasn't an issue with two displays Same issue.
So when repoing with my Macbook Pro MacOS would only give me options for scaled resolutions on the TV (I'm using a 4k TV). My game is a pixel art game so I only needed the TV to be in 720p mode at most. MacOS only provides "720p (scaled)" which I'm guessing means the display is running at 1080p or 4k, the OS is drawing 720 but scaling the result.
So I went looking for a way to force the Mac to go 720p and found this Q&A. I downloaded the software from the accepted answer, SwitchResX. I tried forcing the Mac to emit a 720p 60hz signal. That did not fix the problem. Then I tried 720p 50hz and the jank disappeared! Also tried on Mac Mini and it solved the issue there too.
As pointed out above this does not seem to be a perf issue. The game runs plenty fast. It's some kind of timing issue with Unity and how it's swapping the screen buffers on Mac such that sometimes it misses its chance to swap them and an old frame gets displayed (or at least that's my guess since screen capture shows every frame).
In any case my use case is for a promotional event so I can purchase SwitchResX, install it on the machine in question and use that for the promotional event.
Still, if it was an end user game it would be nice to know how to fix it from unity's side