mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
3.7 KiB
3.7 KiB
Example 03: Bouncing Ball
Animating graphics and managing application state.
This example builds upon the 02_basic_window guide. It demonstrates how to constantly update the screen to simulate a bouncing square moving freely inside the window bounds.
📝 Concepts Introduced
- Maintaining application state across frames (Velocity/Position).
- Drawing primitives (
ui_fill_rect,ui_draw_string). - The importance of clearing the screen on a new frame.
- Explicitly forcing standard visual updates via
ui_mark_dirty().
💻 The Code (src/userland/gui/bounce.c)
#include <stdlib.h>
#include <libui.h>
#include <syscall.h>
// Window Dimensions
#define W_WIDTH 400
#define W_HEIGHT 300
// Square Dimensions
#define SQ_SIZE 30
int main(void) {
ui_window_t wid = ui_window_create("Bouncing Box Animation", 50, 50, W_WIDTH, W_HEIGHT);
if (wid < 0) return 1;
// Define object state variables
int pos_x = 50;
int pos_y = 50;
int vel_x = 2; // Move 2 pixels per frame horizontally
int vel_y = 2; // Move 2 pixels per frame vertically
gui_event_t event;
while (1) {
// 1. Process Events
while (ui_get_event(wid, &event)) {
if (event.type == GUI_EVENT_CLOSE) {
return 0; // Exit cleanly
}
}
// 2. Physics & Logic Update
pos_x += vel_x;
pos_y += vel_y;
// Collision logic (Bounce off edges)
// The window has a 20px title bar, so the usable client height is W_HEIGHT - 20.
if (pos_x <= 0 || pos_x + SQ_SIZE >= W_WIDTH) {
vel_x = -vel_x; // Reverse horizontal direction
}
if (pos_y <= 0 || pos_y + SQ_SIZE >= W_HEIGHT - 20) {
vel_y = -vel_y; // Reverse vertical direction
}
// 3. Rendering Update
// Step A: Clear the entire background to Black (0xFF000000)
ui_draw_rect(wid, 0, 0, W_WIDTH, W_HEIGHT, 0xFF000000);
// Step B: Draw our shape in Red (0xFFFF0000) at the new position
ui_draw_rect(wid, pos_x, pos_y, SQ_SIZE, SQ_SIZE, 0xFFFF0000);
// Step C: Draw some UI text over the animation in White
ui_draw_string(wid, 10, 10, "BoredOS Animation Demo!", 0xFFFFFFFF);
// Step D: Instruct the compositor to flush our drawing buffer to the physical screen
ui_mark_dirty(wid, 0, 0, W_WIDTH, W_HEIGHT);
// 4. Yield and throttle
sys_yield();
}
return 0;
}
🛠️ How it Works
- State Management: We store
pos_x,pos_y,vel_x, andvel_y. These variables represent the "physics" of our system. Notice that they update outside the event-checking logic so that the animation runs even if the user isn't clicking the mouse. - Screen Clearing: We must fill the screen with black (
ui_draw_rect(wid, 0, 0, W_WIDTH, W_HEIGHT, ...)). If we don't clear the screen, the red square will leave a permanent trailing smear everywhere it goes! - The Double Buffer:
ui_draw_rectandui_draw_stringdo not immediately appear on your monitor. They just color a hidden buffer within the kernel. ui_mark_dirty: This is the crucial command that tells the kernel Window Manager, "I'm done drawing my frame. Can you quickly copy my hidden buffer over to the real screen now?"
Warning
Because
sys_yield()'s pause duration depends heavily on CPU load and how many other processes are running (or QEMU emulation speed), tying physics/movement strictly to loops can make the game run faster on faster computers. Advanced developers will want to calculate delta time (time elapsed since the last frame) for smooth motion.