前言
在 Unity 中,SerializeReference
、SerializeField
和 Serializable
都是用于序列化数据的属性,但它们的作用和使用场景有所不同。以下是它们的详细介绍和区别。
1. [Serializable]
作用:
- 用于标记一个类、结构体或枚举,使其可以被 Unity 序列化(保存到场景或预制体中)。
- 如果字段是自定义类型(非 Unity 内置类型,如
int
、string
、GameObject
等),必须加上[Serializable]
才能被序列化。
使用场景:
- 定义需要保存到场景或预制体的自定义数据结构(如配置数据、状态数据等)。
- 配合
System.Serializable
命名空间使用(using System;
)。
示例:
using UnityEngine;
using System;
[Serializable]
public class PlayerStats
{
public int health;
public float speed;
}
public class Player : MonoBehaviour
{
public PlayerStats stats; // 这个字段会被序列化,因为 PlayerStats 标记了 [Serializable]
}
效果
注意:
- 如果类未标记
[Serializable]
,即使字段是public
或标记了[SerializeField]
,也不会被序列化。
2. [SerializeField]
作用:
- 强制 Unity 序列化一个私有字段或受保护字段(默认情况下,Unity 只序列化
public
字段)。 - 可以用于显示私有字段到 Inspector 面板,方便调试或设计时修改。
使用场景:
- 需要将私有字段暴露到 Inspector 面板。
- 避免直接使用
public
字段(遵循封装原则)。
示例:
public class Enemy : MonoBehaviour
{
[SerializeField]
private int maxHealth; // 私有字段,但会被序列化并显示在 Inspector
[SerializeField]
protected float attackCooldown; // 受保护字段,也会被序列化
}
效果
注意:
- 如果字段是自定义类型(如
PlayerStats
),其类必须标记[Serializable]
,否则即使有[SerializeField]
也不会被序列化。
3. [SerializeReference]
作用:
- 允许 Unity 序列化多态对象(即基类字段存储的子类实例)。
- 默认情况下,Unity 的序列化系统不支持多态,会丢失子类特有的字段。
[SerializeReference]
解决了这个问题。 - 需要 Unity 2020.1 或更高版本。
使用场景:
- 需要保存接口或抽象类的具体实现类型。
- 支持运行时动态替换子类实例并保留数据。
示例:
using System;
using UnityEngine;
//可序列化的抽象基类
[Serializable]
public abstract class Weapon
{
public abstract void Attack();
}
//创建具体实现类1
[Serializable]
public class Sword : Weapon
{
public int damage = 10;
public override void Attack() => Debug.Log("Swing sword!");
}
//创建具体实现类2
public class Gun : Weapon
{
public int bullets = 30;
public override void Attack() => Debug.Log("Fire gun!");
}
public class Player : MonoBehaviour
{
[SerializeReference]
private Weapon currentWeapon;
}
效果
可以看到添加SerializeReference属性后, Inspector面板上已经可以显示currentWeapon字段了, 但是由于此时currentWeapon字段的值为null, 所以并没有显示其他信息.
我可以为currentWeapon字段添加一个默认值,并且添加右键菜单项,不需要运行,点击后执行指定的方法切换不同赋予。
public class Player : MonoBehaviour
{
[ContextMenuItem("实例化剑", nameof(NewSword), order = 0)]
[ContextMenuItem("实例化枪", nameof(NewGun), order = 1)]
[SerializeReference]
private Weapon currentWeapon;
private void NewSword() => currentWeapon = new Sword();
private void NewGun() => currentWeapon = new Gun();
// 当编辑器中的属性值发生更改时调用
void OnValidate()
{
if (currentWeapon == null) NewSword();
}
}
效果
注意
:一定要先给currentWeapon一个默认值,我这里使用OnValidate方法,才能右键调用ContextMenuItem方法切换实例,一定不能省略。
注意:
- 被引用的子类必须标记
[Serializable]
。 - 性能开销比直接序列化更大(因为需要维护引用关系)。
- 添加SerializeReference后, 即使字段是私有的, 也无需添加SerializeField属性,二者同有将私有字段序列化的能力。
- SerializeReference属性允许字段为null, 这点与SerializeField默认序列化行为不同, 默认序列化会自动实例化一个值
- 它支持序列化数组、列表和单个元素,不过如果是数组或者列表数据,记得基类要加上
Serializable
特性,我这里已经加了。
三者的区别总结
对比项 | [Serializable] | [SerializeField] | [SerializeReference] |
---|---|---|---|
存储方式 | 声明一个类型可以被 Unity 序列化 | 直接存储值(复制整个对象) | 存储引用(类似指针) |
适用类型 | 自定义类、结构体、枚举 | 具体类型(int、float、GameObject、具体类) | 抽象类、接口、多态类型(支持运行时子类) |
多态支持 | ❌ 仅标记可序列化 | ❌ 不支持(必须明确指定具体类型) | ✅ 支持(可存储任何子类实例) |
Null 支持 | ✅ 支持 null | ❌ 值类型(如 int)不能是 null | ✅ 可以明确设置为 null |
适用场景 | 使自定义类型可序列化 | 强制序列化非公有字段,并显示在 Inspector,序列化简单数据 | 动态切换子类,支持多态数据(如技能系统) |
补充说明
-
序列化的条件:
- 字段必须是
public
,或标记了[SerializeField]
。 - 字段的类型必须是 Unity 支持的内置类型,或标记了
[Serializable]
的自定义类型。
- 字段必须是
-
[SerializeReference]
的局限性:- 不支持循环引用(如两个对象互相引用)。
- 在 Inspector 中编辑时可能需要手动选择子类类型。
-
默认序列化行为:
- Unity 默认会序列化所有
public
字段,但不会序列化属性(如public int Health { get; set; }
)。
- Unity 默认会序列化所有
专栏推荐
完结
好了,我是向宇
,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!