Unity ScriptableObject詳解

一、ScriptableObject類概述、屬性及函數

首先提供官方參考文檔:API-Reference-ScriptableObject & Scriptable-Manual & SctiptableObject-Candycat

概述: ScriptableObject類直接繼承自Object類;它和MonoBehaviour是並列的,都繼承自Object(但MonoBehaviour並不是直接繼承自Object),會在後面介紹ScriptableObject和MonoBehavior的比較;

腳本化對象就是一個數據容器,可以用來存儲大量的數據,它是可序列化的,這個特點也正決定了它的主要用途;一個主要用處就是通過將數據存儲在ScriptableObject對象中來減少工程以及遊戲運行時因拷貝值所造成的內存佔用;

ScriptableObject與預製體:當你有一個預製體,它附加了一些mono腳本,包含了一些數據,每次我們實例化預製體的時候它都會拷貝assets下原預製體的值生成一份自己的拷貝,然後我們可以修改場景內預製體的值而並不影響assets下預製體的值,這是prefab的特性,對於我們從一個prefab模板生成屬性不同的遊戲對象是很有用的,但是如果prefab裏的腳本數據是不需要修改的,它就會造成很大的資源浪費,尤其在數據很多的時候;爲了避免這種問題,我們可以在不需要修改prefab裏的腳本數據時,考慮使用ScriptableObject來存儲這些重複的數據,然後其它所有預製體都可以使用引用的方式來訪問這份數據,這就意味着不管場景中實例了多少預製體,在內存中就只需要有一份數據;它所帶給我們的啓示就是,當預製體中的腳本里有大量重複數據時,我們要想着將數據抽離,單獨保存在本地

舉一個子彈的例子,這是一個比較典型的例子,因爲一個子彈會包含很多屬性,而且在場景中需要大量的實例化,我們要先做一個子彈的預製體,爲了體現它的屬性,我們會寫一個派生自MonoBehaviour的腳本Bullet,在裏面添加一些屬性,然後將其附加到一個子彈遊戲對象上,完成了一個子彈預製體的製作;之後我們在場景中實例化新的子彈的時候,這個實例也會有一個Bullet實例,而且最重要的是這些子彈都會有這樣一份Bullet而且它們的數據是相同的,這就是之前講到的因爲拷貝值產生新的實例導致了大量的內存佔用;這個時候我們就可以把這個派生自MonoBehaviour的腳本里的數據放置在派生自ScriptableObject的腳本,然後就可以在創建Assets下創建一份數據文件,設置完數據後,在Bullet中我們就定義一個指向ScriptableOjbect對象的引用,這樣它就由原來的拷貝大量值並存儲重複的值變成了拷貝一個引用並存儲一個引用

應用場景及方式:當使用編輯器運行遊戲的時候,可以將數據保存到ScriptableObject裏(當創建一個腳本化對象實例後使用AssetDatabase.CreateAsset()保存該資源),退出之後也不會丟失,因爲它是作爲Assets下的資源存在的;它是僅在編輯器中纔可以保存修改的數據(因爲ScriptableObject對象雖然聲明在UnityEngine中,但是它的Scriptable是通過UnityEditor命名空間下的類例如Editor類等來實現的),所以在部署構建的時候不可以用於存儲遊戲運行時更改的數據,但是可以使用之前存儲好的數據,也就是ScriptableObject生成的數據資源文件在Editor外具有隻讀屬性,這是非常需要注意的一點,如果你需要在遊戲中修改數據並存儲下來,就不推薦使用ScriptableObject了

總結:它就是用來在編輯器模式下保存和存儲數據到本地Assets下的,數據保存以後是可以共享的,就像紋理、shader等資源一樣,是可以共享於當前整個工程和其它工程的;這個ScriptableObject在真機上不可修改的,就像我們不可以在遊戲運行時修改一個shader資源的代碼、不可以修改一個紋理資源的像素內容一樣,而在Unity Editor裏可以修改ScriptableObject是因爲Unity的編輯器對它格式的支持,就像使用vs code修改shader和使用ps修改一張紋理一樣;

屬性

  • name:返回對象的名稱

