OpenFlight port
TL;DR: OpenFlight’s actual lift comes from hand position deltas — detect flap, apply upward impulse proportional to hand speed. CVR WASM does not expose hand transforms to scripts, so the flap model cannot be reproduced. The port keeps the orchestration layer (FlightMode state machine, avatar-whitelist detection, CanFly / CannotFly hooks) and provides a stand-in SimpleHoverFlight driven by CVRInput axes.
What ports and what doesn’t
Section titled “What ports and what doesn’t”| Part | Ports? | Notes |
|---|---|---|
FlightMode enum + state machine | Yes | Identical |
AvatarDetection whitelist | Yes | Uses Avatar.GetContentId() instead of VRC avatar id |
SwitchFlight(bool) toggling a sub-GameObject | Yes | Same SetActive(bool) pattern |
WingFlightPlusGlide flap lift | No | Needs hand velocity; not exposed |
| VR-only flight | Partial | No direct IsUserInVR(), but LocalPlayer.GetPlaySpaceOffset() returns Vector3.zero unconditionally in desktop (VR returns a non-zero playspace offset); use that as a heuristic. Edge case: a VR user at the exact tracking centre also reads zero until they move. |
| JSON avatar whitelist downloaded at runtime | No | Possible via WebRequest world permission; left to author |
Port strategy
Section titled “Port strategy”The original ships three runtime scripts you need (OpenFlight.cs, AvatarDetection.cs, WingFlightPlusGlide.cs) plus a pile of UI, integration, and logger helpers. The port provides:
OpenFlight— state machine + wiring, unchanged in shape.AvatarDetection— keeps theReevaluateFlight()public entry point; whitelist is a staticstring[]in the inspector.SimpleHoverFlight— replacesWingFlightPlusGlide. ReadsCVRInput.Movementand theJump/Sprintbuttons, writes viaLocalPlayer.SetVelocity().
Everything else (PoolController, PlayerInfoStore, the UI stack, CyanPlayerObjectPool integration) is skipped — those scripts are infrastructure for VRChat-specific player pool syncing and have no direct analogue.
Wiring
Section titled “Wiring”- Put
OpenFlighton an empty root object (world scale must be 1). - Put
SimpleHoverFlighton a child GameObject; assign that child toOpenFlight.wingedFlight. - Put
AvatarDetectionon another GameObject; wire it back atOpenFlight.avatarDetection. FillflightWhitelistwith avatar content ids. - When the player swaps avatars, call
avatarDetection.ReevaluateFlight(). There’s no automatic avatar-change hook exposed to scripts — call it from your content’s equivalent of the OnPlayerRespawned or a timer.