Markdown version: https://ai.hiina.space/reports/hand-driven-vr-locomotion-and-mounts/05-prototypes-1-to-3-chair-wheelchair-and.md · index

Index of everything on this site: https://ai.hiina.space/index.md

Hand-driven VR locomotion leaves vehicles and mounts open: 5. Prototypes 1 to 3: chair, wheelchair and handcar

5. Prototypes 1 to 3: chair, wheelchair and handcar

All six prototypes assume Godot 4 with OpenXR. XR Tools ships climb, glide (with flap and arm-roll), grapple, jog and flight providers according to a changelog copy on a third-party mirror; check the official repo's VERSIONS.md before relying on specific versions. From background knowledge of the API rather than a cited source, a custom provider plugs in via XR Tools' movement-provider hook on its CharacterBody3D player body. Where a prototype needs a vehicle, parent XROrigin3D under the vehicle body. Controller transforms are then already vehicle-relative, which makes hand-on-rim and hand-on-crank maths trivial. The rule numbers (R1 to R10) refer to the design rules in section 7.

1. Office-chair push-off: the Gorilla Tag core on a plane, with real yaw for free

This is the cheapest because it is a near-direct port of Player.cs to a body constrained to the floor. The player sits on a real swivel chair. The virtual chair is a CharacterBody3D locked to XZ translation. Its yaw follows the hip tracker if present, otherwise head yaw, as Player.cs does for its collider. Translation comes only from hands pushing furniture. Each physics tick, sphere-cast each hand (r ≈ 0.05 m) from its last world position to its tracked world position against a "pushable" layer. On a hit, anchor the hand and move the chair by (anchor − tracked_hand_world) projected onto XZ, averaging when both hands touch. Push the chair's per-frame displacement over dt into a ring buffer. While touching and |avg| > v_min, set v = min(k·|avg|, v_max)·avg.normalized(), the same structure as Player.cs, with your own constants. After release, coast with caster friction, v *= exp(−μ·dt). Give μ per floor material (carpet versus polished concrete), which is Surface.slipPercentage's job in Gorilla Tag. Bounce off walls with v −= (1+e)(v·n)n. Gripping a desk edge with a still hand zeroes velocity, the same as Player.cs zeroing velocity on contact, which gives a brake and a pivot.

The goofy layer: caster wobble that adds slow lateral drift, swappable chairs (racing chair, broken chair, exercise ball), knockable stacks of paper, and a coffee cup that spills past a lateral-acceleration threshold. The Drag x Drive team found players preferred crashing into cones to avoiding them and built a tackle mechanic around it (Nintendo), so reward collisions.

The comfort rule is R2: never generate artificial yaw. A tangential push spins the real swivel chair because the player's body twists; the game only translates. My inference is that this is the main comfort advantage of the concept over a wheelchair. The first test is a desk corridor time trial with two caster-friction values. Measure whether players can stop on a line, and take a fast motion sickness (FMS) rating at about 10 minutes.

2. Push-rim wheelchair derby: wheel torque from hand tangential velocity, with a straightness assist

Model two drive wheels (radius r_w), a rim at radius r_rim, and track width W. Starting values of about 0.3 m, 0.27 m and 0.55 m are my assumption from typical chairs. A hand grabs a rim when it is within a few centimetres of the rim torus and grip is pressed. In chair-local coordinates, take the radial vector r = p_hand − hub projected into the wheel plane, the tangent t = normalize(axle × r), and the hand angular rate ω_hand = dot(v_hand, t) / |r|. Couple wheel to hand through a grip spring with a torque cap rather than locking them: ω_w += clamp(K·(ω_hand − ω_w), −a_max, a_max)·dt. A still, gripping hand then brakes, and a slow hand cannot magically match a fast wheel. Released wheels coast with rolling resistance. Drive with differential kinematics: v = r_w(ω_L + ω_R)/2 and yaw_rate = r_w(ω_R − ω_L)/W.

Then add Nintendo's fix. Raw input caused unintended turning because dominant hands push harder, so they added a "straightness correction" that aligns both wheels when both inputs point roughly forward, and spent another two to three weeks tuning straight motion and turning (Nintendo). A workable version: when both ω_hand values share a sign and differ by less than about 25% (my starting guess), blend both toward their mean. Drag x Drive also used a rattling HD Rumble synced to wheel motion that made it feel "like you were actually turning a wheel". Approximate it with trigger_haptic_pulse at a pulse rate proportional to |ω_w| while gripped. Goofy layer: wheelies from leaning back (HMD or hip offset behind the calibrated seated pose) plus a hard push, half-pipes, rocket rims, and a tackle that knocks rivals sideways.

The comfort rule is R2 and R3. Differential turning is artificial yaw, and both precedents ran into it. Wheelchair Basketball VR added an "Indirect" teleport hybrid because direct mode sickened some players (devlog). Phantom's smooth stroke-turning caused discomfort until snap turning was added (Road to VR review, secondary). Ship three yaw modes: smooth yaw capped near 30°/s, inside the 15 to 35°/s range that 10 of 12 pilot participants preferred; accumulated differential yaw released as 22.5° snaps (Farmani & Teather); and a teleport hybrid. Damp the view's wheelie pitch hard (R3). Plan for arm fatigue (R9): keep races to a few minutes with coasting stretches. The first test is a cone slalom with straightness correction on and off. Measure unintended drift (degrees of heading error per 10 m when the player intends to go straight) and compare FMS across the yaw modes.

3. Hand-cranked handcar: the bike pipeline driven by hands, with pseudo-haptic load

This reuses an existing stationary-bike pipeline: cadence feeds a flywheel, and the flywheel feeds speed. Only the input changes. A crank hub sits in the vehicle frame with handle radius r_c (about 0.17 m). While gripped, the target angle is θ_h = atan2 of the hand projected onto the crank plane. The crank's own state (θ_c, ω_c) is driven by an angular spring with a torque cap, τ_hand = clamp(k·wrap(θ_h − θ_c) − c·ω_c, ±τ_max), against an effective inertia: I·ω̇_c = τ_hand − (τ_roll + τ_grade + τ_aero)/G, where G is the gear ratio.

The trick is to render the hand on the crank handle at θ_c, not at the tracked position. The visible lag between the real and rendered hand becomes the cue for load. Rietzler et al. showed that visual hand offsets convey weight. "Good" ratings held to about 20 to 24 cm and acceptance to about 35 to 42 cm, and the authors advise staying below about 24 cm for sustained interaction (Rietzler, Geiselhart, Gugenheimer, Rukzio, CHI 2018). Applying those thresholds to cranking resistance is my extrapolation from a lifting study. If the offset exceeds about 24 cm, the hand slips off the handle, which doubles as a goofy failure. A handcar variant replaces the crank with a see-saw lever pumped by both hands, with the lever angle driving the crank through a fixed linkage. It is the obvious two-player upgrade, covered in prototype 6.

The comfort rule is R1 and R2: put it on rails. Speed is the player's own effort smoothed by the flywheel, so it never stops dead. Yaw comes only from gentle track curves at bounded rates. Speed-synced airflow is a cheap add (R6). My inference is that this is the most comfortable prototype on the list. The first test is a hill climb at two gear ratios. Ask players to rate perceived heaviness when only the visual offset differs, and log how often hands slip off.


Text licensed CC BY 4.0 (attribution: Hiina, ai.hiina.space). Source: https://github.com/hiinaspace/ai-hiina-space