- Home /
Calling OnValidate on a ScriptableObject with a list of ScriptableObjects
I have created a Container ScriptableObject which has a list of instantiated BaseNode ScriptableObjects, and a custom editor to display all their attributes in the inspector.
My Inspector looks like this:
In this example, I display an integer from my container class, as well as an integer from each of the nodes in the list.
I would like to run some code when validating my Container inspector attributes. However, unlike when I have a list of pure C# objects, when I modify a field from the BaseNode list (ex. "nodeAttribute" from Node 0), OnValidate is not called in my Container object. Instead, it is only called in my BaseNode object.
Is there a way to make it such that, when I change a field from a Node, my Container.OnValidate() is called?
Note that when I modify the "containerAttribute" field, the OnValidate is called as expected.
BaseNode.cs:
public class BaseNode : ScriptableObject
{
[SerializeField] int nodeAttribute;
void OnValidate()
{
Debug.Log("Node On Validate");
}
}
Container.cs:
public class Container : ScriptableObject
{
[SerializeField] int containerAttribute;
[HideInInspector] public List<BaseNode> Nodes;
private void OnEnable()
{
Nodes = new List<BaseNode>();
AddNew(typeof(BaseNode));
AddNew(typeof(BaseNode));
AddNew(typeof(BaseNode));
}
// Instantiates a new ScriptableObject and adds it to the list
public void AddNew(Type assetType)
{
BaseNode newAsset = (BaseNode)CreateInstance(assetType);
AssetDatabase.AddObjectToAsset(newAsset, this);
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(newAsset));
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Nodes.Add(newAsset);
}
void OnValidate()
{
Debug.Log("Container On Validate");
}
}
ContainerEditor.cs:
public class ContainerEditor : Editor
{
Dictionary<ScriptableObject, Editor> editor;
private void OnEnable()
{
editor = new Dictionary<ScriptableObject, Editor>();
}
public override void OnInspectorGUI()
{
//Serialize other attributes
base.OnInspectorGUI();
// Get the instance that is using the editor
Container container = (Container)target;
serializedObject.Update();
// Create a sub-inspector for each Scriptable Object
for (int i = 0; i < container.Nodes.Count; i++)
{
EditorGUILayout.Space();
GUILayout.Label("Node " + i+":");
ScriptableObject node = container.Nodes[i];
if (!editor.ContainsKey(node))
editor.Add(node, Editor.CreateEditor(container.Nodes[i]));
editor[node].OnInspectorGUI();
}
serializedObject.ApplyModifiedProperties();
}
}
Answer by Meishin · Aug 17, 2019 at 11:43 AM
Hi @caykroyd,
I like your coding style :D
i haven't tested it but did you try classic Events / Delegates ?
For example with delegates ;
in BaseNode.cs, define delegate
public delegate void OnValidateEvent();
public event OnValidateEvent onValidateEvent;
then in the OnValidate function of the BaseNode, call delegate methods
if(onValidateEvent != null)
onValidateEvent.invoke();
Create and put whatever you want in a container onValidateEvent function, or event call OnValidate function directly
private void onValidateEvent(){
target.SendMessage("OnValidate", null, SendMessageOptions.DontRequireReceiver);}
and finally register method when creating a new node
newAsset.onValidateEvent += this.onValidateEvent();
Your answer
Follow this Question
Related Questions
[Custom Editor] MonoBehaviour vs Scriptable Object 0 Answers
"Unbroken Reference" problem when using a custom Editor to Save/Load a ScriptableObject Asset 0 Answers
Creating a Scriptable Object Custom Editor Footer 0 Answers
ScriptableObject Custom Asset Error on Build and First Starting Unity 0 Answers
CustomEditor for an ScriptableObject asset only works after recompile. 1 Answer