ScriptableObject.CreateInstance vs Instantiate

what is the performance profile of createinstance?

1 Like

ScriptableObject.CreateInstance and Instantiate are totally different.
Instantiate creates copy of Object and it works runtime.
SO.CreateInstance creates instance of ScriptableObject which is “.asset” file and its editor only.
~~https://docs.unity3d.com/ScriptReference/ScriptableObject.CreateInstance.html~~
~~https://docs.unity3d.com/ScriptReference/Object.Instantiate.html~~

1 Like

Nope. You can use ScriptableObject.CreateInstance as much as you want at runtime.

For OP's question, Are you wondering if it's faster to eg. create a ton of copies of an existing SO than to create a ton of new SOs? It's not that hard to check!

public class Create : MonoBehaviour
{
    public SomeSO toClone;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            var stop = Stopwatch.StartNew();
            for (int i = 0; i < 10000; i++)
            {
                var so = ScriptableObject.CreateInstance<SomeSO>();
            }

            stop.Stop();
            Debug.Log(stop.Elapsed);
        }

        if (Input.GetKeyDown(KeyCode.Alpha0))
        {
            var stop = Stopwatch.StartNew();
            for (int i = 0; i < 10000; i++)
            {
                var so = Instantiate(toClone);
            }

            stop.Stop();
            Debug.Log(stop.Elapsed);
        }
    }
}

The results are that CreateInstance is in the ballpark of 12-13 ms, while Instantiate takes about 4.

20 Likes

3x slower is shockingly bad. You know who made this?

They're both pretty irrelevant actually- making copies of ScriptableObjects during runtime is pretty pointless in most cases, and if you're doing it in the editor then it doesn't matter what the performance hit is (which is why they still use the OnGUI system for editor scripts despite its terrible performance).

ScriptableObjects don't benefit from any of the built-in tools at runtime, since the AssetDatabase is an editor class, so it's essentially the same as using any serializable class type since you need to write and load it from the disk yourself manually either way. I could see using it as a factory pattern I suppose, treating the pre-made SOs like blueprints and just making copies when they're needed, but that would be trivial to handle during scene loads rather than mid-play, and CreateInstance wouldn't be used at all in that scenario. In my own solutions, the pre-made SOs ("definitions", "blueprints", w/e) simply generate non-SOs, so there's no extra overhead for saving the objects to disk.

I actually can't imagine a single scenario where CreateInstance is ever actually useful at runtime, to be perfectly honest.

6 Likes

i instantiated SO at runtime as data and function buckets, so you say createinstance is in fact making an asset?

It's just got a lot of pointless overhead for no real benefit outside of the editor. You can use a non-ScriptableObject and just make a new instance with the "new" keyword easily enough, so what's the point of it being a ScriptableObject in the first place? If it was for a factory pattern, then Instantiate would be the function to use so that you can make a duplicate of the original.

5 Likes

there is no point in Using ScriptableObjects.CreateInstance. since scriptable Objects should just be used as Data Containers.. and also i am pretty sure u cannot use Instantiate on a Scriptable Object. since it has no objects or data that can be used to create a object

you really need to watch some of the more recent unity vids on how to use scriptableobjects if you still think that.

7 Likes

Instantiate can absolutely be used on ScriptableObjects- they're derived from UnityEngine.Object after all.

@LeftyRighty To be fair, some of the recent Unity vids abuse ScriptableObjects in really bizarre ways- ways that aren't even supported properly, and which cause some pretty serious side effects. For instance, instantiating new ScriptableObjects without saving them to the disk as assets, and then assigning them to ScriptableObject references in a scene is actually done in one of those tutorials, made me cringe like crazy while watching it, and in practice creates all sorts of problems in the Editor, like dirtying every open scene every time its done, not having a proper reference target in the inspector, creating a bunch of garbage to be GCed, etc...

They're inherently just YAML files, which means they are, at their core, just data containers, like XML or JSON files. The extra functionality given by serializing them and giving them custom editors isn't much different than writing inspectors for any other asset type, like ZIP files for instance, which is easy enough.

