Persistence
Saving a player's progress, with two verbs instead of sixteen types and the one timing rule handled for you.
VRChat can remember things about a player between visits. The API it gives you for that is a
key-value store with a method per type: SetInt, GetInt, TryGetInt, and the same three again
for each of sixteen types.
Three things about it go wrong quietly.
You have to remember which method wrote a key. Read "level" with GetString when it was
written with SetInt and nothing complains — you get an empty string and carry on.
GetInt returns zero for a key nobody wrote, which is also what a saved zero looks like. A
shop that gives newcomers a starting balance cannot tell a new player from one who spent
everything.
Saved data is not there yet in Start. It arrives a moment after the player does. Read too
early and you get defaults rather than an error, so the world behaves perfectly in the Editor,
where there is nothing to restore, and greets a returning player as a stranger.
This package is two verbs and a base class:
public class Shop : SaveBehaviour
{
private int coins;
public override void OnSaveReady()
{
coins = Save.Get("coins", 100);
}
public void Buy(int price)
{
coins = coins - price;
Save.Set("coins", coins);
}
}
The value decides the rest. Save.Set("name", "ada") stores a string and Save.Set("coins", 5)
stores an integer, without you naming either. Reading takes the value you want when nothing is
saved, which is the same value you would have written in the if below a TryGet — and having
to write it is what separates "never saved" from "saved as nothing".
OnSaveReady runs at the only moment reading means anything. Put your loading there instead of
in Start and the ordering stops being something you have to remember.
What is here
Save |
Set and Get, for every type VRChat stores |
SaveBehaviour |
OnSaveReady, which runs when the data is real |
PlayerObjects |
Finding the copy of an object that belongs to a player |
Storage |
How much of a player's allowance the world has spent |