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
EasyGroup each beam's settings in one struct.
- Above
Flashlight, declarepublic struct Beamwith publicColor color,float rangeandfloat angle. Mark it[System.Serializable]so the Inspector shows it. - Replace the six loose fields with
public Beam wide;andpublic Beam focused;. - In
Apply(), set the light's three values fromfocusedOn ? 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
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular TurretRecap: Twin Cannon4Serialized Data
Serializable ClassesLists in the InspectorEnums in the InspectorRecap: Loot TableRecap: Roll the Loot2Interfaces in Unity
IDamageableInterfaces and TryGetComponentGetComponentsRecap: Interact KeyRecap: Nearest in ReachPractice on your own: Unity playground