Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Game Development
  4. Smooth character movement

Smooth character movement

Scheduled Pinned Locked Moved Unsolved Game Development
spritemove
27 Posts 6 Posters 12.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • 8 Offline
    8 Offline
    8Observer8
    wrote on 5 Sept 2022, 23:51 last edited by 8Observer8 9 May 2022, 23:58
    #10

    I am rewriting my sample game with SDL2 + OpenGL 2.1. I made the same example as above. As you can see, this is better than the Qt demo. But WebGL is the best. I expected SDL2 to be as good as WebGL. You can download EXE for Windows 10 64-bit. Use wad-keys or arrow-keys (or spacebar to jump)

    • SDL2 + OpenGL 2.1 + GLM. Demo for Windows 10 64 bit: Jumps_Box2DOpenGL2_SDL2Cpp.zip (3.21 MB)

    ad42312d-b727-4118-9fec-4fd676b00fff-image.png

    I use Free Texture Packer and Tiled Map Editor to load textures and level/colliders from the JSON file. You can see Qt6Core.dll in SDL2 project because I use QResource, QJson and QFile:

    73fac6a9-fb74-463c-9fa9-76735d8cdeb3-image.png

    1 Reply Last reply
    1
    • 8 Offline
      8 Offline
      8Observer8
      wrote on 6 Sept 2022, 00:09 last edited by 8Observer8 9 Jun 2022, 08:31
      #11

      I should note that all three demos above use 60 fps. I use QTimer and QElapsedTimer for game loop. What are the options for improving the game loop? Maybe because of the message queue in Qt it is not possible to improve it?

      #include <QtCore/QElapsedTimer>
      #include <QtCore/QTimer>
      
      /* ... */
      QElapsedTimer m_elapsedTimer;
      QTimer m_timer;
      float m_deltaTime;
      /* ... */
      
      /* ... */
      connect(&m_timer, &QTimer::timeout, this, &Widget::animationLoop);
      /* ... */
      
      /* ... */
      m_elapsedTimer.start();
      m_timer.start(1000.f / 60.f);
      /* ... */
      
      /* ... */
      void Widget::paintGL()
      {
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      /* ... */
      
      void Widget::animationLoop()
      {
          m_deltaTime = m_elapsedTimer.elapsed() / 1000.f;
          m_elapsedTimer.restart();
      
          if (m_isLeftPressed)
          {
              b2Vec2 vel = m_pPlayerBody->GetLinearVelocity();
              vel.x = -5.f;
              m_pPlayerBody->SetLinearVelocity(vel);
          }
      
          if (m_isRightPressed)
          {
              b2Vec2 vel = m_pPlayerBody->GetLinearVelocity();
              vel.x = 5.f;
              m_pPlayerBody->SetLinearVelocity(vel);
          }
      
          if (!m_isLeftPressed && !m_isRightPressed)
          {
              b2Vec2 vel = m_pPlayerBody->GetLinearVelocity();
              vel.x = 0.f;
              m_pPlayerBody->SetLinearVelocity(vel);
          }
      
          m_pWorld->Step(m_deltaTime, 6, 2);
      
          update();
      }
      
      1 Reply Last reply
      0
      • C Offline
        C Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 6 Sept 2022, 00:44 last edited by Chris Kawa 9 Jun 2022, 00:45
        #12

        @8Observer8 You are not rendering at 60 FPS. You are scheduling an update and it's only at around 60 times a second. Timers are not precise enough to get a steady clock like that and they are not synchronized in any way whatsoever with the monitor's refresh rate. Because of that every few frames you're missing the vertical sync by just a little and that is a skipped frame that results in the choppy animation.

        The only way to get a smooth animation like that is to not use timer at all and tie yourself to the vertical refresh instead. Qt has multiple ways to draw graphics on screen and sadly not all of them expose this moment to you, but if you're using a QOpenGLWindow there's a frameSwapped() signal that is emitted after a vertical sync. Connect update() to it and inside the paint event handler calculate the amount of time passed from last update (QElapsedTimer is fine for that).

        Keep in mind that this signal will be fired at the native refresh rate of the monitor, so for example on a 165Hz screen it will be emitted 165 times a second. If your game can keep up with that that's great, but if not you'll have to calculate a reasonable rate at which you update. For example if the monitor refreshes at 120Hz and you want to update at 60Hz you'll have to skip every other frame. For smooth animation the frequency at which you update needs to be an integer divisor of the monitor's refresh rate e.g. for a 60Hz monitor you can update at 60, 30, 20, 15 etc. Everything between will either skip or repeat frames and will look choppy.

        Also note that refreshing at a fixed rate like 60 is not a good idea. For example on a 165Hz monitor a 60 updates per second would result in a frame every 2.75 vertical syncs and that obviously won't work smoothly. You'll again either lag behind or skip ahead and get a choppy animation. You need to figure out monitor's refresh rate and choose your update rate accordingly.

        1 Reply Last reply
        3
        • 8 Offline
          8 Offline
          8Observer8
          wrote on 6 Sept 2022, 09:28 last edited by 8Observer8 9 Jun 2022, 09:34
          #13

          @Chris-Kawa thank you very much! I found your reply here that has another very useful links on your replies. I will rewrite my example with QOpenGLWindow instead of QOpenGLWidget. I will explore your idea with the frameSwapped() signal. Thanks again!

          1 Reply Last reply
          0
          • C Offline
            C Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on 6 Sept 2022, 15:00 last edited by
            #14

            @8Observer8 No problem. You might find this old post useful too, at least for visualization of what happens. The context is a bit different. It was about drawing with QPainter, so on the CPU, but the idea is the same. Your case falls into the 17ms example category there - being occasionally just a little too late for the refresh.

            1 Reply Last reply
            2
            • 8 Offline
              8 Offline
              8Observer8
              wrote on 7 Sept 2022, 09:56 last edited by 8Observer8 9 Jul 2022, 09:57
              #15

              @Chris-Kawa said in Smooth character movement:

              but if you're using a QOpenGLWindow there's a frameSwapped() signal that is emitted after a vertical sync. Connect update() to it and inside the paint event handler calculate the amount of time passed from last update (QElapsedTimer is fine for that)

              I made this part in this simple example, source code: QOpenGLWindow_frameSwapped.zip with movement a square with WASD or arrow keys. The first problem is that movement does not start at once (you should to try run it to understand) and the second problem is the same - the choppy movement.

              I connected the frameSwapped signal with the update slot:

              connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
              

              9da9b68a-1e98-4223-b32a-dad23876067b-image.png

              @Chris-Kawa said in Smooth character movement:

              Keep in mind that this signal will be fired at the native refresh rate of the monitor, so for example on a 165Hz screen it will be emitted 165 times a second. If your game can keep up with that that's great, but if not you'll have to calculate a reasonable rate at which you update. For example if the monitor refreshes at 120Hz and you want to update at 60Hz you'll have to skip every other frame. For smooth animation the frequency at which you update needs to be an integer divisor of the monitor's refresh rate e.g. for a 60Hz monitor you can update at 60, 30, 20, 15 etc. Everything between will either skip or repeat frames and will look choppy.

              I printed the delta time, but it is 5-6 ms. It's 166-200 frames per second. I do not understand what to do now, but I will study your messages very carefully.

              QOpenGLWindow_frameSwapped.pro

              QT       += core gui opengl
              
              QT += widgets
              
              CONFIG += c++11
              
              SOURCES += \
                  OpenGLWindow.cpp \
                  main.cpp
              
              HEADERS += \
                  OpenGLWindow.h
              
              RESOURCES += \
                  Shaders.qrc
              

              OpenGLWindow.h

              #ifndef OPENGLWINDOW_H
              #define OPENGLWINDOW_H
              
              #include <QtCore/QElapsedTimer>
              #include <QtGui/QOpenGLFunctions>
              #include <QtOpenGL/QOpenGLWindow>
              #include <QtOpenGL/QOpenGLShaderProgram>
              #include <QtOpenGL/QOpenGLBuffer>
              #include <QtGui/QMatrix4x4>
              #include <QtGui/QKeyEvent>
              
              class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions
              {
                  Q_OBJECT
              
              public:
                  OpenGLWindow();
                  ~OpenGLWindow();
              
              private:
                  void initializeGL() override;
                  void resizeGL(int w, int h) override;
                  void paintGL() override;
              
                  void keyPressEvent(QKeyEvent *event) override;
              
              private:
                  QOpenGLShaderProgram m_program;
                  QOpenGLBuffer m_vertBuffer;
              
                  int m_uMvpMatrixLocation;
                  QMatrix4x4 m_projMatrix;
                  QMatrix4x4 m_viewMatrix;
                  QMatrix4x4 m_projViewMatrix;
                  QMatrix4x4 m_modelMatrix;
                  QMatrix4x4 m_mvpMatrix;
                  float m_x = 50.f;
                  float m_y = 50.f;
                  float m_speed = 200.f;
                  QElapsedTimer m_elapsedTimer;
                  float m_dt;
              };
              #endif // OPENGLWINDOW_H
              

              OpenGLWindow.cpp

              #include "OpenGLWindow.h"
              
              OpenGLWindow::OpenGLWindow()
              {
                  resize(500, 500);
                  setTitle("QOpenGLWindow, OpenGL 2.1, C++");
              }
              
              OpenGLWindow::~OpenGLWindow()
              {
              }
              
              void OpenGLWindow::initializeGL()
              {
                  initializeOpenGLFunctions();
                  glClearColor(0.2f, 0.2f, 0.2f, 1.f);
              
                  connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
              
                  m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/assets/shaders/default.vert");
                  m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/assets/shaders/default.frag");
                  m_program.link();
              
                  float vertPositions[] = {
                      -0.5f, -0.5f, 0.f,
                      -0.5f, 0.5f, 0.f,
                      0.5f, -0.5f, 0.f,
                      0.5f, -0.5f, 0.f,
                      -0.5f, 0.5f, 0.f,
                      0.5f, 0.5f, 0.f
                  };
              
                  m_vertBuffer.create();
                  m_vertBuffer.bind();
                  m_vertBuffer.allocate(vertPositions, sizeof(vertPositions));
              
                  m_program.bind();
                  int aPositionLocation = m_program.attributeLocation("aPosition");
                  m_program.setAttributeBuffer(aPositionLocation, GL_FLOAT, 0, 3);
                  m_program.enableAttributeArray(aPositionLocation);
                  m_uMvpMatrixLocation = m_program.uniformLocation("uMvpMatrix");
              
                  m_projMatrix.ortho(0.f, 100.f, 100.f, 0.f, 50.f, 0.f);
                  m_viewMatrix.lookAt(QVector3D(0.f, 0.f, 40.f), QVector3D(0.f, 0.f, 0.f), QVector3D(0.f, 1.f, 0.f));
                  m_projViewMatrix = m_projMatrix * m_viewMatrix;
              
                  m_elapsedTimer.start();
              }
              
              void OpenGLWindow::resizeGL(int w, int h)
              {
                  glViewport(0, 0, w, h);
              }
              
              void OpenGLWindow::paintGL()
              {
                  glClear(GL_COLOR_BUFFER_BIT);
              
                  m_dt = m_elapsedTimer.elapsed() / 1000.f;
                  m_elapsedTimer.restart();
              
                  m_modelMatrix.setToIdentity();
                  m_modelMatrix.translate(m_x, m_y, 0.f);
                  m_modelMatrix.scale(5.f, 5.f, 1.f);
              
                  m_mvpMatrix = m_projViewMatrix * m_modelMatrix;
                  m_program.bind();
                  m_program.setUniformValue(m_uMvpMatrixLocation, m_mvpMatrix);
                  glDrawArrays(GL_TRIANGLES, 0, 6);
              }
              
              void OpenGLWindow::keyPressEvent(QKeyEvent *event)
              {
                  switch (event->key())
                  {
                      case Qt::Key::Key_W:
                      case Qt::Key::Key_Up:
                      {
                          m_y -= m_speed * m_dt;
                          break;
                      }
                      case Qt::Key::Key_A:
                      case Qt::Key::Key_Left:
                      {
                          m_x -= m_speed * m_dt;
                          break;
                      }
                      case Qt::Key::Key_S:
                      case Qt::Key::Key_Down:
                      {
                          m_y += m_speed * m_dt;
                          break;
                      }
                      case Qt::Key::Key_D:
                      case Qt::Key::Key_Right:
                      {
                          m_x += m_speed * m_dt;
                          break;
                      }
                  }
              }
              

              main.cpp

              #include "OpenGLWindow.h"
              
              #include <QtWidgets/QApplication>
              
              int main(int argc, char *argv[])
              {
                  QApplication a(argc, argv);
                  OpenGLWindow w;
                  w.show();
                  return a.exec();
              }
              

              default.vert

              
              attribute vec3 aPosition;
              uniform mat4 uMvpMatrix;
              
              void main()
              {
                  gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
              }
              

              default.frag

              
              void main()
              {
                  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
              }
              
              1 Reply Last reply
              0
              • C Offline
                C Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on 7 Sept 2022, 17:12 last edited by Chris Kawa 9 Jul 2022, 17:18
                #16

                First of all you're doing your input handling wrong. Different keyboards have a different signal frequency. Second, if you press and hold a key you will get an immediate key press event, then a pause and then consecutive key presses in a steady interval, which also varies from keyboard to keyboard and can be changed in Windows settings. That's because keyboards are made for typing. Game input is not their primary role.
                Apart from all that keyboard events are, again, not at all synced with your monitor refresh rate. The way you have it now your character moves at your keyboard update rate and will vary with different keyboards, which is not good at all.

                To handle input correctly you would make an array or a map of key states (bools). At key press you set a given key to true, on release you set it to false. In your game update code, which is synced to your monitor's refresh rate, you query that state. This way you're updating the game independently of the input frequency. You're just checking if the key is pressed at the time of the update.

                See this other old thread for an example code how to handle keyboard for gaming input correctly.

                The other thing is - make sure you actually have vertical synchronization turned on. In vanilla OpenGL this is done through platform specific extensions like WGL_EXT_swap_control, but Qt has it abstracted as QSurfaceFormat::setSwapInterval(). In your class constructor set a format that has swap interval set to 1, which means you will get the frameSwapped signal at monitor's refresh rate. If you set it to 0 you will get that signal as fast as is possible i.e. a lot faster than your monitor actually refreshes. With a value of 2 you will get a signal every other refresh, with 3 every third and so on.

                1 Reply Last reply
                2
                • 8 Offline
                  8 Offline
                  8Observer8
                  wrote on 8 Sept 2022, 16:07 last edited by 8Observer8 9 Aug 2022, 16:17
                  #17

                  @Chris-Kawa said in Big Issue with Qt key inputs for gaming:

                  Sorry if I made it sound scarry. It's actually as easy as this:

                  class MyWidget : public QWidget
                  {
                      Q_OBJECT
                  public:
                      MyWidget()
                      { setFocusPolicy(Qt::StrongFocus); startTimer(1000/60); }
                  
                      void keyPressEvent(QKeyEvent *e)
                      { keys[e->key()] = true; QWidget::keyPressEvent(e); }
                  
                      void keyReleaseEvent(QKeyEvent *e)
                      { keys[e->key()] = false; QWidget::keyReleaseEvent(e); }
                  
                      void timerEvent(QTimerEvent *)
                      { if(keys[Qt::Key_Up]) /* some game logic */; }
                  
                  private:
                      QMap<int, bool> keys;
                  };
                  

                  As you can see input can be put into the map from wherever, key events, mouse events, gamepad or whatever.
                  Of course there are considerations, like map performance or accessing the map in multi-threaded environment but that's the basic idea.

                  EDIT: I don't know Python that well but you should be able to translate it easily

                  So far I've only tried this method on my sample. It's already much better than it used to be:

                  • EXE for Window 10, 64 bit: KeysQMap_OpenGL2_Qt6_Cpp_EXE.rar (6 MB)
                  • Source code: KeysQMap_OpenGL2_Qt6_Cpp_Source.rar (2.84 KB)

                  KeysQMap_OpenGL2_Qt6_Cpp.pro

                  QT += core gui opengl
                  
                  QT += widgets
                  
                  CONFIG += c++11
                  
                  SOURCES += \
                      OpenGLWindow.cpp \
                      main.cpp
                  
                  HEADERS += \
                      OpenGLWindow.h
                  
                  RESOURCES += \
                      Shaders.qrc
                  

                  OpenGLWindow.h

                  #ifndef OPENGLWINDOW_H
                  #define OPENGLWINDOW_H
                  
                  #include <QtCore/QElapsedTimer>
                  #include <QtCore/QMap>
                  #include <QtGui/QOpenGLFunctions>
                  #include <QtOpenGL/QOpenGLWindow>
                  #include <QtOpenGL/QOpenGLShaderProgram>
                  #include <QtOpenGL/QOpenGLBuffer>
                  #include <QtGui/QMatrix4x4>
                  #include <QtGui/QKeyEvent>
                  
                  class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions
                  {
                      Q_OBJECT
                  
                  public:
                      OpenGLWindow(QWindow *parent = 0);
                      ~OpenGLWindow();
                  
                  private:
                      void initializeGL() override;
                      void resizeGL(int w, int h) override;
                      void paintGL() override;
                  
                      void keyPressEvent(QKeyEvent *event) override;
                      void keyReleaseEvent(QKeyEvent *event) override;
                  
                  private:
                      QOpenGLShaderProgram m_program;
                      QOpenGLBuffer m_vertBuffer;
                  
                      int m_uMvpMatrixLocation;
                      QMatrix4x4 m_projMatrix;
                      QMatrix4x4 m_viewMatrix;
                      QMatrix4x4 m_projViewMatrix;
                      QMatrix4x4 m_modelMatrix;
                      QMatrix4x4 m_mvpMatrix;
                      float m_x = 50.f;
                      float m_y = 50.f;
                      float m_speed = 50.f;
                      QElapsedTimer m_elapsedTimer;
                      float m_dt;
                  
                      QMap<int, bool> m_keys;
                  };
                  #endif // OPENGLWINDOW_H
                  

                  OpenGLWindow.cpp

                  #include "OpenGLWindow.h"
                  
                  OpenGLWindow::OpenGLWindow(QWindow *parent)
                      : QOpenGLWindow(NoPartialUpdate, parent)
                  {
                      resize(500, 500);
                      setTitle("QOpenGLWindow, OpenGL 2.1, C++");
                  }
                  
                  OpenGLWindow::~OpenGLWindow()
                  {
                  }
                  
                  void OpenGLWindow::initializeGL()
                  {
                      initializeOpenGLFunctions();
                      glClearColor(0.2f, 0.2f, 0.2f, 1.f);
                  
                      connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
                  
                      m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/assets/shaders/default.vert");
                      m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/assets/shaders/default.frag");
                      m_program.link();
                  
                      float vertPositions[] = {
                          -0.5f, -0.5f, 0.f,
                          -0.5f, 0.5f, 0.f,
                          0.5f, -0.5f, 0.f,
                          0.5f, -0.5f, 0.f,
                          -0.5f, 0.5f, 0.f,
                          0.5f, 0.5f, 0.f
                      };
                  
                      m_vertBuffer.create();
                      m_vertBuffer.bind();
                      m_vertBuffer.allocate(vertPositions, sizeof(vertPositions));
                  
                      m_program.bind();
                      int aPositionLocation = m_program.attributeLocation("aPosition");
                      m_program.setAttributeBuffer(aPositionLocation, GL_FLOAT, 0, 3);
                      m_program.enableAttributeArray(aPositionLocation);
                      m_uMvpMatrixLocation = m_program.uniformLocation("uMvpMatrix");
                  
                      m_projMatrix.ortho(0.f, 100.f, 100.f, 0.f, 50.f, 0.f);
                      m_viewMatrix.lookAt(QVector3D(0.f, 0.f, 40.f), QVector3D(0.f, 0.f, 0.f), QVector3D(0.f, 1.f, 0.f));
                      m_projViewMatrix = m_projMatrix * m_viewMatrix;
                  
                      m_elapsedTimer.start();
                  }
                  
                  void OpenGLWindow::resizeGL(int w, int h)
                  {
                      glViewport(0, 0, w, h);
                  }
                  
                  void OpenGLWindow::paintGL()
                  {
                      glClear(GL_COLOR_BUFFER_BIT);
                  
                      m_dt = m_elapsedTimer.elapsed() / 1000.f;
                      m_elapsedTimer.restart();
                  
                      if (m_keys[Qt::Key::Key_W] || m_keys[Qt::Key::Key_Up])
                      {
                          m_y -= m_speed * m_dt;
                      }
                      if (m_keys[Qt::Key::Key_A] || m_keys[Qt::Key::Key_Left])
                      {
                          m_x -= m_speed * m_dt;
                      }
                      if (m_keys[Qt::Key::Key_S] || m_keys[Qt::Key::Key_Down])
                      {
                          m_y += m_speed * m_dt;
                      }
                      if (m_keys[Qt::Key::Key_D] || m_keys[Qt::Key::Key_Right])
                      {
                          m_x += m_speed * m_dt;
                      }
                  
                      m_modelMatrix.setToIdentity();
                      m_modelMatrix.translate(m_x, m_y, 0.f);
                      m_modelMatrix.scale(5.f, 5.f, 1.f);
                  
                      m_mvpMatrix = m_projViewMatrix * m_modelMatrix;
                      m_program.bind();
                      m_program.setUniformValue(m_uMvpMatrixLocation, m_mvpMatrix);
                      glDrawArrays(GL_TRIANGLES, 0, 6);
                  }
                  
                  void OpenGLWindow::keyPressEvent(QKeyEvent *event)
                  {
                      switch (event->key())
                      {
                          case Qt::Key::Key_W:
                          case Qt::Key::Key_Up:
                          {
                              m_keys[Qt::Key::Key_W] = true;
                              m_keys[Qt::Key::Key_Up] = true;
                              break;
                          }
                          case Qt::Key::Key_A:
                          case Qt::Key::Key_Left:
                          {
                              m_keys[Qt::Key::Key_A] = true;
                              m_keys[Qt::Key::Key_Left] = true;
                              break;
                          }
                          case Qt::Key::Key_S:
                          case Qt::Key::Key_Down:
                          {
                              m_keys[Qt::Key::Key_S] = true;
                              m_keys[Qt::Key::Key_Down] = true;
                              break;
                          }
                          case Qt::Key::Key_D:
                          case Qt::Key::Key_Right:
                          {
                              m_keys[Qt::Key::Key_D] = true;
                              m_keys[Qt::Key::Key_Right] = true;
                              break;
                          }
                      }
                  }
                  
                  void OpenGLWindow::keyReleaseEvent(QKeyEvent *event)
                  {
                      switch (event->key())
                      {
                          case Qt::Key::Key_W:
                          case Qt::Key::Key_Up:
                          {
                              m_keys[Qt::Key::Key_W] = false;
                              m_keys[Qt::Key::Key_Up] = false;
                              break;
                          }
                          case Qt::Key::Key_A:
                          case Qt::Key::Key_Left:
                          {
                              m_keys[Qt::Key::Key_A] = false;
                              m_keys[Qt::Key::Key_Left] = false;
                              break;
                          }
                          case Qt::Key::Key_S:
                          case Qt::Key::Key_Down:
                          {
                              m_keys[Qt::Key::Key_S] = false;
                              m_keys[Qt::Key::Key_Down] = false;
                              break;
                          }
                          case Qt::Key::Key_D:
                          case Qt::Key::Key_Right:
                          {
                              m_keys[Qt::Key::Key_D] = false;
                              m_keys[Qt::Key::Key_Right] = false;
                              break;
                          }
                      }
                  }
                  

                  main.cpp

                  #include "OpenGLWindow.h"
                  
                  #include <QtWidgets/QApplication>
                  
                  int main(int argc, char *argv[])
                  {
                      QApplication a(argc, argv);
                      OpenGLWindow w;
                      w.show();
                      return a.exec();
                  }
                  

                  default.vert

                  
                  attribute vec3 aPosition;
                  uniform mat4 uMvpMatrix;
                  
                  void main()
                  {
                      gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
                  }
                  

                  default.frag

                  
                  void main()
                  {
                      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
                  }
                  
                  1 Reply Last reply
                  0
                  • 8 Offline
                    8 Offline
                    8Observer8
                    wrote on 8 Sept 2022, 16:39 last edited by
                    #18

                    @Chris-Kawa said in Smooth character movement:

                    The other thing is - make sure you actually have vertical synchronization turned on. In vanilla OpenGL this is done through platform specific extensions like WGL_EXT_swap_control, but Qt has it abstracted as QSurfaceFormat::setSwapInterval(). In your class constructor set a format that has swap interval set to 1, which means you will get the frameSwapped signal at monitor's refresh rate. If you set it to 0 you will get that signal as fast as is possible i.e. a lot faster than your monitor actually refreshes. With a value of 2 you will get a signal every other refresh, with 3 every third and so on.

                    Seems to be a little better.

                    • EXE for Window 10, 64 bit: setSwapInterval_OpenGL2_Qt6_Cpp_EXE.rar (6 MB)
                    • Source code: setSwapInterval_OpenGL2_Qt6_Cpp_Source.rar (3 KB)

                    setSwapInterval_OpenGL2_Qt6_Cpp.pro

                    QT += core gui opengl
                    
                    QT += widgets
                    
                    CONFIG += c++11
                    
                    SOURCES += \
                        OpenGLWindow.cpp \
                        main.cpp
                    
                    HEADERS += \
                        OpenGLWindow.h
                    
                    RESOURCES += \
                        Shaders.qrc
                    

                    OpenGLWindow.h

                    #ifndef OPENGLWINDOW_H
                    #define OPENGLWINDOW_H
                    
                    #include <QtCore/QElapsedTimer>
                    #include <QtCore/QMap>
                    #include <QtGui/QOpenGLFunctions>
                    #include <QtOpenGL/QOpenGLWindow>
                    #include <QtOpenGL/QOpenGLShaderProgram>
                    #include <QtOpenGL/QOpenGLBuffer>
                    #include <QtGui/QMatrix4x4>
                    #include <QtGui/QKeyEvent>
                    
                    class OpenGLWindow : public QOpenGLWindow, private QOpenGLFunctions
                    {
                        Q_OBJECT
                    
                    public:
                        OpenGLWindow(QWindow *parent = 0);
                        ~OpenGLWindow();
                    
                    private:
                        void initializeGL() override;
                        void resizeGL(int w, int h) override;
                        void paintGL() override;
                    
                        void keyPressEvent(QKeyEvent *event) override;
                        void keyReleaseEvent(QKeyEvent *event) override;
                    
                    private:
                        QOpenGLShaderProgram m_program;
                        QOpenGLBuffer m_vertBuffer;
                    
                        int m_uMvpMatrixLocation;
                        QMatrix4x4 m_projMatrix;
                        QMatrix4x4 m_viewMatrix;
                        QMatrix4x4 m_projViewMatrix;
                        QMatrix4x4 m_modelMatrix;
                        QMatrix4x4 m_mvpMatrix;
                        float m_x = 50.f;
                        float m_y = 50.f;
                        float m_speed = 50.f;
                        QElapsedTimer m_elapsedTimer;
                        float m_dt;
                    
                        QMap<int, bool> m_keys;
                    };
                    #endif // OPENGLWINDOW_H
                    

                    OpenGLWindow.cpp

                    #include "OpenGLWindow.h"
                    
                    OpenGLWindow::OpenGLWindow(QWindow *parent)
                        : QOpenGLWindow(NoPartialUpdate, parent)
                    {
                        resize(500, 500);
                        setTitle("QOpenGLWindow, OpenGL 2.1, C++");
                    
                        QSurfaceFormat format;
                        format.setSwapInterval(1);
                        setFormat(format);
                    }
                    
                    OpenGLWindow::~OpenGLWindow()
                    {
                    }
                    
                    void OpenGLWindow::initializeGL()
                    {
                        initializeOpenGLFunctions();
                        glClearColor(0.2f, 0.2f, 0.2f, 1.f);
                    
                        connect(this, SIGNAL(frameSwapped()), this, SLOT(update()));
                    
                        m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/assets/shaders/default.vert");
                        m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/assets/shaders/default.frag");
                        m_program.link();
                    
                        float vertPositions[] = {
                            -0.5f, -0.5f, 0.f,
                            -0.5f, 0.5f, 0.f,
                            0.5f, -0.5f, 0.f,
                            0.5f, -0.5f, 0.f,
                            -0.5f, 0.5f, 0.f,
                            0.5f, 0.5f, 0.f
                        };
                    
                        m_vertBuffer.create();
                        m_vertBuffer.bind();
                        m_vertBuffer.allocate(vertPositions, sizeof(vertPositions));
                    
                        m_program.bind();
                        int aPositionLocation = m_program.attributeLocation("aPosition");
                        m_program.setAttributeBuffer(aPositionLocation, GL_FLOAT, 0, 3);
                        m_program.enableAttributeArray(aPositionLocation);
                        m_uMvpMatrixLocation = m_program.uniformLocation("uMvpMatrix");
                    
                        m_projMatrix.ortho(0.f, 100.f, 100.f, 0.f, 50.f, 0.f);
                        m_viewMatrix.lookAt(QVector3D(0.f, 0.f, 40.f), QVector3D(0.f, 0.f, 0.f), QVector3D(0.f, 1.f, 0.f));
                        m_projViewMatrix = m_projMatrix * m_viewMatrix;
                    
                        m_elapsedTimer.start();
                    }
                    
                    void OpenGLWindow::resizeGL(int w, int h)
                    {
                        glViewport(0, 0, w, h);
                    }
                    
                    void OpenGLWindow::paintGL()
                    {
                        glClear(GL_COLOR_BUFFER_BIT);
                    
                        m_dt = m_elapsedTimer.elapsed() / 1000.f;
                        m_elapsedTimer.restart();
                    
                        if (m_keys[Qt::Key::Key_W] || m_keys[Qt::Key::Key_Up])
                        {
                            m_y -= m_speed * m_dt;
                        }
                        if (m_keys[Qt::Key::Key_A] || m_keys[Qt::Key::Key_Left])
                        {
                            m_x -= m_speed * m_dt;
                        }
                        if (m_keys[Qt::Key::Key_S] || m_keys[Qt::Key::Key_Down])
                        {
                            m_y += m_speed * m_dt;
                        }
                        if (m_keys[Qt::Key::Key_D] || m_keys[Qt::Key::Key_Right])
                        {
                            m_x += m_speed * m_dt;
                        }
                    
                        m_modelMatrix.setToIdentity();
                        m_modelMatrix.translate(m_x, m_y, 0.f);
                        m_modelMatrix.scale(5.f, 5.f, 1.f);
                    
                        m_mvpMatrix = m_projViewMatrix * m_modelMatrix;
                        m_program.bind();
                        m_program.setUniformValue(m_uMvpMatrixLocation, m_mvpMatrix);
                        glDrawArrays(GL_TRIANGLES, 0, 6);
                    }
                    
                    void OpenGLWindow::keyPressEvent(QKeyEvent *event)
                    {
                        switch (event->key())
                        {
                            case Qt::Key::Key_W:
                            case Qt::Key::Key_Up:
                            {
                                m_keys[Qt::Key::Key_W] = true;
                                m_keys[Qt::Key::Key_Up] = true;
                                break;
                            }
                            case Qt::Key::Key_A:
                            case Qt::Key::Key_Left:
                            {
                                m_keys[Qt::Key::Key_A] = true;
                                m_keys[Qt::Key::Key_Left] = true;
                                break;
                            }
                            case Qt::Key::Key_S:
                            case Qt::Key::Key_Down:
                            {
                                m_keys[Qt::Key::Key_S] = true;
                                m_keys[Qt::Key::Key_Down] = true;
                                break;
                            }
                            case Qt::Key::Key_D:
                            case Qt::Key::Key_Right:
                            {
                                m_keys[Qt::Key::Key_D] = true;
                                m_keys[Qt::Key::Key_Right] = true;
                                break;
                            }
                        }
                    }
                    
                    void OpenGLWindow::keyReleaseEvent(QKeyEvent *event)
                    {
                        switch (event->key())
                        {
                            case Qt::Key::Key_W:
                            case Qt::Key::Key_Up:
                            {
                                m_keys[Qt::Key::Key_W] = false;
                                m_keys[Qt::Key::Key_Up] = false;
                                break;
                            }
                            case Qt::Key::Key_A:
                            case Qt::Key::Key_Left:
                            {
                                m_keys[Qt::Key::Key_A] = false;
                                m_keys[Qt::Key::Key_Left] = false;
                                break;
                            }
                            case Qt::Key::Key_S:
                            case Qt::Key::Key_Down:
                            {
                                m_keys[Qt::Key::Key_S] = false;
                                m_keys[Qt::Key::Key_Down] = false;
                                break;
                            }
                            case Qt::Key::Key_D:
                            case Qt::Key::Key_Right:
                            {
                                m_keys[Qt::Key::Key_D] = false;
                                m_keys[Qt::Key::Key_Right] = false;
                                break;
                            }
                        }
                    }
                    

                    main.cpp

                    #include "OpenGLWindow.h"
                    
                    #include <QtWidgets/QApplication>
                    //#include <QtGui/QSurfaceFormat>
                    
                    int main(int argc, char *argv[])
                    {
                        QApplication a(argc, argv);
                        OpenGLWindow w;
                    
                    //    QSurfaceFormat format;
                    //    format.setSwapInterval(1);
                    //    w.setFormat(format);
                    
                        w.show();
                        return a.exec();
                    }
                    

                    default.vert

                    
                    attribute vec3 aPosition;
                    uniform mat4 uMvpMatrix;
                    
                    void main()
                    {
                        gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
                    }
                    

                    default.frag

                    
                    void main()
                    {
                        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
                    }
                    
                    1 Reply Last reply
                    0
                    • johngodJ Offline
                      johngodJ Offline
                      johngod
                      wrote on 20 Sept 2022, 16:42 last edited by
                      #19

                      Thank you all for this usefull post. I have a qml game, I'm using a Timer that is triggered with a bool variable that is binded to keys pressed and release, at a fixed speed of 1000/60.
                      The animation looks smooth in some cases in but in other cases it does not. So the correct aproach should be not to use a Timer, but trigger the animations with vsync, right? Is it possible to do this in qml? Also it seems qml does not 'respect' the timer interval, sometimes I get higher frame rates, maybe I have something else mess up. Besides Timers, I am using NumberAnimations, I think that will mess the Timer interval ? I have done some testings using Timers, and qml render.stats, in my laptop with a Nvidia GTX1050 TI, I get:

                      • windows with native screen, animations are ok, smooth, renderstats shows aprox. 60fps
                      • windows with gaming monitor of 165Hz, animations are not smooth, renderstats shows aprox. 77fps
                      • linux mint, native screen, animations are ok, smooth, renderstats shows aprox. 60fps
                      • linux mint, gaming monitor of 165Hz, animations are ok, smooth, renderstats shows aprox. 120fps
                      • macbook air, native screen, animations are ok, smooth, renderstats shows aprox. 60fps
                      • macbook air, gaming monitor, animations not smooth, renderstats shows aprox. 45fps, maybe because of the hdmi cable with a display port adapter, that mess the performance
                      • iPhone 7, animations are ok, smooth, renderstats shows aprox. 60fps
                      • android phone, animations are ok, smooth, renderstats shows aprox. 60fps
                        Is there any cross plataform way of checking the vsinc and triger it to render the game loop ?
                        By the way, I have builds of my game for Windows, Mac, Linux (only binary) here https://drive.google.com/drive/folders/1JWrkcLyNfIZSmleJOJRkunM8mZUiIxDG?usp=sharing
                        also the code, if anybody wants to check it https://bitbucket.org/joaodeusmorgado/davidgalacticadventures/src/master/
                        Thanks
                        printescreen.png
                      8 1 Reply Last reply 29 Mar 2024, 14:37
                      0
                      • 8 Offline
                        8 Offline
                        8Observer8
                        wrote on 29 Mar 2024, 14:33 last edited by 8Observer8 4 Mar 2024, 23:57
                        #20

                        I created the next bug report and attached archives with examples for testing:

                        The frameSwapped() signal produces a terrible choppy animation on desktop, but a nice smooth animation on Android and WASM: https://bugreports.qt.io/browse/QTBUG-123862

                        I attached a source code (see the simple-keyframe-animation-opengles2-qt6-cpp.zip (5 kB) file) of my simple example with keyframe animation on QOpenGLWindow.

                        Try to run the WASM example: https://6606cb229a3fbbbdbb1d4ab5--resonant-brioche-d1b569.netlify.app/

                        Or Android example: see the attached simple-keyframe-animation-opengles2-qt6-cpp-android-apk.zip (8.92 MB) file)

                        And compare it with desktop example: see the attached simple-keyframe-animation-opengles2-qt6-cpp-win10x64-exe.zip 10.8 MB - zipped file, 26.1 MB - unzipped.

                        You will see that the frameSwapped() signal produces a terrible choppy animation on desktop, but a nice smooth animation on Android and WASM.

                        1 Reply Last reply
                        0
                        • johngodJ johngod
                          20 Sept 2022, 16:42

                          Thank you all for this usefull post. I have a qml game, I'm using a Timer that is triggered with a bool variable that is binded to keys pressed and release, at a fixed speed of 1000/60.
                          The animation looks smooth in some cases in but in other cases it does not. So the correct aproach should be not to use a Timer, but trigger the animations with vsync, right? Is it possible to do this in qml? Also it seems qml does not 'respect' the timer interval, sometimes I get higher frame rates, maybe I have something else mess up. Besides Timers, I am using NumberAnimations, I think that will mess the Timer interval ? I have done some testings using Timers, and qml render.stats, in my laptop with a Nvidia GTX1050 TI, I get:

                          • windows with native screen, animations are ok, smooth, renderstats shows aprox. 60fps
                          • windows with gaming monitor of 165Hz, animations are not smooth, renderstats shows aprox. 77fps
                          • linux mint, native screen, animations are ok, smooth, renderstats shows aprox. 60fps
                          • linux mint, gaming monitor of 165Hz, animations are ok, smooth, renderstats shows aprox. 120fps
                          • macbook air, native screen, animations are ok, smooth, renderstats shows aprox. 60fps
                          • macbook air, gaming monitor, animations not smooth, renderstats shows aprox. 45fps, maybe because of the hdmi cable with a display port adapter, that mess the performance
                          • iPhone 7, animations are ok, smooth, renderstats shows aprox. 60fps
                          • android phone, animations are ok, smooth, renderstats shows aprox. 60fps
                            Is there any cross plataform way of checking the vsinc and triger it to render the game loop ?
                            By the way, I have builds of my game for Windows, Mac, Linux (only binary) here https://drive.google.com/drive/folders/1JWrkcLyNfIZSmleJOJRkunM8mZUiIxDG?usp=sharing
                            also the code, if anybody wants to check it https://bitbucket.org/joaodeusmorgado/davidgalacticadventures/src/master/
                            Thanks
                            printescreen.png
                          8 Offline
                          8 Offline
                          8Observer8
                          wrote on 29 Mar 2024, 14:37 last edited by 8Observer8
                          #21

                          @johngod your demo doesn't work on my laptop with Window 10. It shows a white scene:

                          08ac1aab-7e8e-45c0-a7aa-215090b64795-image.png

                          johngodJ 1 Reply Last reply 30 Mar 2024, 05:02
                          0
                          • 8 8Observer8
                            29 Mar 2024, 14:37

                            @johngod your demo doesn't work on my laptop with Window 10. It shows a white scene:

                            08ac1aab-7e8e-45c0-a7aa-215090b64795-image.png

                            johngodJ Offline
                            johngodJ Offline
                            johngod
                            wrote on 30 Mar 2024, 05:02 last edited by
                            #22

                            @8Observer8 Are you compiling from git or running the zip file ? If compiling the source do you have any errors and what version of Qt are you using ? (you will need Qt6.2 iirc) What's your gpu ?

                            8 1 Reply Last reply 30 Mar 2024, 08:34
                            0
                            • johngodJ johngod
                              30 Mar 2024, 05:02

                              @8Observer8 Are you compiling from git or running the zip file ? If compiling the source do you have any errors and what version of Qt are you using ? (you will need Qt6.2 iirc) What's your gpu ?

                              8 Offline
                              8 Offline
                              8Observer8
                              wrote on 30 Mar 2024, 08:34 last edited by 8Observer8
                              #23

                              @johngod I downloaded a zip with EXE. I have: Asus K53SV; 8 GB RAM, i3 2.2 GHertz (2 cores); Intel HD Graphics 3000; Nvidia GeForce GT 540M (1 GB); Windows 10

                              It is because my laptop run your app with integrated video card (Intel HD Graphics 3000 that uses OpenGL 3.1). I can run it with the second one:

                              860d0a37-6c21-4e05-8586-680e67ed90ab-image.png

                              I click on the space ship (on Earth, and on ship again) in the top left corner:

                              928a734f-db79-4af2-8a56-14a0fe21e230-image.png

                              and see a black screen only with interrupted music:

                              78dbaa65-a30a-4d59-b0c7-4a24782886fe-image.png

                              johngodJ 1 Reply Last reply 30 Mar 2024, 10:00
                              0
                              • 8 8Observer8
                                30 Mar 2024, 08:34

                                @johngod I downloaded a zip with EXE. I have: Asus K53SV; 8 GB RAM, i3 2.2 GHertz (2 cores); Intel HD Graphics 3000; Nvidia GeForce GT 540M (1 GB); Windows 10

                                It is because my laptop run your app with integrated video card (Intel HD Graphics 3000 that uses OpenGL 3.1). I can run it with the second one:

                                860d0a37-6c21-4e05-8586-680e67ed90ab-image.png

                                I click on the space ship (on Earth, and on ship again) in the top left corner:

                                928a734f-db79-4af2-8a56-14a0fe21e230-image.png

                                and see a black screen only with interrupted music:

                                78dbaa65-a30a-4d59-b0c7-4a24782886fe-image.png

                                johngodJ Offline
                                johngodJ Offline
                                johngod
                                wrote on 30 Mar 2024, 10:00 last edited by
                                #24

                                @8Observer8 I have no clue sorry, but that is a very old demo. I just made a new build https://drive.google.com/file/d/1pdyL4lq2XpOcpv0ePxB57SPE30rBvsgw/view?usp=sharing maybe this will work. It it still does not work, maybe you can compile from source and see if there are any errors. Thank you for the interest in my game.

                                8 1 Reply Last reply 30 Mar 2024, 10:32
                                0
                                • johngodJ johngod
                                  30 Mar 2024, 10:00

                                  @8Observer8 I have no clue sorry, but that is a very old demo. I just made a new build https://drive.google.com/file/d/1pdyL4lq2XpOcpv0ePxB57SPE30rBvsgw/view?usp=sharing maybe this will work. It it still does not work, maybe you can compile from source and see if there are any errors. Thank you for the interest in my game.

                                  8 Offline
                                  8 Offline
                                  8Observer8
                                  wrote on 30 Mar 2024, 10:32 last edited by 8Observer8
                                  #25

                                  @johngod this build works without problems above. I didn't select the discrete video card manually to run. But when I move the ship with the AD keys from side to side, the animation of the movement is very choppy, like in a slideshow.

                                  johngodJ 1 Reply Last reply 30 Mar 2024, 12:02
                                  0
                                  • 8 8Observer8
                                    30 Mar 2024, 10:32

                                    @johngod this build works without problems above. I didn't select the discrete video card manually to run. But when I move the ship with the AD keys from side to side, the animation of the movement is very choppy, like in a slideshow.

                                    johngodJ Offline
                                    johngodJ Offline
                                    johngod
                                    wrote on 30 Mar 2024, 12:02 last edited by
                                    #26

                                    @8Observer8 I had problems whith choppy movement all over the place, because of using a Timer with a fixed valued has Crhis Kawa exaplained in https://forum.qt.io/post/278010 . But it got partially solved when changing Timer to FrameAnimation, however the AD keys are using a camera drift animation which is still buggy. Also using FrameAnimation breaks the AD keys drift animation, I still have to fix this bugs.
                                    If you click on the button "?" you will see some options where you can change back from FrameAnimation to Timer, and a lot more "chopiness" will appear.

                                    1 Reply Last reply
                                    1
                                    • 8 Offline
                                      8 Offline
                                      8Observer8
                                      wrote on 1 Apr 2024, 08:46 last edited by 8Observer8 4 Apr 2024, 00:17
                                      #27

                                      I added the next comment to the bug report: https://bugreports.qt.io/browse/QTBUG-123862

                                      Try to run my example on macOS and Linux. Maybe it is a Windows issue. But requestAnimationFrame works perfectly smoothly on Windows: https://plnkr.co/edit/DebSXYMQ6TlgC7KT?preview It has a fixed 60 FPS, which means that the load on the processor is minimal.

                                      The worst result was with swapInterval=1, which I had at the beginning (I had not tried other values before). swapInterval with 0 and 10 is better, but not as good as requestAnimationFrame in JavaScript: https://plnkr.co/edit/B5t34XdK3MVi1WNb?preview (it is just a translation animation). I attached a source code "simple-translation-animation" (without rotation and scale animations). I attached EXE files for swapInterval = 0, swapInterval = 1, and swapInterval = 10:

                                      • swapInterval = 0 - simple-translation-animation-opengles2-qt6-cpp-win10x64-exe-swap-interval-0.zip
                                      • swapInterval = 1 - simple-translation-animation-opengles2-qt6-cpp-win10x64-exe-swap-interval-1.zip
                                      • swapInterval = 10 - simple-translation-animation-opengles2-qt6-cpp-win10x64-exe-swap-interval-10.zip

                                      4623e1c6-0872-494d-881f-3b9677f8c17c-image.png

                                      1 Reply Last reply
                                      1
                                      • johngodJ johngod referenced this topic on 16 Aug 2024, 23:04

                                      • Login

                                      • Login or register to search.
                                      • First post
                                        Last post
                                      0
                                      • Categories
                                      • Recent
                                      • Tags
                                      • Popular
                                      • Users
                                      • Groups
                                      • Search
                                      • Get Qt Extensions
                                      • Unsolved