Menu
CoddyTech

Serializable Classes

Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 18 of 64.

A script with many settings gets a long, flat list in the Inspector. A plain C# class can group them: mark it [System.Serializable], and a field of that class shows as a foldout named after the field, with the class's fields inside. One class can serve several fields and several scripts:

[System.Serializable]
public class Stats
{
    public float speed = 2f;
    public int damage = 10;
}

public class Enemy : MonoBehaviour
{
    public Stats normal;
    public Stats enraged;
    private Stats current;

    void Start()
    {
        current = normal;
    }

    void Update()
    {
        transform.Translate(0f, 0f, current.speed * Time.deltaTime);
    }

    public void Enrage()
    {
        current = enraged;
    }
}

The Inspector shows two foldouts, Normal and Enraged, each with a Speed and a Damage. Every Enemy gets its own two Stats objects, filled in its own Inspector, and code reads them as normal.speed. Unity creates these objects when it loads the script, so normal is never null even without = new Stats(), and the field initializers (2 and 10) are what a new Enemy starts with. A struct works the same way with the attribute, [System.Serializable] public struct Range { public float min; public float max; }, and like Vector3 it is copied when it is assigned.

The attribute is needed because Unity saves only the types it is told to. Without it the class is invisible to the Inspector: there is no foldout, values set there are not saved, and Unity does not create the object, so normal.speed throws a NullReferenceException. Inside the class the usual Inspector rules hold: public fields or [SerializeField] private ones, never properties or static fields.

challenge icon

Challenge

Easy

Group each beam's settings in one struct.

  1. Above Flashlight, declare public struct Beam with public Color color, float range and float angle. Mark it [System.Serializable] so the Inspector shows it.
  2. Replace the six loose fields with public Beam wide; and public Beam focused;.
  3. In Apply(), set the light's three values from focusedOn ? focused : wide.

Press Play and Space: the beam reaches the crystal. Then Check.

Try it yourself

using UnityEngine;
using UnityEngine.InputSystem;

public class Flashlight : MonoBehaviour
{
    public Color wideColor = Color.white;
    public float wideRange = 5f;
    public float wideAngle = 60f;
    public Color focusedColor = Color.white;
    public float focusedRange = 10f;
    public float focusedAngle = 30f;
    private bool focusedOn;
    private Light lamp;

    void Start()
    {
        lamp = GetComponent<Light>();
        Apply();
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            focusedOn = !focusedOn;
            Apply();
        }
    }

    void Apply()
    {
        lamp.color = focusedOn ? focusedColor : wideColor;
        lamp.range = focusedOn ? focusedRange : wideRange;
        lamp.spotAngle = focusedOn ? focusedAngle : wideAngle;
    }
}

All lessons in Game Architecture in Unity

Practice on your own: Unity playground