structures
Staircase
Stepped staircase with per-step colliders.
Install
npx runek add staircase
Pulls: core@react-three/rapier@^2.2.0
Props
export interface StaircaseProps {
position?: Vec3
rotation?: Vec3
steps?: number
/** Total rise, in units. Ascends along +y and +z from the origin. */
totalHeight?: number
width?: number
/** Total run (depth), in units. */
depth?: number
/** Defaults to the world palette's `stone` slot. */
color?: string
} Source
Staircase.tsx
import { RigidBody } from '@react-three/rapier'
import { useWorld, type Vec3 } from '@runek/core'
export interface StaircaseProps {
position?: Vec3
rotation?: Vec3
steps?: number
/** Total rise, in units. Ascends along +y and +z from the origin. */
totalHeight?: number
width?: number
/** Total run (depth), in units. */
depth?: number
/** Defaults to the world palette's `stone` slot. */
color?: string
}
export function Staircase({
position = [0, 0, 0],
rotation = [0, 0, 0],
steps = 6,
totalHeight = 1.5,
width = 1.2,
depth = 2.4,
color,
}: StaircaseProps) {
const { unit, palette } = useWorld()
const stoneColor = color ?? palette.stone
const w = width * unit
const rise = (totalHeight * unit) / steps
const run = (depth * unit) / steps
return (
<RigidBody type="fixed" colliders="cuboid" position={position} rotation={rotation}>
{Array.from({ length: steps }, (_, i) => {
const h = rise * (i + 1)
return (
<mesh
key={`step-${h.toFixed(4)}`}
castShadow
receiveShadow
position={[0, h / 2, run * (i + 0.5)]}
>
<boxGeometry args={[w, h, run]} />
<meshStandardMaterial color={stoneColor} />
</mesh>
)
})}
</RigidBody>
)
}