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()
        {
            // ...
        }
    }

}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章