component
Level
A stackable wall ring plus optional slab: per-side openings, sides that can be omitted, and a stairwell hole — the unit a building is composed from.
⌖ Explore the library in 3D → · View as Markdown
Add it
npx @runek/cli add level
Pulls floor, wall, @runek/core@^0.15.0.
Use it
import { Level } from './runek/Level'
<Level position={[0, 0, 0]} />
Or as a node in a world file (worlds as data):
{ "type": "Level", "props": { "position": [0, 0, 0] } }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
position |
[x, y, z] |
[0, 0, 0] |
Position [x, y, z] in units (1 unit = 1 m, Y-up). |
rotation |
[x, y, z] |
[0, 0, 0] |
Euler rotation [x, y, z] in radians. |
size |
[number, number] |
[8, 8] |
Interior footprint [width, depth], in units. |
height |
number |
3 |
Height, in units. |
thickness |
number |
0.2 |
Thickness, in units. |
walls |
LevelWalls |
Per-side wall configs; an omitted side renders a solid wall. | |
floor |
boolean | LevelFloorConfig |
true |
The slab underfoot; false for none, or a config with a stairwell opening. |
color |
color |
palette wall |
Defaults to the world palette’s wall slot. |
seed |
number |
Reserved for procedural variation. |
LevelWalls
| Prop | Type | Default | Description |
|---|---|---|---|
front |
LevelWallConfig |
||
back |
LevelWallConfig |
||
left |
LevelWallConfig |
||
right |
LevelWallConfig |
LevelWallConfig
| Prop | Type | Default | Description |
|---|---|---|---|
present |
boolean |
Set false to leave this side open (porch, lean-to). |
|
openings |
WallOpening[] |
||
color |
color |
palette wall |
Defaults to the level color, then the world palette’s wall slot. |
WallOpening
| Prop | Type | Default | Description |
|---|---|---|---|
offset |
number |
Horizontal center offset from the wall center, in units. | |
width |
number |
required | |
height |
number |
required | |
sill |
number |
Height of the opening’s base above the wall base, in units. |
LevelFloorConfig
| Prop | Type | Default | Description |
|---|---|---|---|
opening |
FloorOpening |
||
thickness |
number |
||
color |
color |
palette floor |
Defaults to the world palette’s floor slot. |
FloorOpening
| Prop | Type | Default | Description |
|---|---|---|---|
x |
number |
Hole center offset from the slab center along X, in units. | |
z |
number |
Hole center offset from the slab center along Z, in units. | |
width |
number |
required | |
depth |
number |
required |
Registry manifest
registry manifest /r/components/level.json Self-contained JSON: inlined source plus resolved dependencies, exactly whatrunek add level fetches.
Browse the whole catalog in the gallery →.
Source (Level.tsx)
import { useWorld, type Vec3 } from '@runek/core'
import { Floor, type FloorOpening } from './Floor'
import { Wall, type WallOpening } from './Wall'
/** One side of a level's wall ring. */
export interface LevelWallConfig {
/** Set `false` to leave this side open (porch, lean-to). */
present?: boolean
openings?: WallOpening[]
/** Defaults to the level `color`, then the world palette's `wall` slot. */
color?: string
}
/** Per-side wall configs: front is +z, back is -z, left is -x, right is +x. */
export interface LevelWalls {
front?: LevelWallConfig
back?: LevelWallConfig
left?: LevelWallConfig
right?: LevelWallConfig
}
export interface LevelFloorConfig {
opening?: FloorOpening
thickness?: number
/** Defaults to the world palette's `floor` slot. */
color?: string
}
export interface LevelProps {
position?: Vec3
rotation?: Vec3
/** Interior footprint `[width, depth]`, in units. */
size?: [number, number]
height?: number
thickness?: number
/** Per-side wall configs; an omitted side renders a solid wall. */
walls?: LevelWalls
/** The slab underfoot; `false` for none, or a config with a stairwell `opening`. */
floor?: boolean | LevelFloorConfig
/** Defaults to the world palette's `wall` slot. */
color?: string
/** Reserved for procedural variation. */
seed?: number
}
const SIDE_TURN: Vec3 = [0, Math.PI / 2, 0]
/**
* One ring of walls plus an optional slab — the stackable unit of a building.
* Its origin is the base of its walls, so level N sits at the summed height of
* the levels below it. Composes `Wall` and `Floor`.
*/
export function Level({
position = [0, 0, 0],
rotation = [0, 0, 0],
size = [8, 8],
height = 3,
thickness = 0.2,
walls,
floor = true,
color,
}: LevelProps) {
const { unit } = useWorld()
const [w, d] = size
const halfW = (w / 2) * unit
const halfD = (d / 2) * unit
const floorConfig = floor === true ? {} : floor
const side = (config: LevelWallConfig | undefined, pos: Vec3, turned: boolean, width: number) =>
config?.present === false ? null : (
<Wall
position={pos}
rotation={turned ? SIDE_TURN : undefined}
width={width}
height={height}
thickness={thickness}
color={config?.color ?? color}
openings={config?.openings}
/>
)
return (
<group position={position} rotation={rotation}>
{floorConfig && (
<Floor
position={[0, 0, 0]}
size={size}
thickness={floorConfig.thickness ?? thickness}
opening={floorConfig.opening}
color={floorConfig.color}
/>
)}
{side(walls?.front, [0, 0, halfD], false, w)}
{side(walls?.back, [0, 0, -halfD], false, w)}
{side(walls?.left, [-halfW, 0, 0], true, d)}
{side(walls?.right, [halfW, 0, 0], true, d)}
</group>
)
}