- Home /
Instantiating a nested class ScriptableObject
Hi there, I'm having an issue creating an instance of a ScriptableObject where the class definition is nested within another class. The scenario is that I have "worker" classes that derive from ScriptableObject. Theses class have an internal data representation that for serialization reasons also needs to derive from ScriptableObject. The internal classes are public so are visible as types to the runtime, but ScriptableObject.Create() returns null.
Some repo code:
using UnityEditor;
using UnityEngine;
//Worker class
public class WorkingClass : ScriptableObject
{
//Internal data representation
public class InternalData : ScriptableObject
{
public string InternalDataSet = "DATA";
}
//End internal data class
private InternalData _internalData;
//An accessor to the internal data
public string DataAccess
{
get
{
return _internalData.InternalDataSet;
}
}
//Worker class constructor
public WorkingClass()
{
//This returns null :-(
_internalData = (InternalData)ScriptableObject.CreateInstance(typeof(WorkingClass.InternalData));
}
}
//Test harness
public class TestWindow : EditorWindow
{
//Menu option to open the window
[MenuItem("Window/Debug/Show Test Window")]
public static void OpenWindow()
{
EditorWindow.GetWindow<TestWindow>();
}
//Worker class instance
private WorkingClass _workingClass;
//Test harness constructor
public TestWindow()
{
this._workingClass = (WorkingClass)ScriptableObject.CreateInstance(typeof(WorkingClass));
string shouldEqual_DATA = _workingClass.DataAccess; //Returns null :-(
}
}
Obviously this example is greatly simplified. The actual implementation has multiple instances of the InternalData records.
If I move the InternalData out of the WorkingClass, the Create requests work fine, but these classes have no business being in the public namespace and really shouldn't be there as they are not relevant outside of their parent Worker.
Does anyone know how to get this working?
Kind regards,
A.
I can't tell you how to fix it, but I can tell you that I replicated the problem in Unity 5.3.4.
I "solved" {ahem} the problem by breaking out the nested class into its own file as a main class.
I'm aslo looking for a solution, though I believe Unity works with only first scope classes which name correspond to the filename. Its the same for all special objects ( behaviours, components, scriptableObject). I'm not sure there is a solution there. Did you found any workaround ?
Have you checked if your constructor is getting called? I wouldn't have expected it to (since ScriptableObjects are creats using ScriptableObject.CreateInstance rather than with new).
You could try something like this (untested)....
public class WorkingClass : ScriptableObject
{
// your stuff plus....
static public WorkingClass CreateInstance()
{
WorkingClass instance = ScriptableObject.CreateInstance<WorkingClass>();
instance._internalData = ScriptableObject.CreateInstance<WorkingClass.InternalData>();
return instance;
}
}
and then create them using WorkingClass.CreateInstance() ins$$anonymous$$d of ScriptableObject.CreateInstance();
I just tested this and it solves your test case, so long as you use OnEnable() in TestWindow rather than its constructor. Like RobAnthem though, I am sceptical about whether or not ScriptableObjects is the right way to go with all this.
The problem is that ScriptableObjects are more like containers, your nested class should not ALSO be a scriptable object, just a normal class object with [System.Serializable] tag to tell Unity to serialize it, then it will appear in editor when used as a field in the main object. I do this all the time and it works fine.
using UnityEditor;
using UnityEngine;
//Worker class
public class WorkingClass : ScriptableObject
{
[System.Serializable]
public class InternalData
{
public string InternalDataSet = "DATA";
}
//End internal data class
private InternalData _internalData;
//An accessor to the internal data
public string DataAccess
{
get
{
return _internalData.InternalDataSet;
}
}
//Worker class constructor
public WorkingClass()
{
//This returns null :-(
_internalData = new _internalData();
}
}
//Test harness
public class TestWindow : EditorWindow
{
//$$anonymous$$enu option to open the window
[$$anonymous$$enuItem("Window/Debug/Show Test Window")]
public static void OpenWindow()
{
EditorWindow.GetWindow<TestWindow>();
}
//Worker class instance
private WorkingClass _workingClass;
//Test harness constructor
public TestWindow()
{
this._workingClass = (WorkingClass)ScriptableObject.CreateInstance(typeof(WorkingClass));
string shouldEqual_DATA = _workingClass.DataAccess; //Returns null :-(
}
}
As a side note, while you can't serialize Unity Objects with a Binary Formatter, you can still create serializable object fields in nested classes that contain unity objects, like this.
public class SomeScriptableObject : ScriptableObject
{
[System.Serializable]
public class ExtendedObjectInfo
{
public GameObject obj;
public ObjectType objectType;
public ObjectSubtype objectSubType;
}
}
Be aware though, that you can't change the data of a scriptable object at run time, it's for gameplay data and back-end usage, not localized storage. However BE AWARE any calls to change a scriptable object in editor, even in play mode, will change the scriptable object in the file system and retain the changes outside of play mode.