🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

SDL2, Box2D, and Display Stuttering

Started by
12 comments, last by Zouflain 4 years, 4 months ago

I have a basic implementation of SDL2 and Box2D with a ball bouncing inside of a box. Ball movement is occasionally jittery. After a couple of days of debugging, I still can't figure out what's wrong.

Here are my constants:

    const float HALF = 0.5f;
    const float DOUBLE = 2.0f;
    const float INVERT = -1.0f;
    const float FRAMES_PER_SECOND = 60.0f;

    const int PHYSICS_POSITION_ITERATIONS = 8;
    const int PHYSICS_VELOCITY_ITERATIONS = 12;

    const float WORLD_UNIT = 2.0f;
    const float WORLD_WIDTH = 80.0f;
    const float WORLD_HEIGHT = 60.0f;
    const float WORLD_TIME_RESOLUTION = 1.0f / FRAMES_PER_SECOND;
    const float WORLD_TIME_ELAPSED_ZERO = 0.0f;
    const float WORLD_GRAVITY_ZERO = 0.0f;
    const float WORLD_SLOW_MOTION_FACTOR = 10.0f;

    const int SCREEN_WIDTH = 640;
    const int SCREEN_HEIGHT = 480;

    const float PIXELS_PER_METER_X = SCREEN_WIDTH / WORLD_WIDTH;
    const float PIXELS_PER_METER_Y = SCREEN_HEIGHT / WORLD_HEIGHT;

    const float BODY_DENSITY_PERFECT_SOLID = 1.0f;
    const float BODY_RESTITUTION_PERFECT_BOUNCE = 1.0f;
    const float BODY_ROTATION_ZERO = 0.0f;
    const float BODY_FRICTION_ZERO = 0.0f;
    const float BODY_DAMPING_ZERO = 0.0f;
    const float BODY_MASS_ZERO = 0.0f;

    const float BALL_RADIUS = WORLD_UNIT * HALF;
    const float BALL_START_POSITION_X = 4.0f;
    const float BALL_START_POSITION_Y = -30.0f;
    const float BALL_START_VELOCITY_X = 40.0f;
    const float BALL_START_VELOCITY_Y = 0.0f;

Here are my Box2D world and ball instantiations:

	b2Vec2* gravity = new b2Vec2(WORLD_GRAVITY_ZERO, WORLD_GRAVITY_ZERO);
	b2World* world = new b2World(*gravity);
    world->SetContinuousPhysics(true);

    b2BodyDef* ballBodyDef = new b2BodyDef();
    ballBodyDef->type = b2_dynamicBody;
    ballBodyDef->position.Set(BALL_START_POSITION_X, BALL_START_POSITION_Y);
    ballBodyDef->linearVelocity.Set(BALL_START_VELOCITY_X, BALL_START_VELOCITY_Y);

    b2MassData* ballBodyMassData = new b2MassData();
    ballBodyMassData->mass = BODY_MASS_ZERO;

    b2Body* ballBody = world->CreateBody(ballBodyDef);
    ballBody->SetBullet(true);
    ballBody->SetMassData(ballBodyMassData);
    ballBody->SetLinearDamping(BODY_DAMPING_ZERO);

    b2CircleShape* ballFixtureShape = new b2CircleShape();
    ballFixtureShape->m_radius = BALL_RADIUS;

    b2FixtureDef* ballFixtureDef = new b2FixtureDef();
    ballFixtureDef->shape = ballFixtureShape;
    ballFixtureDef->density = BODY_DENSITY_PERFECT_SOLID;
    ballFixtureDef->restitution = BODY_RESTITUTION_PERFECT_BOUNCE;
    ballFixtureDef->friction = BODY_FRICTION_ZERO;

    b2Fixture* ballFixture = ballBody->CreateFixture(ballFixtureDef);

Here is my SDL2 ball sprite instantiation:

    float ballShapeRadius = ballBody->GetFixtureList()[0].GetShape()->m_radius;
    SDL_Rect* ballSprite = new SDL_Rect();
    ballSprite->x = ballBody->GetPosition().x * PIXELS_PER_METER_X;
    ballSprite->y = ballBody->GetPosition().y * PIXELS_PER_METER_Y  * INVERT;
    ballSprite->w = (int)(ballShapeRadius * DOUBLE * PIXELS_PER_METER_X);
    ballSprite->h = (int)(ballShapeRadius * DOUBLE * PIXELS_PER_METER_Y);

