Photo mode
Freeze the game, fly a free camera around the player, change field of view, roll, exposure and filter, hide the HUD and capture a picture.
A photo mode for your game (photo.mode) #
What it is for #
photo.mode opens a photo mode in any game: a key (or a call) freezes the game, a free camera flies around the place where the player stood, and the player picks a field of view, a roll, an exposure and a filter, hides the HUD and takes a picture. The kit holds the camera state and the pause; the photo overlay (shipped with the kit as an ui.webui fragment) drives it and the page draws the picture, because only the page can read the canvas. It decides nothing of your game: a mod or the game can forbid opening it with a hook.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "photo.mode" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "photo.mode",
config: { "radius": 20, "filters": ["none", "sepia", "noir"] } }
It requires game.engine and input.actions. By default the key O opens and closes the mode; flight keys are input actions too (W A S D or the arrows, R and F) that the player can rebind in the input settings.
First example #
<!-- test: kits/game_engine/host/tests/photo_mode.rs::a_shot_is_taken_through_the_capture_source_while_the_world_is_frozen --> The test installs the kit with the filters none and noir, then drives the photo.mode service:
capture()before entering answersnull: there is no shot outside the mode.enter()answerstrue, and the game phase becomespaused.set({ filter: "noir" })picks a filter;capture()calls the capture source with[width, height, camera]and answers its picture (a data URL). The size defaults to 1920 x 1080 and is clamped to 16..8192 per side.photo.shotscounts the pictures taken. A source that answers nothing, or fails, is not a shot.
On the web, the photo overlay registers the capture source for you with setCaptureSource.
Settings and defaults #
| Setting | Type | Default | Meaning |
|---|---|---|---|
radius | number (1..500) | 12 | how far the camera may fly from where the player stood |
fov | number (10..120) | 60 | field of view when the mode opens, in degrees |
fovMin | number (5..90) | 15 | narrowest field of view |
fovMax | number (30..150) | 110 | widest field of view |
filters | list of strings | none, mono, sepia, warm, cool, vivid, noir, dream | filters offered, in order; none is always added first if missing |
pauseWorld | boolean | true | freeze the game while the mode is open |
All six are moddable. The filter names are the ones the photo overlay knows.
Services, events, variables #
The service photo.mode:
enter(): opens the mode when the game is playing. Answersfalseif it is already open, if the game is not playing, or if a hook cancelled it.exit(): closes it and, if the kit paused the game, resumes it. Answersfalsewhen it was not open.active():truewhile the mode is open.camera(): the current camera{ position, yaw, pitch, roll, fov, exposure, filter, hideHud, anchor, radius }, ornull.look(dyaw, dpitch): turns the camera; pitch is limited to 85 degrees.move(forward, right, up): moves the camera along its view, kept insideradiusof the anchor.set(patch): any offov(clamped tofovMin..fovMax),roll(+-90 degrees),exposure(-3..3),filter(must be one offilters),hideHud.reset(): back to the camera the mode opened with.filters(): the list of filters.capture(width?, height?): asks the capture source for a picture; answers it, ornull.setCaptureSource(fn): gives the function that draws the picture.shots(): pictures taken so far.
Events: photo.entered (anchor), photo.exited (shots), photo.captured (width, height, filter). The kit listens to game.phase: if anything else resumes the game (a menu), the mode closes with it.
Variables: photo.active (boolean, mode open) and photo.shots (number, pictures taken).
Input actions: photo_mode, photo.forward, photo.right, photo.up.
Hooks and modding #
The hook photo.enter is run just before the mode opens, with { anchor } (where the player stands). It is cancellable: a game or a mod forbids the photo mode during a scene, a fight or a timed run, and then enter() answers false, nothing is paused and nothing is emitted. The settings radius, fov, fovMin, fovMax, filters and pauseWorld can be changed by a mod.
Engines #
The logic is the same Rust module everywhere. The overlay (look, fly, field of view, roll, exposure, filters, HUD hiding, shutter, and the button that opens the mode) is an ui.webui fragment; the capture source reads the rendered canvas of the page.
When it does not do what you expect #
enter()answersfalse: the mode is already open, the game is not in theplayingphase, or aphoto.entertap cancelled it.capture()answersnull: the mode is closed, no capture source was set, or the source answered nothing.- A filter does not apply: its name is not in
filters. - The camera stops at a distance: it is confined to
radiusaround where the player stood. - The game keeps running behind the overlay:
pauseWorldisfalse.
Limits #
- Only the number of pictures is saved. A loaded game is never in photo mode, and a save made inside it resumes playing.
- The kit does not write the picture to disk: the page that gave the capture source decides what to do with it.
- It needs a capture source to take a picture; without a page (headless) the camera still moves but nothing is captured.
Versions #
Kit version 1.2.0. 1.1.0 added the photo.enter hook and the moddable settings; 1.2.0 made the photo keys input actions that the player can rebind.