Attributes
Mark fields on a MonoBehaviour or ScriptableObject - they are scanned automatically
when you call the builder overload Vault.Save(key, mb, build) or Vault.Save(key, so).
[Save]
Marks a field for inclusion in Save / Load object scanning.
public class PlayerStats : MonoBehaviour
{
[Save] public int health = 100; // stored at "player.PlayerStats.health"
[Save] public float speed = 5f; // stored at "player.PlayerStats.speed"
[Save("gold_count")]
public int gold = 0; // stored at "player.gold_count"
public int transientScore; // no [Save] - never written
} [NoSave]
Prevents a member from being saved. Wins over [Save] - useful for suppressing a field inherited from a base class.
[Save][NoSave] public int ignored; // NoSave wins [VaultRename]
Renames a [Save] field or property - or a field inside a [Serializable] blob -
without losing existing save data, no migration code needed. On load, if Vault can't find the current
name in the saved JSON, it falls back to the historical name(s) you list.
[Save]
[VaultRename("hp")] // this field used to be called "hp"
public int health = 100; Multiple old names
The attribute takes params string[] and allows multiple attributes, so there are two ways
to record more than one previous name:
// (a) several old names in one attribute
[Save]
[VaultRename("health", "hp")]
public int hitPoints;
// (b) stacked attributes - reads as a rename history
[Save]
[VaultRename("health")]
[VaultRename("hp", "hitpoints")]
public int hitPoints; Resolution order
Say the field evolved hp (v1) → health (v2) → hitPoints (current):
[Save]
[VaultRename("health", "hp")] // most-recent old name first
public int hitPoints = 100; On load Vault checks names in this order, and the first one found in the save file wins (it stops searching immediately):
hitPoints- the current name (always checked first)health- first old namehp- second old name
Across stacked attributes it reads top → bottom; within a single attribute, left → right.
Order old names newest → oldest
A save file normally holds only one of the historical keys, so order is harmless. It matters when a save somehow contains more than one - legacy or partially-migrated data, or a file hand-edited in the viewer. Then "first match wins" decides which value is used:
// Save file (messy) contains BOTH: "health": 80, "hp": 50
[VaultRename("health", "hp")] // reads "health" = 80 (newer value - correct)
[VaultRename("hp", "health")] // reads "hp" = 50 (staler value - wrong) Rule of thumb: list old names newest → oldest, matching the order you actually renamed the field. That way the most recent historical value wins over an older, staler leftover key.