Serialization
TL;DR: A WasmBehaviour’s state is serialized during build (for baking initial values set in the Unity inspector) and deserialized into the WASM guest at instantiation. Public fields of supported types serialize by default. Use [WasmSerialized] to include private fields or custom classes, [NonWasmSerialized] to exclude a public field.
Default behavior
Section titled “Default behavior”Without any attribute, a field is serialized if all of the following are true:
- It is a public instance field on a
WasmBehavioursubclass (or a chain of classes that are themselves serialization-eligible). - Its type is one of the built-in supported shapes below.
Supported types (from the WasmSerializedAttribute doc comment):
| Category | Types |
|---|---|
| C# primitives | bool, byte, sbyte, short, ushort, int, uint, long, ulong, float, double, decimal, char, string |
| Unity objects | UnityEngine.Object subclasses (GameObject, Component, ScriptableObject, assets) |
| Containers | T[], IList<T>, IDictionary<K,V> where T/K/V are themselves serializable |
Unity-specific structs (Vector3, Quaternion, Color, Bounds, …) are supported via the Unity type path as long as the type is bound into the WASM surface — which the standard ones are.
The attributes
Section titled “The attributes”Source: CVR.CCK.Wasm/Scripting/Attributes/SerializationAttributes.cs.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class)]public class WasmSerializedAttribute : Attribute {} // include
[AttributeUsage(AttributeTargets.Field)]public class NonWasmSerializedAttribute : Attribute {} // exclude[WasmSerialized] on a field
Section titled “[WasmSerialized] on a field”Opts a non-public field into serialization.
public partial class LapState : WasmBehaviour{ [WasmSerialized] private int bestLapMs = int.MaxValue;}[WasmSerialized] on a class
Section titled “[WasmSerialized] on a class”Required for any custom (non-Unity) class whose fields you want serialized. Without it, the outer serializer visits the reference but skips its contents entirely.
[WasmSerialized]public class Checkpoint{ public int index; public Vector3 position;}
public partial class Track : WasmBehaviour{ public Checkpoint[] checkpoints; // array of a WasmSerialized class -> deep serialized}[NonWasmSerialized] on a public field
Section titled “[NonWasmSerialized] on a public field”Suppresses serialization for transient UI/debug state.
public partial class Hud : WasmBehaviour{ public int score; [NonWasmSerialized] public float debugAnimatedValue; // not baked}When serialization happens
Section titled “When serialization happens”- Build time —
WasmBuildProcessor.BuildScriptscallsWasmSerializer.SerializeBehaviour(component, eventContext)on each in-sceneWasmBehaviour, capturing the inspector state as aWasmSerializedObject(references[]+ a blob). This is stored on the replacementWasmRuntimeBehaviour.serializationData. - Runtime — on VM setup, for each behaviour:
storeData.AccessManager.ToWrapped(behaviour, ...)→ a WASM handle.MarshalSerializedObject(storeData)copies the serialized blob into guest memory and returns a pointer._deserializeInstance(handle, ptr)asks the guest to reconstruct its field state.
Because the blob also carries references[] (an array of Unity object references), GameObject/Component fields stay live across the build→runtime boundary.
What does not survive serialization
Section titled “What does not survive serialization”- Fields of types not in the supported list and without
[WasmSerialized]— always null after deserialization. - Local variables in methods — they only exist at runtime.
- Static fields — not per-instance, treated as global guest state (persist across behaviours but not across VM restarts).
- Delegates / function pointers / raw native pointers.
UnityEvents
Section titled “UnityEvents”Persistent calls added via the Unity inspector (e.g. Button.onClick) are rewritten at build time to call WasmRuntimeBehaviour.TriggerScriptEvent*(...). The original m_Arguments/m_Mode serialized state is preserved — the runtime just reroutes the target. See Unity Events Rewiring.
Interface
Section titled “Interface”The marker interface IWasmSerialized (in both CVR.CCK.Wasm/Scripting/IWasmSerialized.cs and CVR-GameFiles/WasmScripting/IWasmSerialized.cs) lets the serializer identify participants. WasmBehaviour implements it implicitly.
Practical guidance
Section titled “Practical guidance”- Put anything you want to show in the inspector on a public field. It both persists and edits cleanly.
- Private helpers you need to keep across play sessions:
[WasmSerialized]. - Caches / UI state you rebuild in
StartorOnEnable:[NonWasmSerialized]on public, or just keep private without[WasmSerialized]. - Custom data types: mark the class with
[WasmSerialized]. Fields follow the normal public/attribute rule.
Related
Section titled “Related”- Authoring — where these attributes fit in the normal workflow.
- Runtime Lifecycle —
_deserializeInstancecall path. - Events —
OnBeforeSerialize/OnAfterDeserializehooks if you need to transform state.