0
\$\begingroup\$

I've been using C++ and SDL2, but can't seem to get smooth motion when I have objects moving in the window. I was originally using SDL_GetTicks() and SDL_Delay() in my game loop, but one of the posts I read helped make the motion a bit more steady:

How can I avoid jittery motion in SDL2?

However, I'm still getting regular jumps in the movement. It's almost like a rubber band effect, where the graphic will appear to jump back slightly. It seems to be most noticeable at regular intervals (maybe around every 0.8 seconds). It's a little more noticeable than the one in the linked post above. Like the other one mentioned, slow movement makes the issue less obvious.

I have a main loop set up as follows:

main.cpp

#include <SDL.h>
#include "GameWindow.h"
#include "Game.h"

struct Timer {
    Uint64 previousTicks = 0;
    float elapsedSeconds = 0.0f;

    void Tick() {
        const Uint64 currentTicks = SDL_GetPerformanceCounter();
        const Uint64 delta = currentTicks - previousTicks;
        previousTicks = currentTicks;
        static const Uint64 TICKS_PER_SECOND = SDL_GetPerformanceFrequency();
        elapsedSeconds = delta / static_cast<float>(TICKS_PER_SECOND);
    }
};

int main(int argc, char* argv[]) {
    const int FPS = 60;
    const double FRAME_DELAY = 1.0f / FPS;
    static Timer SystemTimer;
    float accumulatedSecs = 0.0f;

    const int WINDOW_WIDTH = 1920;
    const int WINDOW_HEIGHT = 960;
    const bool FULL_SCREEN = false;

    if (SDL_Init(SDL_INIT_VIDEO) == 0) {
        GameWindow window;

        window.Init("Game Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, FULL_SCREEN);

        if (window.GetSDLWindow()) {
            SDL_Renderer* renderer = SDL_CreateRenderer(window.GetSDLWindow(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

            if (renderer) {
                Game game;

                if (game.Init(renderer)) {
                    game.Start();

                    static Timer PhysicsTimer;
                    while (game.IsRunning()) {
                        SystemTimer.Tick();
                        accumulatedSecs += SystemTimer.elapsedSeconds;

                        game.ProcessInput();

                        if (std::isgreater(accumulatedSecs, FRAME_DELAY)) {
                            accumulatedSecs -= FRAME_DELAY;

                            PhysicsTimer.Tick();

                            if (PhysicsTimer.elapsedSeconds > 0.06f)
                                PhysicsTimer.elapsedSeconds = FRAME_DELAY;

                            game.Update(PhysicsTimer.elapsedSeconds);

                            game.Render();
                        }
                    }
                }
                game.CleanUp();
            }
            window.CleanUp();
        }
    }
    return 0;
}

I've tried with SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC on and off, as well as with each one enabled on its own, but no joy.

I added the following lines, as the initial elapsed time was huge when the game first starts.

if (PhysicsTimer.elapsedSeconds > 0.06f)
    PhysicsTimer.elapsedSeconds = FRAME_DELAY;

I've removed any keyboard input checking, and instead just set a "MOVE_RIGHT" message on every ProcessInput() run.

The Update function in Game.cpp just passes the elapsedTime (and the "input") on to the Entity's Update function:

Game.cpp

void Game::Update(float elapsedTime) {
    this->testEntity->Update(this->playerInput, elapsedTime);
}

The Entity passes on to its InputComponent:

Entity.cpp

void Entity::Update(std::string input, float elapsedTime) {
    this->inputComponent->Update(*this, input, elapsedTime);
}

Which then deals with the input and updates the position:

PlayerInputComponent.cpp

void PlayerInputComponent::Update(Entity& entity, std::string input, float elapsedTime) {   
    if (input == "MOVE_RIGHT") {
        entity.SetX(std::roundf(float(entity.GetX() + 0.5f + (entity.GetVelX() * elapsedTime))));
    }
}

The render function looks like this:

RenderSystem.cpp

void RenderSystem::Update(std::vector<Entity*> entities) {
    SDL_RenderClear(this->renderer);

    for (auto it = entities.begin(), end = entities.end(); it != end; ++it) {
        SDL_Rect destRect;
        destRect.x = (*it)->GetX();
        destRect.y = (*it)->GetY();
        destRect.w = 64;
        destRect.h = 64;

        SDL_RenderCopy(this->renderer, (*it)->GetRenderComponent()->GetTexture(), &(*it)->GetRenderComponent()->GetSrcRect(), &destRect);
    }

    SDL_RenderPresent(this->renderer);
}

I haven't stored the destRect on the Entity, so it's in there for now.

Is there anything there that looks strange at all? I've also tried using floor and ceiling functions when rounding in the update function, but can't get rid of the stuttering movement. I'm using SDL_image with SDL_Texture objects.

Any advice would be appreciated.

Many thanks,

\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.