Here are my SDL2 window and renderer instantiations:

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "Could not initialize display: %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window* window = SDL_CreateWindow(
        "TV Tennis",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        SCREEN_WIDTH, SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN
    );
    if (window == nullptr) {
        fprintf(stderr, "Could not create window: %s\n", SDL_GetError());
        return 1;
    }

    SDL_Renderer* renderTarget = SDL_CreateRenderer(
        window,
        -1,
        SDL_RENDERER_SOFTWARE
    );
    if (renderTarget == nullptr) {
        fprintf(stderr, "Could not create renderer: %s\n", SDL_GetError());
        return 1;
    }

Here is my clock instantiation and event loop:

    std::chrono::steady_clock clock;
    float deltaTime = WORLD_TIME_ELAPSED_ZERO;
    std::chrono::_V2::steady_clock::time_point lastTick = clock.now();
    std::chrono::_V2::steady_clock::time_point thisTick;
   

    //Initialize Input
    const Uint8* keyState;
    SDL_Event event;


    // //Enter Game Loop
    bool quitFlag = false;
    while ( quitFlag == false ){

        thisTick = clock.now();
        std::chrono::duration<float> timeElapsedSinceLastTick = thisTick - lastTick;
        deltaTime += timeElapsedSinceLastTick.count();
        lastTick = thisTick;

        while ( deltaTime >= WORLD_TIME_RESOLUTION ){

            while ( SDL_PollEvent(&event) ){

                switch ( event.type ){

                    case SDL_QUIT:
                        quitFlag = true;
                        break;

                    case SDL_KEYDOWN:
                        if ( event.key.keysym.sym == SDLK_ESCAPE ){
                            quitFlag = true;
                        }
                        break;
                }

            world->Step( deltaTime, PHYSICS_VELOCITY_ITERATIONS,
            	PHYSICS_POSITION_ITERATIONS );
          
            ballSprite->x = (int)((ballBody->GetPosition().x - ballBody->GetFixtureList()[0].GetShape()->m_radius) * PIXELS_PER_METER_X);
            ballSprite->y = ((int)((ballBody->GetPosition().y + ballBody->GetFixtureList()[0].GetShape()->m_radius) * PIXELS_PER_METER_Y)) * INVERT;

            deltaTime -= WORLD_TIME_RESOLUTION;
            
        }
        //Render Here
}

Any help would be greatly appreciated.

Advertisement

Assuming your movement is being done correctly and without going through all your code (I prefer if people actually give something I can quickly compile and run for such questions)… It looks like you're Rendering as fast as possible and locking your logic to your tick rate. If this is the case then you have to consider interpolation otherwise your graphics will not move smoothly visually unless the render updates are happening as per your refresh rate. You can also force VSYNC, but I personally don't recommend it as a solution only on the basis that you cannot guarantee the end user hasn't disabled it via their graphics card control panel (or whatever software they use for profiling).

I'm assuming the above based on coming across similar issues where graphics “jitter” and no known problem can be found by way of debugging.

You also double posted this: https://www.gamedev.net/forums/topic/705729-sdl2-box2d-and-display-stuttering/5421055/

Programmer and 3D Artist

Thanks for your quick reply! Sorry about the double-post. I accidentally put this in Artificial Intelligence and couldn't figure out how to delete it.

From what you see, how do I decouple the render speed from mu update frequency? I thought I was doing that by controlling my deltaTime loop with a fixed WORLD_TIME_RESOLUTION.

The complete code is posted below. Please ignore the mess =):

#include <iostream>
#include <chrono>
#include <string>
#include <Box2D/Box2D.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>

using namespace std;

