学它 -->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
{
public Dictionary<int, string> dic;
#region 序列化
[SerializeField, HideInInspector]
private SerializationData serializationData;
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);
}
#endregion
}
}
保存与读取序列化数据
网址
我们无法保存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() { }
}
}
创建自定义分组(设置背景色)
官网
功能:创建自定义的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()
{
// ...
}
}
}