If you have ever used the flythrough camera mode in the Scene view inside Unity’s editor (or written your own camera controls) and noticed that it stutters annoyingly even in a simple scene as you move around, you’re not alone! This seems to happen most often with the General / Interaction Mode preference in the editor set to Monitor Refresh Rate.

At some point, you may have also noticed that in the Game view, there is a VSync setting:

Game View VSync Setting

If you’ve seen this setting, then you’ve probably read the note that says “Game view only”. At some point I noticed that toggling this setting off and back on resulted in smooth camera motion after switching back to the Scene view. (You might try this first to see if it helps in your scene.)

Unfortunately, any time the game is actually played and then stopped, it seems to reset. After digging around on the forums and in the PlayModeView.cs source code, there appears to be an EnableVSync() method (on the view’s parent) that is called when entering play mode. Unfortunately, the parent property it uses is internal as is the EnableVSync() method (found in GUIView.bindings.cs). Fortunately, with a bit of reflection magic, this is not a problem!

If you’re wondering why I wanted to try and fix this issue, it’s because I also wrote a custom isometric-style camera for use in the editor that mimics the in-game camera (its angle and terrain following behavior). This makes it very easy to pan around an area and place objects and see how they’ll be viewed in the final game. The stuttering camera motion, however, was driving me insane!

Until Unity fixes the issue, here’s some code that should help with the stuttering when called and should limit the frame rate to the monitor’s vsync (even if the Interaction Mode preference is set to No Throttling):

// Copyright 2021 by Hextant Studios. https://HextantStudios.com
// Licensed under CC BY 4.0. http://creativecommons.org/licenses/by/4.0/
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace Hextant.Editor
{
    public static class SceneViewExtensions
    {
        // Attempts to enable VSync in the scene view since Unity refuses to fix this issue.
        // Referenced from the PlayModeView.SetVSync(): https://bit.ly/3nUoeLi
        public static void EnableVSync( this SceneView sceneView )
        {
            // Initialize SceneView.m_Parent field info.
            var bindFlags = BindingFlags.NonPublic | BindingFlags.Instance;
            if( _sceneViewParentField == null )
                _sceneViewParentField = typeof( SceneView ).GetField( "m_Parent",
                    bindFlags );
            // Get 'm_Parent' field:
            var parent = _sceneViewParentField.GetValue( sceneView );

            // The parent type (DockArea/GUIView) has the EnableVSync method. 
            // Note that the 'parent' changes whenever the SceneView is maximized/restored.
            // void EnableVSync(bool value);
            if( _enableVSync == null || _enableVSync.Target != parent )
            {
                var methodInfo = parent.GetType().GetMethod( "EnableVSync", bindFlags );
                _enableVSync = ( EnableVSyncDelegate )methodInfo.CreateDelegate(
                    typeof( EnableVSyncDelegate ), parent );
            }

            // Call the EnableVSync() method.
            _enableVSync?.Invoke( true );
        }

        // Cache the SceneView.m_Parent FieldInfo.
        static FieldInfo _sceneViewParentField;

        // Helper delegate to call internal EnableVSync() method on the SceneView parent.
        delegate void EnableVSyncDelegate( bool enable );

        // Cache the EnableVSync method.
        static EnableVSyncDelegate _enableVSync;
    }
}

This code adds the EnableVSync() extension method to the SceneView class. It needs to be called on a SceneView instance, but when it’s called matters. In addition to the setting being reset when exiting play mode, it’s also reset when maximizing the Scene view and in a few other cases. This makes it a bit difficult to fix generically without simply spamming the EnableVSync() call, but it can be done in an EditorApplication.update callback by checking to see if a Scene view window was recently focused or maximized / un-maximized. Something like:

// Copyright 2021 by Hextant Studios. https://HextantStudios.com
// Licensed under CC BY 4.0. http://creativecommons.org/licenses/by/4.0/
using UnityEditor;
using UnityEngine;

namespace Hextant.Editor
{
    // Using EditorSingleton<T>. See: https://hextantstudios.com/unity-singletons/
    public class SceneViewEnableVSync : EditorSingleton<SceneViewEnableVSync>
    {
        // Register for the global editor update.
        void OnEnabled() => EditorApplication.update += OnUpdate;

        void OnUpdate()
        {
            var sceneView = SceneView.lastActiveSceneView;

            // Determine if the SceneView has focus.
            if( sceneView == null || sceneView != EditorWindow.focusedWindow )
            {
                _focused = false;
            }
            // Re-enable VSync when focused or its maximized state has changed.
            else if( !_focused || _maximized != sceneView.maximized )
            {
                sceneView.EnableVSync();
                _focused = true;
            }
            _maximized = sceneView.maximized;
        }

        [System.NonSerialized] bool _focused;
        [System.NonSerialized] bool _maximized;
    }
}

(See: Editor Singleton. When I get more time, I’ll zip up a complete sample.)

As you can see, it’s not the easiest bug to fix, so if you know of a better way please let me know!

  • Note that this isn’t a “normal” VSync issue where the contents of the window tear visibly as you say rotate or move the camera. It appears to be related to how Unity throttle’s framerate and syncs updates and rendering in the editor.

  • Also note that it probably won’t help stuttering caused by low framerates.

  • And finally, I don’t have a FreeSync or GSync monitor to test this on, so I’m not sure how much it will help on these.