AgentStack
SKILL verified MIT Self-run

Serialization Safety

skill-xeldaralz-everything-claude-unity-serialization-safety · by XeldarAlz

Unity serialization rules — FormerlySerializedAs on renames, SerializeField vs public, SerializeReference for polymorphism, Unity null check (== null not ?.). CRITICAL: prevents silent data loss.

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-xeldaralz-everything-claude-unity-serialization-safety

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Serialization Safety? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Serialization Safety

This is the single most important skill. Serialization mistakes cause silent data loss — every configured value in every scene, prefab, and ScriptableObject resets to default with zero warning.

Rule 1: FormerlySerializedAs on ANY Rename

// BEFORE: field is called _speed
[SerializeField] private float _speed = 5f;

// AFTER: renaming to _moveSpeed — MUST add FormerlySerializedAs
[FormerlySerializedAs("_speed")]
[SerializeField] private float _moveSpeed = 5f;

Why: Unity serializes fields by name. Renaming breaks the name → value mapping. Every scene, prefab, and SO that configured this field silently loses its value. [FormerlySerializedAs] tells Unity "this field used to be called X."

The attribute stays forever. Never remove it.

Rule 2: Unity Null Check

// CORRECT — Unity overrides == to detect destroyed objects
if (_target == null) return;
if (_target != null) _target.TakeDamage(10);

// WRONG — bypasses Unity's destroyed-object detection
if (_target is null) return;        // C# null check, misses destroyed
_target?.TakeDamage(10);            // ?. bypasses Unity ==, calls on destroyed
_target ??= FindNewTarget();        // ??= uses C# null, not Unity null

Why: Unity objects can be "destroyed" (C++ side freed) but not yet garbage collected (C# reference still exists). Unity overrides == to return true for destroyed objects. C# pattern matching (is null, ?., ??) uses reference equality, which returns false — so you call methods on destroyed objects, causing crashes or undefined behavior.

Rule 3: What Unity Serializes

Serialized:

  • public fields (without [NonSerialized])
  • [SerializeField] private/protected fields
  • Types: int, float, bool, string, Vector2/3/4, Color, Rect, Quaternion, AnimationCurve, Gradient, enums, UnityEngine.Object subclasses, arrays, List, [Serializable] structs/classes

NOT Serialized:

  • Properties (getters/setters) — even with [SerializeField]
  • static fields
  • readonly fields
  • const fields
  • Dictionary — use ISerializationCallbackReceiver
  • Interfaces / abstract types — use [SerializeReference]
  • Delegates / events

Rule 4: SerializeField Private Over Public

// GOOD — controlled exposure
[SerializeField] private float _health = 100f;
public float Health => _health; // Read-only access

// BAD — anyone can modify, clutters API
public float health = 100f;

Rule 5: SerializeReference for Polymorphism

// Without SerializeReference: Unity serializes as base type, losing derived data
[SerializeField] private IAbility _ability; // ERROR: interfaces not serialized

// With SerializeReference: polymorphic serialization
[SerializeReference] private IAbility _ability; // Works: stores concrete type

Rule 6: NonSerialized for Cached Data

public class Enemy : MonoBehaviour
{
    [SerializeField] private float _maxHealth = 100f;

    [NonSerialized] public float CurrentHealth; // Runtime-only, not saved
    
    private Transform _cachedTransform; // Private non-serialized by default (good)
}

Rule 7: ISerializationCallbackReceiver for Dictionaries

public class DataStore : MonoBehaviour, ISerializationCallbackReceiver
{
    // Unity serializes these lists
    [SerializeField] private List _keys = new();
    [SerializeField] private List _values = new();

    // Runtime dictionary (not serialized directly)
    private Dictionary _data = new();

    public void OnBeforeSerialize()
    {
        _keys.Clear();
        _values.Clear();
        foreach (KeyValuePair pair in _data)
        {
            _keys.Add(pair.Key);
            _values.Add(pair.Value);
        }
    }

    public void OnAfterDeserialize()
    {
        _data = new Dictionary();
        for (int i = 0; i k__BackingField")]
[field: SerializeField] public float MoveSpeed { get; private set; }

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.