tags: [vr-ar]
Developing progress save systems for VR games
Save in VR is not same scenario as regular game. User puts on headset, plays 20–40 minutes, takes it off. Perhaps puts it on again next day. Game must restore not just inventory and quests, but spatial state: where objects stand in room, mechanism position, what player held in hands at exit.
Plus platform specifics: Quest stores app data in limited persistent storage, PCVR uses standard file path, but path depends on platform (SteamVR, OpenXR). Save system must be abstracted from specific storage.
What to save: full state snapshot vs incremental
Two approaches to save architecture. Full snapshot: every N minutes or on demand save entire world state to one JSON/binary file. Simple, reliable, easy implement. Problem: file size grows with object count, large snapshot loading takes seconds — can't show loading screen in VR without discomfort.
Incremental/event-based: save only delta — what changed since last checkpoint. File small, loading fast, but recovery requires applying all deltas in order. If one chunk corrupts, progress from it lost.
For VR prefer hybrid: one full baseline checkpoint at session start + lightweight delta updates every 2–3 minutes. On load: read baseline → apply deltas → ready. Baseline resaves at session end.
Transform and physics object serialization
Interactive objects in VR have position, rotation, physics state. JsonUtility doesn't serialize Transform directly — need to create [Serializable] DTO:
[Serializable]
public struct TransformData {
public float[] position; // Vector3 as array
public float[] rotation; // Quaternion as array
public bool isGrabbed;
public string grabbedByPlayerId;
}
For physical objects additionally save Rigidbody.velocity and angularVelocity — else on load object "hangs" in air, not continuing motion. In single-player VR games usually not critical (game loads in pause), but in multiplayer sessions — important.
Object identification: each save object must have unique string ID stable between sessions. GUID assigned in [ExecuteInEditMode] or via custom Inspector tool. Position in scene hierarchy unreliable identifier, especially for dynamically-created objects.
XR state saving: HMD and controllers
VR-specific save detail: player position in real room (XROrigin.transform) and game world position are different things. On load must restore game position, not move physical XROrigin — causes teleportation.
Correct approach: save XROrigin.transform.position as baseline game world point. On load — XROrigin.MoveCameraToWorldLocation(savedPosition) (method from Unity XR Core Utilities), which adjusts tracking space without physical rig movement.
Controller state (what each hand holds) save via grabbed object ID. On load: restore object → restore XRGrabInteractable → call XRBaseInteractor.StartManualInteraction(interactable) for programmatic grab.
Cloud saves: Unity Cloud Save and Meta Platform SDK
For Quest two cloud storage options. Unity Cloud Save (Unity Gaming Services) — cross-platform, stores key-value pairs, available via CloudSaveService.Instance.Data.Player.SaveAsync. Works on Quest, PC, mobile. Requires Unity Authentication (anonymous or account).
Meta Platform SDK → CloudStorage — native Oculus/Meta storage. Tied to Meta account, works only on Meta devices. Advantage: player transitions Quest 2 to Quest 3 — saves automatically transfer via Meta Cloud.
Recommend double write: locally (in Application.persistentDataPath) + cloud when connected. On load: check local file date vs cloud — take newer. Standard conflict resolution without needing user choice dialog.
| Save system complexity | Estimated timeline |
|---|---|
| Basic saving (progress, inventory) | 3–7 days |
| Full state snapshot + physical objects | 1–3 weeks |
| Cloud sync + multi-platform | 2–5 weeks |
Cost calculated after analyzing save data volume, platform, and reliability requirements.





