Unity [SerializeReference] Explained
πŸͺ

Unity [SerializeReference] Explained

image

Serialization plays a crucial role in game development, allowing us to save 🧾 and load πŸ”„ data, transfer information across networked systems 🌐, and maintain object state between sessions.

In Unity, the serialization process automatically converts objects and data structures into a format that can be stored or transmitted. πŸ’½

Unity serializes fields either by value or by reference, depending on their types.

Serializing by reference means that Unity stores a reference πŸ”— to the object, rather than duplicating its data.

  1. Serialization by reference: This is typically used for fields derived from UnityEngine.Object, such as MonoBehaviour components. When serialized by reference, changes to the referenced object will be reflected wherever it is used, ensuring consistency and avoiding data duplication βœ….
  2. On the other hand, serializing by value means that Unity stores a copy of the object's data πŸ“‹, rather than a reference to the object itself. This is commonly used for basic value types (int, float, etc.) or custom serializable classes or structures. When serialized by value, changes to the original object will not affect other instances, as each instance has its own copy of the data.

The choice between serializing by reference or value depends on the specific requirements of your project.

Serializing by reference is useful when you want changes to propagate across multiple instances of an object, maintaining consistency and reducing memory usage πŸ’Ύ. Serializing by value is suitable when you need to preserve the state of an object at a specific moment, independent of other instances πŸ•.

By using the [SerializeReference] attribute, you can explicitly specify that a field should be serialized by reference, even if it's not derived from UnityEngine.Object.

This gives you more control over the serialization process πŸ‘¨β€πŸ’» and enables advanced functionalities like polymorphism, where different derived types can be assigned to the same field while preserving their individual behaviors and properties 🎭.

🀨 [SerializeReference] In a Nutshell

So, what is this [SerializeReference] attribute all about? 🧐 It's a powerful tool that allows you to tell Unity, "Hey, serialize this field by reference, not by value!" πŸ“£

[SerializeReference] is a real game-changer! ⚑ Typically, Unity serializes fields by reference if they're derived from UnityEngine.Object and by value otherwise.

But with [SerializeReference], you can flip this script, forcing Unity to serialize a field by reference, even if it's not derived from UnityEngine.Object.

Here's why this is cool 😎: With [SerializeReference], you can serialize polymorphic types, which means you can have a field of a base class type and assign it any subclass at runtime, and Unity will remember it.

For example, if you have a base class Animal 🐾 and subclasses like Dog πŸ• and Cat 🐈, a field declared as Animal can hold a reference to either a Dog or a Cat object.

Without [SerializeReference], Unity would forget the specific type (Dog or Cat) after serialization and only remember the base type (Animal). But with [SerializeReference], Unity will remember whether it was a Dog or a Cat and keep the reference intact.

This is super useful if you want to use different subclasses in different instances of a GameObject, giving you the power to define highly dynamic and flexible behaviors πŸ”€.

1️⃣ Use Case: SerializeField with polymorphic objectsπŸ• 🐈

Polymorphism is a principle of object-oriented programming that allows objects of different types to be treated as objects of a common, usually a base type.

It's what lets you create a list of 'Animal' objects, but then fill that list with 'Dog' πŸ•, 'Cat' 🐈, and 'Bird' 🐦 objects, each of which might have their unique behaviors or attributes.

Traditionally in Unity, you'd run into trouble when serializing these types of objects.

Unity would "forget" the specific subclass type upon serialization, remembering only the base type, which could cause some unexpected behavior.

But this is where the [SerializeReference] attribute really shines! 😎

Let's look at a quick example. Suppose you have a 'GameCharacter' base class and 'Player' and 'Enemy' subclasses:

public abstract class GameCharacter
{
    public abstract void Speak();
}

public class Player : GameCharacter
{
    public override void Speak()
    {
        Debug.Log("I am a Player!");
    }
}

public class Enemy : GameCharacter
{
    public override void Speak()
    {
        Debug.Log("I am an Enemy!");
    }
}

In your code, you might have a field of type 'GameCharacter' and assign it a 'Player' or 'Enemy' object during runtime:

public class CharacterManager : MonoBehaviour
{
    [SerializeReference] public GameCharacter character;

    private void Start()
    {
        character.Speak(); // Will output "I am a Player!" or "I am an Enemy!" based on the assigned object
    }
}

In this code, we assign either a new Player or Enemy to the character field. Now, even after serialization, Unity will remember whether it's a Player or an Enemy thanks to [SerializeReference].

Note that for this setup, assigning the Player or Enemy to the character field happens in the code and not directly in the Unity Inspector.

