1

In my game (C++/Raylib) when I move the player diagonally there is a lot of jitter for the rest of the elements When I move the character I "Scroll" the level (adds an offset to an mathematical 2D vector) and then the level draws all the elements it contains with that scroll offset so it appears the player is centered in the middle of the screen, when i move up down left and right, it looks decently smooth with no jittering, but when moving diagonally it jitters a lot, the scroll is updated before drawing, not while or after so it cant be too related to how the scroll is applied, the vector representing the scroll offset is composed of 2 doubles that represent x and y

//the move function
inline bool DynamicLevelObject::move(const dvec2 amt, bool should_scroll) {
 //returns true if we moved at all
 rect future = collision;
 future.x += amt.x;
 collision_hit hit;
 moving = false;
 if (amt.x != 0 && (hit = Level->colliding(future, &collision)).max_collider_status != collisionType::BLOCK_ALL) {
 moving = true;
 on_ground = hit.walkable;
 collision.x += amt.x;
 last_movement.x = amt.x;
 } else {
 moving = false;
 future.x -= amt.x;
 last_movement.x = 0;
 }
 future.y += amt.y;
 if ((amt.y != 0) && (hit = Level->colliding(future, &collision)).max_collider_status != collisionType::BLOCK_ALL) {
 moving = true;
 on_ground = hit.walkable;
 collision.y += amt.y;
 last_movement.y = amt.y;
 } else {
 last_movement.y = 0;
 }
 move_dir = vtod(last_movement);
 if (on_ground) {
 last_valid_pos = collision.pos();
 }
 if (should_scroll) Level->focusScroll(collision.center());
 return moving;
}
//in the level class
void draw(dvec2 offset) override {
 for (const auto& obj: objects) {
 obj->draw(scroll);
 }
 }
//the player draw function, most of the other object draw functions are very similar 
//to this, I severely doubt it has anything to do with the animations
void draw(const dvec2 offset) override {
 sprite::drawShadow({collision.center().x-offset.x-1, collision.y+collision.h-offset.y}, collision.w/2.5f, 0.8f*collision.h);
 DrawAnimation(*current_animation, collision.pos()-offset-dvec2{5, 25});
 }

I have tried rounding the scroll, rounding the players position, rounding everything and basically anything related to rounding I have tried, with varying success and many many extra issues that come with the rounding (such as massively slower movement, jittery player, weird movement behavior etc)

(Minimum reproductible example)

#include "raylib.h"
#include <cmath>
#include <iostream>
#include <string>
struct dvec2 {
 double x;
 double y;
 dvec2 operator+(const dvec2& other) {
 return {x + other.x, y + other.y};
 }
 dvec2& operator +=(const dvec2& other) {
 x += other.x;
 y += other.y;
 return *this;
 }
};
void focusScroll(dvec2& scroll, dvec2 pos) {
 scroll = {pos.x-640, pos.y-360};
}
void normalize_vec(dvec2& vec) {
 const double len = sqrt((vec.x * vec.x) + (vec.y * vec.y));
 vec.x /= len;
 vec.y /= len;
}
int main() {
 InitWindow(1280, 720, "main");
 dvec2 player_pos = {0, 0};
 dvec2 scroll = {0, 0};
 focusScroll(scroll, player_pos);
 //replace with any 32*32 pixel texture, helps to see the jitter
 Texture floor = LoadTexture("FloorTiles.png");
 SetTargetFPS(60);
 while (!WindowShouldClose()) {
 dvec2 movement = {0, 0};
 if (IsKeyDown(KEY_A)) {
 movement.x = -1;
 }else if (IsKeyDown(KEY_D)) {
 movement.x = 1;
 }
 if (IsKeyDown(KEY_W)) {
 movement.y = -1;
 }else if (IsKeyDown(KEY_S)) {
 movement.y = 1;
 }
 //normalize movement
 if (movement.x != 0 || movement.y != 0) {
 normalize_vec(movement);
 //3pixels per frame, roughly the actual speed of the character in the game
 movement.x *= 3;
 movement.y *= 3;
 player_pos += movement;
 scroll += movement;
 }
 BeginDrawing();
 ClearBackground(BLACK);
 DrawTexturePro(floor, {0, 0, 512, 512},
 {static_cast<float>(-scroll.x), static_cast<float>(-scroll.y), 512, 512}, {0, 0},
 0.0, WHITE);
 DrawCircle(static_cast<int>(player_pos.x-scroll.x), static_cast<int>(player_pos.y-scroll.y), 20.0, RED);
 DrawText((std::string("Scroll: ") + std::to_string(scroll.x) + ", " + std::to_string(scroll.y)).c_str(), 20, 20, 20, WHITE);
 EndDrawing();
 }
}