int main(int argc, char *args[]){

    //*************
    //* Constants *
    //*************

    //Utility
    const float HALF = 0.5f;
    const float DOUBLE = 2.0f;
    const float INVERT = -1.0f;
    const float FRAMES_PER_SECOND = 60.0f;

    //Box2D
    const int PHYSICS_POSITION_ITERATIONS = 8;
    const int PHYSICS_VELOCITY_ITERATIONS = 12;

    const float WORLD_UNIT = 2.0f;
    const float WORLD_WIDTH = 80.0f;
    const float WORLD_HEIGHT = 60.0f;
    const float WORLD_TIME_RESOLUTION = 1.0f / FRAMES_PER_SECOND;
    const float WORLD_TIME_ELAPSED_ZERO = 0.0f;
    const float WORLD_GRAVITY_ZERO = 0.0f;
    const float WORLD_SLOW_MOTION_FACTOR = 10.0f;

    //SDL
    const float SCREEN_WIDTH = 1600.0f;
    const float SCREEN_HEIGHT = 1200.0f;
    const string SCREEN_TITLE = "Physual C++";

    const float PIXELS_PER_METER_X = SCREEN_WIDTH / WORLD_WIDTH;
    const float PIXELS_PER_METER_Y = SCREEN_HEIGHT / WORLD_HEIGHT;

    const unsigned char COLOR_TERMINALGREEN_R = 64;
    const unsigned char COLOR_TERMINALGREEN_G = 192;
    const unsigned char COLOR_TERMINALGREEN_B = 64;
    const unsigned char COLOR_TERMINALGREEN_A = 255;
    
    const unsigned char COLOR_BLACK_R = 0;
    const unsigned char COLOR_BLACK_G = 0;
    const unsigned char COLOR_BLACK_B = 0;
    const unsigned char COLOR_BLACK_A = 255;


    const string SAMPLE_FILE_BIP = "res\\bip.wav";
    const string SAMPLE_FILE_BEEP = "res\\beep.wav";

    const string MUSIC_FILE = "res\\Axel Goes Funky.flac";
    const float MUSIC_VOLUME = 75.0f;

    const string TEXT_FONT_FILE = "res\\pannetje_10.ttf";
    const int TEXT_FONT_SIZE = 23;
    const string TEXT_MESSAGE_TOP = "Physual C++: Demonstration of a 2D Game Development Stack (C++, Box2D, and SDL2)";
    const string TEXT_MESSAGE_BOTTOM = "(S)low Debug      (P)ause      (C)ircular Puck      (M)usic Toggle      (ESC)ape      Arrows Nudge";
    const int TEXT_MESSAGE_TOP_MARGIN = 4;

    //Box2D
    const float BODY_DENSITY_PERFECT_SOLID = 1.0f;
    const float BODY_RESTITUTION_PERFECT_BOUNCE = 1.0f;
    const float BODY_ROTATION_ZERO = 0.0f;
    const float BODY_FRICTION_ZERO = 0.0f;
    const float BODY_DAMPING_ZERO = 0.0f;
    const float BODY_MASS_ZERO = 0.0f;

    const float BALL_RADIUS = WORLD_UNIT * HALF;
    const float BALL_START_POSITION_X = 4.0f;
    const float BALL_START_POSITION_Y = -30.0f;
    const float BALL_START_VELOCITY_X = 40.0f;
    const float BALL_START_VELOCITY_Y = 0.0f;
    const float BALL_MIN_VELOCITY_MAGNITUDE_X = 12.0f;
    const float BALL_NUDGE_FACTOR = 12.0f;

    const float WALL_THICKNESS = WORLD_UNIT;



	b2Vec2* gravity = new b2Vec2(WORLD_GRAVITY_ZERO, WORLD_GRAVITY_ZERO);
	b2World* world = new b2World(*gravity);
    world->SetContinuousPhysics(true);


    //Ball
    b2BodyDef* ballBodyDef = new b2BodyDef();
    ballBodyDef->type = b2_dynamicBody;
    ballBodyDef->position.Set(BALL_START_POSITION_X, BALL_START_POSITION_Y);
    ballBodyDef->linearVelocity.Set(BALL_START_VELOCITY_X, BALL_START_VELOCITY_Y);

    b2MassData* ballBodyMassData = new b2MassData();
    ballBodyMassData->mass = BODY_MASS_ZERO;

    b2Body* ballBody = world->CreateBody(ballBodyDef);
    ballBody->SetBullet(true);
    ballBody->SetMassData(ballBodyMassData);
    ballBody->SetLinearDamping(BODY_DAMPING_ZERO);

    b2CircleShape* ballFixtureShape = new b2CircleShape();
    ballFixtureShape->m_radius = BALL_RADIUS;

    b2FixtureDef* ballFixtureDef = new b2FixtureDef();
    ballFixtureDef->shape = ballFixtureShape;
    ballFixtureDef->density = BODY_DENSITY_PERFECT_SOLID;
    ballFixtureDef->restitution = BODY_RESTITUTION_PERFECT_BOUNCE;
    ballFixtureDef->friction = BODY_FRICTION_ZERO;

    b2Fixture* ballFixture = ballBody->CreateFixture(ballFixtureDef);
    



    //Right Wall
    b2BodyDef* wallRightBodyDef = new b2BodyDef();
    wallRightBodyDef->type = b2_staticBody;
    wallRightBodyDef->position.Set(WORLD_WIDTH - (WORLD_UNIT * HALF), (WORLD_HEIGHT * HALF) * INVERT);

    b2Body* wallRightBody = world->CreateBody(wallRightBodyDef);

    b2PolygonShape* wallRightFixtureShape = new b2PolygonShape();
    wallRightFixtureShape->SetAsBox(WALL_THICKNESS, WORLD_HEIGHT);

    b2FixtureDef* wallRightFixtureDef = new b2FixtureDef();
    wallRightFixtureDef->shape = wallRightFixtureShape;
    wallRightFixtureDef->density = BODY_DENSITY_PERFECT_SOLID;

    b2Fixture* wallRightFixture = wallRightBody->CreateFixture(wallRightFixtureDef);




    //Left Wall
    b2BodyDef* wallLeftBodyDef = new b2BodyDef();
    wallLeftBodyDef->type = b2_staticBody;
    wallLeftBodyDef->position.Set(WORLD_UNIT * HALF, (WORLD_HEIGHT * HALF) * INVERT);

    b2Body* wallLeftBody = world->CreateBody(wallLeftBodyDef);

    b2PolygonShape* wallLeftFixtureShape = new b2PolygonShape();
    wallLeftFixtureShape->SetAsBox(WALL_THICKNESS, WORLD_HEIGHT);

    b2FixtureDef* wallLeftFixtureDef = new b2FixtureDef();
    wallLeftFixtureDef->shape = wallLeftFixtureShape;
    wallLeftFixtureDef->density = BODY_DENSITY_PERFECT_SOLID;

    b2Fixture* wallLeftFixture = wallLeftBody->CreateFixture(wallLeftFixtureDef);



    //Top Wall
    b2BodyDef* wallTopBodyDef = new b2BodyDef();
    wallTopBodyDef->type = b2_staticBody;
    wallTopBodyDef->position.Set(WORLD_WIDTH * HALF, (WORLD_UNIT * HALF) * INVERT);

    b2Body* wallTopBody = world->CreateBody(wallTopBodyDef);

    b2PolygonShape* wallTopFixtureShape = new b2PolygonShape();
    wallTopFixtureShape->SetAsBox(WORLD_WIDTH, WALL_THICKNESS);

    b2FixtureDef* wallTopFixtureDef = new b2FixtureDef();
    wallTopFixtureDef->shape = wallTopFixtureShape;
    wallTopFixtureDef->density = BODY_DENSITY_PERFECT_SOLID;

    b2Fixture* wallTopFixture = wallTopBody->CreateFixture(wallTopFixtureDef);



    //Bottom Wall
    b2BodyDef wallBottomBodyDef;
    wallBottomBodyDef.type = b2_staticBody;
    wallBottomBodyDef.position.Set(WORLD_WIDTH * HALF, (WORLD_HEIGHT - (WORLD_UNIT * HALF)) * INVERT);

    b2Body* wallBottomBody = world->CreateBody(&wallBottomBodyDef);

    b2PolygonShape wallBottomFixtureShape;
    wallBottomFixtureShape.SetAsBox(WORLD_WIDTH, WALL_THICKNESS);

    b2FixtureDef wallBottomFixtureDef;
    wallBottomFixtureDef.shape = &wallBottomFixtureShape;
    wallBottomFixtureDef.density = BODY_DENSITY_PERFECT_SOLID;

    wallBottomBody->CreateFixture(&wallBottomFixtureDef);




    //Ball Sprite
    float ballShapeRadius = ballBody->GetFixtureList()[0].GetShape()->m_radius;
    SDL_Rect* ballSprite = new SDL_Rect();
    ballSprite->x = ballBody->GetPosition().x * PIXELS_PER_METER_X;
    ballSprite->y = ballBody->GetPosition().y * PIXELS_PER_METER_Y  * INVERT;
    ballSprite->w = (int)(ballShapeRadius * DOUBLE * PIXELS_PER_METER_X);
    ballSprite->h = (int)(ballShapeRadius * DOUBLE * PIXELS_PER_METER_Y);

    //Right Wall Sprite
    b2Vec2* wallRightBodyVertice = &(((b2PolygonShape*)wallRightBody->GetFixtureList()[0].GetShape())->m_vertices[0]);
    float wallRightShapeWidth = std::abs( wallRightBodyVertice->x * DOUBLE );
    float wallRightShapeHeight = std::abs( wallRightBodyVertice->y * DOUBLE );
    SDL_Rect* wallRightSprite = new SDL_Rect();
    wallRightSprite->w = (int)(wallRightShapeWidth * PIXELS_PER_METER_X);
    wallRightSprite->h = (int)(wallRightShapeHeight * PIXELS_PER_METER_Y);
    wallRightSprite->x = (wallRightBody->GetPosition().x - ( wallRightShapeWidth * HALF )) * PIXELS_PER_METER_X;
    wallRightSprite->y = ((wallRightBody->GetPosition().y + ( wallRightShapeHeight * HALF )) * PIXELS_PER_METER_Y) * INVERT;

    //Left Wall Sprite
    b2Vec2* wallLeftBodyVertice = &(((b2PolygonShape*)wallLeftBody->GetFixtureList()[0].GetShape())->m_vertices[0]);
    float wallLeftShapeWidth = std::abs( wallLeftBodyVertice->x * DOUBLE );
    float wallLeftShapeHeight = std::abs( wallLeftBodyVertice->y * DOUBLE );
    SDL_Rect* wallLeftSprite = new SDL_Rect();
    wallLeftSprite->w = (int)(wallLeftShapeWidth * PIXELS_PER_METER_X);
    wallLeftSprite->h = (int)(wallLeftShapeHeight * PIXELS_PER_METER_Y);
    wallLeftSprite->x = (wallLeftBody->GetPosition().x - ( wallLeftShapeWidth * HALF )) * PIXELS_PER_METER_X;
    wallLeftSprite->y = ((wallLeftBody->GetPosition().y + ( wallLeftShapeHeight * HALF )) * PIXELS_PER_METER_Y) * INVERT;

    //Top Wall Sprite
    b2Vec2* wallTopBodyVertice = &(((b2PolygonShape*)wallTopBody->GetFixtureList()[0].GetShape())->m_vertices[0]);
    float wallTopShapeWidth = std::abs( wallTopBodyVertice->x * DOUBLE );
    float wallTopShapeHeight = std::abs( wallTopBodyVertice->y * DOUBLE );
    SDL_Rect* wallTopSprite = new SDL_Rect();
    wallTopSprite->w = (int)(wallTopShapeWidth * PIXELS_PER_METER_X);
    wallTopSprite->h = (int)(wallTopShapeHeight * PIXELS_PER_METER_Y);
    wallTopSprite->x = (wallTopBody->GetPosition().x - ( wallTopShapeWidth * HALF )) * PIXELS_PER_METER_X;
    wallTopSprite->y = ((wallTopBody->GetPosition().y + ( wallTopShapeHeight * HALF )) * PIXELS_PER_METER_Y) * INVERT;

    //Bottom Wall Sprite
    b2Vec2* wallBottomBodyVertice = &(((b2PolygonShape*)wallBottomBody->GetFixtureList()[0].GetShape())->m_vertices[0]);
    float wallBottomShapeWidth = std::abs( wallBottomBodyVertice->x * DOUBLE );
    float wallBottomShapeHeight = std::abs( wallBottomBodyVertice->y * DOUBLE );
    SDL_Rect* wallBottomSprite = new SDL_Rect();
    wallBottomSprite->w = (int)(wallBottomShapeWidth * PIXELS_PER_METER_X);
    wallBottomSprite->h = (int)(wallBottomShapeHeight * PIXELS_PER_METER_Y);
    wallBottomSprite->x = (wallBottomBody->GetPosition().x - ( wallBottomShapeWidth * HALF )) * PIXELS_PER_METER_X;
    wallBottomSprite->y = ((wallBottomBody->GetPosition().y + ( wallBottomShapeHeight * HALF )) * PIXELS_PER_METER_Y) * INVERT;



    //Initialize Display
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "Could not initialize display: %s\n", SDL_GetError());
        return 1;
    }

    SDL_Window* window = SDL_CreateWindow(
        "TV Tennis",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        (int)SCREEN_WIDTH, (int)SCREEN_HEIGHT,
        SDL_WINDOW_SHOWN
    );
    if (window == nullptr) {
        fprintf(stderr, "Could not create window: %s\n", SDL_GetError());
        return 1;
    }

    SDL_Renderer* renderTarget = SDL_CreateRenderer(
        window,
        -1,
        SDL_RENDERER_SOFTWARE
    );
    if (renderTarget == nullptr) {
        fprintf(stderr, "Could not create renderer: %s\n", SDL_GetError());
        return 1;
    }
    


    //Cue Music
    //synthesizer.Play();


    //Initialize Clock
    std::chrono::steady_clock clock;
    float deltaTime = WORLD_TIME_ELAPSED_ZERO;
    std::chrono::_V2::steady_clock::time_point lastTick = clock.now();
    std::chrono::_V2::steady_clock::time_point thisTick;
   

    //Initialize Input
    const Uint8* keyState;
    SDL_Event event;


    // //Enter Game Loop
    bool quitFlag = false;
    while ( quitFlag == false ){

        thisTick = clock.now();
        std::chrono::duration<float> timeElapsedSinceLastTick = thisTick - lastTick;
        deltaTime += timeElapsedSinceLastTick.count();
        lastTick = thisTick;

        while ( deltaTime >= WORLD_TIME_RESOLUTION ){

            while ( SDL_PollEvent(&event) ){

                switch ( event.type ){

                    case SDL_QUIT:
                        quitFlag = true;
                        break;

                    case SDL_KEYDOWN:
                        if ( event.key.keysym.sym == SDLK_ESCAPE ){
                            quitFlag = true;
                        }
                        break;
                }

                keyState = SDL_GetKeyboardState(NULL);
                b2Vec2 nudgedVelocity = ballBody->GetLinearVelocity();

                if (keyState[SDL_SCANCODE_UP]) { nudgedVelocity.y += BALL_NUDGE_FACTOR; }
                if (keyState[SDL_SCANCODE_DOWN]) { nudgedVelocity.y -= BALL_NUDGE_FACTOR; }
                if (keyState[SDL_SCANCODE_LEFT]) { nudgedVelocity.x -= BALL_NUDGE_FACTOR; }
                if (keyState[SDL_SCANCODE_RIGHT]) { nudgedVelocity.x += BALL_NUDGE_FACTOR; }
                ballBody->SetLinearVelocity(nudgedVelocity);
            }

            if ( keyState[SDL_SCANCODE_P] ){
                do {
                    SDL_PollEvent(&event);
                    lastTick = clock.now();
                } while (keyState[SDL_SCANCODE_P]);
            }

            world->Step( deltaTime / ( keyState[SDL_SCANCODE_S] ? WORLD_SLOW_MOTION_FACTOR : 1.0f ), PHYSICS_VELOCITY_ITERATIONS, PHYSICS_POSITION_ITERATIONS );
          
            ballSprite->x = (int)((ballBody->GetPosition().x - ballBody->GetFixtureList()[0].GetShape()->m_radius) * PIXELS_PER_METER_X);
            ballSprite->y = ((int)((ballBody->GetPosition().y + ballBody->GetFixtureList()[0].GetShape()->m_radius) * PIXELS_PER_METER_Y)) * INVERT;

            deltaTime -= WORLD_TIME_RESOLUTION;
            
        }




    //     //Press 'M' to Toggle Music
    //     if (Keyboard.IsKeyPressed(Keyboard.Key.M))
    //     {
    //         if (readyToToggleMusic)
    //         {
    //             readyToToggleMusic = false;
    //             if (synthesizer.Status == SoundStatus.Playing)
    //             {
    //                 synthesizer.Stop();
    //             }
    //             else
    //             {
    //                 synthesizer.Play();
    //             }
    //         }
    //     }
    //     else
    //     {
    //         readyToToggleMusic = true;
    //     }



        


        //Render Screen
        SDL_SetRenderDrawColor(
            renderTarget,
            COLOR_BLACK_R,
            COLOR_BLACK_G,
            COLOR_BLACK_B,
            COLOR_BLACK_A
        );
        SDL_RenderClear(renderTarget);


        SDL_SetRenderDrawColor(
            renderTarget,
            COLOR_TERMINALGREEN_R,
            COLOR_TERMINALGREEN_G,
            COLOR_TERMINALGREEN_B,
            COLOR_TERMINALGREEN_A
        );

        SDL_RenderFillRect(renderTarget, wallRightSprite);
        SDL_RenderFillRect(renderTarget, wallLeftSprite);
        SDL_RenderFillRect(renderTarget, wallTopSprite);
        SDL_RenderFillRect(renderTarget, wallBottomSprite);
        SDL_RenderFillRect(renderTarget, ballSprite);
        


            //     screen.Draw(topText);
            //     screen.Draw(bottomText);

            SDL_RenderPresent(renderTarget);



            //     //Play Sound Effect for Current Collision
            //     if (ball.ContactList != null)
            //     {
            //         if (readyToPlaySoundEffect)
            //         {
            //             readyToPlaySoundEffect = false;
            //             sampler[soundEffectCycle].Play();
            //             soundEffectCycle = (soundEffectCycle + 1) % sampler.Length;
            //         }
            //     }
            //     else
            //     {
            //         readyToPlaySoundEffect = true;
            //     }
            // }








    }


    SDL_DestroyRenderer(renderTarget);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