Out of curiosity, what tutorials on ScriptableObjects are you referring to, that they aren't just treated like data containers with a custom inspector?

7 Likes

Yeah, I see very little benefit to creating a new ScriptableObject at runtime. The only real difference between a ScriptableObject and a vanilla class is that you can make a ScriptableObject into an asset in the editor. There seems to be little point to doing this at runtime.

I suppose you could use it if you want to pump a regular class through the same Instantiation/Enable/Disable/Destroy pipeline that GameObjects follow. But it doesn't seem especially smart to do this.

2 Likes

it might be what i’m doing.

i use them to avoid making custom inspector, all I have is this thing which shows the content of all referenced object within the inspector - it’s convenient.

i use SO in the following scenario:
Actor is a component attached to each agent, it has a list of Action SO
when the AI triggers an action, it sends an reference of an instance of the Action SO’s to a centralized ActionManager, packaged alongside a target and other parameters which allow me to conveniently format all actions the same way.
the ActionManager calls the Action’s precondition, if the preconditions are met it locks the target, adds the Action package to the list, processes that list each frame. the Action Package includes the Action and things like time it takes for the action to complete, callbacks when completed or failed etc…

instantiation is now no longer an issue because i’ve capped the AI ability to spawn actions @ 1 action/agent so i pre-instantiate the SO at agent startup but I want to hear how you guys use SO and how you handle actor->action->target

1 Like

For example:

https://www.youtube.com/watch?v=cHUXh5biQMg

https://www.youtube.com/watch?v=raQ3iHhE_Kk

Also you can use them as expandable enums (by designer), although it’s more or less data storing feature.

2 Likes

pipped to it, but specifically the AI tutorial and the “monobehaviour tyranny” talk where they discuss “plug and socket” architecture to use functions on scriptableobjects, rather than just using them as data holders

1 Like

no. i dont. cause scriptable objects are normally used for Data Containers. and some functions since it lives as a asset

It’s obviously up to you what new techniques you want to learn or not, but that statement, that the scriptableobjects “normally” used as data containers is, well, silly. SOs have multiple functions, multiple goals and multiple usage. It’s up to you if you utilize those or not. But that’s your shortcoming if you don’t.

17 Likes

[quote]
For example:

https://www.youtube.com/watch?v=cHUXh5biQMg|list=PLX2vGYjWbI0ROSj_B0_eir_VkHrEkd4pi


Also you can use them as expandable enums (by designer), although it's more or less data storing feature.
[/quote]

That second video was fantastic! (I know this is an old topic). I was curious how he gets around the One Object / One Variable problem. If I wanted to clone an enemy several times, they couldn't all reference the same HP variable. Also I imagine your project would become quite full of ScriptableObject instances.

3 Likes

You can create .asset(instances) and of a scriptable object in editor time and save it. Without SO you can accomplish the same by hardcoding it with new(), albeit would be pretty ugly code.

If you are editing and saving SO.asset at runtime, it’s equivalent to serializing a regular object.

The way Unity explain and position SO is very confusing when I first learned about it and tried to understand it. Eventually I realized ScriptableObjects are just regular objects that which you can instantiate and initialize in editor time. Otherwise they aren’t any different. No more confusion!

Bit late, sorry.

If we have a GameObject Enemy, all we need to do is make a decision about who is concerned (which other Objects) are interested in the data it holds. If other objects are concerned with it, we should use a ScriptableObject, and reference that data. Or, if no other Objects are concerned with the data, then our Enemy can just have an internal Instance Variable instead.

Let's use a little example, and say that our Enemy has three variables it's interested in, health.current, health.max and movement.speed.

In our game there's also floating health bars that appear above an enemies head (something like this), which is looked after by our players HUD (probably a Canvas UI element billboarded). As such, our Enemy Object needs some way of telling the Player's HUD Object about what its health.current and health.max values are. This is where ScriptableObjects would be used, and both the Enemy and the Player's HUD would reference that same ScriptableObject Data.

