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.
1. Spinning cube
Section titled “1. Spinning cube”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: UdonSharpBehaviour → WasmBehaviour, class must be partial (the binder emits a partial half).
Source: examples/ports/Snippets/SpinningCube.cs.
2. Player settings
Section titled “2. Player settings”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.
3. Interact
Section titled “3. Interact”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.
4. Teleport player
Section titled “4. Teleport player”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.
5. Get players
Section titled “5. Get players”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:
| UdonSharp | CVR WASM |
|---|---|
| Caller allocates a ceiling-sized array | Player.GetAllPlayers() returns a fresh right-sized Player[] |
.displayName property | .GetUsername() method |
| Manual null-skip | Still 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.
6. Cross-behaviour communication
Section titled “6. Cross-behaviour communication”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.
| Udon | CVR 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.