Player
<Player> spawns the player’s character inside a World and draws her. WASD and the arrows walk her in the camera’s frame; the on-screen stick steers the same way. She runs at 5 m/s unless her movement says otherwise. Game listens to the keys, not Player, so a Player that remounts while a key is down, as one inside a <World key={round}> does on a new round, walks on. A walk walkHeroTo starts, as a click on the ground does, shows a mark on the ground at the point asked for until she arrives or the player takes over: a pulsing ring by default, a pillar of light, a pillar and ring or a pulse of light that land with a snap, or nothing, by marker. In a room the walk goes to the room too, which finds her path on its own navmesh, baked from the same map, and starts her on it at the step her page did.
An avatar is a VrmBody: the URL of a VRM file the game ships, its scale, its turn rotateY in radians, the armSpread that holds its arms off its body, and, for a body with cloth, the armColliders that keep that cloth off its arms. The engine ships no VRM body, so a game imports its own file with ?url. The mmorpg’s bodies are in its avatars table.
A raycast, a pointer event’s included, meets an avatar as an invisible capsule the size of her physics body rather than her skinned model, so it costs no more over her than over any other mesh. A model or a primitive she wears is met on its own meshes.
Its props are in PlayerProps.
The five body fields are hers at spawn: the physics cuts her capsule to them and climbs and slides by them. climb is her one walkable angle: she climbs, stands on and walks down any ground up to it, and slides down anything steeper. Unreal’s walkable floor angle, Unity’s slope limit and Godot’s floor max angle work the same way, each 45 degrees or near it by default. A game whose ground rises steeper than the default sets climb, the way Unity’s CharacterController and Godot’s CharacterBody3D carry their slope limit on the character. The physics walks her capsule itself, as Unreal’s CharacterMovementComponent does: a move along the floor it found under her, one slide along a wall, a step onto what stands under step, and a push back out of any walker the move left her inside, cast like the move, so a wall or a ball in the way stops it. Two walkers that meet head-on stay a body’s width apart, unless a wall pins one inside the other.
Her Transform is where her capsule stands: the dump and the stream carry it, and a room’s correction rebuilds her capsule on it, in the room and on every client alike. Her model’s feet are drawn apart from it, by her DrawnFeet trait, which the view reads. On a slope the capsule rests above the ground under her axis, and her feet are drawn on that ground, no farther than her step under the capsule and no farther than the slope she rests on lifts a capsule, plus 5 cm. Over a ledge’s rim, where the face she rests on is the ledge’s level top, her feet stay within those 5 cm of the capsule’s bottom, so she stands on the rock rather than in it. The stream carries no drawn feet. Each client casts its own rays against the same ground, so every tab draws her feet at the same height. Unreal’s CharacterMovementComponent and Unity’s CharacterController work the same way: the capsule is the truth, and a separate pass places the mesh’s feet without moving the capsule.
Another walker’s capsule is ground like any other face: she lands on a monster or on another player that blocks her, walks on it and jumps from it as from the ground. A jump from a walker’s top lifts her feet jumpHeight above it, and a press in the air is dropped, so she never climbs walkers in the air. A capsule’s top is a dome, and where she holds on it follows her climb. A default body holds anywhere the dome is within 50 degrees of level, up to about 0.5 m off the walker’s axis for two default capsules, and a landing further out slides her off its side. On a walker her feet are drawn on its top, and every tab draws them at the same height while she moves. Two cases keep a 1 cm gap: she stands still while the walker under her walks away, the gap of a base that does not carry her, and she stands on the middle walker of a stack three high. A room and each of its clients stand her at the same height on a walker that stands still, and a walker that moves does not carry her. Fortnite and Roblox let players stand on heads the same way. Unreal lets a pawn stand on another unless the base refuses it, and Zelda slides the player off an enemy.
Movement
Section titled “Movement”How she runs, stops, turns and jumps lives on her Movement trait: her speed, her acceleration and deceleration, how fast she turns at speed, whether she turns on the spot from a stand, past which angle and at what rate, and how high she jumps. preset picks a whole record. Responsive, the default, brings her to full speed in a tenth of a second and pivots her at speed. Realistic takes a third of a second to reach speed and to stop, and plants her feet to turn on the spot before she walks off. movement lays single settings over the preset: movement={{ speed: 7 }}. The devtools inspector shows her settings on her row, each number as a slider and turnInPlace as a switch, and an edit moves her at once. Once a number feels right, the card’s Copy as prop button copies the changed settings as movement={{ speed: 7 }}, ready to paste into the Player.
In a room her settings are the room’s. The game’s room module exports movement, a preset’s name or a record, and the room spawns every hero with it; a Player on a room’s world writes none, and each client reads her settings from the stream. The inspector shows them on her row read-only, greyed with the reason, unless the room takes edits. A room takes edits only when startRoom is given allowEdits: true or the room’s process runs with ROOM_EDITS=1, as the arena’s room target does, so no client sets its own speed in a game others play. Where it does, a slider edit on your own hero goes to the room as any devtools edit does, and the room clamps it to the slider’s range and streams it to every client; the devtools page says how. On a room’s world her row holds the room’s settings as they stood when she joined, so an edited card reads “Tried, not saved” and Copy as prop copies the fields that differ from the room’s. To keep them, put those fields in the room module’s movement, since the room spawns her with that and not with the Player’s props.
The jump
Section titled “The jump”A press of Space jumps her, and so does a tap on the phone: a game mounts <Tap label="Jump" onTap={jump}> beside its Joystick, with jump read off the useInput store. She leaves the ground at the speed that lifts her feet by jumpHeight on her Movement trait, 1.2 m by default, under the World’s gravity, and the same gravity brings her down. Under the default 9.8 m/s² she leaves at 4.85 m/s and lands 0.99 s later; a game that raises gravity keeps the height and shortens the arc. Unity’s Starter Assets state the jump the same way, as a 1.2 m JumpHeight, and Roblox as Humanoid.JumpHeight.
- One press is one jump. A held Space jumps once, as in Unity’s and Godot’s templates. Space on a focused button presses that button and does not jump her.
- In the devtools’ edit mode, Space pauses the world and does not jump her.
- She jumps only from the ground. A press in the air is dropped: there is no coyote time and no jump buffer.
- The stick steers her in the air as on the ground, at the same speed.
- A jump that meets a ceiling stops rising there and falls from it, as in Unreal and Godot.
- The step carries the press as its input’s
jump, so a test, the harness and a room jump her as the keys do. The step takes the press off the input it reads, sosendInput(game, { jump: true })jumps her once, on the next step.
Her Jump trait says whether the last step ended with her on the ground, grounded, how long she has been off it, airSeconds, and her vertical speed in metres a second, negative on the way down, fallSpeed. The physics writes all three after it moves her, so a game and the dump read them off her entity, as Unity’s isGrounded and Godot’s is_on_floor() do. In a room, a correction puts her body in the room’s state, in the air or on the ground, so a correction halfway through a jump leaves her the rest of the room’s arc.
On a VRM body she plays three clips over the jump:
jump.vrmawhen she leaves the ground standing, andrun-jump.vrmawhen she leaves it running. The one chosen as she leaves holds until she lands, however the stick moves in the air.land.vrmaafter she lands with the stick idle, held for the clip’s whole length. A stick held on landing, or pushed during the crouch, goes straight to the run.
The land clip plays from the moment her feet touch, 0.25 s in, because the frames before it carry a fall in her hips that the capsule has already made.
Collision layers
Section titled “Collision layers”Every collider in the physics world stands in one CollisionLayer: Ground holds the map’s heightfield and its edge walls, Obstacles its scatter hulls, Players her capsule, Walkers every other walker’s capsule, and Props the body an Entity’s collider gives it. collides lists the layers her sweeps and contacts test against; a layer left out never stops her and never costs her a test. The default is every layer, except on a chaser, which leaves out Players and Walkers. Two colliders meet only when each lists the other’s layer, so a game that wants its monsters to block her lists Players on their bodies. A ghost that walks through everything but the floor lists [CollisionLayer.Ground], the way Unity’s layer collision matrix and Godot’s collision mask narrow what a body meets. The five layers are fixed; a game defines none of its own.
The default body
Section titled “The default body”With no avatar, the player is a blue capsule cut to her height and radius, 1.4 metres tall and 0.35 metres in radius by default, standing on its position. It is drawn from geometry rather than a model file, so a game starts without any other game’s character and downloads no VRM or VRMA file; hand it an avatar to wear a VRM body, and her body and clips start downloading as the Player first renders.
shape swaps the capsule for another primitive and color paints it. A Box is as tall as her and as wide as her capsule; a Sphere is a ball her radius round, resting on her feet. shape={ColliderShape.Sphere} color="red" makes her a red ball.
model names a model from the same registry Entity reads, so the registerModel line that game add asset prints works for her too. The model stands with its lowest point on her feet and turns with her. It is static: it plays none of its own clips, and she walks as a model that does not move its legs. An avatar wins over a model, and a model over a primitive.
Whatever she wears, her physics body stays the capsule the body fields cut. Her size never comes from her model, as a walker’s does: she is 1.4 m tall and 0.35 m in radius unless the Player sets height or radius.
Where it runs
Section titled “Where it runs”Client. The player is the one entity that says so: everything else spawns under the server’s authority, and the trust boundary page says what that means for a room.
Sample
Section titled “Sample”import { Player, World, registerModel, scatterModels,} from "@daniel-zarinski/engine";
registerModel("tree-round", scatterModels.treeRound);
export function Run() { return ( <World map="meadow"> <Player model="tree-round" position={[0, 0, 4]} /> </World> );}A VRM body she wears with her clips:
import { Player, World, type VrmBody } from "@daniel-zarinski/engine";import model from "../assets/heroine.vrm?url";
const heroine: VrmBody = { model, scale: 1, rotateY: Math.PI, armSpread: { left: { out: 0, forward: 0 }, right: { out: 0, forward: 0 }, },};
export function Run() { return ( <World map="meadow"> <Player avatar={heroine} position={[0, 0, 4]} /> </World> );}