2
\$\begingroup\$

I'm new to SDL programming and I'm not quite sure I got how it handles events. Given an instance like this defined in "game.c":

static struct {

  SDL_Window* window;
  SDL_Renderer* renderer;
  BinaryTree* objects;

} game = { NULL, NULL, NULL };

(Which of course gets properly initialized)

My game loop is defined in the same file as follows:

void game_run() {

  bool closeRequested = false;

  while(closeRequested == false) {

    SDL_RenderClear(game.renderer);

    binaryTree_startIteration(game.objects);

    while(binaryTree_hasNext(game.objects)) {

      GameObject* gameObject = (GameObject*) binaryTree_getNext(game.objects);

      SDL_Event event;

      while(SDL_PollEvent(&event)) {

        if(event.type == SDL_QUIT) {

          closeRequested = true;
        }
        else {

          if(event.type == SDL_KEYDOWN) {

            gameObject_onKeyPressed(gameObject);

            if(event.key.keysym.scancode == SDL_SCANCODE_UP) {

              gameObject_onUpKeyPressed(gameObject);
            }

            if(event.key.keysym.scancode == SDL_SCANCODE_DOWN) {

              gameObject_onDownKeyPressed(gameObject);
            }

            if(event.key.keysym.scancode == SDL_SCANCODE_LEFT) {

              gameObject_onLeftKeyPressed(gameObject);
            }

            if(event.key.keysym.scancode == SDL_SCANCODE_RIGHT) {

              gameObject_onRightKeyPressed(gameObject);
            }
          }

          if(event.type == SDL_KEYUP) {

            //
          }
        }
      }

      gameObject_onRendering(gameObject);
      SDL_RenderCopy(renderer, gameObject_getTexture(gameObject), NULL, gameObject_getDestination(gameObject));
    }

    SDL_RenderPresent(game.renderer);
  }
}

game.objects contains every object which has to be rendered (via game.renderer) onto the the window (game.window). So, on each frame game.objects gets iterated on and for each element various functions get executed, before its texture is loaded to the renderer.

Of course gameObject_onRightKeyPressed() is called only if the right key was pressed, so if this function is defined as

void gameObject_onRightKeyPressed(GameObject* gameObject) {

  gameObject->destination.x += 1,
}

On each frame that the right key is pressed, gameObject's texture will move by 1 pixel to the right.

This works perfectly if game.objects contains only one element, but if a second element gets added, only the first one will smoothly respond to my input, while the other will move by 1 or 2 pixels just once in a while by keeping the right key pressed. It's like the first element contained in game.objects "steals" the input, so that when the second element's turn comes, there's no input for it to respond to. What did I do wrong?

\$\endgroup\$
3
\$\begingroup\$

Your game loop doesn't follow the traditional pattern of polling for input, updating your objects, and finally rendering them. Your current implementation will result in exactly what you describe, where the first object receives all of the input events SDL sends you. So instead of what you have currently, you want something like the following:

while (running)
{
    SDL_Event event;
    while (SDL_PollEvent(&event))
        ProcessEvent(event);

    UpdateAllObjects();
    
    ClearRenderer();
    RenderAllObjects();
    PresentRenderer();
}

The reason for this is that when you poll an event, SDL removes it from the event queue and considers it processed, this is why your first objects receives all the events.

As to how to implement the event handling, one option would be to invert the loop you currently have, so ProcessEvent could look something like the following:

void ProcessEvent(SDL_Event& event)
{
    if (event.type == SDL_KEYDOWN)
    {
        for (GameObject* object : objects)
            object->OnKeyDown(event.key.keysym);
    }
}

So instead of looping over objects first, and events second, we loop over events first, and distribute each event to all objects that listen to that event.

Alternatively you can maintain booleans of the current keyboard state, and check for those while updating the objects.

\$\endgroup\$
4
  • \$\begingroup\$ I see. My loop was actually structured somewhat like this at first, but I changed it to what I posted in order to avoid cycling through the objects container twice (once for consuming the input and another to render each element). Because that's what you do here as well, right? Isn't the other solution you've suggested much better since it would require to check each object only once? Are there any drawbacks to it? \$\endgroup\$
    – Gian
    Aug 10 '20 at 13:39
  • 1
    \$\begingroup\$ The drawback of the alternative solution of manually maintaining keyboard state is that it's harder to implement your input processing in an event/action based fashion, which might for an example make intended event consumption harder to implement. Whether or not more looping is slower depends on the work you do, but it's unlikely you'll spot a big performance difference, since the input still has to be processed (=work has to be done) in the updates of the objects. \$\endgroup\$
    – Tyyppi_77
    Aug 10 '20 at 13:43
  • \$\begingroup\$ I tried to implement it in both ways and it works! Both methods seems pretty similiar in how they behave, for the simple things I'm doing for now. What's weird though is that if I implement both down and right movement, the object moves diagonally down only if both keys were pressed at exactly the same time. If I'm holding the right key and then I begin to press the down key as well, the object simply starts moving down, so the right key input is being ignored. Is this normal? \$\endgroup\$
    – Gian
    Aug 10 '20 at 14:36
  • \$\begingroup\$ That depends on how you have implemented it. If you need help with fixing that, you should ask a new question as it is out of the scope of this one. \$\endgroup\$
    – Tyyppi_77
    Aug 10 '20 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.