Building the platformer¶
The platformer-game is one of the best current examples of a real Novus app that is still small enough to understand in one sitting.
What it imports¶
That already tells you a lot about the architecture:
windowhandles native windowing and drawingphysicshandles collision checksstdprovides general helpers
Game state¶
The project stores almost all state in globals:
- player position and velocity
- grounded state
- camera position
- life count
- current mode (playing / win / lose)
- current view size
That is a very common early-stage Novus pattern: keep the app simple, obvious, and mostly data-driven.
Resizing and view size¶
The platformer asks the window library for the current content size:
fn update_view_size(fd: i32) -> void {
let size: str = window.wm_size(fd);
let sep: i32 = str_find(size, "x");
if (sep < 0) { return; }
let w: i32 = str_to_i32(substr_len(size, 0, sep));
let h: i32 = str_to_i32(substr(size, sep + 1));
if (w >= 320) { view_width = w; }
if (h >= 240) { view_height = h; }
}
That is what lets the game expand the visible play area instead of just stretching pixels.
Update loop¶
The logic loop is straightforward:
fn update_game(input_mask: i32) -> void {
frame_index = frame_index + 1;
if ((input_mask & window.wm_input_quit()) != 0) {
app_running = 0;
return;
}
if (game_mode != 0) {
if ((input_mask & window.wm_input_jump()) != 0) {
restart_game();
}
return;
}
apply_input(input_mask);
update_player();
}
Important design point: the game treats input as a bitmask, not as individual callback events.
Rendering model¶
Rendering is explicit rectangle drawing.
fn render_game(fd: i32) -> void {
window.wm_clear(fd, COLOR_SKY());
draw_world(fd);
draw_hud(fd);
window.wm_present(fd);
}
That is the core frame model:
- clear
- draw the world
- draw the HUD
- present
Clipping¶
The project clips every rectangle against the current view size before it reaches the window backend:
fn draw_rect_clipped(fd: i32, x: i32, y: i32, width: i32, height: i32, color: i32) -> void {
...
if (draw_x + draw_w > VIEW_WIDTH()) {
draw_w = VIEW_WIDTH() - draw_x;
}
if (draw_y + draw_h > VIEW_HEIGHT()) {
draw_h = VIEW_HEIGHT() - draw_y;
}
...
window.wm_rect(fd, draw_x, draw_y, draw_w, draw_h, color);
}
This is a good example of keeping high-level game logic independent from backend details.
Main loop¶
fn main() -> i32 {
let fd: i32 = window.wm_open_default("Novus Platformer");
if (fd < 0) {
return 1;
}
window.wm_resize(fd, VIEW_WIDTH(), VIEW_HEIGHT());
window.wm_show(fd);
update_view_size(fd);
restart_game();
while (app_running == 1) {
let input_mask: i32 = window.wm_input(fd);
update_view_size(fd);
update_game(input_mask);
render_game(fd);
window.wm_sleep_ms(16);
}
window.wm_quit(fd);
return 0;
}
This is a strong example of the normal shape of a Novus application:
- initialise libraries
- run a simple main loop
- keep rendering explicit
- clean up on exit
Why this example matters¶
The platformer demonstrates:
- package-based imports
- a native window API
- drawing without a heavyweight graphics stack
- real input handling
- a simple game loop
- resize-aware rendering
If you want to learn how Novus applications are really put together, study this repo after you are comfortable with the basics.