【unity知识】Serializable、SerializeField 和SerializeReference如何序列化数据和区别

前言

在 Unity 中,SerializeReferenceSerializeFieldSerializable 都是用于序列化数据的属性,但它们的作用和使用场景有所不同。以下是它们的详细介绍和区别。

1. [Serializable]

作用

  • 用于标记一个结构体枚举,使其可以被 Unity 序列化(保存到场景或预制体中)。
  • 如果字段是自定义类型(非 Unity 内置类型,如 intstringGameObject 等),必须加上 [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,序列化简单数据动态切换子类,支持多态数据(如技能系统)

补充说明

  1. 序列化的条件

    • 字段必须是 public,或标记了 [SerializeField]
    • 字段的类型必须是 Unity 支持的内置类型,或标记了 [Serializable] 的自定义类型。
  2. [SerializeReference] 的局限性

    • 不支持循环引用(如两个对象互相引用)。
    • 在 Inspector 中编辑时可能需要手动选择子类类型。
  3. 默认序列化行为

    • Unity 默认会序列化所有 public 字段,但不会序列化属性(如 public int Health { get; set; })。

专栏推荐

地址
【unity游戏开发入门到精通——C#篇】
【unity游戏开发入门到精通——unity通用篇】
【unity游戏开发入门到精通——unity3D篇】
【unity游戏开发入门到精通——unity2D篇】
【unity实战】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架/工具集开发】
【unity游戏开发——模型篇】
【unity游戏开发——InputSystem】
【unity游戏开发——Animator动画】
【unity游戏开发——UGUI】
【unity游戏开发——联网篇】
【unity游戏开发——优化篇】
【unity游戏开发——shader篇】
【unity游戏开发——编辑器扩展】
【unity游戏开发——热更新】
【unity游戏开发——网络】

完结

好了,我是向宇,博客地址:https://xiangyu.blog.csdn.net,如果学习过程中遇到任何问题,也欢迎你评论私信找我。

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向宇it

创作不易,感谢你的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值