`ludic build` produces a program. Double-clicked it opens a Terminal window, it wears the generic executable icon, it calls itself whatever the file is called, and it carries none of its assets. `ludic bundle` produces an application. Everything it needs is in package.ludic, so the command takes no arguments: an Info.plist and PkgInfo from `app` lines, an .icns built by sips and iconutil at all ten sizes macOS asks for from a single source PNG, the asset pack in Contents/Resources, and an ad-hoc signature - which is not optional on Apple silicon, where an unsigned binary is killed rather than warned about. The bundle identifier falls back to the package path reversed, so a project that never thinks about it still gets a defensible one instead of two apps sharing a key Launch Services hangs the Dock, saved state and permissions off. A bundled game is moved to ~/Library/Application Support/<name> before main, because Finder starts a .app with its working directory at "/" where no save could ever be written. Reads come out of the pack, writes land somewhere real and per-user, and the game's save code needs no change and no platform knowledge. The splash is the other half of looking like an application. A game that loads 165 MB spends a visible moment doing it with nothing on screen, which from the outside is indistinguishable from a launch that failed. splash_show puts a borderless window up from the same constructor that mounts the pack - before main, so it appears while the process is still starting rather than after the slow part it exists to cover - and reads the artwork out of the pack like any other asset. It turns the run loop enough times to be mapped and composited there and then; once composited the backing store survives a busy main thread, so it stays up for the whole load. Nothing hides it automatically. Only the game knows when its first real frame is ready, and a splash that vanishes before that leaves the same black gap it was covering, so the game calls App.splash_hide(). Headless there is no splash and the call lowers to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1.1 KiB
1.1 KiB
| id | name | category | kind | tokens | sig | tip | order | ns | member |
|---|---|---|---|---|---|---|---|---|---|
| app-splash_hide | App.splash_hide | app | namespace-method | App.splash_hide | App.splash_hide() -> void | Dismiss the boot splash. | 1 | App | splash_hide |
Dismisses the boot splash. Safe to call when there is no splash, and safe to call twice.
A bundled game shows a splash while it starts: ludic bundle records app splash from the manifest into the bundle, and the runtime puts it on screen before main runs, out of the asset pack. Nothing takes it down on its own, because nothing else knows when the game has a first frame worth showing - a splash that disappears while the terrain is still loading leaves the same black gap it was there to cover.
So the game says when:
program MyGame {
entry {
load_the_world() # the slow part the splash is covering
open_the_window()
draw_one_frame()
App.splash_hide() # ...and only now
}
}
Headless there is no splash and no window, and the call lowers to nothing.
See also: shipping a game.