靜態方法

  • CreateInstance:在遊戲運行時創建一個Scriptable類型的實例,不使用時被GC回收;
//靜態方法,使用了ScriptableObject類約束的泛型參數T
public static T CreateInstance<T>() where T : ScriptableObject;
  • Instantiate:實例化一個對象,返回一個實例;類似於GameObject的Instantiate(),其它函數也和GameObject類似;
public static T Instantiate(T original);
public static T Instantiate(T original, Transform parent);
public static T Instantiate(T original, Transform parent, bool worldPositionStays);
public static T Instantiate(T original, Vector3 position, Quaternion rotation);
public static T Instantiate(T original, Vector3 position, Quaternion rotation, Transform parent);
Parameters

Message:ScriptableObject內部實現上也繼承自MonoBehavior,它只有四個消息函數,Awake()、OnDestroy()、OnEnable()、OnDisable();

二、ScriptableObject與MonoBehaviour

1.兩個類都是繼承自Ojbect類

首先看一下MonoBehaviour類:

ScriptableObject對象是不可以作爲組件附加到對象上的,因爲它的出現就是爲了要讓數據獨立於具體的遊戲對象;

2.ScriptableObject對象擁有非常少的回調函數

Awake():創建一個對象實例時調用;

OnEnable():如下,創建或被加載時調用;

OnDisable / OnDestroy:當調用銷燬函數時,

如果想仔細瞭解ScriptableObject的回調函數何時調用,可以自己定義一個ScriptableObject對象,然後在四個回調函數中加上相應的Debug.Log()即可;

三、案例使用

1.簡單應用:創建Scriptable實例與保存到文件資源

我們已經知道我們在開發中如何去使用腳本化對象了,回顧一下:

  • 第一種:在遊戲運行時創建腳本化對象實例,然後可以將數據保存到本地(如果不保存,它會在遊戲結束後銷燬);
  • 第二種:在代碼中引用Assets文件夾下的腳本化對象資源(也許是從遊戲運行時保存下來的數據,也許是手動創建的數據);

使用案例:遊戲配置文件;商品清單;敵人統計數據等;

2.CreateAssetMenuAttribute-使用ScriptableObject

命名空間在UnityEngine的一個特性,它有三個屬性filename,menuname,order,它用於標記一個派生自Scriptable的類,可以在Assets下的create下添加子菜單項,用於創建.asset資源文件(Scriptable類是和.asset資源關聯的);它和AddComponentMenu特性類似,只不過後者是將腳本組件添加到組件按鈕的子菜單項,Unity中提供了很多用於編輯器菜單項擴展的屬性例如MenuItem、ContextMenu等等,我們往往根據它的名稱就可以大概知道它用在什麼地方;

注意:雖然使用MenuItem也可以在Assets/Create下創建一個菜單項,但是兩種方式還是有區別;

如果是使用MenuItem在靜態方法中調用CreateAsset創建資源,那麼需要指定創建的目錄和資源名稱,如果資源已經存在,則不會創建新資源;而CreateAssetMenu則可以在Assets下任意目錄創建資源,而且可以創建多個資源;

public class MakeScriptableObject {
    [MenuItem("Assets/Create/My Scriptable Object")]
    public static void CreateMyAsset()
    {
        MyScriptableObjectClass asset = ScriptableObject.CreateInstance<MyScriptableObjectClass>();

        AssetDatabase.CreateAsset(asset, "Assets/NewScripableObject.asset");
        AssetDatabase.SaveAssets();

        EditorUtility.FocusProjectWindow();

        Selection.activeObject = asset;
    }
}

 

[CreateAssetMenu(fileName = "data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject {
    public string prefabName;
    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

然後,在Assets下創建一個可編程對象資源,設置好所需數據;如果需要在其它腳本中獲取該數據,是需要聲明一個該類型變量,然後爲其賦值或加載該數據資源;然後,就像使用用一個類中的公有變量一樣使用即可;

public SpawnManagerScriptableObject spawnManagerValues;
//spawnManagerValues.prefabName

提示:可以使用CustomEditor來爲ScriptableOjbect創建自定義屬性面板;

3.ScriptableObject與Json

 

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