I have cout'ed out the body and sprite coordinates in these places and analyzed them for variances, but they appear very tight; no outliers.

You'll notice that the sandbox listens for the P key to pause and the S key to move in slow-motion. You can tap P occasionally and put the system in a state of constant jitter. Looking at the numbers in this state also didn't reveal anything to me.

I cannot test this yet as I have to build box2D which I never use and then setup an SDL2 project.

Can you force VSYNC and see if the same problem occurs to at least factor this out?

Also, if you don't mess with either your Pause or Slow-Motion features is the issue still there?

Programmer and 3D Artist

The ACCELERATED flag seems to help with jitter frequency, but also seems to blur the object. PRESENTVSYNC seems to speed the simulation up, but does not seem to improve jitter in either the SOFTWARE and ACCELERATED renderers.

If I don't PAUSE or SLOW, the jitter seems to happen (anecdotally) every 14 seconds or so. If I run the simulation long enough, I get periods of constant jitter, followed by extending periods of smooth animation. It has a cyclic tendency, almost like two sound waves oscillating in and out of phase harmony..

PixelMischief said:

The ACCELERATED flag seems to help with jitter frequency, but also seems to blur the object. PRESENTVSYNC seems to speed the simulation up, but does not seem to improve jitter in either the SOFTWARE and ACCELERATED renderers.

