NodeCanvas Forums › General Discussion › [Request] Better editor scripting support
Hi, I would like to request a couple of small features that while not impossible to workaround today, adding them would most likely be a small change and would help me write my editor utilis more cleanly.
1. Make OnValidate fire when the editor values changes – Align it with unity behavior for OnValidate
In the unity scriptable objects and editors the OnValidate is fired when the state of the editor changes, this allows for writing validators or side effects to the editor data, in the Task api OnValidate is only fired on when the task is created/duplicated/initialized
2. Expose OnSceneGui from the GraphOwner (editor) so we can use Handles to create more usable gui editors.
For example, when having a list of vectors on a task we can draw them using OnDrawGizmosSelected but we cannot move them, to do that usage of the HandleAPI is required.
Hello again and sorry for the late reply. Thank you for your suggestions.
1) Right now the “OnValidate” in Tasks is also called from the “OnEnable” callback in the Graph class (which is a ScriptableObject of course). In Graph.cs at line #59, there is also an “OnValidate” callback, but it is unused right now since it just fires at the same time the “OnEnable” is fired (or at least so I’ve seen). You can modify the code there to call “Validate()” in the “OnValidate” unity callback. Is this also where you meant you want Validate to be called?
2) That would be interesting. I have added this on the roadmap 🙂
Thank you.
Join us on Discord: https://discord.gg/97q2Rjh
Hya Glvalakis, thanks for the replay,
I tried calling validate where you suggested
1 2 3 4 |
protected void OnValidate() { Validate(); } |
And then I added a log to my action OnValidate method like so:
1 2 3 4 |
public override void OnValidate(ITaskSystem ownerSystem) { Debug.Log("log"); } |
This does not seem to work, did I misunderstood?
Hello again. Yes this is what I meant. The OnValidate is the only callback that Unity provides for Validation on scriptable objects (isn’t that what you originally meant as well?), but as I mentioned above, this callback is not fired when the editor values change as you’d expect. Unfortunately though this is the only callback that Unity gives us nonetheless.
I probably misunderstood your suggestion though. Do you mean that you would like task OnValidate to be called when that task’s inspector values change in the NodeCanvas editor? 🙂
Let me know.
Join us on Discord: https://discord.gg/97q2Rjh
Yes, I would like some way to run code when the inspector values change (validate them)
This is odd as from my experience the native onValidate does trigger on editor value changes.
Actually a quick check in the unity docs verifies that https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnValidate.html
First line states it should fire when inspector values change.
I can only assume that the task inspector is somehow “disconnected” from the main graph
Yes the task inspector use a custom made inspector UI and the graphs are also serialized with a custom serialization system instead of Unity native, and this is why OnValidate is not fired like it would. However adding your suggestion is easy. To do this now, please open up TaskEditor.cs and add this line of code just after line #169.
1 2 3 |
if ( GUI.changed ) { task.OnValidate(task.ownerSystem); } |
Here is the code nearby to get a full view where to add it (its the last line here)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
..... if ( !showTitlebar || ShowTitlebar(callback) == true ) { if ( !string.IsNullOrEmpty(task.description) ) { EditorGUILayout.HelpBox(task.description, MessageType.None); } UndoUtility.CheckUndo(task.ownerSystem.contextObject, "Task Inspector"); SpecialCaseInspector(); ShowAgentField(); onTaskInspectorGUI.Invoke(); UndoUtility.CheckDirty(task.ownerSystem.contextObject); --->>> if ( GUI.changed ) { task.OnValidate(task.ownerSystem); } <<<--- } ..... |
Let me know if that works for you.
Join us on Discord: https://discord.gg/97q2Rjh
Works great, thanks Gavalakis, any chance this comes bundled in the next version (I dont want to remember adding it everytime I upgrade)
BTW, I was able to make OnSceneGUI Work by adding these changes, dont know if this is the best way to do that with the graph structure, let me know if you think it’s fine and if you can add it
GraphOwner class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
protected void Start() { if ( firstActivation == FirstActivation.OnStart ) { if ( !isRunning && enableAction == EnableAction.EnableBehaviour ) { StartBehaviour(); } } InvokeStartEvent(); startCalled = true; // Not sure if this is the right place to do this, but it works for now #if UNITY_EDITOR SceneView.duringSceneGui += OnSceneGUISelected; #endif } protected void OnDestroy() { if ( ParadoxNotion.Services.Threader.applicationIsPlaying ) { StopBehaviour(); } foreach ( var instanceGraph in instances.Values ) { foreach ( var subGraph in instanceGraph.GetAllInstancedNestedGraphs() ) { Destroy(subGraph); } Destroy(instanceGraph); } #if UNITY_EDITOR SceneView.duringSceneGui -= OnSceneGUISelected; #endif } ///<summary>Forward Scene GUI callback</summary> void OnSceneGUISelected(SceneView view) { if ( Editor.GraphEditorUtility.activeElement != null ) { var rootElement = Editor.GraphEditorUtility.activeElement.graph.GetFlatMetaGraph().FindReferenceElement(Editor.GraphEditorUtility.activeElement); if ( rootElement != null ) { foreach ( var task in rootElement.GetAllChildrenReferencesOfType<Task>() ) { task.OnSceneGUISelected(); } } } } |
Task class
1 2 3 |
virtual public void OnSceneGUISelected() { } |
Hello again,
Yes, that would be included in the next version.
Your OnSceneGUI implementation is fine, however it can be implemented in a way so that the OnSceneGUISelected is also called in Edit-time (I presume it is better than only be called in play-mode). I will look into this.
Join us on Discord: https://discord.gg/97q2Rjh
Yes, That would be better but I could not find a good place to insert the event registration, Thanks!