At a glance

1
Export a scene
World glb + NPCs + props + music + manifest from your disc.
export-glb
2
Make a worlds project
VRChat Creator Companion + glTFast + the kit.
Unity 2022.3
3
Build the scene
One menu action wires the whole map from the manifest.
LegaiaWorldBuilder
4
Spawn + test
Point the VRChat descriptor at the spawn marker, Build & Test.
VRC SDK
5
Make it alive
Wandering NPCs, grabbable weapons, more scenes joined by their portals.
Udon kit

1 · Export a scene

./legaia-engine export-glb --scene town01 --out glb-export

Reads extracted/ by default, or the disc directly with --disc "/path/to/Legend of Legaia (USA).bin". --all-scenes exports everything that assembles and reports what each scene yielded. One scene produces:

  • town01.glb - the assembled world: ground, terrain, buildings, every placed object at its resolved transform, with the PSX point-sampled look baked into the materials. Where the scene has one (Rim Elm does), the shoreline's wave motion rides along as a looping blendshape clip.
  • npcs/*.glb - one animated file per catalogued villager, carrying every scene animation whose skeleton matches.
  • props/*.glb - the animated placements: windmills, house doors, gate leaves.
  • music/bgm_*.wav - the scene's entry BGM rendered through the engine's own SPU, cut to exactly its loop region so a looping AudioSource repeats it seamlessly.
  • manifest.json - transforms, labels, doorway-teleport triggers and landings, door tags, the suggested spawn, and the scale/axis conventions that tie it all together.

The default --scale is 1/128: one 128-unit walk tile becomes 1 m. Retail's field proportions are generous, and in-headset the earlier 2 m-per-tile default made the village loom over a real-scale player. NPC and prop files carry the scale baked on their root node, so one dragged into a Unity scene by hand is already world-sized.

2 · Set up a VRChat worlds project

  1. Install the VRChat Creator Companion and create a Worlds project (Unity 2022.3).
  2. Add glTFast so Unity can import .glb: Window > Package Manager > + > Add package by name...com.unity.cloud.gltfast. On the VRChat-mandated 2022.3.22f1 editor, pin version 6.14.1 - newer glTFast releases require a newer Unity than the SDK allows, so the Package Manager refuses them. Equivalently, one line in Packages/manifest.json under "dependencies": "com.unity.cloud.gltfast": "6.14.1".
  3. Copy the kit from the repo - scripts/vrchat-world/world-project/Assets/LegaiaWorld/ - into the project's Assets/: the editor builder + its realism, camp-props and audio modules, a JSON reader, four small shaders, and eight UdonSharp behaviours (doorway teleport, proximity door, wander, day/night, rack pickup, torch, settings panel, firelight flicker).

glTFast is a Unity-registry package, so it never appears inside the Creator Companion - and the VRChat packages live in Packages/vpm-manifest.json, so don't be alarmed when Packages/manifest.json shows no com.vrchat.* entries. Both are normal.

3 · Import and build the scene

  1. Copy the exported scene folder into the project, e.g. Assets/LegaiaImports/town01/, keeping npcs/, props/ and manifest.json beside the world glb. Let Unity import - glTFast picks up every .glb with no material fixup needed.
  2. Menu Legaia > Build Scene From Manifest..., browse to the copied manifest.json, and Build scene.

Two default-on options are worth knowing before you press the button. Match explorer orientation mirrors the finished root on X so the scene reads the way this site's field-scene viewer presents it (the raw export genuinely is mirrored). Merged + welded builds one welded, double-sided collision mesh for the whole world instead of a collider per mesh - it closes the hairline seams a player capsule slips through, and because the source data's triangle winding is mixed, a single-sided collider would drop you through roughly half the floors.

The result is one Legaia_town01 root:

  • world - the full map with its collider; the shoreline morph clip and the BGM loop play from here. Semi-transparent meshes stay out of collision - they represent light (window shafts) and you walk straight through them - except water sheets, so the sea keeps its floor.
  • npcs/ - every catalogued villager at their retail spawn tile, looping their spawn animation, capsule-collided.
  • props/ - windmills spinning, gate leaves, and house doors. Instances the manifest tags as doors - and any prop retail parks held at spawn (interior doors, cupboards, drawers, per the bind record's own spawn pass) - open on first approach and stay open instead of looping their opening - the kit's LegaiaDoor behaviour. Only the props retail leaves free-running loop, which is what keeps the windmill turning.
  • teleports/ - one trigger volume per retail doorway. Walk into a house door and you land in its interior facing the authored direction: house interiors sit in an unused corner of the same map, and repositioning the player is the retail door mechanism (LegaiaDoorway). The trigger boxes are sized for a player capsule, not for retail's point-player check - each one is verified reachable against the exported world mesh itself, so a door works from a normal walk-up rather than only at the exact retail contact point.
  • LegaiaSpawn - the manifest's suggested spawn, the village centre.

The builder creates the UdonSharp program assets its behaviours need, compiles them, and syncs every wired field - so a plain copy of the kit's .cs files is enough; no manual UdonSharp setup.

4 · Descriptor, test, upload

  1. Drop the SDK's VRCWorld prefab into the scene if the template didn't already.
  2. Select it, expand the VRC Scene Descriptor's Spawns list, and drag Legaia_town01/LegaiaSpawn into element 0, replacing the prefab's own Spawn child. Moving a spawn marker does nothing until the list references it. Players face the spawn transform's +Z, so rotate the marker toward the view you want.
  3. Delete the template's GridFloor plane (or sink it far below as a fall catch) so it can't mask the real walk surface.
  4. VRChat SDK > Show Control Panel > Build & Test. Doors and teleports react to players, so test in Build & Test or ClientSim - flying the editor scene camera through a trigger does nothing.
  5. When it feels right, Upload. A fresh world is private until you explicitly publish it to Community Labs - leave it private for legal reasons.

5 · Making it feel alive

  • Wandering villagers - add the kit's LegaiaNpcWander behaviour to any NPC the builder placed and tune radius/speed. Movement is computed per player: fine for ambience, use synced variables if everyone must agree.
  • Grabbable weapons - export-glb --items exports every equipment item in the game (all four characters' weapons, Ra-Seru, armour, headgear, footwear). Copy the items/ folder into Assets/, point the builder's Equipment props section at its manifest, and Place equipment rack near spawn lines every weapon up grounded on the world collider - scaled (half the world ratio by default: they are battle-mode models, oversized against field proportions), wrapped in a tight convex hull collider cooked from its rest pose, and wired as VRC Pickup physics props, so you can walk to the rack and hold Vahn's sword. Props spawn frozen on the rack and only go physical the first time someone drops one, so world load can't scatter them or drop them through the ground. Untick Weapons only to also rack the armour and Ra-Seru.
  • Camp props - the builder also places a settings panel and a small camp near spawn (all primitives + generated materials, no game data). The panel's world-space buttons mute the music for you alone or jump the shared day/night cycle to noon or midnight for everyone - click them while the board stands (grab it only by its bottom handle; the handle is the panel's whole grab collider so it can't swallow button clicks); the two torches and two campfires are pickups you can carry anywhere and light with Use - fire and smoke particles, a warm flickering point light (no visible glow orb), and a spatial synthesized crackle, with the lit state synced across the instance.
  • More scenes - build several exported scenes into the same Unity scene and the town-exit portals connect automatically: when a portal's target root (Legaia_map01, say) exists, walking out the town gate teleports you onto the world map.
  • More animation - every NPC glb carries all the scene's skeleton-matching clips as record_N takes; retarget the generated Animator at any of them.

The playable slot machine

The kit also rebuilds the Sol casino's slot minigame as a cabinet you walk up to and play. The rules are an UdonSharp port of the engine's Ghidra-traced kernel - the retail RNG and reel strips, the five-payline evaluation, the feature odds, the bonus rounds - and the screen is the retail scene itself: the reel drums re-derived per frame on the retail ellipse, the glass furniture, the dot-matrix marquee and the paytable, composed directly onto the cabinet's screen face through the retail software projection read off your disc.

  1. Export the machine's art and data (like every export, disc-decoded and never redistributed):
    ./asset slot-art extracted/PROT/0975_*.BIN extracted/PROT/1200_*.BIN --out "<unity project>/Assets/LegaiaImports/slot-art"
  2. Put a cabinet mesh in the scene. The repo ships none (a modelled cabinet tends to carry disc-derived art on its faces, which can't be committed) - any model works: a node named screen (a flat face looking along the cabinet's +Z) lets the builder snap and size the display automatically, three button nodes become the reel-stop buttons, and every part has a fallback - missing buttons get generated pads, no screen node means hand placement, and no cabinet at all still builds a playable machine at the scene root. A Blender-baked cabinet often fails glTFast's import with UVMulti errors; the kit's fix-glb-uvsets.py repairs the file losslessly.
  3. Menu Legaia > Build Slot Machine..., point it at the cabinet and the art folder, Build. Accept Unity's Import TMP Essentials prompt when it appears (the HUD text is TextMeshPro), then build once more so the text picks up the font.

In an instance, one player holds the machine per session - ownership only changes hands while it sits idle, and outcomes plus both RNG streams sync, so onlookers watch the same spin the player sees. Two editor menus keep the port honest: Legaia > Verify Slot Rules replays a committed rules-parity fixture (generated by the engine's own test suite, pure arithmetic, no game data) against the actual UdonSharp class, and Legaia > Slot Screen Snapshot saves the composed screen as a PNG to eyeball against the minigames page's browser port of the same machine.

Realism enhancements

The builder window's Realism enhancements foldout layers a set of passes over the built root. Every pass defaults on - untick them all for the faithful retail-shaded scene. Everything the passes create is generated from scratch (shaders, dome and grass geometry, synthesized audio), so no game data is produced beyond what the export already decoded. An apply to the already-built root button reruns just these passes, so tuning a slider doesn't force a rebuild.

  • Realistic lighting - the exported materials are unlit and the meshes carry no normals, so the pass generates smoothed normals, swaps in lit vertex-colour shaders, and adds a warm directional sun with soft shadows and a three-colour ambient. The baked retail shading keeps modulating every surface, so the scene holds its palette - Rim Elm at golden hour still looks like Rim Elm. Villagers and props get light wrap: on a low-poly face the lighting terminator cuts a harsh dark band, so their shading is flattened toward even light while buildings keep the full directional response.
  • Day / night cycle - an Udon behaviour sweeps the sun through a full day on a fixed cycle (night compressed), synced across all players from the shared server clock. Night genuinely darkens the landscape - the ambient and fog sweep down to a moonlit, blue-shifted fraction of their daytime values (a "night darkness" slider sets how far) - and night lamps light up: small warm lights in the village windows - light only, no visible bulb - anchored where the retail scene's own authored window-glow shafts meet the buildings, so light spills out of the windows exactly where retail painted it, plus burning stake torches planted beside each doorway and each tree (trees are found as green canopy clusters in the world mesh), all switched on only while the sun is down.
  • Sky + distance fog - a procedural skybox that tracks the sun (so it darkens with the cycle) and linear fog scaled to the scene's size.
  • Ground foliage - procedural wind-swayed grass, scattered where the terrain reads green and tinted from the ground it grows on - ground only: green triangles floating above other geometry (tree canopies, roofs) are rejected. Density, coverage threshold and seed are sliders; each rerun rescatters instead of stacking.
  • Interior room shells - the doorway-teleport interiors are unused corners of the same map, so from inside a room you'd see the skybox above and the floating village past the doorway. The pass detects each detached room from the manifest's teleport data and wraps it in a black dome fitted tightly to the room's own geometry, facing inward only: black space from inside (retail's own framing for these rooms), invisible from outside, and shadow-free so the room stays lit - with a warm fill light so it reads window-lit.
  • Smooth textures - bilinear + anisotropic filtering instead of the PSX point sampling, for the "remaster" look.
  • Ambient audio beds - quiet synthesized loops under the BGM (generated noise and sines, not disc audio): a wind/surf base, plus a daytime bed (breeze, leaf rustle, soft birds) and a night bed (crickets) that crossfade with the sun - the atmosphere holds even with the music muted from the settings panel.
  • Villagers wander - wires the stroll behaviour on every talk-role NPC automatically, so the whole town ambles. Strolls are collision-aware: a walk that would clip a hut re-picks instead, and a downward ray keeps feet on the floor. Movement is forward-only - a direction change pivots the whole body in place before stepping off - and facing is measured, not assumed: each NPC reads the direction its torso mesh visibly faces straight off the transform chain every walking frame (the authored facing is baked into the rig's rest pose, under a stack of mirrors), and turns until the mesh faces the walk direction.

Lighting, sky and fog are per-Unity-scene render settings: the last applied root wins, and turning the options off later doesn't revert them - reset via Window > Rendering > Lighting. Grass plus realtime shadows is a PC-world budget; trim density and shadow strength for Quest.

Troubleshooting

  • Doors don't open / teleports do nothing: the Udon behaviours need the VRChat worlds SDK in the project, and they only react to players - test in Build & Test or ClientSim. A door that opens but never teleports is usually a scene-exit portal whose target scene isn't built in this Unity scene.
  • Falling through the floor: use the default Merged + welded world collider - per-mesh colliders leave hairline seams and collide single-sided against mixed-winding source data.
  • Everything looks mirrored: the raw export is X-mirrored relative to the site's viewers; the builder's default-on Match explorer orientation compensates. If you unticked it, that's the whole difference.
  • A hand-placed NPC or prop is a giant: current exports bake the scale onto each NPC/prop glb's root, so a dragged-in file is world-sized as-is - re-export if your files predate that (the builder reads the manifest's npc_prop_units flag and handles both).
  • A scene looks empty: world-map scenes have no villagers, and some scenes are cutscene-only shells; --all-scenes reports what each yielded.
  • The shoreline doesn't move: the morph clip lives in the world glb - the manifest should carry world_anim, Animate world morphs should be on, and the glb's import inspector's Animation tab must keep animations enabled.

The kit's committed README carries the full troubleshooting list, including the collider, transparency and orientation deep-dives: scripts/vrchat-world/README.md.

Faithful vs. approximated

The geometry, textures, shading, placements, floor heights, walk surface, doorway trigger/landing data, shoreline morph deltas and the music render are the engine's own retail-parity kernels - the same code this site's field-scene viewer draws with. What a static export approximates: NPCs loop a clip instead of running their scripts, non-door props free-run instead of waiting for script triggers, a door's full choreography collapses to open-on-approach + teleport, and script-door variants are frozen at the fresh-game story state. The manifest already carries each NPC's role and first dialog line, so dialog and shop counters are the natural next Udon layer to build yourself.

See also