Unity Editor Scripting (A kick-starter guide) - Part 3

May 30, 2017
protect

This article is originally published on devcrew.io.

In our previous posts on editor scripting, we discussed an overview and importance of editor scripting. We explored Gizmos, Custom Inspectors and Editor Windows in the part 1 and part 2. Now in this part, we will give you an insight and overview of scriptable objects.

What are Scriptable Objects?

According to Unity:

“A class, derived from Unity’s Object class, whose references and fields can be serialized.”

How scriptable objects look like

Scriptable object is a special type of object that doesn't need to be present in the scene or doesn't need to be attached on a game object to exist, because it can be saved as an asset in the project. The major use of scriptable object is to save data in a persistent way. It is just a data-only version of MonoBehavior and can only hold data variables.

What they can do?

Scriptable objects can be built/generated into Unity and can be saved as assets inside the project. They can be saved during runtime which means if you have to change something on runtime or play-mode, you can retain those changes using scriptable objects. You don't have to worry about parsing/serializing your data during runtime, they will do that for you. As it can store only data and is almost same as MonoBehavior, you don't have to worry about it's inter-op communication with your classes.

What are the limitations?

There are some limitations of scriptable objects which should be kept in mind while using them. They require editor scripting because they can only be created inside the editor. Also, you can't edit them outside Unity using a third-party tool. Because of this, they are meant to be used for saving and managing development life-cycle data only; i.e. they cannot be modified or saved once deployed. In short, scriptable objects are best for storing game development/game design data and optimized data loading.

Possible Usage Scenarios

  • You want to tweak values during play mode and want to retain them.

  • You want to change all game objects of some type.

  • You don't want to change game designer or artist to mess with the irrelevant values of your game object.

In all above scenarios, scriptable objects are useful and can always be saved into their own unique asset file. You can easily edit ScriptableObject instances during play mode and let game designers/artist iterate without worrying about the actual game data. Moreover, your scenes and prefabs will save and load faster. Also, it has a robust Separation of Concern (SoC) pattern implemented.

Example

Let's get hands on playing with scriptable objects. We have a simple class 'Player.cs' right now:


public class Player : MonoBehaviour
{
    //Player data
    public string _name;
    public int _health;
    public Color _color;

    private MeshRenderer _renderer;

    void Awake()
    {
        _renderer = GetComponent<MeshRenderer>();
        _name = "John";
        _health = 100;
        _renderer.material.color = Color.green;
    }

Our player is a placeholder cube (to keep it simple to understand) with a name, health and a color. In the Awake() callback, we are assigning our data to the player. Let's make a scriptable object. It's just a simple C# class 'PlayerData' which extends ScriptableObject:


public class PlayerData : ScriptableObject
{
    public string m_name;
    public int m_health;
    public Color m_color;    
}

Saving Scriptable Object as Asset

To save our SO as an asset in the project, create a simple C# class and include UnityEditor namespace to access editor functions and it doesn't need to extend any class:


using UnityEditor;
using UnityEngine;

public class MyEditorUtils
{

}

Now create a static generic method CreateAsset<T>() inside the class. Generic methods have type parameters and they provide a way to use types as parameter in a method:


public class MyEditorUtils
{
    public static void CreateAsset<T>() where T : ScriptableObject
    {

    }
}

Add this code inside the method:


public static void CreateAsset<T>() where T : ScriptableObject
{
     T asset = ScriptableObject.CreateInstance<T>();

     string path = AssetDatabase.GetAssetPath(Selection.activeObject);
     string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/New " + typeof(T).ToString() + ".asset");

     AssetDatabase.CreateAsset(asset, assetPathAndName);
     AssetDatabase.SaveAssets();
     AssetDatabase.Refresh();
     EditorUtility.FocusProjectWindow();
     Selection.activeObject = asset;
}

Now let's breakdown the code line by line:

  • Line 3: We are creating an instance of scriptable object of type T we are getting as our method's parameter.

  • Line 5, 6: We are getting the path of the selected folder in the project and then constructing a path and name string for our SO asset.

  • Line 8: We are creating a '.asset' file with our asset name and our desired path.

  • Line 9: Saving our SO asset to our project.

  • Line 10: Refreshing and updating our assets view and data.

  • Line 11, 12: Making project window focused and our saved SO asset selected.

Now let's get it wrapped in a static method so that it can be triggered on a [MenuItem] attribute action and pass PlayerData in the parameters:


public class MyEditorUtils
{
    [MenuItem("Assets/Create/Create Player Data Object")]
    public static void CreateAsset()
    {
        CreateAsset<PlayerData>();
    }

    public static void CreateAsset<T>() where T : ScriptableObject
    {
        T asset = ScriptableObject.CreateInstance<T>();

        string path = AssetDatabase.GetAssetPath(Selection.activeObject);
        string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/New " + typeof(T).ToString() + ".asset");

        AssetDatabase.CreateAsset(asset, assetPathAndName);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        EditorUtility.FocusProjectWindow();
        Selection.activeObject = asset;
    }
}

Go to Unity editor now and see it in action:

This is how our PlayerData SO looks like in inspector:

[CreateAssetMenu] Attribute

From Unity 5.1 and onwards, you can create an asset in just one line of code using [CreateAssetMenu] attribute. This attribute allows you to create menu items in the Assets/Create context menu. This is pretty quick to create an asset and there is no need of the above class to do this. All you have to do is, just mark our PlayerData class with a [CreateAssetMenu] attibute like this:


[CreateAssetMenu(fileName = "My Scriptable Object", menuName = "My Content/Create Player Scriptable Object", order = 0)]
public class PlayerData : ScriptableObject
{
    public string m_name;
    public int m_health;
    public Color m_color;    
}
  • fileName: is the name of the asset file which will be created in the assets.

  • menuName: is the hierarchy of the menu inside the Asset/Create menu.

  • order: is the order of your menu item in the Asset/Create menu. I set it 0 to be shown on top of every menu.

and here is the result:

Linking and Referencing the Scriptable Object

Referencing and using the data from the scriptable object is pretty easy. All you have to do is just create a variable of the type PlayerData

JikGuard.com, a high-tech security service provider focusing on game protection and anti-cheat, is committed to helping game companies solve the problem of cheats and hacks, and providing deeply integrated encryption protection solutions for games.

Read More>>