Quick answer: When the app backgrounds, the AR session is torn down. On resume, tracking restarts with new origin and your placed objects drift. Use ARAnchor components on every placed object so they re-anchor on relocalization. Only call ARSession.Reset as a last resort; it wipes all anchors.
Here is how to fix Unity AR Foundation apps that lose all placed AR content after the player tabs away and back. Your virtual furniture, characters, or markers reposition randomly or vanish. The cause is the underlying ARKit (iOS) or ARCore (Android) session being suspended and a new session starting on resume, often with a different world origin.
The Symptom
User places content in the AR scene. They lock the phone or switch apps. On return, content is in the wrong location, floating in space, or gone entirely. Console may show Tracking lost or Session reinitialized messages.
What Causes This
Session teardown on backgrounding. Mobile OS suspends camera and sensor access. AR Foundation pauses the session. On resume the session restarts and the world coordinate origin may differ.
No anchors on placed content. Without ARAnchor components, placed objects exist in arbitrary world coordinates. Without an anchor, the system has no way to relocalize them.
Failed relocalization. If the camera does not see enough of the original environment to relocalize, tracking returns but anchors are not restored.
Reset called too aggressively. Some templates call ARSession.Reset on every resume, wiping anchors and starting fresh.
The Fix
Step 1: Anchor every placed object.
using UnityEngine;
using UnityEngine.XR.ARFoundation;
public class PlaceContent : MonoBehaviour
{
[SerializeField] private ARAnchorManager anchors;
[SerializeField] private GameObject prefab;
public void PlaceAt(Pose pose)
{
ARAnchor anchor = anchors.AddAnchor(pose);
var spawn = Instantiate(prefab, pose.position, pose.rotation, anchor.transform);
// Spawn now follows anchor through pause/resume
}
}
Anchors are tracked by the AR system across sessions when relocalization succeeds.
Step 2: Watch session state.
void OnEnable()
{
ARSession.stateChanged += OnSessionState;
}
void OnDisable()
{
ARSession.stateChanged -= OnSessionState;
}
void OnSessionState(ARSessionStateChangedEventArgs args)
{
switch (args.state)
{
case ARSessionState.SessionInitializing:
ShowHint("Move your phone slowly");
break;
case ARSessionState.SessionTracking:
HideHint();
break;
case ARSessionState.NotTracking:
ShowHint("Tracking lost. Point at a textured surface.");
break;
}
}
Step 3: Handle pause and resume explicitly.
void OnApplicationPause(bool paused)
{
if (paused)
{
// AR session pauses automatically. Save anchor data if needed.
SaveAnchors();
}
else
{
// Wait for relocalization before judging tracking quality
StartCoroutine(WaitForTracking());
}
}
IEnumerator WaitForTracking()
{
float deadline = Time.time + 5f;
while (Time.time < deadline && ARSession.state != ARSessionState.SessionTracking)
yield return null;
if (ARSession.state != ARSessionState.SessionTracking)
ShowResetButton(); // only if user truly cannot recover
}
Step 4: Use AR World Map (iOS) for persistence. ARKit supports saving the entire world map to disk. Restore on next launch to retain all anchors and surface understanding. Android has Cloud Anchors for similar purposes.
Step 5: Avoid ARSession.Reset on resume. Reset is a nuclear option that destroys all anchors. Use it only when the user explicitly chooses to start over. For normal pause/resume, let AR Foundation handle session lifecycle.
UX For Tracking Loss
Show a clear, friendly hint when tracking degrades: “Look around the room slowly to recover.” Pause gameplay or fade content while NotTracking. Resume gracefully when state returns to SessionTracking. Players forgive a few seconds of recovery; they do not forgive content vanishing without explanation.
“Anchors survive sessions when relocalization succeeds. Without anchors, every pause is a fresh start with new coordinates.”
Related Issues
For pause-related lifecycle issues, see Unity Crashes on Exit. For mobile audio after pause, see Audio Silent After Tab Switch.
Anchor everything. Subscribe to stateChanged. Reset only as last resort. Tracking returns gracefully.