學它 -->Unity插件Odin
Serializer
方式一:繼承
可以讓Monobehaviour
繼承SerializedMonoBehaviour
或者讓ScriptableObject
繼承SerializedScriptableObject
,如下所示可以把屬性和字典序列化並在Inspector上可編輯。
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_DictionaryDrawer : SerializedMonoBehaviour
{
[SerializeField]
private object SerializedPrivateField;
public object SerializedField;
[OdinSerialize]
public object SerializedProperty { get; set; }
public Dictionary<int, GameObject> SerializedDic;
}
}
方式二:接口
在需要序列化的類上實現接口ISerializationCallbackReceiver
,然後再用Odin的UnitySerializationUtility
類。
相當於把繼承的那部分拆開了,一般來說繼承比較方便,不用重複的實現接口。如下所示就可以把字典序列化到Inspector
上了。
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
/// <summary>
/// 接口方式序列化
/// </summary>
[ShowOdinSerializedPropertiesInInspector]
public class ZYF_CustomSerializedMonoBehaviour : MonoBehaviour, ISerializationCallbackReceiver, ISupportsPrefabSerialization
{
[SerializeField,HideInInspector]
private SerializationData serializationData;
public Dictionary<int, string> dic;
SerializationData ISupportsPrefabSerialization.SerializationData { get { return this.serializationData; } set { this.serializationData = value; } }
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
}
}
}
保存與讀取序列化數據
網址
我們無法保存Unity Object,因此保存玩家數據時要把數據與Unity Object相關的分離開,可以把數據放到一個單獨的類中,只對這個類對象進行序列化數據的保存讀取操作。
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_SaveGameData_Player : MonoBehaviour {
[System.Serializable]
public class PlayerState {
public Vector3 position;
public float health;
public List<Item> inventory;
}
[System.Serializable]
public class Item {
public string id;
public int count;
}
[SerializeField,HideInInspector]
private PlayerState state = new PlayerState();
[ShowInInspector]
public float Health {
get => this.state.health;
set => this.state.health = value;
}
[ShowInInspector]
public List<Item> Inventory {
get => this.state.inventory;
set => this.state.inventory = value;
}
string path => Application.dataPath + "/playerStateData.d";
private void OnEnable()
{
LoadState(path);
}
private void OnDisable()
{
SaveState(path);
}
public void SaveState(string filePath) {
this.state.position = transform.position;
byte[] bytes = SerializationUtility.SerializeValue(this.state,DataFormat.Binary);
File.WriteAllBytes(filePath,bytes);
Debug.Log($"保存數據:{filePath}");
}
public void LoadState(string filePath) {
if (!File.Exists(filePath))
{
return;
}
byte[] bytes = File.ReadAllBytes(filePath);
this.state = SerializationUtility.DeserializeValue<PlayerState>(bytes,DataFormat.Binary);
this.transform.position = this.state.position;
Debug.Log($"讀取數據:{filePath}");
}
}
}
Unity 序列化方案瞭解下
On Unitys Serialization Protocol
1.Unity 序列化方案非常簡單直接,這樣可以非常快的序列化和反序列化。
2.只有被Serializable
特性標識才能被Unity序列化。
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_SerializationDebugger : MonoBehaviour
{
public TypeWithoutSerializable NotSerialized;
public TypeWithSerializable Serialized;
public class TypeWithoutSerializable { }
[Serializable]
public class TypeWithSerializable { }
}
}
3.任何泛型類變量都不能被Unity序列化(List<T>除外
),但是繼承自該泛型類的非泛型子類可以。
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_SerializationDebugger : MonoBehaviour
{
public MyGenericClass<int> NonSerialized;
public MyIntClass Serialized;
public List<int> SerializedList;
[Serializable]
public class MyGenericClass<T>
{
public T Value;
}
[Serializable]
public class MyIntClass : MyGenericClass<int>
{
}
}
}
4.多態(抽象類,接口,object),不會被序列化。
Odin 序列化方案瞭解下
On Odins Serialization Protocol
1.Odin
對MonoBehaviours,ScriptableObjects…的序列化只是對Unity 序列化的一種擴展
。
2.Odin
把Unity沒序列化的字段、屬性轉成Unity可以序列化的數據格式(SerializationData
)。
3.當然這種方案有可能會把一個字段序列化兩次(Unity+Odin),不過Odin
已經做了處理過濾掉了已經被Unity序列化的那些字段,可以用Serialization Debugger
查看腳本的序列化情況。
4.當然了Unity方案簡單而且比Odin快的多,如果可以還是不用Odin的序列化哈
5.Odin不會覆蓋Unity已序列化的部分
序列化相關的特性
1.[NonSerialized, OdinSerialize]:強制使用Odin序列化方案而不用Unity的方案;
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_SerializationDebugger : SerializedMonoBehaviour {
// Unity 負責序列化這個字段但是不會序列化該對象內部的‘MyDictionary’字段
public MySerializedObject UnitySerialized;
// 只使用Odin序列化這個字段,並且內部的‘MyDictionary’也會被序列化保存。
[NonSerialized, OdinSerialize]
public MySerializedObject OdinSerialized;
//也可以把這個特性去掉而不用上面的NonSerialized特效屏蔽Unity的序列化
[Serializable]
public class MySerializedObject
{
//[NonSerialized, OdinSerialize]等類似的特性在這裏沒啥作用,因爲序列化只取決於根級別序列化方式
public Dictionary<int, string> MyDictionary;
}
}
}
2.[ShowInInspector]:可以顯示所有字段或屬性(包括靜態的),但是它的功能只是讓字段或屬性的值在Inspector上顯示而已,並不能序列化它們切記!
using Sirenix.OdinInspector;
using Sirenix.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_SerializationDebugger : SerializedMonoBehaviour
{
[ShowInInspector]
private int myPrivateInt;
[ShowInInspector]
public int MyPropertyInt { get; set; }
[ShowInInspector]
public int ReadOnlyProperty
{
get { return this.myPrivateInt; }
}
[ShowInInspector]
public static bool StaticProperty { get; set; }
}
}
好多神奇的特性
官網
訪問時需要添加using Sirenix.OdinInspector;
FilePath
可以提取你在inspector中選擇的文件的路徑。
Button
可以把方法暴露在inspector上。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[FilePath(Extensions =".unity")]
public string scenePath;
[Button(ButtonSizes.Large)]
public void SayHello() {
Debug.Log("Hello button");
}
}
}
HideInInspector
隱藏字段
ShowInInspector
顯示字段(包含靜態
)及屬性(包含靜態
)的值(如果沒有被序列化修改它們是無效的!!
)
PreviewField
添加預覽框
Required
如果目標是空的就會提示
AssetsOnly
限定從Project中選擇而不是從Scene中
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[PreviewField(Height =200),Required,AssetsOnly]
public GameObject prefab;
}
}
PropertyOrder
可以重新排列字段或屬性在Inspector上的顯示順序。
HideLabel
隱藏Inspector上顯示的標籤。
HorizontalGroup&VerticalGroup
HorizontalGroup
:定義一個水平分組,指定了group後可以使用VerticalGroup把該水平分組分割成垂直分組,VerticalGroup需要指定groupid爲HorizontalGroup定義時的group+‘/’+VerticalGroupId
。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using System;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[HorizontalGroup("Split", Width = 50), HideLabel, PreviewField(50)]
public Texture2D icon;
#region VerticalGroup:Split/Properties
[VerticalGroup("Split/Properties")]
public string minionName;
[VerticalGroup("Split/Properties")]
public string health;
[VerticalGroup("Split/Properties")]
public string demage;
#endregion
#region VerticalGroup:Split/Properties1
[VerticalGroup("Split/Properties1")]
public string minionName1;
[VerticalGroup("Split/Properties1")]
public string health1;
[VerticalGroup("Split/Properties1")]
public string demage1;
#endregion
}
}
引用字段值($或@)
如下所示可以把iAmLabel的標籤內容引用爲該字段的值。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using System;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[LabelText("$iAmLabel")]
public string iAmLabel;
}
}
引用方法(ListDrawerSettings)
如下所示CustomAddFunction
:指定在Inspector添加一個item時執行對應綁定的方法CreateNewGUID
;
CustomRemoveIndexFunction
:指定在Inspector移除一個item時執行對應綁定的方法RemoveGUID
。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using System;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[ListDrawerSettings(CustomAddFunction ="CreateNewGUID",CustomRemoveIndexFunction ="RemoveGUID")]
public List<string> guidList;
private string CreateNewGUID() {
return Guid.NewGuid().ToString();
}
private void RemoveGUID(int index) {
this.guidList.RemoveAt(index);
}
}
}
GroupAttributes
TabGroup
如果在Inspector上有一堆的變量,可以用TabGroup
對它們進行分組保持界面整潔。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using System;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[TabGroup("Tab group 0")]
public int firstTab;
[ShowInInspector,TabGroup("Tab group 0")]
public int SecondTab { get; set; }
[TabGroup("Tab group 1")]
public float floatValue;
[TabGroup("Tab group 1"),Button]
public void Button() {
floatValue++;
}
}
}
FoldoutGroup
創建一個可摺疊的組。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using System;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour {
[Button(ButtonSizes.Large)]
[FoldoutGroup("Buttons in Boxes")]
[HorizontalGroup("Buttons in Boxes/Horizontal", Width = 60)]
[BoxGroup("Buttons in Boxes/Horizontal/One")]
public void Button1() { }
[Button(ButtonSizes.Large)]
[BoxGroup("Buttons in Boxes/Horizontal/Two")]
public void Button2() { }
[Button]
[BoxGroup("Buttons in Boxes/Horizontal/Double")]
public void Accept() { }
[Button]
[BoxGroup("Buttons in Boxes/Horizontal/Double")]
public void Cancel() { }
}
}
創建自定義分組xxxGroup
官網
功能:創建自定義的ColorGroupAttribute
,可以設置每一組的背景色,效果如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour
{
[ZYF_Test_ColoredFoldoutGroup("group 0", 1, 0, 0)]
public int test;
[ZYF_Test_ColoredFoldoutGroup("group 0")]
public int test1;
[ZYF_Test_ColoredFoldoutGroup("group 1", 0, 0, 1)]
public int test2;
[ZYF_Test_ColoredFoldoutGroup("group 1")]
public int test3;
}
}
步驟:
1.繼承PropertyGroupAttribute
。
using Sirenix.OdinInspector;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_ColoredFoldoutGroupAttribute : PropertyGroupAttribute
{
public float r, g, b, a;
public ZYF_Test_ColoredFoldoutGroupAttribute(string groupId) : base(groupId)
{
}
public ZYF_Test_ColoredFoldoutGroupAttribute(string groupId, float r, float g, float b, float a = 1f) : base(groupId)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
protected override void CombineValuesWith(PropertyGroupAttribute other)
{
var otherAttr = other as ZYF_Test_ColoredFoldoutGroupAttribute;
this.r = Mathf.Max(otherAttr.r, this.r);
this.g = Mathf.Max(otherAttr.g, this.g);
this.b = Mathf.Max(otherAttr.b, this.b);
this.a = Mathf.Max(otherAttr.r, this.a);
}
}
}
2.在Editor文件夾下新建Drawer(繼承OdinGroupDrawer),繪製Inspector。
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities.Editor;
using UnityEngine;
namespace ZYF
{
public class ZYF_ColoredFoldoutGroupAttributeDrawer : OdinGroupDrawer<ZYF_Test_ColoredFoldoutGroupAttribute>
{
private LocalPersistentContext<bool> isExpanded;
protected override void Initialize()
{
this.isExpanded = this.GetPersistentValue<bool>(key: $"{nameof(ZYF_ColoredFoldoutGroupAttributeDrawer)}.{nameof(isExpanded)}", defaultValue:
GeneralDrawerConfig.Instance.ExpandFoldoutByDefault);
}
protected override void DrawPropertyLayout(GUIContent label)
{
//box背景顏色修改
GUIHelper.PushColor(new Color(this.Attribute.r, this.Attribute.g, this.Attribute.b, this.Attribute.a));
SirenixEditorGUI.BeginBox();
GUIHelper.PopColor();
//摺疊按鈕
SirenixEditorGUI.BeginBoxHeader();
this.isExpanded.Value = SirenixEditorGUI.Foldout(this.isExpanded.Value, label);
SirenixEditorGUI.EndBoxHeader();
//組內Item繪製
if (SirenixEditorGUI.BeginFadeGroup(this, this.isExpanded.Value))
{
for (int i = 0; i < this.Property.Children.Count; i++)
{
this.Property.Children[i].Draw();
}
}
SirenixEditorGUI.EndFadeGroup();
SirenixEditorGUI.EndBox();
}
}
}
Meta Attributes
Meta 特性可以在值改變時做輸入驗證,方法調用等等…
ValidateInput
using Sirenix.OdinInspector;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour
{
[ValidateInput("IsValid")]
public int greaterThanZero;
private bool IsValid(int value) {
return value > 0;
}
}
}
OnValueChanged
using Sirenix.OdinInspector;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour
{
[OnValueChanged("UpdateRigidbody"), PreviewField]
public GameObject prefab;
[SerializeField]
private Rigidbody prefabRigidbody;
private void UpdateRigidbody(GameObject prefab)
{
if (prefab != null)
{
prefabRigidbody = prefab.GetComponent<Rigidbody>();
}
else
{
prefabRigidbody = null;
}
}
}
}
Required
引用丟失或爲空時會有警告提示。
using Sirenix.OdinInspector;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour
{
[Required,PreviewField]
public GameObject prefab;
}
}
InfoBox
顯示信息提示。
using Sirenix.OdinInspector;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour
{
[InfoBox( message:"這條消息只有在myInt是偶數時才顯示",visibleIfMemberName:"IsEven")]
public int myInt;
private bool IsEven() {
return this.myInt % 2 == 0;
}
}
}
PropertyOrder
可以改變在Inspector上的顯示順序,降序排列。
using Sirenix.OdinInspector;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ZYF
{
public class ZYF_Test_OdinAttributes : MonoBehaviour
{
[PropertyOrder(1)]
public int Last;
[PropertyOrder(-1)]
public int First;
//默認爲0
public int Middle;
[Button]
[PropertyOrder(-2)]
public void FirstMethod()
{
// ...
}
}
}