Despite this limitation, [SerializeReference] provides a powerful way to maintain polymorphic relationships and ensure data integrity during serialization.

This flexibility allows you to create more dynamic and complex game behaviors. In the next section, we'll see how [SerializeReference] is beneficial in component-based systems. Stay tuned! πŸ“‘

What If i want to see the character variable directly in the inspector?

In this case you either code you own inspector or you use some pretty cool asset like Odin Inspector.

Benefit of using Unity SerializeReference

The [SerializeReference] attribute in Unity is a small detail with big implications. It offers a slew of advantages that significantly boost the flexibility and extensibility of your game code.

  1. Facilitating Polymorphism: [SerializeReference] enables Unity's serialization system to maintain and respect the polymorphic relationships in your code. Whether it's handling different character behaviors, dialogue responses, or any other use case that benefits from polymorphism, [SerializeReference] has got you covered. 🎭
  2. Preserving References: This feature reduces memory usage and keeps your game running efficiently by avoiding unnecessary data duplication. By preserving object references, [SerializeReference] ensures that changes to a referenced object reflect across all instances, keeping your data consistent.
  3. Supporting Custom Types: Another powerful feature of [SerializeReference] is its support for custom types. If you're designing systems that involve custom classes or structs, [SerializeReference] allows you to serialize these fields as references, increasing the extensibility of your systems. πŸ’»

It's safe to say that [SerializeReference] is a valuable tool that makes Unity's serialization system even more powerful.

Best Practice and consideration πŸŽ“πŸ”

While [SerializeReference] is a powerful tool, like any tool, it should be used wisely and appropriately. Here are some tips and considerations to ensure you're getting the most out of it:

  1. Selective Usage: Just because you can use [SerializeReference] doesn't mean you should apply it to every field in your class. It's best used when you specifically need to preserve object references or facilitate polymorphic behavior. Applying it indiscriminately may unnecessarily increase the complexity of your code and could potentially introduce unwanted side effects. 🚧
  2. Performance Considerations: Unity needs to keep track of references during serialization and deserialization, which might introduce overhead when dealing with large amounts of data. Make sure to test the performance impacts of using [SerializeReference] in your project, especially when serializing large data sets or complex object graphs. 🏎️
  3. Use With Custom Types: [SerializeReference] truly shines when used with custom classes or structs, particularly those that benefit from polymorphism. If your custom type is not derived from UnityEngine.Object, consider leveraging [SerializeReference] for it. 🎨
  4. Design For Serialization: Design your classes with serialization in mind. Remember that private fields are not serialized by Unity by default, so you might need to use the [SerializeField] attribute in combination with [SerializeReference] for private fields that need to be serialized as references. Also, consider that constructors are not called during the deserialization process, so prepare your classes accordingly. πŸ—οΈ

Using [SerializeReference] appropriately and wisely can be a game-changer in your Unity projects. It provides you with more control over your data and behaviors, allowing you to create more intricate and efficient systems.

Conclusion ✌️

And there you have it, folks! We've taken a deep dive into the exciting world of Unity's [SerializeReference] attribute. So let's wrap this journey up and bring home the key points we've discussed. πŸ“πŸŽ―

Firstly, we explored the concept of object serialization in Unity and its importance in game development.

Unity serializes fields either by value or by reference, a choice that hinges on the specific requirements of your project. However, the [SerializeReference] attribute gives you more control over this process, allowing you to explicitly specify fields to be serialized by reference, even if they're not derived from UnityEngine.Object. πŸ”πŸ‘¨β€πŸ’»

The benefits of using [SerializeReference] are considerable, with highlights being the preservation of object references (avoiding data duplication), the facilitation of polymorphism, and the enhancement of modularity in your game systems.

[SerializeReference] also supports the serialization of custom types, opening up a world of possibilities in your project development. πŸŒŸπŸš€

The best way to use [SerializeReference] it’s in combination with a custom inspector tool like Odin Inspector, this way, you will be able to assign custom classes and type directly in the Unity Inspector.

Resources πŸ“—

  1. Serialize Reference - Unity Documentation
  2. [SerializeReference] is very powerfull, why is no one speaking about it? - Reddit Discussion
  3. SerializeReference in Unity - Video

✍️ Author

image

Marco Mignano πŸ‘‹ ➑️ Passionate Unity Game Developer Marco - coding aficionado, video game enthusiast, and self-proclaimed piazza addict. Constantly conquering new challenges, one line of code at a time. Got an exciting project? Let's make your game next game together!

You may also like πŸ’™