From 9d4ca2e3a6c23a8651f2c22704faa3aded6f2bf7 Mon Sep 17 00:00:00 2001 From: Luxferre Date: Sat, 24 Oct 2026 08:43:39 +0300 Subject: [PATCH] games: 60 fps rendering loop + smoother Snake controls Both games now render at 60 fps independent of the game-logic step rate: the main loop caps select() at one frame (16 ms) so the screen updates smoothly even when gravity/snake steps are slow, and renders every frame or whenever state changed. Snake: replaced the turn queue with a single buffered direction (latest keypress wins) so rapid key presses don't queue up and misfire. Base speed set to 100 px/sec (~90 ms per step) for controllable, responsive play. Progressive difficulty (+1 px/sec per 5 food) and edge wrap are unchanged. --- overlay/rootfs/usr/bin/snake | Bin 13668 -> 13672 bytes overlay/rootfs/usr/bin/tetris | Bin 17764 -> 17764 bytes prebuilt/games/usr/bin/snake | Bin 13668 -> 13672 bytes prebuilt/games/usr/bin/tetris | Bin 17764 -> 17764 bytes src/games/snake.cpp | 67 ++++++++++++++++------------------ src/games/tetris.cpp | 28 ++++++++++---- 6 files changed, 52 insertions(+), 43 deletions(-) diff --git a/overlay/rootfs/usr/bin/snake b/overlay/rootfs/usr/bin/snake index eb21fd565c5a682400ac0fa946b17697a06a8acc..ed4eb6158f5d3651b5ebdb67c5855d56fe256e9c 100755 GIT binary patch delta 3726 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 QMiictAtPSKi!vts6M3Xpp#T5? delta 4004 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 diff --git a/prebuilt/games/usr/bin/snake b/prebuilt/games/usr/bin/snake index eb21fd565c5a682400ac0fa946b17697a06a8acc..ed4eb6158f5d3651b5ebdb67c5855d56fe256e9c 100755 GIT binary patch delta 3726 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 QMiictAtPSKi!vts6M3Xpp#T5? delta 4004 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 diff --git a/src/games/snake.cpp b/src/games/snake.cpp index 08a96a0..49f49b0 100644 --- a/src/games/snake.cpp +++ b/src/games/snake.cpp @@ -102,13 +102,12 @@ static State state = PLAYING; static int snake[COLS * ROWS][2]; // x,y per segment (0 = tail) static int snakelen = 4; static int dirx = 1, diry = 0; // current direction -static int tqx[4], tqy[4]; // queued pending turns (rapid presses not lost) -static int tqh = 0, tqt = 0, tqn = 0; +static int ndirx = 1, ndiry = 0; // buffered next direction (latest keypress wins) static int foodx, foody; static int score = 0; static int best = 0; static int speed = 90; // ms per step (derived from pps below) -static int pps = 130; // base speed px/sec; +1 px/sec per 5 food eaten +static int pps = 100; // base speed px/sec; +1 px/sec per 5 food eaten static unsigned long nextTick = 0; static bool running = true; static unsigned long now_ms() { @@ -164,33 +163,19 @@ static void reset_game() { snake[i][1] = cy; } dirx = 1; diry = 0; - tqh = tqt = tqn = 0; + ndirx = 1; ndiry = 0; score = 0; - pps = 130; + pps = 100; update_speed(); place_food(); state = PLAYING; nextTick = now_ms() + speed; } -// Queue a pending direction change; rapid key presses are stored so none are lost. -static void tq_push(int dx, int dy) { - if (tqn >= 4) return; // queue full: drop the new turn - tqx[(tqh + tqn) % 4] = dx; - tqy[(tqh + tqn) % 4] = dy; - tqn++; -} - static void step() { - // apply the oldest queued turn (no reversing); rapid presses are not lost - int tdx = 0, tdy = 0; - if (tqn > 0) { - tdx = tqx[tqt]; tdy = tqy[tqt]; - tqt = (tqt + 1) % 4; - tqn--; - } - if (!(tdx == -dirx && tdy == -diry) && !(tdx == 0 && tdy == 0)) { - dirx = tdx; diry = tdy; + // apply buffered direction (no reversing); latest keypress wins + if (!(ndirx == -dirx && ndiry == -diry) && !(ndirx == 0 && ndiry == 0)) { + dirx = ndirx; diry = ndiry; } int hx = snake[snakelen - 1][0] + dirx; int hy = snake[snakelen - 1][1] + diry; @@ -217,7 +202,7 @@ static void step() { score += 10; if (score > best) best = score; int food = score / 10; // food items eaten so far - pps = 130 + food / 5; // +1 px/sec per 5 food eaten (progressive difficulty) + pps = 100 + food / 5; // +1 px/sec per 5 food eaten (progressive difficulty) update_speed(); place_food(); } @@ -288,13 +273,13 @@ static void render() { static void handle_key(KeySym ks) { switch (ks) { case XK_Up: case XK_w: case XK_KP_Up: case XK_2: - tq_push(0, -1); break; + ndirx = 0; ndiry = -1; break; case XK_Down: case XK_s: case XK_KP_Down: case XK_8: case XK_x: - tq_push(0, 1); break; + ndirx = 0; ndiry = 1; break; case XK_Left: case XK_a: case XK_KP_Left: case XK_4: - tq_push(-1, 0); break; + ndirx = -1; ndiry = 0; break; case XK_Right: case XK_d: case XK_KP_Right: case XK_6: - tq_push(1, 0); break; + ndirx = 1; ndiry = 0; break; case XK_5: case XK_Return: case XK_KP_Enter: case XK_space: case XK_p: if (state == PLAYING) state = PAUSED; else if (state == PAUSED) { state = PLAYING; nextTick = now_ms() + speed; } @@ -390,12 +375,19 @@ int main() { render(); int xfd = ConnectionNumber(dpy); + const unsigned long FRAME_MS = 1000 / 60; // 60 fps rendering + unsigned long lastFrame = now_ms(); + while (running) { - long until = (long)nextTick - (long)now_ms(); + unsigned long now = now_ms(); + long until = (long)nextTick - (long)now; if (until < 0) until = 0; + // Never block select longer than one frame so the screen stays smooth + long wait = (until < (long)FRAME_MS) ? until : (long)FRAME_MS; + struct timeval tv; - tv.tv_sec = until / 1000; - tv.tv_usec = (until % 1000) * 1000; + tv.tv_sec = wait / 1000; + tv.tv_usec = (wait % 1000) * 1000; fd_set fds; FD_ZERO(&fds); FD_SET(xfd, &fds); @@ -407,17 +399,22 @@ int main() { XNextEvent(dpy, &e); handle_event(e); if (!running) break; + dirty = true; } if (!running) break; - dirty = true; } - if (running && now_ms() >= nextTick) { + now = now_ms(); + if (running && now >= nextTick) { if (state == PLAYING) step(); - if (state == PLAYING) nextTick = now_ms() + speed; - else nextTick = now_ms() + 80; // keep loop responsive while paused/over + if (state == PLAYING) nextTick = now + speed; + else nextTick = now + 80; // keep loop responsive while paused/over dirty = true; } - if (dirty) render(); + // Render at 60 fps or whenever state changed + if (dirty || now - lastFrame >= FRAME_MS) { + render(); + lastFrame = now; + } } XFreeGC(dpy, gc); diff --git a/src/games/tetris.cpp b/src/games/tetris.cpp index 8d5576d..86b0d81 100644 --- a/src/games/tetris.cpp +++ b/src/games/tetris.cpp @@ -499,12 +499,19 @@ int main() { render(); int xfd = ConnectionNumber(dpy); + const unsigned long FRAME_MS = 1000 / 60; // 60 fps rendering + unsigned long lastFrame = now_ms(); + while (running) { - long until = (long)nextTick - (long)now_ms(); + unsigned long now = now_ms(); + long until = (long)nextTick - (long)now; if (until < 0) until = 0; + // Never block select longer than one frame so the screen stays smooth + long wait = (until < (long)FRAME_MS) ? until : (long)FRAME_MS; + struct timeval tv; - tv.tv_sec = until / 1000; - tv.tv_usec = (until % 1000) * 1000; + tv.tv_sec = wait / 1000; + tv.tv_usec = (wait % 1000) * 1000; fd_set fds; FD_ZERO(&fds); FD_SET(xfd, &fds); @@ -516,17 +523,22 @@ int main() { XNextEvent(dpy, &e); handle_event(e); if (!running) break; + dirty = true; } if (!running) break; - dirty = true; } - if (running && now_ms() >= nextTick) { + now = now_ms(); + if (running && now >= nextTick) { if (state == PLAYING) step(); - if (state == PLAYING) nextTick = now_ms() + speed; - else nextTick = now_ms() + 80; + if (state == PLAYING) nextTick = now + speed; + else nextTick = now + 80; dirty = true; } - if (dirty) render(); + // Render at 60 fps or whenever state changed + if (dirty || now - lastFrame >= FRAME_MS) { + render(); + lastFrame = now; + } } XFreeGC(dpy, gc);