I'm a complete novice to Godot, looking to branch out after using Gamemaker since I was a little kid. I'm going through the "getting started" documentation (love that it's written, not video!), finished Listening to Player Input, and noticed definite input lag, maybe 30-40ms.

Some quick googling shows the common solution is disabling vsync and limiting FPS to 60, which gives good, snappy controls, and it's mostly visually smooth but every ten seconds or so I'll get a few seconds of choppy video. I've tried various vsync modes and framerate caps, as well as enabling fullscreen as suggested here but nothing seems to help, with the same results running in debug or as an exported project.

How do I get both smooth visuals and no input lag?

I'm using Godot 4.0 on Windows, C# with Visual Studio Code. This doesn't happen when working with GameMaker or Unity, nor with games made with them.

EDIT: I tried the same project in Godot 3.5 and it doesn't seem like there's any lag with vsync enabled. However, I get the same choppyness issue when running at a fixed 60FPS, even in fullscreen mode.

    You should handle input in process rather than physics process. For physics functions (like move and slide) it is supposed to be in physics process, but I found lower latency using process. Mouse camera control should always be in process.

    You can also use mailbox mode for vsync in Godot 4.0, which lowers latency as well.

      alfredbaudisch

      Thanks for the suggestion! They do already match however, each set to 60fps. Increasing them to 144, the max refresh on my monitor, makes the input lag worse with vsync, and makes the choppyness worse without.

      I should be clear, this isn't a massive amount of lag, maybe 40ms between pressing the key and seeing the change. Still, it's more than I get in GameMaker projects, and I don't recall seeing it in the small amount of time I messed with Unity, or in games made by others with those engines.

      cybereality I'm only on the second page of the "getting started" guide. This is the entirety of the only script in the project:

      using Godot;
      using System;
      
      public partial class sprite_2d : Sprite2D{
          private int Speed = 400;
          private float AngularSpeed = MathF.PI;
      
          public override void _Process(double delta){
              Input.UseAccumulatedInput = false;
              var direction = 0;
              
              if (Input.IsActionPressed("ui_left")){
                  direction = -1;
                  }
              if (Input.IsActionPressed("ui_right")){
                  direction = 1;
              }
              Rotation += AngularSpeed * direction * (float)delta;
      
              var velocity = Vector2.Zero;
              if (Input.IsActionPressed("ui_up")){
                  velocity = Vector2.Up.Rotated(Rotation) * Speed;
              }
              Position += velocity * (float)delta;
              }
      }

      It just moves the little built-in robot icon around, no physics or anything. I've also tried all three forms of vsync available.

      The button presses should be handled in the _Input function. They should set class variables you can read in _Process. That is likely the issue.

        cybereality
        Thanks for all the help! I'm now trying this script instead:

        using Godot;
        using System;
        
        public partial class sprite_2d : Sprite2D{
            private int Speed = 400;
            private float AngularSpeed = MathF.PI;
            private float direction = 0;
            private Vector2 velocity = Vector2.Zero;
        
            public override void _Input(InputEvent @event)
            {
                if (@event is InputEventKey keyEvent && keyEvent.Pressed){
                    if (keyEvent.Keycode == Key.Left){
                        direction -= 1;
                    }
                    if (keyEvent.Keycode == Key.Right){
                        direction += 1;
                    }
                    if (keyEvent.Keycode == Key.Up){
                        velocity = Vector2.Up.Rotated(Rotation) * Speed;
                    }
                }
            }
        
            public override void _Process(double delta){
                Input.UseAccumulatedInput = false;
                
                Rotation += AngularSpeed * direction * (float)delta;
                direction = 0;
                
                Position += velocity * (float)delta;
                velocity = Vector2.Zero;
                }
        }

        I'm sure I'm just not understanding how to use the _Input event - there's little documentation on it, or at least it's difficult to find - but this has it's own set of problems: only one key input is registered at a time, and when you hold down a key it moves for one frame, then pauses for a couple frames, then continues moving smoothly, like when you hold down a key in a text editor. On top of that, there's still the same input delay, small but noticeable with vsync, gone but with occasionally choppy visuals without.

        But if using the Input singleton causes a delay in player input (I was told the same thing on Reddit), why have it in the first place, and encourage it's use in official tutorials and documentation?

        This is GDScript, but it should be similar in C#. You have to set input maps in the project settings (second tab).

        func _input(event):
            if event.is_action_pressed("left"):
                direction -= 1;
            if event.is_action_pressed("right"):
                direction += 1;

        You define the words "left" and "right" to be whatever in the input map. You should never use keycodes, it's there for advanced usage but not for game control.

          cybereality
          I replaced it with this:

          public override void _Input(InputEvent @event)
              {
                  if (@event.IsAction("ui_left")){
                      direction -= 1;
                  }
                  if (@event.IsAction("ui_right")){
                      direction += 1;
                  }
                  if (@event.IsAction("ui_up")){
                      velocity = Vector2.Up.Rotated(Rotation) * Speed;
                  }
              }

          and get the exact same results as with the last script I posted. If I use IsActionPressed instead of IsAction, it's the same thing except the icon only moves on the initial keypress, not when held down.

          Yes, is action pressed is only for the initial button. You can set a boolean (like left_is_pressed = true) and then check it in process. This also helps if you want to handle multiple key presses.

          But your way is probably fine as well. What operating system are you using. I haven't really noticed any major lag so it might be a OS specific thing, or even something with your keyboard.

            cybereality It's not major lag, as I said about 40ms, but it's enough to notice, especially if it were in a fast-paced action game.

            I'd be surprised if it were my system - as I said, I don't see this kind of lag when using other engines, or when playing games made with them. It also goes away in Godot when I disable vsync, but again, that causes choppyness even when locking the game to 60fps and running the game in fullscreen (which again, I don't see in other engines).

            Can you just list your system, OS and version, video card, CPU, your monitor type resolution and refresh rate, maybe the keyboard model?

              cybereality Sure. Windows 10, Radeon RX 580, Ryzen 3 2200G. Monitor is AOC Agon AG241QG4, 1440p at 144hz with freesync. Keyboard is a Das Keyboard 4 (I got it on sale refurbished nearly a decade ago, it's a great keyboard but I wouldn't pay full price for one)

              Okay, that system is good. You shouldn't cap at 60 fps as that will only work on 60 Hz monitors. If you have a high refresh monitor with freesync you should use Riva Tuner to cap at 2 frames below your monitor refresh (so 142 for you). Some people said you should disable VSync in game, and force it on in the Nvidia/AMD control panel, but I'm not sure if this is still needed (but feel free to try). Enabling VSync always adds some lag versus VSync off, but with FreeSync and the settings I just mentioned, it should only be a few ms difference (below 10 ms). In Godot 4.0 you can also enable Mailbox sync mode, which should be the lowest lag possible in combination with FreeSync.

              Hmm.. I think is see what the issue is. My other demos were more complex with 3D and physics. If I just try to change the position of a sprite I think I see the lag you're talking about. It doesn't feel as smooth as Godot 3.x.

              See this demo project I made and just press the space button to jump: https://godotforums.org/assets/files/2023-03-11/1678525216-38166-jumplag.zip

              My guess is that Godot 4.0 was designed to use the physics functions for movement (CharacterBody and functions like MoveAndSlide, which work). Manual sprite positioning would not be used for player or enemy movement but maybe for background effects.

                cybereality Your project shows it exactly! And like in mine, the lag goes away if you turn off vsync in the project settings. Thanks for helping show I'm not crazy, at least.

                I can think of lots of situations and styles of game where I'd prefer not to use physics and instead have pixel-perfect control over movement. Beyond that, even games that use physics for all movement would still have this lag when using special abilities, navigating menus, etc. This seems like a pretty egregious thing, and isn't present in 3.5. Is this maybe just the growing pains of 4.0, something that will likely be fixed soon, or is there somewhere I can report this as an issue to be fixed?

                @RhyminGarfunkle It's probably worth reporting to the issuer tracker. But it's definitely something that need a reproduction project, set up to both show the issue and steps to work-around/fix it.

                @cybereality Think your minimal project could do that?

                Yes, feel free to use my project as a minimal reproduction.

                I also noticed (in my other demo) that the project is smoother if I export it and close the Godot editor before running. Something about having the Godot editor open breaks FreeSync from working.