How to create instance of ScriptableObject and pass its constructor a parameter?

The only way to create an instance of a ScriptableObject seems to be:

ScriptableObject.CreateInstance("MyScriptableObject");

It is possible to give parameters while I’m creating the instance so that the constructor of my ScriptableObject class can initialise the instance using those particular parameters?

At the moment, I’m having to make do with creating the ScriptableObject instances using “new” (i.e. as if they were standard objects) but I’m getting warnings about doing this in the console which annoys me.

I know that I could directly set the values for properties of my instances once they’ve been created:

MyScriptableObject someInstance = ScriptableObject.CreateInstance("MyScriptableObject");
someInstance.intVariable = 5

but this just feels messy & wrong… I’m hoping that there’s an alternative?

Edit/Update 4th Oct '13 Thanks for the replies all, in particular Kryptos for explaining why things are the way that they are. I would have voted you the right answer but I really like HanClinto’s solution/answer and it’s perfect for my needs… I don’t know why I didn’t think of it earlier :S

Thanks once again all - the info is very much appreciated!

perhaps it would feel less wrong to call a function that initializes everything?

6 Answers

6

I would like to add another answer - a solution that I found to be a very elegant. Full credits go to @Jamora.

The idea is to use a Factory method. A method that is responsible for creating you the object you want, with the inputs you like.

public class PlayerData : ScriptableObject
{
   private string name;
   private float damage;
   private int id;
   
   public void Init(string name, float damage, int id)
   {
      this.name = name;
      this.damage = damage;
      this.id = id;
   }

   public static PlayerData CreateInstance(string name, float damage, int id)
   {
      var data = ScriptableObject.CreateInstance<PlayerData>();
      data.Init(name, damage, id);
      return data;
   }
}

Usage:

PlayerData data = PlayerData.CreateInstance("nitex", 55.8f, 11);

This is a nice abstraction, but I wonder if it has any overhead when these scriptable objects get serialized? I imagine unity's serialization is smart enough to remove all methods before serialization, but I wonder if there is any overhead associated with that.

I concur with the other answers – having a parameterless constructor is very valuable, in that the order of instantiation cleans up a LOT of potential headaches when serializing / deserializing objects.

However, to add something new, if you want to do a similar thing like what you’re talking about, instead of setting individual parameters after instantiation (I agree, that’s very messy with a lot of parameters), I would recommend creating an init() method that you pass all of your initialization parameters into.

