Skip to content

UdonSharp snippet ports

TL;DR: Side-by-side conversions of the six UdonSharp snippets commonly used as “hello world” for VRChat world scripting. Each section shows the original, the CVR WASM port, and what changed.

Rotate on Y at 90°/s. Trivial port — only the base class changes.

UdonSharp:

using UnityEngine;
using UdonSharp;
public class RotatingCubeBehaviour : UdonSharpBehaviour
{
private void Update()
{
transform.Rotate(Vector3.up, 90f * Time.deltaTime);
}
}

CVR WASM:

using UnityEngine;
using WasmScripting;
public partial class SpinningCube : WasmBehaviour
{
void Update()
{
transform.Rotate(Vector3.up, 90f * Time.deltaTime);
}
}

Changes: UdonSharpBehaviourWasmBehaviour, class must be partial (the binder emits a partial half).

Source: examples/ports/Snippets/SpinningCube.cs.

This one does not port. VRChat lets world scripts tune SetJumpImpulse, SetWalkSpeed, SetRunSpeed, SetGravityStrength on VRCPlayerApi. None of those are exposed on CVR’s LocalPlayer — jump / gravity / walk speed are client-side only. The closest CVR analogues are SetVelocity(Vector3) (one-shot velocity override) and SetImmobilized(bool) (freeze input-driven movement). The port file keeps the original field names as inspector fields, marks them non-functional, and demonstrates the velocity / immobilize API.

UdonSharp (reference):

playerApi.SetJumpImpulse(jumpImpulse);
playerApi.SetWalkSpeed(walkSpeed);
playerApi.SetRunSpeed(runSpeed);
playerApi.SetGravityStrength(gravityStrengh);

CVR WASM (best-effort analogue):

void Start()
{
if (initialVelocityKick != Vector3.zero) LocalPlayer.SetVelocity(initialVelocityKick);
if (immobilize) LocalPlayer.SetImmobilized(true);
}

Source: examples/ports/Snippets/PlayerSettings.cs.

Click a GameObject to deactivate it.

UdonSharp:

public override void Interact()
{
gameObject.SetActive(false);
}

CVR WASM:

public partial class ClickMe : WasmBehaviour
{
public void OnInteracted()
{
gameObject.SetActive(false);
}
}

CVR has no Interact() override. Drop a CCK CVRPointer or CVRInteractable on the object and wire its OnInteract UnityEvent to OnInteracted. See Unity Events Rewiring for how persistent calls reach the WASM guest.

Source: examples/ports/Snippets/ClickMe.cs.

Click to teleport the local player to a transform.

UdonSharp:

public override void Interact()
{
Networking.LocalPlayer.TeleportTo(
targetPosition.position,
targetPosition.rotation,
VRC_SceneDescriptor.SpawnOrientation.Default,
false);
}

CVR WASM:

public void OnInteracted()
{
if (targetPosition == null) return;
LocalPlayer.SetPositionAndRotation(
targetPosition.position,
targetPosition.rotation,
updateGround: false);
}

Changes: Networking.LocalPlayer.TeleportTo(...)LocalPlayer.SetPositionAndRotation(...). No spawn-orientation concept in CVR — it’s a straight set. Wire via pointer OnInteract as in the Interact example.

Source: examples/ports/Snippets/TeleportPlayer.cs.

Enumerate everyone in the instance.

UdonSharp:

VRCPlayerApi[] players = new VRCPlayerApi[20];
VRCPlayerApi.GetPlayers(players);
foreach (VRCPlayerApi player in players) {
if (player == null) continue;
Debug.Log(player.displayName);
}

CVR WASM:

Player[] players = Player.GetAllPlayers();
if (players == null) return;
for (int i = 0; i < players.Length; i++)
{
Player p = players[i];
if (p == null) continue;
Debug.Log(p.GetUsername());
}

Changes:

UdonSharpCVR WASM
Caller allocates a ceiling-sized arrayPlayer.GetAllPlayers() returns a fresh right-sized Player[]
.displayName property.GetUsername() method
Manual null-skipStill null-safe; CVR uses live refs, but guard anyway

Player.GetRemotePlayers() is also available if you want to skip yourself.

Source: examples/ports/Snippets/GetPlayersExample.cs.

The Udon example shows four idioms: reading another behaviour’s field, calling its method, broadcasting to everyone, and targeting the owner. The WASM translations collapse the first two to plain C# and keep the network surface explicit.

UdonCVR WASM
other.GetProgramVariable("publicBoolean") / .SetProgramVariable(...)other.publicBoolean (plain field access — both behaviours live in the same WASM module)
SendCustomEvent("RunMethod") / other.SendCustomEvent(...)RunMethod() / other.RunMethod()
SendCustomNetworkEvent(NetworkEventTarget.All, "X")Networking.SendMessage(..., playerIds: null, SendType.Reliable, loopback: true) + a tag byte
SendCustomNetworkEvent(NetworkEventTarget.Owner, "X")Networking.SendMessage(..., new short[] { owner.GetNetworkId() }, SendType.Reliable)

The port wires two behaviours (CommsExample, AnotherExample) in one file, each subscribing to Networking.OnReceiveMessage and dispatching by a leading byte tag. CommsExample.DoStuff() broadcasts MSG_NETWORK_EVENT_STUFF; on receive, NetworkEventStuff() mutates other.publicBoolean, calls other.RunMethod(), and sends MSG_DO_OWNER_STUFF to the current instance owner.

This model looks more verbose than the Udon one, but it’s strictly necessary: WASM has no reflection, so the network dispatch must be bytes-to-method by your own table, not name strings.

Source: examples/ports/Snippets/CommsExample.cs.