3 votes

SDL2 qui suit la position de la souris a un délai

J'essaie de faire un simple rectangle qui suit la souris. Peu importe ce que j'utilise pour obtenir la position de la souris, il y a un délai. Ce n'est pas vraiment gênant au début, jusqu'à ce que vous ayez besoin de faire des mouvements non linéaires ou des mouvements rapides.

Le code suivant est l'intégralité du code que j'exécute pour le tester, ce délai apparaissant même dans le cadre le plus dépouillé. Le code suivant est fps-uncapped donc c'est moins pire, mais vous pouvez toujours voir le retard. Je ne pense pas que ce soit lié au matériel :

  • J'ai un assez bon ordinateur ;
  • J'ai trouvé BEAUCOUP de vieux sujets sur le même problème que moi, la plupart non résolus ou semblant avoir une réponse qui ne fonctionne pas pour moi. réponse qui ne fonctionne pas pour moi.

La plupart des réponses que j'ai trouvées étaient "désactiver v-sync/fps cap", ce que j'aimerais ne pas faire.

N'y a-t-il vraiment aucun moyen de le faire fonctionner ? Pourquoi ce délai existe-t-il ? Je comprendrais si le mouvement des boîtes n'était pas fluide à cause du plafond d'images par seconde, mais pourquoi sont-elles à la traîne au lieu de se téléporter à la position de la souris ?

#include <windows.h>
#include "SDL2/SDL.h"

#define ARRAY_CONSINT (const int[])
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

