Movement code isn't working

Edit My game updating too fast was exactly the problem. I now have a target variable called FPS. At the end of my loop I check the time, if the game is updating faster than my desired rate, I sleep for the extra time

I’m trying to get a rectangle to move when I press the “WASD” keys. I handle the movement code using a Movement struct to capture when keys are down or up, the functions handleKeyDown, handleKeyUp, and handleInput. I’ve got the movement kind of working. If I use the keys “A”, or “W” it works, until I hit the end of the screen, then it gets stuck. If I try to move with the keys “D” or “S” the rectangle doesn’t move. I’m stumped as to why it’s only partially working

#include <stdio.h>
#include <SDL2/SDL.h>
#include <stdbool.h>
#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 720

struct Movement {
    bool up;
    bool down;
    bool left;
    bool right;
};

struct Movement movement = {false, false, false, false};

void handleKeyDown(SDL_KeyboardEvent *event)
{
    if (event->repeat == 0)
    {
        if (event->keysym.scancode == SDL_SCANCODE_W)
        {
            movement.up = true;
        }

        if (event->keysym.scancode == SDL_SCANCODE_S)
        {
            movement.down = true;
        }

        if (event->keysym.scancode == SDL_SCANCODE_A)
        {
            movement.left = true;
        }

        if (event->keysym.scancode == SDL_SCANCODE_D)
        {
            movement.right = true;
        }
    }
}

void handleKeyUp(SDL_KeyboardEvent *event)
{
    if (event->repeat == 0)
    {
        if (event->keysym.scancode == SDL_SCANCODE_W)
        {
            movement.up = false;
        }

        if (event->keysym.scancode == SDL_SCANCODE_S)
        {
            movement.down = false;
        }

        if (event->keysym.scancode == SDL_SCANCODE_A)
        {
            movement.left = false;
        }

        if (event->keysym.scancode == SDL_SCANCODE_D)
        {
            movement.right = false;
        }
    }
}
void handleInput(void){
    SDL_Event event;

    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                exit(0);
                break;

            case SDL_KEYDOWN:
                handleKeyDown(&event.key);
                break;

            case SDL_KEYUP:
                handleKeyUp(&event.key);
                break;

            default:
                break;
        }
    }
}

int main() {

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("Couldn't initialize SDL: %s\n", SDL_GetError());
        exit(1);
    }

    SDL_Window *window = SDL_CreateWindow("First Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                          SCREEN_WIDTH, SCREEN_HEIGHT, 0);

    if (!window) {
        printf("Failed to open %d x %d window: %s\n", SCREEN_WIDTH, SCREEN_HEIGHT, SDL_GetError());
        exit(1);
    }

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);

    if (!renderer) {
        printf("Failed to create renderer: %s\n", SDL_GetError());
        exit(1);
    }

    // Rectangle properties
    SDL_Rect player = {100, 100, 200, 100}; // x, y, width, height
    double speed = 300.0; // Speed of movement in pixels per second

    bool running = true;
    double currentTime;
    double lastTime = (double) SDL_GetTicks64();

    // Event loop
    while (running) {
        currentTime = (double) SDL_GetTicks();
        double deltaTime = (currentTime - lastTime) / 1000.0; // Delta time in seconds
        lastTime = currentTime;
        handleInput();
        if (movement.up) {
            player.y -= speed * deltaTime;
        }
        if (movement.down)  {
            player.y += speed * deltaTime;
        }
        if (movement.right) {
            player.x += speed * deltaTime;
        }
        if (movement.left) {
            player.x -= speed * deltaTime;
        }

        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderClear(renderer);

        // Draw the rectangle
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // Red
        SDL_RenderFillRect(renderer, &player);

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}


First of all, combine the handleKeyDown and handleKeyUp into one function. The pressed state of the key is provided in the event structure, with the event->state field. :wink:

1 Like

After the call of SDL_RenderPresent, put a call to SDL_Delay, something like: SDL_Delay(15).
Because the delay will give to the processor some time to “breath” and your game can run even with less uses of power processing.

1 Like

For me it doesn’t get stuck but I noticed that the rectangles move much faster up and left compared to down and right. I think the reason for this is because you use integer coordinates for the player and without using vsync or SDL_Delay the fps becomes very high which means the deltaTime becomes very low.

For example, let’s say that that speed * deltaTime is 3.6 and that the current coordinate is 100.
If you subtract 3.6 from 100 you get 96.4 which when truncated to int gives you the value 96.
If you add 3.6 to 100 you get 103.6 which when truncated to int gives you the value 103.

int x = 100;
x -= 3.6;
printf("%d\n", x); // prints 96

int y = 100;
y += 3.6;
printf("%d\n", y); // prints 103

Notice that the difference between 96 and 100 is larger than between 100 and 103 which explains why it moved faster in the up/left direction. An easy fix for this would be to just truncate the value (or round it) to an int before doing the subtraction/addition but that doesn’t solve the problem of “getting stuck”.

If deltaTime gets low enough it might cause speed * deltaTime to drop below 1 which means the rectangle wouldn’t be able to move down or right at all (but it would still be able to move up or left).

int x = 100;
x -= 0.9;
printf("%d\n", x); // prints 99

int y = 100;
y += 0.9;
printf("%d\n", y); // prints 100

So what probably happens for you is that when the rectangle starts going outside the screen there is less to draw so it runs faster and deltaTime drops low enough that it prevents any movement down or right.

To fix this issue I think you should use floating-point numbers for the player coordinates (and all other coordinates in the game). Only convert to integers when you have to in your graphics drawing code but never let them affect the game logic.

1 Like