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!

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);

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.

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.

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.

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.

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.