If I don't PAUSE or SLOW, the jitter seems to happen (anecdotally) every 14 seconds or so. If I run the simulation long enough, I get periods of constant jitter, followed by extending periods of smooth animation. It has a cyclic tendency, almost like two sound waves oscillating in and out of phase harmony..

Alright, it will take me some time to get all this up as I have to build some libraries. I didn't check out your time step fully but make sure you're 100% sure you're executing it correctly as per the tick rate, I believe you have it set to 60 ticks per second? Then you're rendering until the next tick becomes due correct?

Also double check you're not converting float to int on your movement. Please check this!

Programmer and 3D Artist

Thanks! I will do just what you said.

Meantime, I am creating a second implementation that does away with the Box2D integration and uses integers and floats explicitly. Want to test my timinge logic alone

.

I was able to build and run everything and I changed a few things:

  1. I made a float for ballSpriteX and ballSpriteY instead of using your (int) ballSprite→x and ballSprite→y.
  2. I changed everything over to the floats from ints you had before
  3. I update the draw position based on the new variables prior to rendering
  4. I used the flag SDL_RENDERER_PRESENTVSYNC but even without VSYNC it looks fine

The jittering doesn't seem really noticeable, but to be honest I hadn't noticed a lot of jitter before changing unless I was at a certain speed.

I'm half asleep right now so I could be wrong here…. It would be better visually with interpolation. Aside from that it could be possible your simulation is going out of wack due to your time step or physics calculation - maybe both… but I have to run through it first to know and I don't have much time left took dive into this more.

If you leave the keys alone the object should move at a steady rate back and forth. I ran a quick test and found that every tick your movement goes back and forth a bit in terms of distance per tick.

For example I took your X position against your prior X position, to see the difference per tick:

Sometimes you go a bit faster or slower per tick.

Even using SLOW MODE without touching anything:

Keep in mind your sprite will move based on the position at render time when taking the float → int as SDL uses int for the render positions. Now you can do this with openGL and utilize floating positions.

Programmer and 3D Artist

That very, very much for your attention! Can I see your version? I don't see an attachment.

This topic is closed to new replies.

Advertisement