Quick answer: Only [Export] fields/properties on a Godot.Resource survive saving and loading. Mark every field you want persisted — raw C# fields aren’t enough.
A custom Resource saves a config file, but on reload one field is back to its default. That field wasn’t marked [Export], so Godot didn’t serialize it.
Export Is Required for Persistence
public partial class EnemyConfig : Resource
{
[Export] public int Hp { get; set; } = 100;
[Export] public float Speed { get; set; } = 5.0f;
// NOT serialized — no Export
public Vector2 LastKnownPos;
}
Resources only serialize what they advertise to the editor — which means [Export] members. A plain field is invisible to the saver.
Backing Field vs Property
Auto-properties work; backing fields with a custom getter/setter also work as long as the property itself has [Export]. Apply the attribute to the property, not the backing field.
Collections Need Typed Wrappers
For lists/dictionaries, use Godot.Collections.Array<T> / Godot.Collections.Dictionary<K, V> with [Export]. Plain List<T> doesn’t serialize through the resource system.
Re-Save After Adding
An existing .tres saved before you added the new [Export] field has no value for it — it loads as the default. Re-save once after adding to bake the current values in.
Verifying
Save the resource, reload — every [Export] field round-trips. Non-exported fields stay at their defaults (as intended).
“Resources serialize Exports. No Export, no persistence — even on public fields.”
For purely runtime caches you don’t want saved, deliberately omit Export — it’s a feature, not a bug, once you know the rule.