Authoring a WASM Script
TL;DR: Open Unity with the CCK Wasm package. Create a C# script via Assets > Create > CVR Wasm Behavior (C#) and subclass WasmBehaviour. Add a CCKWasmProjectDescriptor to your avatar/prop/world root and either drop the component on any child or add the script to the descriptor’s includedScripts list. Methods named after the UnityEvents or GameEvents enums are auto-detected and dispatched at runtime.
Prerequisites
Section titled “Prerequisites”- Unity version matching the CVR CCK.
- The
CVR.CCK.Wasmpackage imported into your project. - Internet — on first build the pipeline downloads .NET 10 preview, WASI SDK 25, and the CCK WasmModule into
%LOCALAPPDATA%/ChilloutVR/CVRBuildTools/.
Create a behaviour
Section titled “Create a behaviour”Unity menu: Assets > Create > CVR Wasm Behavior (C#).
Template (CVR.CCK.Wasm/CCK/Editor/Templates/WasmBehaviorTemplate.cs.template):
using UnityEngine;using WasmScripting;
public partial class NewWasmBehavior : WasmBehaviour{ void Start() { } void Update() { }}Constraints on the class:
- Must inherit
WasmBehaviour(directly or transitively). - Cannot be abstract if you want instances on GameObjects (
WasmBuildProcessorskips abstract types from the descriptor list). partialis required — both theWasmBehavioursubclass and any custom class you mark[WasmSerialized]must be partial. The build pipeline emits a generated partial half that plugs into the serializer. A non-partialWasmBehaviourclass compiles but won’t serialize correctly.- File-scoped namespace or single top-level type per file works; the CCK pipeline only compiles the paths it collects, not an entire Unity
asmdef.
Hook events
Section titled “Hook events”The build processor finds events by method name. Define a public or private method whose name matches a value in either enum and it will be dispatched automatically.
Unity events (subset; see Events for all)
Section titled “Unity events (subset; see Events for all)”void Awake() { }void Start() { }void Update() { }void LateUpdate() { }void FixedUpdate() { }void OnEnable() { }void OnDisable() { }void OnDestroy() { }void OnTriggerEnter(Collider other) { }void OnCollisionEnter(Collision hit) { }Game events
Section titled “Game events”void OnPlayerJoined(Player p) { }void OnPlayerLeft(Player p) { }void OnPropSpawned(Prop p) { }void OnInstanceOwnerChange() { }void OnWorldPermissionsChanged() { }Disallowed Unity events
Section titled “Disallowed Unity events”These are explicitly stubbed out on WasmBehaviour so they never dispatch, even if you define them:
OnServerInitializedOnConnectedToServerOnGUIOnApplicationQuitOnApplicationFocus(bool)OnApplicationPause(bool)
Source: CVR.CCK.Wasm/Scripting/WasmBehaviour.cs (the #if !CVR_SCRIPTING_ENVIRONMENT block).
Serialize state
Section titled “Serialize state”Public fields serialize by default. Use the attributes in WasmScripting:
using WasmScripting;
public partial class Score : WasmBehaviour{ public int points; // serialized
[NonWasmSerialized] public float uiJiggleTimer; // NOT serialized (transient UI state)
[WasmSerialized] private int hiddenCombo; // private, BUT serialized
[WasmSerialized] private CustomBag savedBag; // custom class must also carry [WasmSerialized]}Defaults serialized without annotation:
- C# primitives (
int,bool,string,float, …), UnityEngine.Objectreferences,- Arrays,
IList<T>,IDictionary<K,V>.
Source: CVR.CCK.Wasm/Scripting/Attributes/SerializationAttributes.cs.
More detail: Serialization.
Attach to a content root
Section titled “Attach to a content root”A root GameObject (the avatar/prop/world root) must carry a CCKWasmProjectDescriptor. Add it from the inspector menu Add Component > ChilloutVR Editor > CCK Wasm Project Descriptor.
Descriptor fields (CVR.CCK.Wasm/CCK/CCKWasmProjectDescriptor.cs):
| Field | Purpose |
|---|---|
enableDebugging | Build in Debug config (slower, includes symbols). Also toggled automatically in Play Mode. |
enableReflection | Omit <IlcDisableReflection>true</IlcDisableReflection> from the generated csproj. Enables runtime reflection at the cost of a much bigger .wasm. |
includedScripts[] | C# scripts compiled into the module even if no component using them is present in the hierarchy. Each entry: isActive + MonoScript reference. |
projectDefines[] | Custom preprocessor defines (isActive + define). Names are auto-sanitized to valid identifiers. |
externalProjectPath | Path to your own .csproj; if set, the pipeline invokes dotnet publish on it directly (skipping csproj generation) and includes its output as the module. |
Two ways to surface a script to the pipeline:
- Component pickup — drop your
WasmBehavioursubclass onto any GameObject inside the root. The build processor scans the hierarchy. - Descriptor list — add the
MonoScripttoincludedScripts. Use this for utility types or callbacks you invoke dynamically but never add as a component.
Preprocessor defines
Section titled “Preprocessor defines”The generated csproj always defines one of the following based on the content type being built:
CVR_SCRIPTING_CONTEXT_AVATARCVR_SCRIPTING_CONTEXT_PROPCVR_SCRIPTING_CONTEXT_WORLD
Plus any custom defines from projectDefines.
Inside the script environment, CVR_SCRIPTING_ENVIRONMENT is additionally defined (see CVR.CCK.Wasm/Scripting/WasmBehaviour.cs:10). Use it to branch code between “in-editor Unity tooling” and “compiled-for-WASM” — for example, hiding editor-only types behind #if !CVR_SCRIPTING_ENVIRONMENT.
Invoke and call
Section titled “Invoke and call”Direct, intra-script calls work as normal C# method calls. Calls that cross from Unity’s UnityEvent system (e.g. Button.onClick) into your behaviour are handled automatically — the build processor rewrites persistent calls to route through TriggerScriptEvent(...). See Unity Events Rewiring.
Current limitations
Section titled “Current limitations”- All
WasmBehavioursubclasses and[WasmSerialized]custom classes must bepartial. Required by the serialization generator. - Scripts on Prefabs outside the main scene are not built into the WASM module (CCK limitation). Place referenced objects in the main scene and leave them inactive until needed; instantiate them from there.
- Crawling Prefabs outside a scene throws permission errors — the scope resolver tags prefab contents as
ScopeContext.Noneand every access is rejected. ScriptableObjectfields are not supported. Store shared data as serialized fields on aWasmBehaviourcomponent instead.[DefaultExecutionOrder]orders only among WasmBehaviours within the same VM. It does not change when your scripts run relative to Unity’s or CVR’s built-in systems. For “after-everything” semantics usePostUpdate/PostLateUpdate/PostFixedUpdate— see Events → Execution order.- You cannot animate fields on a
WasmBehaviourvia anAnimator. Drive state by calling methods from script. StartCoroutineand async patterns run on the single WASM thread — they don’t yield back to Unity between awaits. UseUpdate/FixedUpdatefor periodic work.
Quick sanity checklist before build
Section titled “Quick sanity checklist before build”- Root has
CCKWasmProjectDescriptor. - All referenced
MonoScripttypes inheritWasmBehaviourand arepartial. - No usage of disallowed APIs (
UnityEngine.Input,Application,System.IO,System.Net, etc.) — see Not Exposed. - No usage of disallowed Unity events above.
- State you want to persist into play is on fields using default serialization rules or
[WasmSerialized]. - Any referenced runtime-instantiated GameObject is already in the main scene (disabled is fine).
- No
ScriptableObjectfields on theWasmBehaviour.
Source
Section titled “Source”CVR.CCK.Wasm/Scripting/WasmBehaviour.cs— theMonoBehaviourbase class WASM scripts inherit from (also contains the empty stubs forOnGUI/OnApplicationQuit/OnApplicationFocus/OnApplicationPause/OnServerInitialized/OnConnectedToServer).CVR.CCK.Wasm/Scripting/Attributes/—ExternallyVisibleAttribute,SerializationAttributes(WasmSerialized,NonWasmSerialized),WasmRuntimeInitializeOnLoadMethod.CVR.CCK.Wasm/CCK/CCKWasmProjectDescriptor.cs— descriptor fields (enableDebugging,enableReflection,includedScripts,projectDefines,externalProjectPath).CVR.CCK.Wasm/CCK/Editor/Templates/WasmBehaviorTemplate.cs.template— the template used byAssets > Create > CVR Wasm Behavior (C#).
Related
Section titled “Related”- Events — full event catalog with signatures.
- Permissions — why a call might throw
WasmAccessDeniedException. - Build Pipeline — what happens when you press the CCK build button.