void drawRectangle( SDL_Renderer *renderer, SDL_Rect rect, const int clr[], int fill)
{
  SDL_SetRenderDrawColor(renderer, clr[0], clr[1], clr[2], clr[3]);
  if ( fill == 0)
    SDL_RenderDrawRect(renderer, &rect);
  else
    SDL_RenderFillRect(renderer, &rect);
  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

int main( int argc, char* args[])
{
  struct Mouse_s{
    int X;
    int Y;
  };
  int lQuit;
  POINT Windows_Mouse;
  SDL_Window *gWindow;
  SDL_Surface *screenSurface;
  SDL_Renderer *renderer;
  Uint32 startTicks;
  Uint32 endTicks;
  Uint32 DeltaTime;
  int showFPS;

  struct Mouse_s SDL_Mouse; 
  struct Mouse_s Motion_Mouse;
  lQuit = 0;
  SDL_Mouse.X = 0;
  SDL_Mouse.Y = 0;
  Motion_Mouse.X = 0;
  Motion_Mouse.Y = 0;
  startTicks = 0;
  endTicks = 0;
  DeltaTime = 0;

  SDL_Init( SDL_INIT_EVERYTHING );

  gWindow = SDL_CreateWindow( "Window", -1, -1, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
  renderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED ); // | SDL_RENDERER_PRESENTVSYNC
  screenSurface = SDL_GetWindowSurface( gWindow ); 
  SDL_UpdateWindowSurface( gWindow );

  while ( lQuit == 0 )
  {
    startTicks = SDL_GetTicks();
    DeltaTime =  startTicks - endTicks;
    if ( DeltaTime > 1000/60.0 )
    {
      //endTicks = SDL_GetTicks();
      SDL_RenderClear(renderer); 
      SDL_Event EventHandler;
      while( SDL_PollEvent( &EventHandler ) != 0)
      {
        if( EventHandler.type == SDL_QUIT )
          lQuit = 1;
        else if ( EventHandler.type == SDL_MOUSEMOTION )
        {
          Motion_Mouse.X = EventHandler.motion.x;
          Motion_Mouse.Y = EventHandler.motion.y;
        }
      }

      SDL_GetMouseState(&SDL_Mouse.X, &SDL_Mouse.Y);
      const SDL_Rect rect = {SDL_Mouse.X,SDL_Mouse.Y-50,100,50};
      drawRectangle( renderer, rect, ARRAY_CONSINT{255,0,0,255}, 0);

      GetCursorPos(&Windows_Mouse);
      const SDL_Rect rect2 = {Windows_Mouse.x,Windows_Mouse.y-50,70,40};
      drawRectangle( renderer, rect2, ARRAY_CONSINT{0,255,0,255}, 0);

      const SDL_Rect rect3 = {Motion_Mouse.X,Motion_Mouse.Y-50,40,30};
      drawRectangle( renderer, rect3, ARRAY_CONSINT{0,0,255,255}, 0);

      SDL_RenderPresent(renderer); 
    }
  }

  return 0;
}

0voto

Craig Estey Points 12780

Quelques problèmes ...

  1. Le mélange des appels de l'API Windows (pour la position de la souris) peut être une partie du problème. Oubliez les appels winAPI et utilisez simplement la position de la souris SDL.
  2. J'ai testé votre programme sous linux, j'ai donc dû supprimer les éléments winAPI. Le résultat semble être "correct".
  3. Tu le fais. no réinitialiser endTicks après avoir fait un rendu. Donc, après la première fois, il sera appelé sur chaque la boucle extérieure. Donc, c'est martelage le moteur de rendu.
  4. Mieux vaut faire la boucle d'événement à l'extérieur de du bloc de rendu.
  5. AFAICT, il n'est pas nécessaire de (re)prendre la position de la souris. La dernière position du dernier événement de mouvement est suffisante.

J'ai produit quelques versions pour montrer progressivement les corrections :

  1. Il suffit de supprimer les appels winAPI.
  2. Déplacer la boucle d'événement en dehors du rendu if et mettre endTicks correctement.
  3. Utilisez simplement la position de la souris à partir du dernier événement de mouvement.
  4. Version finale, entièrement nettoyée ( sans #if 0 ).

Dans le code ci-dessous, j'utilise cpp conditionnels pour indiquer l'ancien et le nouveau code (par exemple) :

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

(1) Voici le code remanié. Ce juste a supprimé les appels winAPI :

#if 0
#include <windows.h>
#endif
#include "SDL2/SDL.h"

#define ARRAY_CONSINT (const int[])
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

void
drawRectangle(SDL_Renderer *renderer, SDL_Rect rect, const int clr[], int fill)
{
    SDL_SetRenderDrawColor(renderer, clr[0], clr[1], clr[2], clr[3]);
    if (fill == 0)
        SDL_RenderDrawRect(renderer, &rect);
    else
        SDL_RenderFillRect(renderer, &rect);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

int
main(int argc, char *args[])
{
    struct Mouse_s {
        int X;
        int Y;
    };
    int lQuit;
#if 0
    POINT Windows_Mouse;
#endif
    SDL_Window *gWindow;
    SDL_Surface *screenSurface;
    SDL_Renderer *renderer;
    Uint32 startTicks;
    Uint32 endTicks;
    Uint32 DeltaTime;
    int showFPS;

    struct Mouse_s SDL_Mouse;
    struct Mouse_s Motion_Mouse;

    lQuit = 0;
    SDL_Mouse.X = 0;
    SDL_Mouse.Y = 0;
    Motion_Mouse.X = 0;
    Motion_Mouse.Y = 0;
    startTicks = 0;
    endTicks = 0;
    DeltaTime = 0;

    SDL_Init(SDL_INIT_EVERYTHING);

    gWindow = SDL_CreateWindow("Window", -1, -1, SCREEN_WIDTH, SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN);
#if 0
    renderer = SDL_CreateRenderer(gWindow, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
#else
    renderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
#endif
    screenSurface = SDL_GetWindowSurface(gWindow);
    SDL_UpdateWindowSurface(gWindow);

    while (lQuit == 0) {
        startTicks = SDL_GetTicks();
        DeltaTime = startTicks - endTicks;

        if (DeltaTime > 1000 / 60.0) {
            // endTicks = SDL_GetTicks();
            SDL_RenderClear(renderer);
            SDL_Event EventHandler;

            while (SDL_PollEvent(&EventHandler) != 0) {
                if (EventHandler.type == SDL_QUIT)
                    lQuit = 1;
                else if (EventHandler.type == SDL_MOUSEMOTION) {
                    Motion_Mouse.X = EventHandler.motion.x;
                    Motion_Mouse.Y = EventHandler.motion.y;
                }
            }

            SDL_GetMouseState(&SDL_Mouse.X, &SDL_Mouse.Y);
            const SDL_Rect rect = { SDL_Mouse.X, SDL_Mouse.Y - 50, 100, 50 };
            drawRectangle(renderer, rect, ARRAY_CONSINT {
                255, 0, 0, 255}, 0);

#if 0
            GetCursorPos(&Windows_Mouse);
            const SDL_Rect rect2 = { Windows_Mouse.x, Windows_Mouse.y - 50,
                70, 40 };
#else
            const SDL_Rect rect2 = { SDL_Mouse.X, SDL_Mouse.Y - 50, 70, 40 };
#endif
            drawRectangle(renderer, rect2, ARRAY_CONSINT {
                0, 255, 0, 255}, 0);

            const SDL_Rect rect3 = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                40, 30 };
            drawRectangle(renderer, rect3, ARRAY_CONSINT {
                0, 0, 255, 255}, 0);

            SDL_RenderPresent(renderer);
        }
    }

    return 0;
}

(2) Voici une version avec la plupart des autres correctifs que j'ai mentionnés (par exemple, la configuration endTicks correctement). Il toujours fait SDL_GetMouseState . Il s'agit de semble pour être un peu plus lisse :

#if 0
#include <windows.h>
#endif
#include "SDL2/SDL.h"

#define ARRAY_CONSINT (const int[])
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

void
drawRectangle(SDL_Renderer * renderer, SDL_Rect rect, const int clr[], int fill)
{
    SDL_SetRenderDrawColor(renderer, clr[0], clr[1], clr[2], clr[3]);
    if (fill == 0)
        SDL_RenderDrawRect(renderer, &rect);
    else
        SDL_RenderFillRect(renderer, &rect);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

int
main(int argc, char *args[])
{
    struct Mouse_s {
        int X;
        int Y;
    };
    int lQuit;
#if 0
    POINT Windows_Mouse;
#endif
    SDL_Window *gWindow;
    SDL_Surface *screenSurface;
    SDL_Renderer *renderer;
    Uint32 startTicks;
    Uint32 endTicks;
    Uint32 DeltaTime;
    int showFPS;

    struct Mouse_s SDL_Mouse;
    struct Mouse_s Motion_Mouse;

    lQuit = 0;
    SDL_Mouse.X = 0;
    SDL_Mouse.Y = 0;
    Motion_Mouse.X = 0;
    Motion_Mouse.Y = 0;
    startTicks = 0;
    endTicks = 0;
    DeltaTime = 0;

    SDL_Init(SDL_INIT_EVERYTHING);

    gWindow = SDL_CreateWindow("Window", -1, -1, SCREEN_WIDTH, SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN);
#if 0
    renderer = SDL_CreateRenderer(gWindow, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
#else
    renderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
#endif
    screenSurface = SDL_GetWindowSurface(gWindow);
    SDL_UpdateWindowSurface(gWindow);

    while (lQuit == 0) {
// NOTE/FIX: do this on every loop
#if 1
        SDL_Event EventHandler;
        while (SDL_PollEvent(&EventHandler) != 0) {
            if (EventHandler.type == SDL_QUIT)
                lQuit = 1;
            else if (EventHandler.type == SDL_MOUSEMOTION) {
                Motion_Mouse.X = EventHandler.motion.x;
                Motion_Mouse.Y = EventHandler.motion.y;
            }
        }
#endif

        startTicks = SDL_GetTicks();
        DeltaTime = startTicks - endTicks;

        if (DeltaTime > 1000 / 60.0) {
// NOTE/FIX: set endTicks to prevent _excessive_ rendering
#if 1
            endTicks = startTicks;
#endif

            SDL_RenderClear(renderer);

// NOTE/BUG: do this _outside_ the rendering time and do _not_ do it after
// the render clear
#if 0
            SDL_Event EventHandler;
            while (SDL_PollEvent(&EventHandler) != 0) {
                if (EventHandler.type == SDL_QUIT)
                    lQuit = 1;
                else if (EventHandler.type == SDL_MOUSEMOTION) {
                    Motion_Mouse.X = EventHandler.motion.x;
                    Motion_Mouse.Y = EventHandler.motion.y;
                }
            }
#endif

            SDL_GetMouseState(&SDL_Mouse.X, &SDL_Mouse.Y);
            const SDL_Rect rect = { SDL_Mouse.X, SDL_Mouse.Y - 50, 100, 50 };
            drawRectangle(renderer, rect, ARRAY_CONSINT {
                255, 0, 0, 255}, 0);

#if 0
            GetCursorPos(&Windows_Mouse);
            const SDL_Rect rect2 = { Windows_Mouse.x, Windows_Mouse.y - 50,
                70, 40 };
#else
            const SDL_Rect rect2 = { SDL_Mouse.X, SDL_Mouse.Y - 50, 70, 40 };
#endif
            drawRectangle(renderer, rect2, ARRAY_CONSINT {
                0, 255, 0, 255}, 0);

            const SDL_Rect rect3 = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                40, 30 };
            drawRectangle(renderer, rect3, ARRAY_CONSINT {
                0, 0, 255, 255}, 0);

            SDL_RenderPresent(renderer);
        }
    }

    return 0;
}

(3) Voici une version qui juste utilise la position de la souris à partir du dernier événement de mouvement :

#if 0
#include <windows.h>
#endif
#include "SDL2/SDL.h"

#define ARRAY_CONSINT (const int[])
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

void
drawRectangle(SDL_Renderer * renderer, SDL_Rect rect, const int clr[], int fill)
{
    SDL_SetRenderDrawColor(renderer, clr[0], clr[1], clr[2], clr[3]);
    if (fill == 0)
        SDL_RenderDrawRect(renderer, &rect);
    else
        SDL_RenderFillRect(renderer, &rect);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

int
main(int argc, char *args[])
{
    struct Mouse_s {
        int X;
        int Y;
    };
    int lQuit;
#if 0
    POINT Windows_Mouse;
#endif
    SDL_Window *gWindow;
    SDL_Surface *screenSurface;
    SDL_Renderer *renderer;
    Uint32 startTicks;
    Uint32 endTicks;
    Uint32 DeltaTime;
    int showFPS;

#if 0
    struct Mouse_s SDL_Mouse;
#endif
    struct Mouse_s Motion_Mouse;

    lQuit = 0;
#if 0
    SDL_Mouse.X = 0;
    SDL_Mouse.Y = 0;
#endif
    Motion_Mouse.X = 0;
    Motion_Mouse.Y = 0;
    startTicks = 0;
    endTicks = 0;
    DeltaTime = 0;

    SDL_Init(SDL_INIT_EVERYTHING);

    gWindow = SDL_CreateWindow("Window", -1, -1, SCREEN_WIDTH, SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN);
#if 0
    renderer = SDL_CreateRenderer(gWindow, -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
#else
    renderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
#endif
    screenSurface = SDL_GetWindowSurface(gWindow);
    SDL_UpdateWindowSurface(gWindow);

    while (lQuit == 0) {
// NOTE/FIX: do this on every loop
#if 1
        SDL_Event EventHandler;
        while (SDL_PollEvent(&EventHandler) != 0) {
            if (EventHandler.type == SDL_QUIT)
                lQuit = 1;
            else if (EventHandler.type == SDL_MOUSEMOTION) {
                Motion_Mouse.X = EventHandler.motion.x;
                Motion_Mouse.Y = EventHandler.motion.y;
            }
        }
#endif

        startTicks = SDL_GetTicks();
        DeltaTime = startTicks - endTicks;

        if (DeltaTime > 1000 / 60.0) {
// NOTE/FIX: set endTicks to prevent _excessive_ rendering
#if 1
            endTicks = startTicks;
#endif

            SDL_RenderClear(renderer);

// NOTE/BUG: do this _outside_ the rendering time and do _not_ do it after
// the render clear
#if 0
            SDL_Event EventHandler;
            while (SDL_PollEvent(&EventHandler) != 0) {
                if (EventHandler.type == SDL_QUIT)
                    lQuit = 1;
                else if (EventHandler.type == SDL_MOUSEMOTION) {
                    Motion_Mouse.X = EventHandler.motion.x;
                    Motion_Mouse.Y = EventHandler.motion.y;
                }
            }
#endif

// NOTE/BUG: no need to reget mouse position -- the motion event has it
#if 0
            SDL_GetMouseState(&SDL_Mouse.X, &SDL_Mouse.Y);
#endif

            const SDL_Rect rect = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                100, 50 };
            drawRectangle(renderer, rect, ARRAY_CONSINT {
                255, 0, 0, 255}, 0);

#if 0
            GetCursorPos(&Windows_Mouse);
            const SDL_Rect rect2 = { Windows_Mouse.x, Windows_Mouse.y - 50,
                70, 40 };
#else
            const SDL_Rect rect2 = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                70, 40 };
#endif
            drawRectangle(renderer, rect2, ARRAY_CONSINT {
                0, 255, 0, 255}, 0);

            const SDL_Rect rect3 = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                40, 30 };
            drawRectangle(renderer, rect3, ARRAY_CONSINT {
                0, 0, 255, 255}, 0);

            SDL_RenderPresent(renderer);
        }
    }

    return 0;
}

(4) Une version entièrement nettoyée :

#include "SDL2/SDL.h"

#define ARRAY_CONSINT (const int[])
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

void
drawRectangle(SDL_Renderer *renderer, SDL_Rect rect, const int clr[], int fill)
{
    SDL_SetRenderDrawColor(renderer, clr[0], clr[1], clr[2], clr[3]);
    if (fill == 0)
        SDL_RenderDrawRect(renderer, &rect);
    else
        SDL_RenderFillRect(renderer, &rect);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

int
main(int argc, char *args[])
{
    struct Mouse_s {
        int X;
        int Y;
    };
    int lQuit;
    SDL_Window *gWindow;
    SDL_Surface *screenSurface;
    SDL_Renderer *renderer;
    Uint32 startTicks;
    Uint32 endTicks;
    Uint32 DeltaTime;
    int showFPS;

    struct Mouse_s Motion_Mouse;

    lQuit = 0;
    Motion_Mouse.X = 0;
    Motion_Mouse.Y = 0;
    startTicks = 0;
    endTicks = 0;
    DeltaTime = 0;

    SDL_Init(SDL_INIT_EVERYTHING);

    gWindow = SDL_CreateWindow("Window", -1, -1, SCREEN_WIDTH, SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN);
    renderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
    screenSurface = SDL_GetWindowSurface(gWindow);
    SDL_UpdateWindowSurface(gWindow);

    while (lQuit == 0) {
        SDL_Event EventHandler;
        while (SDL_PollEvent(&EventHandler) != 0) {
            if (EventHandler.type == SDL_QUIT)
                lQuit = 1;
            else if (EventHandler.type == SDL_MOUSEMOTION) {
                Motion_Mouse.X = EventHandler.motion.x;
                Motion_Mouse.Y = EventHandler.motion.y;
            }
        }

        startTicks = SDL_GetTicks();
        DeltaTime = startTicks - endTicks;

        if (DeltaTime > 1000 / 60.0) {
            endTicks = startTicks;

            SDL_RenderClear(renderer);

            const SDL_Rect rect = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                100, 50 };
            drawRectangle(renderer, rect, ARRAY_CONSINT {
                255, 0, 0, 255}, 0);

            const SDL_Rect rect2 = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                70, 40 };
            drawRectangle(renderer, rect2, ARRAY_CONSINT {
                0, 255, 0, 255}, 0);

            const SDL_Rect rect3 = { Motion_Mouse.X, Motion_Mouse.Y - 50,
                40, 30 };
            drawRectangle(renderer, rect3, ARRAY_CONSINT {
                0, 0, 255, 255}, 0);

            SDL_RenderPresent(renderer);
        }
    }

    return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X