So (in C#) this would look something like:

MyScriptableObject someInstance = ScriptableObject.CreateInstance("MyScriptableObject") as MyScriptableObject;
someInstance.init (5, "Gorlock", 15, owningMap, someOtherParameter);

It’s 2 lines of code instead of 1, but it lets you keep the best of both worlds for parameterless constructors for serialization, and it lets you initialize your object in one clean line, at one clear point in time.

This is a very good solution, +1

In order to make refactoring easier I would rather use typeof(...) instead of a string: MyScriptableObject someInstance = ScriptableObject.CreateInstance(typeof(MyScriptableObject)) as MyScriptableObject;

Shorter: MyScriptableObject obj = ScriptableObject.CreateInstance<MyScriptableObject>();

I believe this is right too... MyScriptableObject obj = ScriptableObject.CreateInstance<MyScriptableObject>().init (5, "Gorlock", 15, owningMap, someOtherParameter);

The absolute best and ONLY way to properly instantiate a scriptable:

public MyScriptableObject obj; // Pass in the ACTUAL scriptable by ref or in the inspector

void Init(){
obj = ScriptableObject.Instantiate(obj);
}

This creates a physical clone of the object without affecting the original. This is how scriptables are meant to be used.

First time I didn't regret going to the second page of Unity Answers. This is the most simple and correct way to do this. Thank you!

Here’s a proposed refinement of HanClinto’s response:


MyScriptableObject someInstance = MyScriptableObject.Create(
    new MyScriptableObjectConfig(){
      level = 5, 
      name = "Gorlock", 
      age = 15, 
      map = owningMap
    }
);

Here, the Create function is a public static shortcut function like this:


public static MyScriptableObject Create(MyScriptableObjectConfig config){
    return MyScriptableObject.CreateInstance<MyScriptableObject>().Init(config);
}

public MyScriptableObject Init(MyScriptableObjectConfig config){
    ..
    return this;
}

This way of doing it has some advantages:

  • the creation parameters are named, so we don’t have to rely on intelisenseless to remind us of what they are
  • support for more (optional) creation parameters can be added for other possible use cases without changing this code at all
  • only one Init function and one Create function is needed, regardless of how many optional initialization parameters are eventually supported
  • the properties of the config object can be types that have explicit or implicit conversions defined for the various potential use cases, allowing the initialization code and the consumption code (above) to be free of having to deal with those kinds of concerns

Note that this code assumes that if CreateInstance() didn’t produce an instance, some sort of exception was (or should be) raised, rather than null being passed back. If the failure of the creation of the instance is a possibility that shouldn’t be an exception, then this code will need to be modified. (But so would HanClinto’s, despite its use of ‘as’.)

If the object isn’t intended to be involved in the effort to persist things (and therefore doesn’t need to support serialization) this alternative could be used:

MyObject someInstance = new MyObject(new MyObjectConfig(){
  .
  .
});

… thereby supporting the use of readonly properties (that can only be set during construction) without fear of ending up proliferating constructor parameters, constructor overloads, etc. If MyObject derives from ScriptableObject, you’ll get a warning about using the constructor, probably because of the serialization issue, but it doesn’t seem to cause any problem if you never serialize or deserialize.

By the way, you can create objects of your own classes that don’t derive from ScriptableObject at all. All you have to do is put them in a file of the same name as the class and it’ll be globally accessible. (The class’s config class will be accessible in any file that you use the class in.)

The definition for the config struct or class can be right above the definition for the object class. It can be as simple as a list of public variables, or as complex as needed to support whatever default values, parameter conversions, et etc. might eventually be desired.

The Create function can be put into a base class, the config object parameter to it can be made nullable, so then, in the case where the defaults are acceptable…

MyScriptableObject someInstance = MyScriptableObject.Create(null);

or

MyObject someInstance = new MyObject(null);

… if the default parameters are obvious enough and serialization is not required. And/or if you feel like it, you can create a single default constructor that passes a default constructed MyObjectConfig to the main constructor. Then you can do this:

MyObject someInstance = new MyObject();

and still never have to worry about ending up with a mass of constructor overloads and explicit constructor parameters.

There are a lot of ways to attack the beast in C#. Not every class is concerned with serialization, in fact separating out the concern of persisting things is a good practice. Passing parameters to constructors can result in less code to create and maintain, and it doesn’t have to end up causing problems. Just don’t let yourself end up with a ton of constructor parameters or overloaded constructors; it’s easy enough to avoid. And don’t pass parameters to constructors that are going to end up needing to be serialized/deserialized. You don’t serialize things like file handles or database connections or request contexts. Under particular circumstances, it may be ok to pass some things like that as optional constructor parameters even to objects that need to support serialization, if they have defaults that can be used in situations where serialization is going on.

Hi! Can you show how to apply values provided by config to a creating object? I don't understand what I should write in Init method to assign all values of config data to all variables of MyObject class. Please share an example.

this just feels messy & wrong

Actually, this is the good way to do it. Design patterns and good programming practices tell us that a constructor should only contruct the object, i.e. allocate the memory needed. Anything related to the goal/function of the object should be set afterwards.

There a lot of reason to do it that way, the first one is readibility to help code maintenance.

ScriptableObject does not escape this rule. And it uses a factory method (CreateInstance) to enforce this rule. The constructor is hidden and will juste create the asset. Any property would be set afterwards.

edit (2013/10/04)

Another reason is that ScriptableObject is a Serializable class, therefore it must have a parameterless constructor. Given this rule, any parameter to an additional constructor is only optionnal. Therefore it is better to enforce this rule by providing no such constructor.

Im sorry but if this indeed was the way to do it, then Unity wont use it in their own classes (e.g. new Color(1,1,1) and a hell lot of other. Having constructor with arguments I find extremely helpfull and usefull

Color is not a good example since it is not a class but a struct (same with Quaternion and Vector). But for example you cannot do new Component() but need to use AddComponent(). So yes even Unity use it for its own calsses.

There's an old adage, if your function takes more than 6 parameters: look again it probably needs 6 more. It implies a poor design. Functions with large numbers of parameters should be avoided if at all possible - they are very hard to read in the code and lead to errors and difficult maintenance.

My point exactly. That applies to all functions, not just constructors. This thread is about passing any arguments at all to a constructor, which has little to do with that old adage.

Hmmm, that's no quite how your comment reads though.

I completely disagree with Kryptos. There are many cases where you want to enforce a variable to be set during initialization so that no misunderstandings can be had when creating the object. You could always just make your own class without deriving from ScriptableObject - and then write your own serialization if that’s what you’re after.