Skip to content

Events

TL;DR: A WasmBehaviour receives events by defining instance methods whose names match values in the UnityEvents or GameEvents enums. Presence is detected at build time via reflection; any you don’t define are never dispatched. Six Unity events are explicitly stubbed and will not fire. Post-phase events (PostUpdate, PostLateUpdate, PostFixedUpdate) run after all Unity and CVR scripts have finished the corresponding phase.

At CCK build, WasmBuildProcessor.ScanForUnityEvents and ScanForGameEvents iterate the UnityEvents / GameEvents enums and match by method name:

foreach (UnityEvents flag in Enum.GetValues(typeof(UnityEvents))) {
MethodInfo method = scriptType.GetMethod(flag.ToString(),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
unityEvents = WasmEventUtils.SetEvent(unityEvents, flag, method != null);
}

Source: CVR.CCK.Wasm/CCK/Editor/WasmBuildProcessor.cs (ScanForUnityEvents, ScanForGameEvents).

Matching is by name only (known TODO in source). A public or private method with the matching name is detected. Parameter types are not checked by the scanner — if you give a wrong-shape signature, the runtime dispatcher may pass mismatched args. Stick to the exact canonical signatures listed below.

The detected-events bitmask is stored on each WasmBehaviourInfo.definedEvents, and the union of definedGameEvents across every behaviour on the content root goes on the WasmVMAnchor.definedGameEvents. At VM setup, WasmVM.InitializeEvents only subscribes to the WasmGameEvents delegates that appear in the union, and WasmRuntimeBehaviour only attaches BaseEventForwarder components (OnAnimatorIKForwarder, OnRenderImageForwarder, …) for the Unity events the behaviour actually implements. Unused events cost nothing at runtime.

EventSignatureNotes
Awakevoid Awake()Before Start.
Startvoid Start()After serialized state is restored.
Updatevoid Update()Every frame.
LateUpdatevoid LateUpdate()After animations.
FixedUpdatevoid FixedUpdate()Physics step. In CVR, Time.fixedDeltaTime scales with the display refresh rate (30–144 Hz) — rate varies, timestep is consistent.
OnEnablevoid OnEnable()Behaviour enabled.
OnDisablevoid OnDisable()Behaviour disabled.
OnDestroyvoid OnDestroy()Before teardown.
OnBeforeSerializevoid OnBeforeSerialize()Before state is snapshotted.
OnAfterDeserializevoid OnAfterDeserialize()After state is restored.
EventSignatureDescription
PostUpdatevoid PostUpdate()Runs after all Unity + scene Update calls have finished.
PostLateUpdatevoid PostLateUpdate()Runs after all LateUpdate calls, including local Avatar IK and Network IK. Best place to sample a player’s final pose.
PostFixedUpdatevoid PostFixedUpdate()Runs after all FixedUpdate calls.

These events are a CVR-specific mechanism that lets a WASM script act after everyone else in a given phase. See Execution order for the scene-scoping gotcha.

EventSignatureNotes
OnTriggerEntervoid OnTriggerEnter(Collider other)3D trigger. other is the entering collider.
OnTriggerStayvoid OnTriggerStay(Collider other)Every frame while inside.
OnTriggerExitvoid OnTriggerExit(Collider other)
OnTriggerEnter2Dvoid OnTriggerEnter2D(Collider2D other)2D variant.
OnTriggerStay2Dvoid OnTriggerStay2D(Collider2D other)
OnTriggerExit2Dvoid OnTriggerExit2D(Collider2D other)
OnCollisionEntervoid OnCollisionEnter(Collision c)3D solid contact.
OnCollisionStayvoid OnCollisionStay(Collision c)
OnCollisionExitvoid OnCollisionExit(Collision c)
OnCollisionEnter2Dvoid OnCollisionEnter2D(Collision2D c)2D variant.
OnCollisionStay2Dvoid OnCollisionStay2D(Collision2D c)
OnCollisionExit2Dvoid OnCollisionExit2D(Collision2D c)
OnParticleTriggervoid OnParticleTrigger()
OnParticleCollisionvoid OnParticleCollision(GameObject other)
OnControllerColliderHitvoid OnControllerColliderHit(ControllerColliderHit hit)Character controller.
OnJointBreakvoid OnJointBreak(float breakForce)
OnJointBreak2Dvoid OnJointBreak2D(Joint2D brokenJoint)
EventSignature
OnBecameVisiblevoid OnBecameVisible()
OnBecameInvisiblevoid OnBecameInvisible()
OnPreCullvoid OnPreCull()
OnPreRendervoid OnPreRender()
OnPostRendervoid OnPostRender()
OnRenderObjectvoid OnRenderObject()
OnWillRenderObjectvoid OnWillRenderObject()
OnRenderImagevoid OnRenderImage(RenderTexture src, RenderTexture dst)
EventSignature
OnMouseEntervoid OnMouseEnter()
OnMouseOvervoid OnMouseOver()
OnMouseExitvoid OnMouseExit()
OnMouseDownvoid OnMouseDown()
OnMouseUpvoid OnMouseUp()
OnMouseUpAsButtonvoid OnMouseUpAsButton()
OnMouseDragvoid OnMouseDrag()

CVR is VR-first; mouse events only fire in desktop mode. For in-world interaction in both modes, use CVR Pointer / Interactable components or bound events.

EventSignature
OnAnimatorMovevoid OnAnimatorMove()
OnAnimatorIKvoid OnAnimatorIK(int layerIndex)
OnAudioFilterReadvoid OnAudioFilterRead(float[] data, int channels)
OnTransformChildrenChangedvoid OnTransformChildrenChanged()
OnTransformParentChangedvoid OnTransformParentChanged()

Fired for any player crossing triggers / colliding with objects inside this script’s content root. Single-argument — the event does not tell you which trigger or collider was involved. If you need that, use Unity’s OnTriggerEnter(Collider) on the specific trigger GameObject and filter for a player collider inside.

EventSignature
OnPlayerJoinedvoid OnPlayerJoined(Player player)
OnPlayerLeftvoid OnPlayerLeft(Player player)
OnPlayerRespawnedvoid OnPlayerRespawned(Player player)
OnPlayerTriggerEntervoid OnPlayerTriggerEnter(Player player)
OnPlayerTriggerStayvoid OnPlayerTriggerStay(Player player)
OnPlayerTriggerExitvoid OnPlayerTriggerExit(Player player)
OnPlayerCollisionEntervoid OnPlayerCollisionEnter(Player player)
OnPlayerCollisionStayvoid OnPlayerCollisionStay(Player player)
OnPlayerCollisionExitvoid OnPlayerCollisionExit(Player player)

Props carry both the Prop handle and the Unity Collider / Collision — useful because a prop can enter multiple triggers in your content.

EventSignature
OnPropSpawnedvoid OnPropSpawned(Prop prop)
OnPropDespawnedvoid OnPropDespawned(Prop prop)
OnPropTriggerEntervoid OnPropTriggerEnter(Prop prop, Collider other)
OnPropTriggerStayvoid OnPropTriggerStay(Prop prop, Collider other)
OnPropTriggerExitvoid OnPropTriggerExit(Prop prop, Collider other)
OnPropCollisionEntervoid OnPropCollisionEnter(Prop prop, Collision c)
OnPropCollisionStayvoid OnPropCollisionStay(Prop prop, Collision c)
OnPropCollisionExitvoid OnPropCollisionExit(Prop prop, Collision c)
EventSignature
OnPortalCreatedvoid OnPortalCreated(Portal portal)
OnPortalDestroyedvoid OnPortalDestroyed(Portal portal)
EventSignatureNotes
OnInstanceOwnerChangevoid OnInstanceOwnerChange(Player newOwner)Fires on GSInfoChanged.InstanceOwner. Dispatched via CVR_WasmBehaviour_OnInstanceOwnerChanged(behavioursPtr, count, playerHandle).
OnInputReadyvoid OnInputReady()CVR input subsystem tick. Dispatched via CVR_WasmBehaviour_OnInputReady(behavioursPtr, count). The host also mirrors the current CVRInputStruct into the guest’s _inputStructPtr every tick, even if the behaviour doesn’t define this event.
OnWorldPermissionsChangedvoid OnWorldPermissionsChanged()Fires only on world VMs (gameObject.scene.buildIndex == -1). Dispatched via CVR_WorldPermissions_OnWorldPermissionsChanged(marshalStructPtr) — single argument, not the behaviour-list shape the others use. The guest-side dispatcher fans it out to every behaviour that defined the method.

OnReceiveMessage is not an auto-detected event — you subscribe explicitly:

void Start()
{
Networking.OnReceiveMessage += HandleNetMessage;
}
void OnDestroy()
{
Networking.OnReceiveMessage -= HandleNetMessage;
}
private void HandleNetMessage(Player sender, Span<byte> message)
{
// parse and act
}

The host dispatches incoming payloads through the guest export CVR_Networking_OnReceiveMessage(senderHandle, msgPtr, msgLen) (WasmVM.CallOnReceiveMessage), which the shim converts into the delegate above. See Networking.

Stubbed to empty in CVR.CCK.Wasm/Scripting/WasmBehaviour.cs inside #if !CVR_SCRIPTING_ENVIRONMENT so they compile in Unity but never dispatch at runtime:

  • OnServerInitialized()
  • OnConnectedToServer()
  • OnGUI()
  • OnApplicationQuit()
  • OnApplicationFocus(bool)
  • OnApplicationPause(bool)

Defining them has no effect. Use OnEnable / OnDisable / OnDestroy for lifecycle hooks.

Unity’s built-in attribute is supported only among WasmBehaviours inside the same VM. It does not change when your scripts run relative to Unity or other CVR systems.

[DefaultExecutionOrder(-100)]
public partial class EarlyBird : WasmBehaviour
{
void Awake() { /* runs before most other WasmBehaviours in this VM */ }
}

When multiple behaviours receive the same event (e.g. OnPlayerJoined), their execution order follows this attribute. Two behaviours without the attribute run in undefined order.

Unity processes execution order per scene. When scenes load additively, each scene runs its full phase before the next:

World scene (active):
FixedUpdate -> Update -> LateUpdate
Remote Avatars/Props/Portals scene (additive):
FixedUpdate -> Update -> LateUpdate
Local Avatar + LateEventManager (DontDestroyOnLoad):
FixedUpdate -> fires PostFixedUpdate for all (subscription order)
Update -> fires PostUpdate for all
LateUpdate -> fires PostLateUpdate for all

This order matters when:

  • You sample a player’s final pose — PostLateUpdate is the only place where local Avatar IK + Network IK have both been applied.
  • You write to shared state that one of the other scenes will read next frame — use Post-phase variants.

Most scripts don’t need to think about this. If you do, read Unity’s execution order docs alongside.

Script-defined events (UI / persistent callback)

Section titled “Script-defined events (UI / persistent callback)”

Any public method on a WasmBehaviour can be the target of a Unity UnityEvent persistent call (e.g. Button.onClick, Slider.onValueChanged). At build time the persistent call is rewritten to WasmRuntimeBehaviour.TriggerScriptEvent*(...) which fires a scripting_call_trigger_script_event* export. See Unity Events Rewiring. Supported argument types:

TypeSetterTrigger
UnityEngine.Objectset__InternalEventObjectArgTriggerScriptEventObject("Method")
intset__InternalEventIntArgTriggerScriptEventInt("Method")
floatset__InternalEventFloatArgTriggerScriptEventFloat("Method")
stringset__InternalEventStringArgTriggerScriptEventString("Method")
boolset__InternalEventBoolArgTriggerScriptEventBool("Method")

Void-signature persistent calls become TriggerScriptEvent("Method").

  • CVR-GameFiles/WasmScripting.Enums/UnityEvents.cs — 47-flag [Flags] long enum, one bit per Unity event.
  • CVR-GameFiles/WasmScripting.Enums/GameEvents.cs — 22-flag [Flags] long enum, one bit per CVR game event.
  • CVR-GameFiles/WasmScripting.Enums/ScriptEvent.cs — non-flag int enum, the values passed to scripting_call_event. Unity events occupy IDs 0–49, game events 200–221.
  • CVR-GameFiles/WasmScripting/WasmRuntimeBehaviour.cs — Unity MonoBehaviour callbacks that route into the VM.
  • CVR-GameFiles/WasmScripting/WasmVM.csInitializeEvents, per-event Call* methods, CallGlobalOnPlayerEvent, CallOnReceiveMessage, CallOnWorldPermissionsChanged.
  • CVR-GameFiles/WasmScripting.Proxies/*Forwarder.cs — engine-side forwarders for the ten Unity events (OnAnimatorIK, OnAnimatorMove, OnAudioFilterRead, OnCollisionStay, OnParticleCollision, OnRenderImage, OnRenderObject, OnTriggerStay, OnWillRenderObject) that WasmRuntimeBehaviour attaches on demand.