That leaves us with the Enemy's movement.speed... Which, at the moment at least, nothing else in our game is concerned with. The Enemy knows it's movement speed is 4f (for example), and it can do its internal calculations to move the model at that speed quite happily. Our Player doesn't care what the Enemy's movement speed is, neither does our HUD. Therefore, the Enemy.movement.speed can just be stored as an Instance Variable inside of our Enemy Object.

If you need to generate a new Enemy at runtime (then you're likely procedurally generating stuff, which is awesome, keep it up!), then you could use something like:

RefInt.cs:

using UnityEngine;

public class RefInt : ScriptableObject
{
  public int Value;
}

Enemy.cs:

using UnityEngine;

public class Enemy : MonoBehaviour
{
  private void Awake()
  {
    // We create a new Integer ScriptableObject to hold our newly created Enemy's Current HP data.
    RefInt enemyCurHP = ScriptableObject.CreateInstance<RefInt>();

    // We assign a value to that
    enemyCurHP.Value = 5;

    // We send a message to the Player's HUD object, saying:
    //  "Hey! I'm a new Enemy!
    //   I think you're interested in knowing what my current HP value is.
    //   I'm currently keeping track of it myself in this ScriptableObject...
    //   Why don't we both just reference the same number, rather than all this chat?"
    PlayerHUD.TrackNewValue(gameObject, enemyCurHP);
  }
}

PlayerHUD.cs:

using System.Collections.Generic;
using UnityEngine;

public class PlayerHUD : MonoBehaviour
{
  // Use Polymorphism to keep the referenced data nice and general.
  private static Dictionary<GameObject, ScriptableObject> _refData = 
      new Dictionary<GameObject, ScriptableObject>();


  // We use this to keep track of any new data that the PlayerHUD needs to know about.
  public static void TrackNewValue(GameObject obj, ScriptableObject reference)
  {
    _refData.Add(obj, reference);
  }


  // We can then Update the screen to represent any changes to this data, independently
  // of how, when or why it's changed.
  private void Update()
  {
    // Just some Pseudo code to give an example...
    someUIElement.text = _refData[anEnemyThatsInRangeOfThePlayer];
  }
}

This is just a thrown together example (please don't use it; there are better ways of doing pretty much everything that's in there). But it gives the general idea... And be sure to remove the references when you're done, to avoid memory leaks.

As for ending up with hundreds, thousands, tens of thousands of pieces of ScriptableObject Data, it's not a problem, as internally that data will exist in a Serialized form, whether or not its an Instance Variable or a ScriptableObject. The Engine doesn't care where the data comes from, and indeed, won't know.

You're not creating more data (each enemy is still going to have HP, right?), you're just changing how the data is used (referencing shared data, instead of asking for it a lot, or storing it multiple times). At runtime, you can use something similar to the above outlined criteria. In the editor, you'll likely have lots of ScriptableObject data files (.asset files)... This is completely normal. Just keep them organised (folders/directories are your friends here), and you shouldn't encounter any issues.

The big difference (and the point of all this) is that rather than storing the EnemyCurHP in multiple places (or constantly asking the Enemy Object, "Hey! What's your HP?", "What's your HP?", "What's your HP?"), you're instead storing it once, and then every object that wants to know about that value can just reference it.

All this is good, and the Engine will reward you with blazzingly fast FPS on potato devices for your trouble!

2 Likes

Have you tried interfaces? Just use them instead of SO’s if all you need is to reduce coupling between components (like player health). No need to overcomplicate the structure of your MonoBehaviors with extra SO’s.
Interface fields are not normally supported by Unity’s inspector, but there is a handy workaround for this. I can show you if you want. Basically you create a special InterfaceReference class and a PropertyDrawer and vuola! - your components don’t care if you put another MonoBehavior or ScriptableObject reference into the field - all they know is the object implements certain interface. So your code is clean and no need to restructure your components.

PS: your UI code is inefficient. You ideally shouldn’t update e.g. “text” property every update. Use Observer pattern to resolve this (plain simple C# public events) or use UniRx - it’s awesome.

3 Likes