iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0

前面應該看到蠻多使用ScriptableObject的例子,現在終於要認真介紹一下這到底是幹什麼了

ScriptableObject是Unity提供讓程式的數據可以直接存在資料夾的方式,尤其要是今天該數據擁有預設值(ex:職業基本數值)那他的實用性就會大幅上升,下面先展示程式並解釋一些Unity本身擁有的功能,Odin的內容那些下篇就會說明這邊先跳過

UnitData.cs

using LinXuan.TBSF.Units; 
using Sirenix.OdinInspector; 
using System.Collections.Generic; 
using TbsFramework.Cells; 
using TbsFramework.Units; 
using UnityEngine; 
using Sirenix.OdinInspector; 

namespace LinXuan.TBSF.Data 
{ 
    [CreateAssetMenu(fileName = "UnitData", menuName = "SaveData/UnitData")] 
    [InlineEditor] 
    public sealed class UnitData : ScriptableObject 
    { 
        [BoxGroup("Basic Info")] 
        [SerializeField] private string m_UnitName; 
        [HorizontalGroup("GameDataGroup")] 
        [PreviewField(75)] 
        [HideLabel] 
        [SerializeField] private UnitView m_UnitView; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_MaxHp; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_UnitNumber; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_MaxStamina; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_MaxMovementTimes; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_MaxActionTimes; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_AttackPoint; 
        [VerticalGroup("GameDataGroup/States")] 
        [LabelWidth(70)] 
        [Range(0, 10)] 
        [SerializeField] private int m_AttackRange; 
        [VerticalGroup("GameDataGroup/States")] 
        [SerializeField] private int m_MoveRange; 
         
    
        private int m_CurrentStamina; 
        private int m_CurrentMovementTimes; 
        private int m_CurrentActionTimes; 
        private List<(Buff buff, int timeLeft)> Buffs; 
        public int CurrentStamina { get => m_CurrentStamina; set { Mathf.Clamp(value, 0, m_MaxStamina); } } 
        public UnitView UnitView { get => m_UnitView; private set => m_UnitView = value; } 
        public int PlayerNumber; 
        public bool Obstructable = true; 
        public string UnitName { get => m_UnitName; set => m_UnitName = value; } 
        [SerializeField] 
        [HideInInspector] 
        private Cell cell; 
        public Cell Cell 
        { 
            get 
            { 
                return cell; 
            } 
            set 
            { 
                cell = value; 
            } 
        } 

        public void Initialize() 
        { 
            Buffs = new List<(Buff, int)>(); 
            CurrentStamina = m_MaxStamina; 
        } 
    } 
}

上面程式在Inspector會長這樣
https://ithelp.ithome.com.tw/upload/images/20221007/20151894BsG8JgpbH5.png

CreateAssetMenu

CreateAssetMenu可以用捷徑的方式獲取ScriptableObject,以[CreateAssetMenu(fileName = "UnitData", menuName = "SaveData/UnitData")] 這段程式來說,當你右鍵並展開Create時,可以看到此路徑
https://ithelp.ithome.com.tw/upload/images/20221007/201518944HCwhnc9FQ.png

最後可以產生出這類物件,每個產生的UnitData都是獨立的物件,可以擁有各自的數值跟資料名稱
https://ithelp.ithome.com.tw/upload/images/20221007/20151894wISok4AzeA.png

不過要是直接使用Asset裡面的ScriptableObject的資料,那要是在遊戲裡資料有所更改,那會一起更動,要注意,需要用那個實體化的指令才能new出一個新的,範例可以看前面的Factory模式(二)那篇

HideInInspector

如果今天參數是publicSerializeField化的資料,可以透過[HideInInspector]這指令讓此參數不會顯示在Inspector中

sealed

sealed 用來防止繼承,用在ScriptableObject這種純粹存資料的地方特別適合。但也因此基本上我只會建議資料類型的class用sealed,因為基本上這杜絕了任何擴充的可能,違反了OCP原則

參考資料

流離之歌


上一篇
Day 25:interface與abstract class
下一篇
Day 27:Unity簡易編輯器製作(一)—使用Odin製作單位編輯面板
系列文
如何在Unity裡寫出具有一定擴充性的遊戲30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言