City district
Build a city district from road tiles and buildings on the lots of each block, with sidewalks, a lane graph for traffic, colliders, named zones and a guarded edge of the world.
A city district (world.district) #
Readable offline: gripforge docs world-district, or the MCP tool gripforge_docs (topic world-district).
What it is for #
world.district builds the world of an open-world city game. The district is a square grid of cells by cells tiles of tile metres. Every avenueEvery row and column is a road; the cells between roads form blocks. Each road cell takes the tile that matches its neighbours (straight, four-way, T or curve) and is turned into place. Each block gets a sidewalk ring and lots along its streets, filled from the buildings you bind, facing the street. Interior lots stay open (courtyards) or are filled.
It provides the ground and building colliders (world.ground), the road tile centres, the sidewalk nodes and the lane graph (world.roads), and the named zones (world.zones). Vehicle and character kits stand on it, traffic and pedestrian kits follow its lanes and sidewalks. Without bound assets it still plays, with flat slabs and procedural boxes.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "world.district" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "world.district",
config: { "cells": 17, "avenueEvery": 4, "seed": 3, "buildingHeight": [8, 30] } }
The kit requires game.engine, physics.rigidbody and physics.port. Bind the four road tiles and any number of buildings to the asset slots below.
First example #
<!-- test: kits/game_engine/host/tests/world_district.rs::the_layout_is_a_function_of_config_and_seed --> With cells 13, tile 8 and avenueEvery 4 and the same seed, the layout is always the same; another seed changes the lots. The district is 104 metres wide with four road lines. It has 3 by 3 blocks of 3 by 3 lots (81 lots, one interior lot per block left open), 36 sidewalk pieces and the three default zones.
Settings and defaults #
| Setting | Type | Default | Meaning |
|---|---|---|---|
layout | tiles or blocks | tiles | road tiles, or the grid of square blocks (below) |
cells | integer 5 to 97 | 13 | tiles per side (an odd number keeps a central block) |
tile | number 4 to 16 | 8 | tile edge in metres; road tiles and lots share it |
avenueEvery | integer 2 to 8 | 4 | a road every N cells; blocks are N-1 cells wide |
buildingHeight | [min, max] | [6, 18] | metres, for the procedural boxes when no building is bound |
seed | integer | 1 | deterministic layout seed |
sidewalks | boolean | true | a sidewalk ring per block |
courtyards | boolean | true | leave the interior lots of a block open |
profile | grid or coastal_city | grid | web: regional land use, parks, coast dressing, neighbourhood zones |
appearance | asset or realistic | asset | web: shared street surfaces, markings, sky, street furniture and skyline |
streetscape | object | none | web: seed, buildingDistance, vegetationDistance, decorDistance, weathering, wetness (all six) |
streamEntities | boolean | false | web: with world.streaming, materialise nearby instances and colliders only |
edge | object | see below | the edge of the world |
The blocks layout (layout set to blocks) builds a blocks by blocks grid of square blocks instead:
| Setting | Default | Meaning |
|---|---|---|
blocks | 3 | blocks per side (1 to 8) |
blockSize | 40 | block edge in metres (20 to 120) |
roadWidth | 8 | road width in metres (4 to 20) |
It uses buildingHeight, seed and sidewalks too, draws box buildings, keeps the default zones and spawn, and is built on the web and Rust hosts; the engine wrappers build the tiles. The tile settings do not apply to it.
The edge of the world, edge: apron (default 40) is the ground in metres that goes on beyond the streets, barrier (rail, wall or none, default rail) holds vehicles and characters on the edge, height (default 1.2) is its height in metres, and recoverBelow (default 12) is how far under the ground, or beyond the apron, an actor can be before it is put back on the nearest road.
Asset slots #
| Slot | Many | Without a binding |
|---|---|---|
road_straight, road_intersection, road_t, road_curve | no | flat slab |
district_building | yes | procedural boxes |
district_tree, district_park_tree, district_skyline | no, yes, no | procedural, in the realistic appearance |
lot_fill | no | flat lot |
city_block | yes | blocks layout: procedural boxes |
ground_texture, skybox | no | flat grey, gradient sky |
Tiles are square, tile metres wide, origin at the centre, +Y up. The straight tile runs along Z, the T keeps its straight along Z with the branch towards +X, the curve links -Z to +X; the kit applies the yaw. Buildings have their origin at the ground centre and their front on -Z; they are fitted to the lot and turned towards their street.
Services, events, variables #
world.district(service):layout()answers{ cells, tile, extent, profile, areas, tiles, sidewalks, sidewalkNodes, lots, blocks, lanes, zones, spawn }.world.roads(service):nearestSidewalk(pos),randomWalk(from, rng)andlanes(), the lanes as{ from, to, width }.world.ground(service): static ground and building colliders, withgroundHeight(x, z).world.ready(event):{ blocks, extent, bounds, zones, tiles, buildings }, once colliders and zones exist; spawn-time kits may place entities. It is sent again when a different district is rebuilt.world.recovered(event):{ entity, cause, x, y, z }, an actor lost under the ground (fell) or beyond the apron (outside) was put back.world.district.area(variable): the neighbourhood the player stands in (coastal profile); empty in the grid profile.zones(collection):{ id, shape (sphere or box), center, radius or size, tags }. The defaults arez_parking,z_downtown(tagpolice) andz_garage; a zone with a default id replaces it.
Hooks and modding #
district.build{ scene, cells, tile, avenueEvery, seed, profile, sidewalks, courtyards }: tiles layout, asked before a build (at the start, when a scene asks for another district, when a load rebuilds it). A tap may changeseed,profile,sidewalksandcourtyards; the grid is read only. Not cancellable.city_block.build{ scene, blocks, blockSize, roadWidth, seed, sidewalks }: the same for the blocks layout. A tap may changeblocks,seedandsidewalks.world.recover{ entity, cause, x, y, z }: asked before an actor is put back. A tap may changex,y,z(its feet); cancelled, the game handles it.
{ "id": "harbour_town", "hook": "district.build", "match": { "scene": "harbour" }, "set": { "profile": "coastal_city" } }
{ "id": "back_to_garage", "hook": "world.recover", "match": { "cause": "outside" }, "set": { "x": 0, "y": 0, "z": 12 } }
A scene whose world names world.district plays the district of its own world.config over the kit config, and rebuilds when it differs. A mod may override the layout and look settings (all but streamEntities) and add, patch or remove zones.
Engines #
The tiles layout is built by the web player and the native player, and by the Godot, Unity and Unreal wrappers with the same layout, roads, lanes and zones. The realistic appearance, the coastal profile, the streetscape and entity streaming are web only; the engines keep the asset look and the eager layout. Hooks run through the Rust kit logic.
When it does not do what you expect #
- Flat slabs and grey boxes: no road tile or building is bound to its slot.
- The streetscape or the realistic look is missing in an engine: those settings are web only.
streamEntitieshas no effect:world.streamingis not installed.- A car drove out of the map and came back: the edge safety net, see
edgeand theworld.recoverhook. - The tile settings are ignored:
layoutisblocks.
Limits #
One district per scene, built from a regular grid. It does not simulate traffic or pedestrians; it gives them lanes and sidewalks.
Versions #
Kit 1.10.0. 1.9.0 added the edge of the world, 1.10.0 the blocks layout and the city_block.build hook; district.build came with 1.7.0. The services world.ground, world.roads and world.zones are at 1.0.0.