Search code examples
c++sdlgame-development

Slight delay when pressing key in SDL. How can I fix it?


I am trying to make a game in SDL and I am starting of with drawing a rectangle to the screen then moving it around with the arrow keys. I have successfully programed this but there is a slight issue. When I press a key, it takes a second for it to register, then it will start moving flawlessly. Also, if I hold down another key as im pressing one, for instance, the left arrow key and up arrow key at the same time, instead of moving diagonal, it moves either up or left, only registering one key.

My original poll event looked like this.

while (!quit)
        {
            if (SDL_PollEvent(&e))
            {
                if (e.type == SDL_QUIT) {
                    quit = true;
                }
                else if (e.type == SDL_KEYDOWN)
                {
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_RIGHT:
                            squareX += 10;
                    }
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_LEFT:
                            squareX -= 10;
                    }
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_UP:
                            squareY -= 10;
                    }
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_DOWN:
                            squareY += 10;
                    }
                }
            }

SquareX is the square's X position and SquareY is the squares Y position. Here is the full code:

#define SDL_MAIN_HANDLED

#include<SDL.h>
#include<iostream>



SDL_Window* mainWindow = NULL;
SDL_Surface* mainWindowSurf = NULL;
SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);

int mainScreenWidth = 1280;
int mainScreenHeight = 720;

int squareX = 50;
int squareY = 50;

const int squareWidth = 50;
const int squareHeight = 100;

const int gravityAffect = 5;

bool mainGameInit();
void mainGameExit();

// Note to self - From here and to the next note comment, everything is functions.

bool mainGameInit()
{
    bool mainGameInitSuccess = true;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        std::cout << "SDL could not INIT. This time around, the window could not be created. For more details, SDL_ERROR: " << SDL_GetError() << std::endl;
        mainGameInitSuccess = false;
    }
    else
    {
        mainWindow = SDL_CreateWindow("Wednesday Night Dash",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            mainScreenWidth,
            mainScreenHeight,
            SDL_WINDOW_SHOWN);
        if (mainWindow == NULL)
        {
            std::cout << "SDL_Window could not be created! For more information, SDL_ERROR: " << SDL_GetError() << std::endl;
            mainGameInitSuccess = false;
        }
        else
        {
            // Create renderer after window creation
            mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
            if (mainRenderer == NULL)
            {
                std::cout << "SDL_Renderer could not be created! For more information, SDL_ERROR: " << SDL_GetError() << std::endl;
                mainGameInitSuccess = false;
            }
        }
    }
    return mainGameInitSuccess;
}

void mainGameExit()
{
    SDL_DestroyWindow(mainWindow);
    mainWindow = NULL;

    SDL_Quit();
}

int main(int argc, char* args[])
{
    if (!mainGameInit())
    {
        std::cout << "Uh oh! SDL (The game code) Failed to init. For more information, SDL_ERROR: " << SDL_GetError() << std::endl;
    }
    else
    {
        SDL_Event e;
        bool quit = false;
        while (!quit)
        {
            if (SDL_PollEvent(&e))
            {
                if (e.type == SDL_QUIT) {
                    quit = true;
                }
                else if (e.type == SDL_KEYDOWN)
                {
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_RIGHT:
                            squareX += 10;
                    }
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_LEFT:
                            squareX -= 10;
                    }
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_UP:
                            squareY -= 10;
                    }
                    switch (e.key.keysym.sym)
                    {
                        case SDLK_DOWN:
                            squareY += 10;
                    }
                }
            }

            // Clear screen
            SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
            SDL_RenderClear(mainRenderer);

            SDL_Rect squareRect = { squareX, squareY, squareWidth, squareHeight };
            SDL_SetRenderDrawColor(mainRenderer, 255, 0, 0, 255);
            SDL_RenderFillRect(mainRenderer, &squareRect);

            // Update screen
            SDL_RenderPresent(mainRenderer);
        }
    }

    mainGameExit();

    return 0;
}

