Unity 預設可以序列化值類型, Serializable屬性修飾的類型, 派生自UnityEngine.Object的類型, 通常這些類型已經足以供日常使用了. 但是有時我們希望在編輯器面板上序列化一個介面或者抽象類, 則需要用到 SerializeReference屬性. 假定我們有一個介面I ...
Unity 預設可以序列化值類型, Serializable屬性修飾的類型, 派生自UnityEngine.Object的類型, 通常這些類型已經足以供日常使用了.
但是有時我們希望在編輯器面板上序列化一個介面或者抽象類, 則需要用到 SerializeReference屬性.
假定我們有一個介面IEatable
, 並實現了兩個類Bread
和Bun
:
public interface IEatable
{
int Calorie { get; }
}
public class Bread : IEatable
{
[field: SerializeField]
public int Calorie { get; private set; } = 100;
}
public class Bun : IEatable
{
public enum Fillings
{
[InspectorName("肉餡")]
Meat,
[InspectorName("韭菜雞蛋")]
LeekAndAgg,
}
[field: SerializeField]
public int Calorie { get; private set; } = 200;
[field: SerializeField]
public Fillings Filling { get; private set; }
}
相應的, 我們定義了一個餐盤類去盛放食物Plate
:
[CreateAssetMenu(menuName = "Plate for Food")]
public class Plate : ScriptableObject
{
private IEatable _eatable;
public IEatable Eatable { get => _eatable; set => _eatable = value; }
}
在Unity編輯器內右鍵新建一個Plate
, 可見Inspector面板上沒有顯示Eatable
欄位.
此時我們為_eatable
欄位添加SerializeReference
屬性.
註意, 添加
SerializeReference
後, 即使欄位是私有的, 也無需添加SerializeField
屬性, 二者同有將私有欄位序列化的能力.
[CreateAssetMenu(menuName = "Plate for Food")]
public class Plate : ScriptableObject
{
[SerializeReference]
private IEatable _eatable;
public IEatable Eatable { get => _eatable; set => _eatable = value; }
}
添加SerializeReference
屬性後, Inspector面板上已經可以顯示Eatable
欄位了, 但是由於此時_eatable
欄位的值為null
, 所以並沒有顯示其他信息.
SerializeReference
屬性允許欄位為null
, 這點與預設序列化行為不同, 預設序列化會自動實例化一個值
接下來我們在Plate
中定義一個方法ServeBread
, 將_eatable
欄位設置為Bread
實例, 並使用ContextMenuItem
屬性將此方法設置為_eatable
欄位的上下文菜單:
[ContextMenuItem("盛放麵包", "ServeBread", order = 0)]
[ContextMenuItem("盛放包子", "ServeBun", order = 1)]
[SerializeReference]
private IEatable _eatable;
...
private void ServeBread() => _eatable = new Bread();
private void ServeBun() => _eatable = new Bun();
...
回到Unity編輯器, 此時我們就可以右鍵點擊Eatable
欄位併在彈出菜單中選擇一項來為_eatable
欄位賦值了.
添加
[field: SerializeField]
後, 屬性也可以像欄位一樣被序列化, 但是其label
會顯示為<屬性名>k__BackingField
, 如果不希望這種現象,可以將屬性轉化為完整屬性併為對應的私有欄位添加SerializeField
.
用文本編輯器打開Plate.asset
文件, 可以看到使用SerializeReference
屬性進行序列化後的內容, 可以對比一下普通的序列化方式.
其中, type
記錄了欄位內容的具體類型class
, 所在命名空間ns
, 所在的程式集asm
. 而data
則記錄了實例的可序列化欄位及內容.
ContextMenuItem
方式只是為了演示, 合理的做法應該是自行實現對應的PropertyDrawer
.
SerializeReference
還可以修飾List<T>
和T[]
, 具體情況可以查看Unity官方文檔, 這裡就不贅述了.