Save an object in your scene

A Persistent Scene Object component marks a GameObject the save keeps, one component at a time:

  1. Add a Persistent Scene Object component to the GameObject.
  2. Tick each component whose state you want kept.
The Persistent Scene Object component with several components ticked

Tick a component under Runtime Components instead when the GameObject gains and loses it while the game runs, and the save decides whether the component is there at all.

The state of every ticked component lands in a Scene State asset, an ordinary persistent asset. A new scene object attaches itself to one, and the package creates the project's first one. Select Create > Persistent Asset > Scene State to add more: each one is a save of its own, with its own manager and its own storage.

Note: A save carries only what changed from what the scene was authored with, and the package reads those authored values as the GameObject first enables, right after its Awake. A value it rolls or computes in Awake therefore counts as authored and stays out of the save, while one set in Start or later is saved.

Supported components

The package ships a codec for each of the following components.

ComponentNotes
TransformLocal position, rotation and scale, so a reparented object still lands in place.
RectTransformAnchors, pivot, anchored position and size, plus rotation and scale.
CharacterControllerSwitched off while the transform is restored, so it can't snap the object back. Tick Transform as well.
Rigidbody, Rigidbody2DTake their position from the restored transform, so tick Transform as well.
NavMeshAgentNeeds Unity's AI module.
AnimatorA transition in flight is not restored, and triggers are never re-fired.
AnimationThe legacy component. Which clips were playing, how far in, and their speed and weight. A crossfade in flight resumes from the weights it had.
AudioSourceWhether it was playing and how far in, its volume, pitch and looping, so music resumes where it left off.
VideoPlayerThe video, how far in, whether it was playing or paused, its speed and looping. A video clip travels as an asset reference. A URL under StreamingAssets or the persistent data folder is saved relative to that folder, so it still points at the video after an app update or on another device. Any other URL is saved as it is; a local file the loading device doesn't have is skipped, and the player keeps the URL your game sets. Needs Unity's Video module.
CameraField of view, projection, background and culling mask.
LightType, color, intensity, range, cone and shadows.
MeshFilterThe mesh, as an asset reference.
MeshRenderer, SkinnedMeshRendererMaterials and meshes travel as asset references, so a slot whose material is not a registered asset keeps the one the object already has. A skinned renderer's bones stay as the rig built them.
SpriteRendererSprite, color and flip. The sprite travels as an asset reference.
LineRendererPoints, widths and colors.
TrailRendererIts length, widths and colors are saved. The trail already drawn is not, so a restored object draws a new one from where it stands.
ParticleSystemWhether it was playing and how far into its timeline it had run are saved. The particles alive at that moment are not: the system replays from its start up to that point.
Slider, Toggle, InputField, Dropdown, ScrollRectRestored without raising the control's change event.
TMP_InputField, TMP_DropdownRestored without raising the control's change event.

To cover a component this package doesn't reach, or to replace one it ships, register a codec of your own.

Note: Two components of the same type are told apart by position, so reordering them swaps their saved state.

Spawned and destroyed objects

A spawned GameObject is saved when the prefab it comes from carries a Persistent Scene Object component. Keep calling plain Instantiate, and a load spawns it again. A GameObject your code builds from nothing has no prefab to bring it back, so nothing about it is saved.

The package records the destruction of an authored GameObject, so it stays gone after a load. Destroying a spawned one drops its record instead. Leaving a scene, quitting, and switching a GameObject off are not destruction.

A load or a slot change that no longer has a destroyed GameObject destroyed cannot put it back in place, because it no longer exists. It comes back the next time its scene loads, and the Console names it. To have the package reload the scene for you, turn on Reload Scenes To Restore Destroyed Objects in Edit > Project Settings > Persistent Asset.

Parenting is saved, as long as the parent carries a Persistent Scene Object component too.

Note: A GameObject that starts switched off and is never switched on gets no OnDestroy call, so destroying it goes unrecorded and it comes back. Switch it on at some point, or call PersistentSceneState.RecordDestroyed.
Note: To save a GameObject that outlives scene loads, call DontDestroyOnLoad on it. The next save files it with the other GameObjects that outlive scene loads.

Save a scene reference in a persistent asset

Unity refuses to serialize a scene reference into a project asset, so a persistent asset can't hold one. Serialize a SceneRef field instead, which stores the object's id and resolves it against what is loaded:

public class GameState : PersistentScriptableObject
{
    public SceneRef lastCheckpoint;
}

gameState.lastCheckpoint = new(checkpointObject);   // a Persistent Scene Object

if (gameState.lastCheckpoint.TryResolve(out Checkpoint checkpoint))
    player.position = checkpoint.transform.position;

In a saved component

A component ticked on a Persistent Scene Object keeps its scene references as ordinary fields: a Transform, a GameObject or a component of any type, in a list or a dictionary too. Each one is saved through the nearest Persistent Scene Object on the referenced GameObject or on one of its parents, so a child of a GameObject that has one works, including inside a prefab you can't edit. The Console names a referenced GameObject with none above it.

Note: A reference reached through a property, or held by a component saved through a codec, isn't saved this way.