Solution

  • Three things:

    1. Process all events in the event queue every frame, not just one:

      if( SDL_PollEvent( &e ) )
      

      ...should be:

      while( SDL_PollEvent( &e ) )
      
    2. Maintain a SDL_Keycode -> bool map of keydown states while processing events and only apply the movement logic once per frame, outside of the event processing loop:

      std::map< SDL_Keycode, bool > keyMap;
      
      ...
      
      while( SDL_PollEvent( &e ) )
      {
          if( e.type == SDL_QUIT )
          {
              quit = true;
          }
          else if( e.type == SDL_KEYDOWN )
          {
              keyMap[ e.key.keysym.sym ] = true;
          }
          else if( e.type == SDL_KEYUP )
          {
              keyMap[ e.key.keysym.sym ] = false;
          }
      }
      
      if( keyMap[ SDLK_RIGHT ] )
          squareX += 1;
      if( keyMap[ SDLK_LEFT ] )
          squareX -= 1;
      if( keyMap[ SDLK_UP ] )
          squareY -= 1;
      if( keyMap[ SDLK_DOWN ] )
          squareY += 1;
      
    3. In the absence of any other frame-rate control/accommodation you should probably make sure the renderer at least attempts to wait for vsyncs via the SDL_RENDERER_PRESENTVSYNC creation flag.

      Ideally though you'd implement some sort of frame-rate independent movement.

    All together:

    #include <SDL.h>
    #include <iostream>
    #include <map>
    
    SDL_Window* mainWindow = NULL;
    SDL_Surface* mainWindowSurf = NULL;
    SDL_Renderer* mainRenderer = SDL_CreateRenderer( mainWindow, -1, 0 );
    
    int mainScreenWidth = 1280;
    int mainScreenHeight = 720;
    
    int squareX = 50;
    int squareY = 50;
    
    const int squareWidth = 50;
    const int squareHeight = 100;
    
    const int gravityAffect = 5;
    
    bool mainGameInit();
    void mainGameExit();
    
    // Note to self - From here and to the next note comment, everything is functions.
    
    bool mainGameInit()
    {
        bool mainGameInitSuccess = true;
    
        if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
        {
            std::cout << "SDL could not INIT. This time around, the window could not be created. For "
                         "more details, SDL_ERROR: "
                      << SDL_GetError() << std::endl;
            mainGameInitSuccess = false;
        }
        else
        {
            mainWindow = SDL_CreateWindow(
                "Wednesday Night Dash",
                SDL_WINDOWPOS_UNDEFINED,
                SDL_WINDOWPOS_UNDEFINED,
                mainScreenWidth,
                mainScreenHeight,
                SDL_WINDOW_SHOWN );
            if( mainWindow == NULL )
            {
                std::cout << "SDL_Window could not be created! For more information, SDL_ERROR: "
                          << SDL_GetError() << std::endl;
                mainGameInitSuccess = false;
            }
            else
            {
                // Create renderer after window creation
                mainRenderer = SDL_CreateRenderer( mainWindow, -1, SDL_RENDERER_PRESENTVSYNC );
                if( mainRenderer == NULL )
                {
                    std::cout << "SDL_Renderer could not be created! For more information, SDL_ERROR: "
                              << SDL_GetError() << std::endl;
                    mainGameInitSuccess = false;
                }
            }
        }
        return mainGameInitSuccess;
    }
    
    void mainGameExit()
    {
        SDL_DestroyWindow( mainWindow );
        mainWindow = NULL;
    
        SDL_Quit();
    }
    
    int main( int argc, char* args[] )
    {
        if( !mainGameInit() )
        {
            std::cout << "Uh oh! SDL (The game code) Failed to init. For more information, SDL_ERROR: "
                      << SDL_GetError() << std::endl;
        }
        else
        {
            std::map< SDL_Keycode, bool > keyMap;
    
            SDL_Event e;
            bool quit = false;
            while( !quit )
            {
                while( SDL_PollEvent( &e ) )
                {
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                    else if( e.type == SDL_KEYDOWN )
                    {
                        keyMap[ e.key.keysym.sym ] = true;
                    }
                    else if( e.type == SDL_KEYUP )
                    {
                        keyMap[ e.key.keysym.sym ] = false;
                    }
                }
    
                if( keyMap[ SDLK_RIGHT ] )
                    squareX += 1;
                if( keyMap[ SDLK_LEFT ] )
                    squareX -= 1;
                if( keyMap[ SDLK_UP ] )
                    squareY -= 1;
                if( keyMap[ SDLK_DOWN ] )
                    squareY += 1;
    
                // Clear screen
                SDL_SetRenderDrawColor( mainRenderer, 0, 0, 0, 255 );
                SDL_RenderClear( mainRenderer );
    
                SDL_Rect squareRect = { squareX, squareY, squareWidth, squareHeight };
                SDL_SetRenderDrawColor( mainRenderer, 255, 0, 0, 255 );
                SDL_RenderFillRect( mainRenderer, &squareRect );
    
                // Update screen
                SDL_RenderPresent( mainRenderer );
            }
        }
    
        mainGameExit();
    
        return 0;
    }