[SerializeField] Unity thực sự serialize được những gì?
Minh Khoa
Tác giả
Câu trả lời ngắn:[SerializeField] cho phép Unity lưu một field private và hiển thị nó trong Inspector. Tuy nhiên, kiểu dữ liệu của field đó vẫn phải được Unity hỗ trợ.
Serialization là gì?
Trong lúc game chạy, dữ liệu của object nằm trong RAM:
Khi lưu Scene, Prefab hoặc Asset, Unity cần chuyển trạng thái này thành dữ liệu có thể lưu trữ.
Object trong RAM
↓ Serialize
Dữ liệu trong Scene, Prefab hoặc Asset
↓ Deserialize
Object được dựng lại
Các khái niệm cần phân biệt:
Khái niệm
Ý nghĩa
Serialization
Chuyển trạng thái object thành dữ liệu có thể lưu
Deserialization
Đọc dữ liệu và dựng lại trạng thái object
Serializer
Hệ thống thực hiện hai quá trình trên
Serialized field
Field được Unity đưa vào dữ liệu cần lưu
Runtime data
Dữ liệu chỉ tồn tại trong phiên chơi hiện tại
Unity serialize dữ liệu, không serialize logic trong method:
public class Enemy : MonoBehaviour
{
[SerializeField] private int maxHealth = 100;
public void TakeDamage(int damage)
{
// Nội dung method này không được serialize
}
}
Unity lưu giá trị maxHealth, nhưng không lưu code bên trong TakeDamage().
Các attribute dễ nhầm lẫn
Attribute
Công dụng
[SerializeField]
Cho phép Unity serialize một field private
[System.Serializable]
Cho phép custom class hoặc struct được Unity serialize
[SerializeReference]
Serialize custom C# object theo reference, hỗ trợ polymorphism và null
[System.NonSerialized]
Không cho Unity serialize field
[HideInInspector]
Field vẫn được serialize nhưng bị ẩn khỏi Inspector
[FormerlySerializedAs]
Đổi tên field mà không làm mất dữ liệu cũ
[SerializeField]
[SerializeField] được đặt trên một field:
[SerializeField] private int maxHealth = 100;
Nó không phải kiểu dữ liệu và cũng không phải method. Nó là một attribute báo với Unity:
Hãy đưa field private này vào hệ thống serialization.
[System.Serializable]
Attribute này được đặt trên custom class hoặc struct:
[System.Serializable]
public class DropEntry
{
public ItemData item;
public float chance;
}
Nó báo rằng các field hợp lệ bên trong DropEntry có thể được Unity serialize.
Hai attribute này có vai trò khác nhau:
[SerializeField] → Đánh dấu một field
[Serializable] → Đánh dấu một class hoặc struct
Khi nào một field được serialize?
Một field phải thỏa đủ ba điều kiện:
Public hoặc có [SerializeField]
+
Không static, const, readonly
+
Type được Unity hỗ trợ
Ví dụ:
public class Enemy : MonoBehaviour
{
public int damage; // ✅
[SerializeField] private int maxHealth; // ✅
private int currentHealth; // ❌
public static int enemyCount; // ❌
[SerializeField] private readonly int id; // ❌
}
[SerializeField] không thể ép Unity serialize một type vốn không được hỗ trợ.
[SerializeField] chỉ đánh dấu field. Nó không làm Dictionary trở thành type được hỗ trợ.
Một cách đơn giản là đổi dữ liệu thành List:
[System.Serializable]
public class InventoryEntry
{
public string itemId;
public int amount;
}
[SerializeField]
private List<InventoryEntry> inventory;
Nếu runtime cần Dictionary, có thể dựng lại nó từ danh sách khi khởi tạo.
Property không hoạt động như field
Unity serializer làm việc trực tiếp với field:
public int Health { get; private set; }
Property trên không được serialize theo cách thông thường.
Cách rõ ràng hơn:
[SerializeField] private int health;
public int Health => health;
Code bên ngoài chỉ được đọc Health, trong khi giá trị gốc vẫn có thể cấu hình từ Inspector.
Inspector cũng không gọi getter hoặc setter. Nếu cần kiểm tra dữ liệu khi chỉnh Inspector, có thể dùng OnValidate:
private void OnValidate()
{
health = Mathf.Max(1, health);
}
Khi nào cần [SerializeReference]?
Mặc định, custom C# class được Unity serialize theo value. Dùng [SerializeReference] khi cần:
Lưu object thông qua base class hoặc interface.
Giữ đúng derived type.
Giữ giá trị null.
Nhiều field cùng tham chiếu một instance.
Dữ liệu dạng tree hoặc graph.
[SerializeReference]
private BaseEffect effect;
[SerializeReference] có thêm overhead và không dùng để sửa hạn chế của Dictionary. Với dữ liệu dùng chung giữa nhiều component, ScriptableObject thường phù hợp hơn.
Đổi tên field có thể làm mất dữ liệu
Giả sử nhiều Prefab đang lưu field:
[SerializeField] private int health;
Nếu đổi tên trực tiếp:
[SerializeField] private int maxHealth;
Unity có thể xem maxHealth là field mới và mất giá trị health cũ.
Cách an toàn:
using UnityEngine.Serialization;
[FormerlySerializedAs("health")]
[SerializeField] private int maxHealth;
Serialization không phải Save Game
Unity serialization chủ yếu phục vụ:
Inspector.
Scene.
Prefab.
ScriptableObject và Asset.
Instantiate.
Hot reload trong Editor.
Nó không tự động lưu tiến độ người chơi.
Runtime State
↓
SaveData
↓
JSON hoặc Binary
↓
Application.persistentDataPath
HP hiện tại, inventory và nhiệm vụ vẫn cần một hệ thống Save/Load riêng.
Cách nhớ
Serialization = Object → Data
Deserialization = Data → Object
[SerializeField] = Cho phép serialize một field private
[Serializable] = Cho phép serialize custom class hoặc struct
Và câu quan trọng nhất:
[SerializeField] chỉ cho field tham gia vào hệ thống serialization. Field có được lưu hay không vẫn phụ thuộc vào modifier và kiểu dữ liệu phía sau.