Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Node Editor Question?

Discussion in 'Scripting' started by Cooper37, Jul 29, 2014.

  1. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Hi all! I'm building a Node editor to help create missions in an open environment, and slowly I'm getting the results I want for this being the first time I extend the editor. I'm just a bit confused on making different types of nodes. In my editor window I can click the Trigger button to create a trigger node and the number of triggers I have will count from 0 and display in the title. I thought I could do the same thing with a new node Null. But it seems that the two nodes are totally dependent on each other and deleting them somehow creates a mess. Does anyone know how I can make these Rect windows independant of each other?
    Code (csharp):
    1.  
    2. class MyEditor extends EditorWindow{
    3. var triggerRect = new List.<Rect>();
    4. var nullRect = new List.<Rect>();
    5.     @MenuItem("Window/Editor %e")
    6.     static function Init(){
    7.         var ink : MyEditor = ScriptableObject.CreateInstance.<MyEditor>();
    8.         ink.Show();
    9.     }
    10.     function OnGUI(){
    11.         Menu();
    12.         for(i = 0; i < triggerRect.Count; i++){
    13.             GUI.color = Color.green;
    14.             triggerRect[i] = GUI.Window(i,triggerRect[i],TriggerWindow, "Trigger " +i);
    15.         }
    16.         for(i = 0; i < nullRect.Count; i++){
    17.             GUI.color = Color.magenta;
    18.             nullRect[i] = GUI.Window(i,nullRect[i],NullWindow, "Null " +i);
    19.         }
    20.     }
    21.     function Menu(){
    22.             GUILayout.Space(30);
    23.             if(GUILayout.Button("Trigger", GUILayout.Width(200), GUILayout.Height(20))){
    24.                 triggerRect.Add(new Rect(200,200,200,120));
    25.             }
    26.             if(GUILayout.Button("Null", GUILayout.Width(200), GUILayout.Height(20))){
    27.                 nullRect.Add(new Rect(200,200,120,120));
    28.             }
    29.     }
    30.     function TriggerWindow(ID : int){
    31.         GUILayout.Space(80);
    32.         if(GUILayout.Button("X", GUILayout.Width(20))){
    33.             triggerRect.RemoveAt(ID);
    34.         }
    35.         GUI.DragWindow();
    36.     }
    37.     function NullWindow(ID : int){
    38.         GUILayout.Space(80);
    39.         if(GUILayout.Button("X", GUILayout.Width(20))){
    40.             nullRect.RemoveAt(ID);
    41.         }
    42.         GUI.DragWindow();
    43.     }
    44. }
    45.  
    Perhaps I need to create an editor script per node type and reference the scripts?
     
    Last edited: Jul 29, 2014
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
  3. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    I knew it had something to do w/ the IDs, but I thought it was the IDs in the function. I changed the nullRect to:
    Code (csharp):
    1.  
    2. nullRect[i] = GUI.Window(i+triggerRect.Count,nullRect[i],NullWindow, "Null " +i);
    3.  
    and it worked perfectly! The only thing is, it doesn't quite work if I want to create a third type of node. So far I have:
    Code (csharp):
    1.  
    2. timerRect[i] = GUI.Window(i+nullRect.Count+triggerRect.Count ,timerRect[i],TimerWindow, "Timer " +i);
    3.  
     
    Last edited: Jul 30, 2014
  4. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    I would expect it to work just like that, assuming all other things are the same
     
  5. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    With everything changed, it still doesn't work for some reason. I decided to try a different approach but I may need a bit more "educating". I'm trying to figure out if there is a way to create nodes represented by empty gameObjects. This is how my Hierarchy is set up:

    +Quest <-This gameobject opens the editor that will hold and edit nodes of its children only
    -Start <-This gameobject is a node that I want shown below, and etc. has "Start" script
    -Obj
    -End

    This way, I can delete the gameObjects, and the nodes will be deleted as well. I thought I could do this with something like extends EditorWindow in the Start script, but the thing is, the Start gameObject has a trigger that'll be activated by the player on enter to start the quest, which can't be done in an editor script. I guess I'm asking a fool's question, is there a way to use an editor script as both an editor and regular gameplay script? How can I make a node from my "Start" script to my "Editor" script?
    Thanks for any help! :)
     
  6. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Regarding your previous post, I tried adding a third set called timer myself, and it worked fine:
    (In c#)
    Code (CSharp):
    1. //Editor/NodeTest.cs
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class NodeTest : EditorWindow {
    8.     List<Rect> triggerRect;
    9.     List<Rect> nullRect;
    10.     List<Rect> timerRect;
    11.  
    12.     [MenuItem( "EDITORS/Node Test" )]
    13.     static void Init () {
    14.         NodeTest ink = ScriptableObject.CreateInstance<NodeTest>();
    15.         ink.Show();
    16.     }
    17.  
    18.     void OnEnable () {
    19.         triggerRect = new List<Rect>();
    20.         nullRect = new List<Rect>();
    21.         timerRect = new List<Rect>();
    22.     }
    23.  
    24.     void OnGUI () {
    25.         Menu();
    26.         BeginWindows();
    27.         for ( int i = 0; i < triggerRect.Count; i++ ) {
    28.             GUI.color = Color.green;
    29.             triggerRect[i] = GUI.Window( i, triggerRect[i], TriggerWindow, "Trigger " + i );
    30.         }
    31.         for ( int i = 0; i < nullRect.Count; i++ ) {
    32.             GUI.color = Color.magenta;
    33.             nullRect[i] = GUI.Window( i + triggerRect.Count, nullRect[i], NullWindow, "Null " + i );
    34.         }
    35.  
    36.         for ( int i = 0; i < timerRect.Count; i++ ) {
    37.             GUI.color = Color.blue;
    38.             timerRect[i] = GUI.Window( i + nullRect.Count + triggerRect.Count, timerRect[i], TimerWindow, "Timer " + i );
    39.         }
    40.         EndWindows();
    41.     }
    42.  
    43.     void Menu () {
    44.         GUILayout.Space( 30 );
    45.         if ( GUILayout.Button( "Trigger", GUILayout.Width( 200 ), GUILayout.Height( 20 ) ) ) {
    46.             triggerRect.Add( new Rect( 200, 200, 200, 120 ) );
    47.         }
    48.         if ( GUILayout.Button( "Null", GUILayout.Width( 200 ), GUILayout.Height( 20 ) ) ) {
    49.             nullRect.Add( new Rect( 200, 200, 120, 120 ) );
    50.         }
    51.         if ( GUILayout.Button( "Timer", GUILayout.Width( 200 ), GUILayout.Height( 20 ) ) ) {
    52.             timerRect.Add( new Rect( 200, 200, 60, 120 ) );
    53.         }
    54.     }
    55.  
    56.     void TriggerWindow ( int ID ) {
    57.         GUILayout.Space( 80 );
    58.         if ( GUILayout.Button( "X", GUILayout.Width( 20 ) ) ) {
    59.             triggerRect.RemoveAt( ID );
    60.         }
    61.         GUI.DragWindow();
    62.     }
    63.  
    64.     void NullWindow ( int ID ) {
    65.         GUILayout.Space( 80 );
    66.         if ( GUILayout.Button( "X", GUILayout.Width( 20 ) ) ) {
    67.             nullRect.RemoveAt( ID - triggerRect.Count );
    68.         }
    69.         GUI.DragWindow();
    70.     }
    71.  
    72.     void TimerWindow ( int ID ) {
    73.         GUILayout.Space( 80 );
    74.         if ( GUILayout.Button( "X", GUILayout.Width( 20 ) ) ) {
    75.             timerRect.RemoveAt( ID - triggerRect.Count - nullRect.Count );
    76.         }
    77.         GUI.DragWindow();
    78.     }
    79. }
    80.  
    While this is fine, it would be simpler if, instead of having seperate lists for each type, you made a class with a selectable type, and just maintain a single list, something like:

    Code (CSharp):
    1. //Editor/NodeTest.cs
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class NodeTest : EditorWindow {
    8.  
    9.     List<MyNode> nodeList;
    10.  
    11.     NodeType selectedType;
    12.  
    13.     enum NodeType { trigger, nul, timer }; //'null' is not allowed
    14.  
    15.     class MyNode {
    16.         public Rect rect;
    17.         public NodeType nodeType;
    18.         public MyNode ( Rect r, NodeType n ) {
    19.             this.rect = r;
    20.             this.nodeType = n;
    21.         }
    22.     }
    23.  
    24.     [MenuItem( "EDITORS/Node Test" )]
    25.     static void Init () {
    26.         NodeTest ink = ScriptableObject.CreateInstance<NodeTest>();
    27.         ink.Show();
    28.     }
    29.  
    30.     void OnEnable () {
    31.         nodeList = new List<MyNode>();
    32.     }
    33.  
    34.     void OnGUI () {
    35.         Menu();
    36.         BeginWindows();
    37.         for ( int i = 0; i < nodeList.Count; i++ ) {
    38.             switch ( nodeList[i].nodeType ) {
    39.                 case NodeType.trigger:
    40.                     GUI.color = Color.green;
    41.                     break;
    42.                 case NodeType.nul:
    43.                     GUI.color = Color.magenta;
    44.                     break;
    45.                 case NodeType.timer:
    46.                     GUI.color = Color.blue;
    47.                     break;
    48.             }
    49.             nodeList[i].rect = GUI.Window( i, nodeList[i].rect, NodeWindow, nodeList[i].nodeType + " " + i );
    50.         }
    51.         EndWindows();
    52.     }
    53.  
    54.     void Menu () {
    55.         GUILayout.Space( 30 );
    56.         selectedType = (NodeType) EditorGUILayout.EnumPopup( selectedType, GUILayout.Width( 200 ), GUILayout.Height( 20 ) );
    57.         if ( GUILayout.Button( "Add node", GUILayout.Width( 200 ), GUILayout.Height( 20 ) ) ) {
    58.             nodeList.Add( new MyNode( new Rect( 200, 200, 200, 120 ), selectedType ) );
    59.         }
    60.     }
    61.  
    62.     void NodeWindow ( int ID ) {
    63.         switch ( nodeList[ID].nodeType ) {
    64.             case NodeType.trigger:
    65.                 GUILayout.Space( 80 );
    66.                 if ( GUILayout.Button( "X", GUILayout.Width( 20 ) ) ) {
    67.                     nodeList.RemoveAt( ID );
    68.                 }
    69.                 break;
    70.             case NodeType.nul:
    71.                 GUILayout.Space( 80 );
    72.                 if ( GUILayout.Button( "X", GUILayout.Width( 20 ) ) ) {
    73.                     nodeList.RemoveAt( ID );
    74.                 }
    75.                 break;
    76.             case NodeType.timer:
    77.                 GUILayout.Space( 80 );
    78.                 if ( GUILayout.Button( "X", GUILayout.Width( 20 ) ) ) {
    79.                     nodeList.RemoveAt( ID );
    80.                 }
    81.                 break;
    82.         }
    83.         GUI.DragWindow();
    84.     }
    85. }
    86.  
    Now you don't have to worry about offsetting the index of each window by the size of each previous collection.

    Regarding you last post, editor scripts cannot function as game components.
    What you can do is write a custom inspector for your trigger component, that adds a button to the inspector, which opens the node editor. When the button is clicked, it checks the objects hierarchy, and creates nodes from it.
     
  7. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Wow, this is great! I'll be sure to build on this all day and update progress. Thank you so much! :)
     
  8. JohnRossitter

    JohnRossitter

    Joined:
    Dec 18, 2013
    Posts:
    1,027
    There was a great example of making node windows here in the forums like a year ago.
    Take a look HERE
     
    rakkarage likes this.
  9. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    lol. Thnx! I've been referencing this this for like two weeks now. :p
     
  10. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Ok. I see how you've done it. It's a great idea to not have to worry about offsetting because it's a pain, but you did it depending on the type selected. I did some "menu rewiring" and decided to use a Generic Menu. Is there a way I can still use switch cases in this instance?
    Editor.js
    Code (csharp):
    1.  
    2. function OnGUI(){
    3.      if(Event.current.button == 1 && Event.current.type == EventType.ContextClick){
    4.           ClassMenu();
    5.      }
    6. }
    7. function ClassMenu(){//The Generic Menu setup
    8.         var menu : GenericMenu = new GenericMenu();
    9.         menu.AddItem(new GUIContent("Setups.../Add Null Set"), false, DoSomething, "null");
    10.         menu.AddItem(new GUIContent("Setups.../Add Trigger Set"), false, DoSomething, "trigger");
    11.      
    12.         menu.AddItem(new GUIContent("Events.../Add Animation Event"), false, DoSomething, "animation");
    13.         menu.AddItem(new GUIContent("Events.../Add Timer Event"), false, DoSomething, "timer");
    14.      
    15.         menu.AddItem(new GUIContent("States/End State"), false, DoSomething, "end");
    16.         menu.AddItem(new GUIContent("States/Start State"), false, DoSomething, "start");
    17.             menu.AddSeparator("");
    18.         menu.AddItem(new GUIContent("Create Embedded Mission"), false, DoSomething, "embed");
    19.             menu.AddSeparator("");
    20.         menu.AddItem(new GUIContent("Exit"),false,Exit, "closed");
    21.             menu.ShowAsContext();
    22.     }
    23.     function DoSomething (obj:Object){
    24.         Debug.Log ("Selected: " + obj);//Tells which menu item has been selected
    25.     }
    26.  
    27.     function Exit(){
    28.         this.Close();
    29.     }
    30.  
     
  11. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Inside DoSomething you can switch obj, once casted to string, it will contain the string you passed from the menu item ("null", "trigger", etc)
     
  12. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    I think I might have the right idea, but it doesn't Debug null with a captial "N". Am I missing something?
    Code (csharp):
    1.  
    2. function ClassMenu(){
    3. var menu : GenericMenu = new GenericMenu();
    4.         menu.AddItem(new GUIContent("Setups.../Add Null Set"), false, MakeNodes, nodes.nul);
    5. }
    6. function MakeNodes (o : Object){
    7.         Debug.Log("Selected: " + o);
    8.             switch(o){
    9.                 case "null":
    10.                     Debug.Log("Null");
    11.                     break;
    12.             }
    13.     }
    14.  
     
  13. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Now youve changed the passed object to 'nodes.nul' which im guessing is an enum
    so your case should also be 'nodes.nul'
     
  14. Cooper37

    Cooper37

    Joined:
    Jul 21, 2012
    Posts:
    383
    Yeah, I still didn't get it, but I guess offsetting it wouldn't be too bad, just a lot of debugging when the list gets long. I did switch the windows, so I won't have to create a new function just to add a window and the window's contents. The switch will just add a window itself, and window contents are taken care of:
    Code (csharp):
    1.  
    2. function ClassMenu(){
    3. var menu : GenericMenu = new GenericMenu();
    4.         menu.AddItem(new GUIContent("Setups.../Add Null Set"), false, Nodes, nodes.nul);
    5.         menu.AddItem(new GUIContent("Setups.../Add Trigger Set"), false, Nodes, nodes.trigger);
    6. }
    7. function Nodes(i : int){
    8.         //Debug.Log("Selected: " + i);
    9.         switch(i){
    10.             case nodes.trigger:
    11.             triggerRect.Add(new Rect(200,200,160,120));
    12.             break;
    13.          
    14.             case nodes.nul:
    15.             nullRect.Add(new Rect(200,200,160,120));
    16.             break;
    17. }
    18.  
    Now, all I have to do is figure out how to connect them and make them work with actual gameObjects. Thank you for all your help! :)
     
    Last edited: Aug 5, 2014