From 0c3a700aa94a0256c5e5b1a14819f10b3d3e869b Mon Sep 17 00:00:00 2001 From: Gene Pasquet Date: Wed, 8 Apr 2026 00:38:55 +0100 Subject: Support scling --- docs/api.org | 45 ++++++++++++++++++++++++++++++++++++++++++--- docs/guide.org | 27 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/api.org b/docs/api.org index 45593e4..c418ec8 100644 --- a/docs/api.org +++ b/docs/api.org @@ -17,6 +17,7 @@ The engine module provides the top-level game lifecycle and state management. (title "Downstroke Game") (width 640) (height 480) + (scale 1) (frame-delay 16) (input-config *default-input-config*) (preload #f) @@ -31,8 +32,9 @@ Creates and initializes a game object. All parameters are optional keywords. | Parameter | Type | Default | Description | |-----------+------+---------+-------------| | ~title~ | string | "Downstroke Game" | Window title | -| ~width~ | integer | 640 | Game window width in pixels | -| ~height~ | integer | 480 | Game window height in pixels | +| ~width~ | integer | 640 | Logical game width in pixels | +| ~height~ | integer | 480 | Logical game height in pixels | +| ~scale~ | positive integer | 1 | Whole-game pixel scaling factor (window = width×scale by height×scale) | | ~frame-delay~ | integer | 16 | Delay between frames in milliseconds (30 FPS ≈ 33) | | ~input-config~ | input-config | *default-input-config* | Keyboard/controller mappings | | ~preload~ | procedure/false | #f | Hook: ~(lambda (game) ...)~ called once before create | @@ -43,13 +45,50 @@ Creates and initializes a game object. All parameters are optional keywords. The game object is the central hub. Use it to store/retrieve assets, manage scenes, and access the current input state. +*** Scaling + +The ~scale:~ parameter controls integer pixel scaling for the entire game. When ~scale:~ is greater than 1, the OS window is created at ~width×scale~ by ~height×scale~ pixels, but SDL2's logical renderer size is set to ~width~ by ~height~. This means all game code (rendering, physics, coordinates) works in the logical resolution — SDL2 handles the upscaling automatically. + +This affects everything uniformly: tiles, sprites, text, colored rectangles, and debug overlays. Mouse/touch input coordinates are also automatically mapped back to the logical resolution. + +#+begin_src scheme +;; 320×240 game rendered in a 640×480 window (2× pixel scaling) +(make-game title: "Pixel Art Game" width: 320 height: 240 scale: 2) + +;; 256×224 NES-style with 3× scaling → 768×672 window +(make-game title: "Retro Game" width: 256 height: 224 scale: 3) +#+end_src + +Only positive integers are accepted; fractional or zero values signal an error. + +*** Fullscreen + +Downstroke does not provide a built-in ~fullscreen:~ keyword, but you can make any game fullscreen by setting the SDL2 window flag after the game starts. Use the ~preload:~ hook (the window exists by then): + +#+begin_src scheme +(make-game + title: "My Game" width: 320 height: 240 scale: 2 + preload: (lambda (game) + ;; 'fullscreen-desktop scales to fill the screen without changing resolution + (sdl2:window-fullscreen-set! (game-window game) 'fullscreen-desktop))) +#+end_src + +The two fullscreen modes are: + +| Mode | SDL2 symbol | Behavior | +|------+-------------+-----------| +| Desktop fullscreen | ~'fullscreen-desktop~ | Fills the screen at the desktop resolution; SDL2 handles scaling and letterboxing. Best for most games. | +| Exclusive fullscreen | ~'fullscreen~ | Changes the display resolution to match ~width×scale~ by ~height×scale~. Use only when you need exclusive display control. | + +Combine with ~scale:~ to get pixel-perfect fullscreen: set your logical resolution small (e.g. 320×240), use ~scale:~ for the default windowed size, and let ~'fullscreen-desktop~ handle the rest. + ** ~game-run!~ #+begin_src scheme (game-run! game) #+end_src -Starts the main event loop. Initializes SDL2, opens the window, and runs the frame loop indefinitely until the user quits or the ~quit~ action is pressed. Never returns. +Starts the main event loop. Initializes SDL2, opens the window (at ~width×scale~ by ~height×scale~ pixels), sets the logical render size when ~scale~ > 1, and runs the frame loop indefinitely until the user quits or the ~quit~ action is pressed. Never returns. Lifecycle order within each frame: 1. Collect SDL2 events diff --git a/docs/guide.org b/docs/guide.org index 380f04b..1c5a8f3 100644 --- a/docs/guide.org +++ b/docs/guide.org @@ -216,6 +216,32 @@ See =demo/platformer.scm= in the engine source for a complete working example. * Development Tips +** Pixel Scaling + +Retro-style games often use a small logical resolution (e.g. 320×240) but need a larger window so players can actually see things. Use ~scale:~ to scale everything uniformly: + +#+begin_src scheme +(make-game title: "Pixel Art" width: 320 height: 240 scale: 2) +;; Creates a 640×480 window; all coordinates remain in 320×240 space +#+end_src + +This is integer-only scaling. All rendering — tiles, sprites, text, debug overlays — is scaled automatically by SDL2. Game logic and coordinates are unaffected. + +See =demo/scaling.scm= for a complete example. + +** Fullscreen + +Use SDL2 window flags in the ~preload:~ hook to go fullscreen: + +#+begin_src scheme +(make-game + title: "Fullscreen Game" width: 320 height: 240 scale: 2 + preload: (lambda (game) + (sdl2:window-fullscreen-set! (game-window game) 'fullscreen-desktop))) +#+end_src + +~'fullscreen-desktop~ fills the screen without changing display resolution; SDL2 handles letterboxing and scaling. ~'fullscreen~ uses exclusive fullscreen at the window size. + ** Debug Mode During development, enable ~debug?: #t~ on ~make-game~ to visualize collision boxes and the tile grid. This helps you understand what the physics engine "sees" and debug collision problems: @@ -252,6 +278,7 @@ Downstroke includes several demo games that showcase different features: | Sprite Font | =demo/spritefont.scm= | Bitmap text rendering using non-contiguous tileset ranges | | Menu | =demo/menu.scm= | State machine menus, keyboard navigation, TTF text rendering | | Tweens | =demo/tweens.scm= | Easing curves, =tween-step=, =#:skip-pipelines= with tile collision | +| Scaling | =demo/scaling.scm= | Integer pixel scaling with =scale: 2=, 2× upscaled rendering | Each demo is self-contained and serves as a working reference for a particular game mechanic. -- cgit v1.2.3