CVR Input
TL;DR: CVR.CVRInput is a static class exposing the local player’s movement, look, and button state from the VR or desktop input system, plus last-frame values and per-frame deltas. The state is double-buffered in guest memory by the host — reads are essentially free. Writes are conditional on context and let a world (or the local avatar’s wearer) drive the player’s input programmatically.
Read state (everyone)
Section titled “Read state (everyone)”Buttons (flag-set):
// Single-frame testsif (CVRInput.WasPressedThisFrame(CVRButton.Jump)) { /* just pressed */ }if (CVRInput.WasReleasedThisFrame(CVRButton.GripLeft)) { /* just let go */ }
// Held stateif (CVRInput.GetButtonDown(CVRButton.Sprint)) { /* holding sprint */ }
// Last-frame equivalent (for your own edge detection)bool wasHolding = CVRInput.WasButtonDown(CVRButton.InteractRight);Continuous analog state:
Vector3 move = CVRInput.Movement; // full 3D movement axisVector2 look = CVRInput.Look; // 2D look axisfloat grabR = CVRInput.InteractRight; // 0..1 grab/trigger analogfloat gripL = CVRInput.GripLeft; // 0..1 grip analogLast-frame and delta companions for every analog field:
Vector3 previousMove = CVRInput.LastMovement;Vector3 deltaMove = CVRInput.MovementDelta; // current - last, per frameAll these are static properties. The host writes them into a fixed memory slot on every OnInputReady dispatch (once per input tick); the guest reads them directly — no host round-trip per access.
Button flags
Section titled “Button flags”Source: CVRInputCCK.cs:52-60.
[Flags]public enum CVRButton : ulong{ Jump = 1ul << 0, Sprint = 1ul << 1, InteractRight = 1ul << 2, InteractLeft = 1ul << 3, GripRight = 1ul << 4, GripLeft = 1ul << 5,}Combined flags are supported — pass CVRButton.Jump | CVRButton.Sprint to test either, or set both in one call via SetAllButtons.
Write state (world or avatar-self only)
Section titled “Write state (world or avatar-self only)”Scripts can override the input stream:
CVRInput.SetMovement(new Vector3(0f, 0f, 1f)); // force-walk forwardCVRInput.SetLook(new Vector2(0.2f, 0f)); // gentle right yawCVRInput.SetInteractRight(1f); // fully-pressed grabCVRInput.SetButton(CVRButton.Jump, true); // assert jumpCVRInput.SetAllButtons(CVRButton.Sprint | CVRButton.Jump);The override takes effect only if the calling VM is allowed to drive input. From WasmVM.Events.cs:518-522, the host commits the write back to CVRInputModuleWasm.Instance.Input iff:
- the VM runs a world script, OR
- the VM runs an avatar script and the avatar is worn by the local player (
OwnerContext == Self).
A remote avatar or a prop script can call the setters without throwing, but the write won’t reach the local input system.
Common use cases:
- Cutscene locomotion — a world forces the player to walk into a scripted camera path.
- Autopilot / assisted driving — a vehicle script on the local avatar drives the input to stabilize control.
- Accessibility mods — remap inputs (though CVR has native input remapping; use sparingly).
OnInputReady event
Section titled “OnInputReady event”Subscribe by defining the matching method:
void OnInputReady() { /* fires once when the input subsystem is initialized */ }Fires once per VM on initial input setup — treat it as “now is a good time to initialize input-dependent state.” It is not a per-frame event; use Update / FixedUpdate to sample current input every frame.
Value-type struct
Section titled “Value-type struct”Source: CVRInputCCK.cs:42-50.
public struct CVRInputStruct{ public CVRButton Buttons; public Vector3 Move; public Vector2 Look; public float InteractRightValue; public float InteractLeftValue; public float GripRightValue; public float GripLeftValue;}This is the on-the-wire shape the host writes to the shared memory slot. You rarely touch it directly; the static property getters dereference it for you.
Permission model
Section titled “Permission model”- Reads: no gate. Any VM context. The memory is always populated for the local player.
- Writes: implicitly gated. The setter methods compile and run from any VM, but the write only reaches
CVRInputModuleWasmfrom a world script or an avatar-on-self script. Remote-avatar and prop writes silently no-op.
Performance
Section titled “Performance”Analog reads are guest-side memory reads — the cheapest possible call. Use freely.
Button tests (GetButtonDown, WasPressedThisFrame, etc.) are also guest-side — the guest computes (current & flag) != 0, (current & flag) != 0 && (last & flag) == 0, etc., from the double-buffered struct.
Setters are the only host-crossing calls, and only world/avatar-self scripts materialize them. Cheap.
Example — forward-while-sprinting
Section titled “Example — forward-while-sprinting”using UnityEngine;using WasmScripting;using CVR;
public partial class ForwardWhileSprinting : WasmBehaviour{ void Update() { if (CVRInput.GetButtonDown(CVRButton.Sprint)) { // Override movement to pure forward while sprint is held. CVRInput.SetMovement(Vector3.forward); } }}Works on a world script; a no-op on a remote player’s client (only the local sprint input is intercepted).
Related
Section titled “Related”- Permissions — why writes fail silently on wrong VM context.
- Events —
OnInputReadysignature. - Performance — why analog reads are free.