Should be able to see the jitter when the player moves diagonally and how otherwise it may be a bit blurry but pretty alright overall

10
  • I would add a video showing what i mean but i dont know how Commented Apr 11, 2025 at 23:02
  • I don't think you can add a video, but often a gif will get the point across and adding images ais reasonably well covered in the site documentation. However because too many folk hopelessly misuse images in questions, new users can't have nice things. Normally I suggest not bothering with images, but you likely have one of the rare cases where an image (animation in this case) probably would help. Make a gif. Put it up on imgur or similar because gifs generally exceed SO's file size limits. Post a link to it. Someone will help you make it visible. Commented Apr 11, 2025 at 23:17
  • Okay ill try to do that Commented Apr 11, 2025 at 23:23
  • Side note: Take the tour you elected to skip when signing up at Stack Overflow. It's not that helpful, but it does cover the bare minimum needed to use SO effectively AND you get a badge. The badge isn't that useful by itself, but if you have the badge, other users can see that you have it and thus you've done at least some reading of the site documentation. People around here tend to respond badly to people who have obviously not read any of the docs. Commented Apr 11, 2025 at 23:24
  • Okay I tried to make it a gif, but its just too slow and you kinda need the framerate to see it, not too much but more than the like 30fps i could get Commented Apr 11, 2025 at 23:25

1 Answer 1

1

Before i give you my solution, let me first explain how the jitter effect comes into play. Raylib provides a function called SetTargetFPS which you do use. However, most people have a misconception that this function guarantees the time between two successive frames to remain the same.

Unfortunately that is not the case. What it actually does is cap the framerate, and tries to render at said framerate. So currently what you are doing is using frame based movement, which is independent of real time. The fix is very simple and something all game developers should learn: DeltaTime

From Newtonian physics, we know that Speed = Distance x Time. Now your distance is constant but the speed with which you scroll is not (which causes the jitter effect). Since we live in a universe where the laws of physics hold supreme, we can deduce that the problem in this case is Time.

Note: Delta Time can sometimes still produce jitters, if that happens then just apply interpolation or cap the value so that it does not go below a certain threshold.

Fix:

#include "raylib.h"
#include <cmath>
#include <iostream>
#include <string>
struct dvec2 {
 double x;
 double y;
 dvec2 operator+(const dvec2& other) {
 return {x + other.x, y + other.y};
 }
 dvec2& operator +=(const dvec2& other) {
 x += other.x;
 y += other.y;
 return *this;
 }
};
void focusScroll(dvec2& scroll, dvec2 pos) {
 scroll = {pos.x - 640.0, pos.y - 360.0};
}
void normalize_vec(dvec2& vec) {
 const double len = sqrt((vec.x * vec.x) + (vec.y * vec.y));
 if (len != 0.0) {
 vec.x /= len;
 vec.y /= len;
 }
}
int main() {
 InitWindow(1280, 720, "main");
 dvec2 player_pos = {0, 0};
 dvec2 scroll = {0, 0};
 focusScroll(scroll, player_pos);
 Texture floor = LoadTexture("FloorTiles.png");
 SetTargetFPS(60);
 const double speed = 3.0;
 while (!WindowShouldClose()) {
 float deltaTime = GetFrameTime(); // <-- Delta Time In Seconds
 dvec2 movement = {0, 0};
 if (IsKeyDown(KEY_A)) {
 movement.x = -1;
 } else if (IsKeyDown(KEY_D)) {
 movement.x = 1;
 }
 if (IsKeyDown(KEY_W)) {
 movement.y = -1;
 } else if (IsKeyDown(KEY_S)) {
 movement.y = 1;
 }
 if (movement.x != 0 || movement.y != 0) {
 normalize_vec(movement);
 // Multiply By Speed * DeltaTime
 movement.x *= speed * deltaTime;
 movement.y *= speed * deltaTime;
 player_pos += movement;
 scroll += movement;
 }
 BeginDrawing();
 ClearBackground(BLACK);
 DrawTexturePro(floor, {0, 0, 512, 512},
 {static_cast<float>(-scroll.x), static_cast<float>(-scroll.y), 512, 512}, {0, 0},
 0.0, WHITE);
 DrawCircle(static_cast<int>(player_pos.x - scroll.x), static_cast<int>(player_pos.y - scroll.y), 20.0, RED);
 DrawText((std::string("Scroll: ") + std::to_string(scroll.x) + ", " + std::to_string(scroll.y)).c_str(), 20, 20, 20, WHITE);
 EndDrawing();
 }
 CloseWindow();
}
answered Apr 23, 2025 at 21:57
Sign up to request clarification or add additional context in comments.

1 Comment

Thats very interesting! I did not know that and if i did then i would have done a delta-time based system in my demo because i already use a delta-time system in the actual game, yet the jitter lingers

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.