Repository navigation
EN Create Geometry
Français · Reference + explanation · Published, reviewed by a person · Applies from: 4.3.0
Where things stand in a room, and how the engine uses it: the layout file, the walk area, approach points, what a tap
touches, the camera of a wide room, and the stage's floors, layers and masks. The logic of the room (what happens) is
in rooms/<room>.ts; everything on this page is in layout/<room>.json.
Audience, context, and the statuses of what this page rests on
Audience: author · Context: engine repository, created project
Each room has a layout, layout/<room>.json, written by the placement editor (/?edit=<room> on the dev server) or the
Studio's Rooms tab (placement and stage in the Studio). You drag things on the picture; the
editor saves the file. You never type coordinates by hand, except for a one-off scripted move ({ walk: [300, 350] },
{ place: … }).
- Logical coordinates. A screen is 640 × 400, origin top-left, whatever the device. A character and a prop are placed by their feet (bottom centre).
-
A room without a layout has nothing clickable:
npm run validatewarnsno layout: nothing will be clickable. A prop or an actor not placed is an error (no position in the layout), and so is a hotspot without a zone (no zone in the layout (rect or poly)). -
Placed but not declared: a zone or a position whose id the room does not declare is a warning
(
zone "x" has no hotspot in the room,position "x" has no prop in the room). -
/?edit=<room>&at=<checkpoint>opens the editor in a checkpoint's state, to place a prop in a given state.
| Field | What | Default |
|---|---|---|
width |
the room's width in logical units; wider than 640, the room scrolls (camera) | 640 |
floor |
the bottom of the floor (logical y): a computed approach point never goes lower | 395 (FLOOR) |
walk |
the walkable area: { area: [[x, y], …], holes?: [[[x, y], …], …] } (an outer polygon, holes for furniture) |
none: walks go in a straight line |
scale |
character scale by depth: [[back y, scale], [front y, scale]]
|
1 |
entries |
the hero's entry points, at least default
|
default at [320, 360] |
hotspots.<id> |
rect: [x, y, w, h] or poly: [[x, y], …], approach, face
|
|
props.<id> |
x, y (the feet), h (height), z, on, flip, flipV, rot, approach, states.<state>
|
|
actors.<id> |
x, y, h, z, flip, approach
|
|
walkZones, walkLinks, layers, occluders, lights, emitters
|
the stage (below) |
Fields read by the engine besides positions:
- On a prop (and in each of its
states):rot(degrees, clockwise, pivot at the feet),flip(horizontal mirror),flipV(vertical mirror: the image sits below the feet line, as on the placement page),z(forced depth line). A field missing from a state falls back to the prop's own.on: truesays the prop stands on a piece of furniture. - On an actor:
zforces their depth (otherwise it follows their feet);flipturns them to face left, unless the room'sfacingfor them (or one a script left) says otherwise. The placement editor's mirror ↔ sets it: Placement. - On the room:
floor.
Before acting on something in the room, the hero walks to its approach point, then turns to it (and a character he
acts on turns to him). The point is, in order (src/engine/core/world-queries.ts):
- the
approachthe layout gives (a hotspot's always; a prop's or an actor's only if it is within 150 units of the thing,NEAR: farther, it predates a move and is recomputed); - for a prop: under its feet, 12 units lower, never below
floor; - for an actor: beside them on the side facing the middle of the room, 44 units away whatever their size; with
actorGapon the game (4.2.4; for example0.8), that share of the two characters' mean height (heightof the hero and of the character, or the actor'shin the layout), times the depth scale where the character stands; - for a hotspot: under the middle of its rectangle or polygon, 12 units lower, never below
floor.
Tall characters who gesture as they talk end up with a hand in each other's head at 44 units: that is what actorGap
is for. An approach point in the layout still wins, for a character who needs the other side. actorGap is
presentation only, like approach points: no rule nor the solver reads it.
The walk area is triangulated once (earcut), then a path between two points is the navmesh's shortest path, pulled
straight where the shortcut stays inside (src/engine/dom/walk.ts). A target outside
the area is replaced by the closest point inside it: the hero walks as near as he can, then the action goes on from
there. A room without walk walks in a straight line anywhere.
scale sets a character's size by the depth of their feet: in the template, 0.8 at y 250 and 1 at y 396. Character
heights are in logical units (in the demo, Pixel is 36, Grandma 120).
The engine computes, for each frame, the targets of the room and their boxes
(src/engine/scene/frame.ts):
- the targets are the room's hotspots, its props with a
name, its actors withoutinteractive: false, and the other playable characters standing there, each only while it is visible; - a target's box is the drawn image's (rotation included) when it shows one, else the layout's rectangle, else its polygon's bounds; a target with none of them is not in the frame: nothing to tap;
- a hotspot with a
polyis tested against its polygon, anything else against its box (edges included); - when several targets contain the tap, the smallest wins (by box area). No target: the tap is a walk to that point of the floor.
Stage layers and occluders are not targets: a foreground layer drawn over a prop does not stop a tap on it.
feature since 3.4.0
A room is 640 × 400 logical units. Give its layout a width (for example 960 or 1280: in the editor's "Room width"
folder, or in layout/<room>.json) and paint its backdrop that wide (800 px tall, the width in proportion;
npm run assets keeps it): the room scrolls, and the camera follows the hero. Positions stay world coordinates (a prop
at x 900 is in the right part). validate refuses a width under 640 and warns about a zone, a prop or an entry
beyond it.
| Command | Effect |
|---|---|
{ camera: { to: 'far_stalls', ms: 900 } } |
Pan to centre on something (an actor, a prop, a zone), then stay there. |
{ camera: { pan: 320, ms: 600 } } |
Pan to a left edge, in logical units (clamped to the room). |
{ camera: 'follow' } (or 'reset') |
Follow the hero again. Entering a room always follows the hero. |
The camera is in the state (a save restores it), the solver ignores it, and "reduce motion" makes every pan instant. A
camera command in a 640-wide room does nothing (validate warns). Animations and the rest of staging:
staging.
feature since 3.4.0
A room can show more than its backdrop. The content says what exists and when (stage in the room: layers, lights,
particles, the logic of walk links); the layout says where; the painter draws it. The content side, lights and
particles are on the stage. The geometry is:
-
walkZones.<id> { area, holes?, scale?, zoom?, elevation? }: several floors. They replacewalk(validatewarns when both are given). Each zone scales the characters by its own depth line (scale) and can zoom the camera (zoom, 1 to 2: a balcony seen closer); a zoomed camera also moves up and down to keep the hero in view. Where zones overlap, the one with the higherelevationholds the point. -
walkLinks.<id> { from: { zone, at }, to: { zone, at }, mode, anim?, ms?, facing?, oneWay? }: how to go from one zone to another;modeiswalk,stairs,ladder,jumporteleport. A path across zones takes the fewest links. Every zone must be joined to another, and reachable from the zone of thedefaultentry with every link open:validaterefuses a zone nothing leads to. -
layers.<id> { x, y, z, parallax: [x, y], blend, opacity }: where a stage layer stands. Its role (in the content) puts it behind everything (backdrop), among the characters by itsz(scenery), in front of them (foreground), or above all (effect).parallax1 moves with the room, 0.5 half as fast (behind), 1.2 faster (in front). -
occluders.<id> { polygon | mask | layer, z, feather?, invert? }: what hides a character standing deeper thanz(a pillar, a counter, a window frame): a polygon, a black-and-white mask image, or a stage layer's alpha. -
lights.<id> { at, radius }andemitters.<id> { area: [x, y, w, h] }: where the radial lights and the particles are.
An old room is a stage of one backdrop (decor) and one zone (walk, named main): nothing to rewrite.
A closed link. stage.links.<id> { if, locked } in the content closes a link while its condition does not hold.
The hero walks to its foot, says its locked line, and the action goes on from there. So the rules of what stands
behind it must check the same condition: npm run lint warns (walk-link-gate) when a rule on a target behind a
closed link does not. The puzzle is the rule; the link only stops the walk.
Guarantee ·
closed-link-stops-walk· referenced · executed on47dccf1b4b15· scope reviewed A closed walk link is never crossed: the walk stops at its foot and names the link.
Guarantee ·
geometry-stage-not-state· referenced · executed on47dccf1b4b15· scope reviewed Layers and lights on the stage are not game state: they do not change what the solver sees. The sample game with a foreground layer and a light on every room proves the same: same verdict, same number of states, same softlocks.
Limit ·
canvas-only-effectsMasks from images or layers, lights, particles and blend modes are drawn only by the Canvas painter:renderer: 'canvas'on the room or the game. On a room the DOM painter draws (the default),npm run validatesays what it will not draw.
/?dev draws the geometry over the room: the walk area in green and its holes in red, hotspots in blue, props in
purple (their box and their feet), actors in green, entry points as pink triangles, the depth-scale lines in orange,
walk links in yellow. An approach point is a dot: filled when the layout gives it, hollow when the engine
computes it. A hidden hotspot or prop (its visible condition false) is not drawn. Press D to hide the overlay. When something cannot be reached, start with
something is visible but unreachable.
Geometry is edited with the mouse against the picture, and changes for other reasons than the story: a new backdrop moves every rectangle and no rule. Keeping it in its own file means a placement never touches the logic, the Studio can write it safely, and the solver ignores it: walking, the camera and the stage are presentation, and what the solver proves does not depend on them (what the solver proves). The reference painter is the DOM, for accessibility; Canvas is allowed for rooms that need its effects (ADR 0001, ADR 0011).
-
FLOOR(395) andNEAR(150) insrc/engine/core/define.ts;actorGapingame.ts. - The types:
Layout,StageDefinsrc/engine/core/types/stage.ts. - Rooms, staging, the stage, placement in the Studio.
Generated publication · publication générée — current documentation · documentation courante: quest'n'click 4.5.4 r2, documentation revision · révision de la documentation 63112c877c587cc4fad16b25a3400408db2c36bd.
Latest documentation · dernière documentation: https://quest-n-click.cleverapps.io/docs · https://quest-n-click.cleverapps.io/fr/docs; earlier versions · versions précédentes: each release's quest-n-click-docs-<version>.tar.gz · l'archive quest-n-click-docs-<version>.tar.gz de chaque release, https://github.com/Quest-n-Click/quest-n-click/releases
An edit made in this wiki is overwritten at the next publication · une modification faite dans ce wiki est écrasée à la publication suivante.
Edit the source · Modifier la source · Report an error · Signaler une erreur
Index (6)
Start (6)
Play (9)
Install (18)
- Requirements
- Install from the release archive
- Install from npm
- Install from the repository to work on the engine
- doctor: checking your machine
- Image toolchain
- Audio toolchain: ffmpeg, fluidsynth, Furnace, uv
- Upgrade
- Upgrade notes: 4.5
- Upgrade notes: 4.4
- Upgrade notes: 4.3
- Upgrade notes: 4.2
- Upgrade notes: 4.1
- Upgrade notes: v3
- Upgrade notes: v2 to v3
- Coming from web-scumm
- Package reference
- Install troubleshooting
Create a game (55)
- Your first game
- Layout of a created project and of the engine repository
- The working loop
- Content principles and the identity contract
- Rooms
- Geometry: placement, walk zones, masks, layers, camera
- Something is visible but unreachable
- Reactions
- Game commands
- Conditions
- Flags, variables and events: which to use
- Custom code
- Characters
- Items and inventory
- World map
- Dialogues
- Scripts and events
- Moving characters: patrols and walks
- Exits
- Chapters and objectives
- State and saves: persistent or temporary, renaming an id, migrations
- Staging: wide rooms and animations
- The stage: lights, particles, floors
- Skin and fonts
- Title and credits
- Heroes: several characters and hero choice
- Built-in minigames
- A custom minigame
- Translations
- Music in stems
- Sound effects
- Voices: per language and per hero
- Images: prompts, cutting sheets, transparency, scale
- Asset provenance
- The storyboard, source of truth
- Puzzle design
- Classic mechanics
- Review pages
- DSL reference
- Checking your game
- What the solver proves
- Solver options
- Playtests
- Remix
- Speedrun for authors
- The sealed ending
- Production template
- Example games
- Working with an AI
- Build and release a game
- Publish on GitHub Pages
- The static game on Clever Cloud
- Other static hosts
- Sub-path, cache and service worker, incomplete deploys, going back
- Testing on a phone over the LAN
Local Studio (24)
- The local Studio: overview and launch
- Edit a room
- Placement and stage
- Preview, apply, undo
- Storyboard
- Assets and cutting
- Check: validator, solver, critical path, heat, health, coverage
- Before and after captures
- Semantic diff
- Play and replay a session
- Rule explainer
- Language and objectives
- Voices and the heroes × languages matrix
- Map
- Notes
- Remix in the Studio
- Music
- The Assistant: bring your own key
- Demo mode and studio-apply
- Local and LAN security
- Studio API
- Local MCP: setting up each client
- MCP tools
- Studio troubleshooting
Team Studio (21)
- What runs where
- Concepts: revision, transaction, span, lease, preview, build
- Starting as an invited dev
- Working together day to day
- Roles and actions: the full matrix
- Invitations
- Managing members
- Write refused on the hosted Studio: stale base revision (409)
- Write refused on the hosted Studio: span locked (423)
- Recovering after a lost lease, a disconnection or a stale preview
- Comments
- Map and semantic diff
- Publishing builds
- Remote MCP
- The game in Git
- Upgrading the engine of a hosted game
- Hosted assets
- The server's command line
- Health and metrics routes
- Trying it locally with compose.yml
- Known limits
Clever Cloud (23)
- Clever Cloud: overview, cost and prerequisites
- Apps and add-ons
- Domains and TLS
- Keycloak
- Generic OIDC and GitLab
- Configuration
- First deployment
- Backups
- RPO and RTO
- Restore the Studio on a fresh installation
- Redeploys
- Upgrading the server and checking state after a migration
- Limits of going back
- Monitoring
- Security
- Rotating keys
- Recovering admin access
- Changing domain or OIDC provider
- Worker, PostgreSQL or Cellar down
- Exporting the game and leaving the installation
- Retention
- Clever Cloud troubleshooting
- Costs and teardown
Reality Bridge and connectors (16)
- The Reality Bridge: what it is
- Quick start
- Guarantees
- Install and run the Bridge
- Backup and restore
- Several tenants
- Signals and the manifest
- The Bridge's command line
- Connectors overview
- Email connector
- Telnet connector
- SSH connector
- Open Badges connector
- The connector SDK
- Security: Biscuit and signatures
- Bridge and connector troubleshooting
Contribute (25)
- Welcome
- Developer setup
- The dev loop
- Before a pull request
- Conventions
- Writing documentation
- Architecture
- Code tour
- Import boundaries
- Which tests run when
- Mutation testing
- The coverage ratchet
- The quality baseline
- CI explained
- Field passes
- Benchmarks
- Releasing
- npm packages
- Decisions and ADRs
- Governance
- Bot pull requests
- Contributing with AI agents
- Security policy
- Code of conduct
- Licences and credits