Search Unity

Simple node editor

Discussion in 'Immediate Mode GUI (IMGUI)' started by unimechanic, Jul 5, 2013.

  1. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Glad you like it:)
    I'll write a more detailed summary of the current plan if you want, it's is more generic and geared towards integration with the current system, including mixes, but can be used as statemachine solution only just fine:) I'm glad you're willing to help;)
     
  2. CommunityUS

    CommunityUS

    Joined:
    Sep 2, 2011
    Posts:
    203
    I think for now I just need to play with it and maybe start on a small task or something that interests me or need for my project. (easiest way to stay motivated)

    one thing that may help is an example of how to write an action/behavior state from scratch. if i understand the to do list/road map correctly, the state machine is there visually but won't actually control anything? I believe that is what is meant by "actual transitioning behavior" not being done. I was a little confused by in one hand saying it is "visually setup" but the "GUI and methodics" were not, I think this means you have a place holder basically?

    I think I will take a look at NodeEditor.cs/Calculation.cs? Transition.cs and Node.cs and see if I can make some progress here. I've never played the offical Jr. programmer role (all self taught, and freelance) so it would be good for me to try to submit to the process a little. But no promises lol. If I think I can commit to it I will let you know. Otherwise I may focus on something easier first just to become familiar with working in this way.
     
    Seneral likes this.
  3. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Well I just noticed, The behaviour is already somewhat implemented at NodeEditor.cs/Calculation region, but isn't executed as of now (WaitForTransitions needs to be called per frame) With GUI I mean selecting the transition like in mecanim and editing its conditions and other properties, aswell as visualizing the current node and transition state...
    That's overrally what's still missing plus some extra...
     
  4. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Ok guys I've got a new hacky idea!;) this time I'll not only hack the grouping system, but the layouting system! This will enable us to read, remove and restore or even manipulate the layouting entries! Just have to take care that we do not disorder them... What is this important for? Well, we can easily calculate the current layouting stack's height, for example, which would allow for dynamic node rect sizes!! :D
    Key types are: GUILayoutUtility.current [GUILayoutUtility.LayoutCache]; GUILayoutGroup; GUILayoutEntry
     
  5. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    On my side, i'm currently working to integrate the function system and i'm doing fine. I have some questions. Currently i'm using the code to detect which function from which class to display. Is there a way to do the same thing Unity does and add a field that let the user drag a script Inside it ? (and catch it in code)
    Also, with the Node Editor, Visual Studio display me 3 solutions, if i develop for example a class AIMethods in the main solution, i'm not able to track it Inside the plugin folder, is there a way to get it ?
     
  6. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Sorry for the late reply. I'd approach it a bit differently, I'd not let the user specify the script, it's a hassle, because in a script may be multiple classes, and you may even want to call functions inside a library. I'd give the user a dropdown of types, then members of this type. If the member is non static, open up a field/input to specify the instance. Of course type/Member fetching means reflection, maybe, when first adding such an action node, you could build a static 'library' containing just the names and extra meta data for each type. Members can then be fetched when a type is selected.

    Regarding your second problem, I assume you mean that you can't access types from inside the plugin folder? YOu could of course relocate the NodeEditor... But I'd rather try a different way. Can you move the other script inside the plugins folder? You can read about the load orders here.
     
  7. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Thanks for the answer.
    I'm having trouble understanding what you would do.
    You want to display types (not the class i suppose) so for example in a dropdown bool, int, etc... And when the user pick it, display all functions that are - for example - in the namespace Library. I'm also having trouble to think about how to specify an instance from the editor, what do you do if the object that is concerned by the node that are you creating is an object that spawn during runtime ?
    I think it would be better to display only statics function, and if you need an instance, but it in the parameter and pass it when the concerned object will call it.
    I may have a different vision than you with the use of it, i'm gonna use it only for designing AI and assigning an AI to specify monster. Maybe you have a different idea on how to use it ?

    About my second problem is that if i have this scenario :
    - Solution
    - Project 1 (game object codes, graphics, prefab, etc... )
    - Project 2 (the plugins solution given by the Node Framework)
    If, for example, i have an IAMethods in the Project 1, i can't access it in the project 2 and vice versa. So i have to duplicate the class in both project which is pretty dirty :<
    I'm gonna take a look at your link and update my post regarding if it fit or not my demand.
     
  8. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    The node would have to expose each object that is involved in the action both as a field for direct assignments and as an input, just like I do with the calculation nodes (if the input is not connected, a float field shows instead). With that you could assign the instance at runtime using another node, maybe even another action node. But static functions would suffice for the start I guess, along as we could extend it when needed.

    I'm working on the State system currently, finishing everything up. The behaviour is mostly implemented, including transition times and conditions. I plan to make that system extendable aswell so you could for example extend it with an AnimationTransition for a anim tree, which handles everything regarding animation state, or a KIStateTransition for a KI system and similar.

    What do you plan to use the second class/the extension for? You might want to consider moving Node Editor out to a seperate folder which should work, but you might want to consider that when sharing you work on Node Editor with us:)
     
  9. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Gosh, i finally made it ! I've succesfully understand the code and customize it my own way. To be fair and honest with you, the base code really need a clean up on some part. Adding connections type, node are fairly easy, but when you start going behind those need it complicated. I wanted to create custome Knob (because i needed it !) and i realised that some part are (sorry :<) badly coded. For example, i had made custom Knob fairly easly, called them inside my Node but i wasn't able to see them inside the Node Editor ! I spend some times and i finally found the reason, Inside the Node class, you have this function :

    Code (CSharp):
    1. public void DrawKnobs ()
    2.         {
    3.             for (int outCnt = 0; outCnt < Outputs.Count; outCnt++)
    4.             {
    5.                 NodeOutput output = Outputs[outCnt];
    6.                 Rect knobRect = output.GetGUIKnob ();
    7.                 GUI.DrawTexture (knobRect, output.knobTexture);
    8.             }
    9.             for (int inCnt = 0; inCnt < Inputs.Count; inCnt++)
    10.             {
    11.                 NodeInput input = Inputs[inCnt];
    12.                 Rect knobRect = input.GetGUIKnob ();
    13.                 GUI.DrawTexture (knobRect, input.knobTexture);
    14.             }
    15.         }
    and excuse me but i find it pretty bad that to render a Knob i have to implement this function with mine... Why didn't you do a main classes (for example... NodeKnob ?) and create List of this class ? Rendering would have been way more easier !
    And this kind of problem is present almost everywhere, i had trouble making connection between those 2 knob, etc...
    For the moment my functions are starting to look like something (see the screenshot below) and i think i have succeded in the hardest part. It will be, i think available next week !



    Don't take it personnaly Seneral, i think we have to do a lot of work with code optimization but the base is awesome !
     
  10. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Don't worry, I gladly accept suggestions to improve the Framework code!
    But, for that specific example, the NodeKnobs, how exactly would you do it instead? There needs to be a List of the Knobs somewhere (Node.Outputs/Node.Inputs), and a function that draws them (Node.DrawKnobs). Of course the functuality of this function could be partially shifted to the NodeKnob class itself (which does exist and serves as a base class for knobs, so I don't understand your note above), but I really don't understand your problem there. Indeed, there a multiple things like these where specific features depend on other features (or simply functions) in other places, but that's not uncommon in a framework;)
    Anyway, if you could explain any further how I could improve it, I'd gladly help you:) Of course, if you couldn't figure out why it does not get drawn, and there's no functuality like that directly in the class, maybe right click the class and let MonoDevelop list all references... This might have helped you with your problem;)
    On a side note, why would you wan't you own NodeKnob?
     
    Last edited: Jan 30, 2016
  11. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    I needed a custom NodeKnob because i needed Input nodes to have multiple connection. Your input node make us able to connect to multiple output but an output could have only one connection. To prevent having the same function shown multiple times on the node, i needed to have this connection.

    What we should do is this :
    > In the Node class, we have this :
    Code (csharp):
    1.  
    2. // Calculation graph
    3.         public List<NodeInput> Inputs = new List<NodeInput>();
    4.         public List<NodeOutput> Outputs = new List<NodeOutput>();
    5.  
    This, in my opinion, is bad. Because you have a list for each Node, for example, when you want to add a connection, this is what you do :

    Code (csharp):
    1.  
    2. public static void ApplyConnection (NodeOutput output, NodeInput input)
    3.  
    And so on (remove, ....)

    What we should do is that :

    Code (csharp):
    1.  
    2.        public List<NodeKnob> allKnobs = new List<NodeKnob>();
    3.        [....] => on a knob creation :
    4.        public static NodeInput Create(....) {
    5.               ....
    6.               NodeBody.allKnobs.Add(input);
    7.         }
    8.  
    What is the current problem is that for Knob we have to pretty write the same code for each new addition. You want to add a special Knob ? Do the same thing that we already did for the other but not in a generic way !
    I feel like the Knob classes need to be rethink in a more generic way, put everything that is needed inside the NodeKnob, make it not abstract (maybe ?) otherwise we wont be able to do the new List<NodeKnob>.
    For drawing, meh, it's kinda the same thing, for the two basics render you do the same thing ! Maybe if you want to be more specific, put a basic drawing method in the NodeKnob class, make it virtual so that if you want to custom it you can and in the rendering method do this :

    Code (csharp):
    1.  
    2.  
    3. // old
    4. public void DrawKnobs ()
    5.         {
    6.             for (int outCnt = 0; outCnt < Outputs.Count; outCnt++)
    7.             {
    8.                 NodeOutput output = Outputs[outCnt];
    9.                 Rect knobRect = output.GetGUIKnob ();
    10.                 GUI.DrawTexture (knobRect, output.knobTexture);
    11.             }
    12.             for (int inCnt = 0; inCnt < Inputs.Count; inCnt++)
    13.             {
    14.                 NodeInput input = Inputs[inCnt];
    15.                 Rect knobRect = input.GetGUIKnob ();
    16.                 GUI.DrawTexture (knobRect, input.knobTexture);
    17.             }
    18.         }
    19.  
    20. // new
    21.  
    22. public void DrawKnobs ()
    23.         {
    24.             for (int i = 0; i < allKnobs.Count; i++)
    25.             {
    26.                 NodeKnob node = allKnobs[i];
    27.                 Rect knobRect = node.GetGUIKnob ();
    28.                 GUI.DrawTexture (knobRect, node.knobTexture);
    29.             }
    30.         }
    31.  
    32.  
    I hope i was a bit more clear, i'll try to make the code a bit more clearer. Maybe i have not read all the code and what i say is impossible, but i'm pretty sure we can and it's the way we should go if we want the framework to be easier on this part.
     
    nicloay likes this.
  12. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Some great points you have there, but also some 'mistakes' in design that I do not like... I'll explain:)
    I agree with you about the changes to the NodeKnob creation and drawing, some redesigning should be made. But, I really hate the idea of having one list of all knobs, indifferentiated between Input and Output, because that would be very unintuitive when you actually want to access them from outside besides of drawing...
    I was already thinking of redesigning the way NodeKnobs behave or even what they are. Ideally they are arbitrary things to add to a node - currently the class really is just shared members and functuality of NodeInput and NodeOutput, but very strict and not very cusomizeable. There is an 'issue' on the repo complaining about this, and was there for about a month or two now - but I kept this in mind all the time and as soon as I finish the Statemachine system and eventually fixed the nested scaling problem, I'll take a look at that.
    Planned was alot more customization, not only a way to overwrite drawing, but also to allow multiple connections (you mentioned this before, didn't you?). Of course you can do this right now, as far as I remember it's marked as open on the tasksheet, but with relatively low priority...
     
  13. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    If you don't like the idea of having just a list, maybe we can do something like this :

    Code (csharp):
    1.  
    2.  
    3. //my old idea
    4. public List<NodeKnob> allKnobs = ...
    5.  
    6. //my new idea
    7. public Dictionnary<string, List<NodeKnob>> allKnobs = new Dictionary....
    8.  
    9. // adding will be like that
    10. public void AddKnob(string knobType, NodeKnob knob) {
    11.      if(!this.allKnobs.containsKey(knobType))
    12.          this.allKnobs.add(knobType, new List<NodeKnob>());
    13.     this.allKnobs[knobType].Add(knob):
    14. }
    15.  
    16.  
    With this, you can separate your differents Node types, but still be able to access to all off them with one variable. Maybe we can even add a constant string to the class to define the Node name that will be use in the dictionary.
    Regarding multiple connection, i don't exactly know what you want to do, but for my personnal use i needed the reverse of what was currently available. Maybe we should think of a way to mix those two ?
    I currently am in the same state of you, i plan to finish the function nodes and will go on polishing the code, maybe we could do it together ?
     
  14. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    That would be a very generic way, indeed, but there's an extra layer of complexity that maybe is unnecessary...
    I wouldn't think of NodeKnobs the same as of, for example, ConnectionTypes - you usually shouldn't need to extend them, better is to make the existing Input and OutputKnobs as flexible as possible, but indeed also extendable.
    Maybe another approach would be to have three Lists, the first two being Inputs/Outputs for Calculating as they already exist and the other being all NodeKnobs in general for drawing, holding arbitrary knobs for different use cases on the node hull.
    That may seem even less confident at first, but I actually prefer it over previous approaches.

    Regarding making NodeInput/NodeOutput flexible, at best every property of them can be changed....But this could also cause issues. For example, if you would add multiple inputs connections, nodes like calculation node would suddenly don't know which connection to use anymore. For what would you use it, if I may ask? Maybe you could just add another input when the first one gets connected, if that's a solution for you....
     
  15. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    I understand your point of view, but what about Knobs that are literraly different than Input and Output knob ? What if i want to do, i don't know but something like an Knob that add a sub knob to the current one ? Or a Knob that add a parameter to the current Node. Ok, we could just do a button inside the node to do that, but hey, we could do the input and the output the same way....

    I'm using for what i said upper, for the function use, with the current Input/Output, the relation is n => 1, and Input can have multiple connection but the output have only one. What i needed with Functions is the ability to connect a function to multiple output, so i can say for example that 2 exit of a function can call the same function (as shown as the picture upper).
     
  16. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    I really don't understand why you need this n => 2 relation.... A function has both inputs (object to call on, parameters) and outputs (return type, ref/out parameters, etc.). Those can each have normal Input/OutputKnobs respectively... and those connections should behave just like the existing ones (you can only specify one value for each parameter, but you can use the output value on multiple following nodes)... Or do you have something different in mind?

    Btw implementing knobs that have 'sub knobs', or collapsed nodes, or any other kind of button or texture, would perfectly fit into the model I proposed previously - but Inputs/Outputs do have their own list for ease of use...
     
  17. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    To be fair i'm trying to find reason, i've already writted multiple example and removed them because i couldn't find the reason to use multiple inputs... Maybe i did not think enough to the purpose of it but my aim was to prevent the call of a same function in two different node. If you have for example the function "isThereAnyNearby" with a parameter containing a range, you need this function to appear once in the Node Editor. Is two other function need to call it, you need to be able to only display this node once and not twice. Otherwise the Node Editor will be a mess of redundance.

    I will be experiencing some ideas and i'll be on touch with you on how it grows.
     
  18. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Great:) But I think, taking your example, it's the user's fault if they call this function twice unnessessarily, and adding this as a feature to the Action Node would just cause confusion;)
     
  19. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Hello Seneral,

    I don't know how you are doing on your side about the Action Node but i hope you are progressing well ! Regarding the functions Node, i'm almost done (i said last time that i was planned to be over last week but didn't had the time to finish it). I have almost finished the Node and it will lead me to the runtime. I have some idea on how i will handle that but they are still idea. I'm pretty satisfied with what i have currently done and it fit the needs that i had at the begining. I hope i'll be able to show them to you asap, but this week will be pretty long for me but i'm nearly sure that next week it will be over !
     
  20. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    No problem, cool you're doing so well! :)
    Did some changes regarding NodeKnobs (which are arbitrary textured rects on the node you can extend now), did an overhaul on the 'Cache' system which is now completely seamless (when switching scenes or on assembly reload for example, the system does reload the canvas but you won't notice:) ) and progressing on the State system. Just finished an internship so I was a bit busy but I've some time now.
    I have a strange glitch regarding focusing only on state nodes connected with transitions so I'm a bit confused xD
     
  21. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Great, i can't wait to see the new cache system (the current one reload each time and you have to wait some second to be sure that it's up to date), it will be a blessing !
    Good luck for the State, if i can give you any help, don't hesitate.
     
  22. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Then I'm very sorry to tell you that the new one has similar, yet different problems.... xD
    The general problem is, you have to create the new asset for it to save correctly and then save the assets, which will not only write the new asset but the whole save file over again to the disk. This has a certain overhead which increases with canvas complexity, but has to be called so data loss is impossible:(

    Previously, I worked with a copy and at some points created a backup (not into the specified savefile, but a temp file). These backup points were as simple as Load/Save/Close and PlaymodeStateChanged. Scene switching did not work as there was no way to save before, so on that occcasion the canvas would be reverted to it's previous save's state:(

    Now, it's directly working on this copy (a link persists between the working canvas and the temporary save file). That means modifying existing stuff is instant and gets saved instantly. BUT, new assets like Node, each NodeKnob, Transition, etc. has to be saved explixitly when created. So EVERYTHING is save, even when crashing totally.

    It's pretty complex, so here's a summary of the options to approach this:
    Option 1 (previously used): Create backups at certain points or after certain times (takes time), potential loss of data when crashing between these saves
    Option 2 (new Implementation): Keep link to temporary file, create new assets write them to disk when adding new Objects (takes time only when creating), NO loss of data possible

    1 has potential of data loss (crash, scene switch), but load times only exist sometimes
    2 Is totally secure, but there are wait times (100-500ms :() when creating stuff.
    In both methods, the wait times increase the more complex a canvas gets

    -----------------------------------

    Regarding the state system, I just basically finished it:) But I have no way of serializing the conditions of a transition:(
    It currently uses Func<Transition, bool> which cannot be serialized easily. I've found UnityEvent and UnityAction pretty useful and used it, but there's for some reason no equivalent to func (UnityFunc); Basically a UnityAction returning something. I'm currently exploring my options:) Any hint appreciated:)
     
  23. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    To be fair with you, i don't exactly know how Asset file works in Unity and i'm still wondering how you can use them. I'll try to fetch the code a bit, but it seems that you are saving each Node instance to the .asset file. Can i ask why you choosed that instead of -for example- XML or JSON ?
    Regarding your 2 options, if i understand correctly, on one side you save on an event and on the other side you save each N seconds. Why not let the save action to the button "save" ? When you are doing paint, photoshop, etc... there is no constant save or every time you put down your pen on the screen it's instantly saved, you have to manually save your file. Why not going in this direction ? (i'm sure it's hella usefull because when there is a crash, it's saved !)
    I'm not in that state regarding the function for the Action. I think i may have something but i don't have it here at work, i'll try to look at it when i'm back home, maybe it'll help you.
     
  24. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Well, the current system I was talking about is just auto save / backup system, it does not override your save file. This is necessary to account for playmode changes or scene switches, unfortunately. Regarding the save format, I chose it because it's easy to edit from the inspector. ScriptableObjects are the first choice to store stuff in the editor, but as you may suggest, they are editor only. So I'm planning a second save format in XML (for runtime, similar to substance's format).
     
  25. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Ok I think I got my head wrapped around now how UnityEvent works, especially regarding the serialization part.
    A quick sum up (You won't be able to follow this per se, I recommend to open the AssemblyBrowser at UnityEngine.Events (non pulics included, C# language) if you're interested!):
    UnityEventBase contains a InvokableCallList that is the list of all subscribers of type InvokableCall, obviously. This cannot be serialized by default, so there are PersistentCalls, summed up in a PersistentCallGroup. Those are serializeable and at load time, they turn their serialized data of the delegate into InvokableCalls so they can be added into the InvokableCallList of an UnityEvent.

    So how does this work, this deserialization?
    PersistentCall has all the call data like target object, method name, arguments, etc. Those are necessary to turn this PersistenCall into an InvokableCall.
    This conversion happens in the static function GetRuntimeCall, which returns the BaseInvokableCall it representates in context of the Event passed. Core is here the call event.FindMethod (this), which returns the MethodInfo of the call. Tracing this you'll find that the method searches in the 'type tree' of the target object for a method with the stored method name and arguments, through reflection. Then, back in the GetRuntimeCall, an ordinary delegate get's created from this MethodInfo.

    But where does this get called and how is it integrated?
    This happens in PersistentCallGroup, which stores all the PersistentCalls for an UnityEvent. There you'll find a method Initialize, which writes all of it's PersistentCalls through GetRuntimeCall into a passed InvokableCallList.
    Voila, calling this at runtime returns a InvokableCallList from a PersistentCallList.

    I plan on using the same principle of something like 'UnityFunc'. Unfortunately, I'll need to rewrite everything, because the base principle is different with a return type. I'd have to use reflection anyway...
    Also, this UnityFunc would not have all fancys of UnityEvent, like the GUI, without further modifications. I might also take a look at that. This has high priority though as it is mandatory for the State System...
     
  26. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    I found my code back that i'll plan to use during the runtime for the function caller, tell me if it is any help :

    Code (CSharp):
    1. namespace TestProject {
    2.     class Program {
    3.         static void Main(string[] args) {
    4.             AIMethods.load();
    5.             var res = AIMethods.actionLists["sayHelloName"].DynamicInvoke("toto");
    6.             Console.WriteLine(res);
    7.             Console.ReadKey();
    8.         }
    9.     }
    10.  
    11.     public class AIMethods {
    12.  
    13.         public static Dictionary<object, Delegate> actionLists = new Dictionary<object, Delegate>();
    14.  
    15.         public static void load() {
    16.             actionLists.Add("sayHello", new Func<string>(sayHello));
    17.             actionLists.Add("sayHelloName", new Func<string, string>(sayHelloName));
    18.         }
    19.  
    20.         private static string sayHello() {
    21.             return "Hello";
    22.         }
    23.  
    24.         private static string sayHelloName(string name) {
    25.             return "Hello " + name;
    26.         }
    27.  
    28.     }
    29. }
    30.  
     
  27. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Well this does not handle serialization of delegated:(
    But I got this. No problem considering the past problems I had with Unity so far:cool:
     
  28. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    The more I dig into this I realize we're doing pretty much the same thing. To explain, I'm working on a serializeable UnityFunc, a way to add the C# Func behaviour to a serializable object in Unity. I think that'd be best used for the Action Node you're working on, right? My next thing would be to do the GUI and method picking. Before I start though I want to ask how you're coming along, incase you already implemented this...
     
  29. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    I didn't had the time to keep the work but basicly i have one thing left to do with the GUI which will not take me a long time. If you want, i can give what i have done and you can use it and help me extend it/make it better. It's not "clean" yet and still has to be documented. Also i think i will remove the Multiple Input/Output that i plan to use because it's non sense and will not be used in my case i think.
    Regarding method picking i'm done, parameters i'm done and result too. I just need to connect an Node with a Type T to a node of a Type Q.
    We can also maybe try to merge what we both did to make it better.
    I also have normally done the runtime part but i need to connect it to the save.
     
    Last edited: Feb 11, 2016
  30. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Yes, please send it. Don't wan't to do duplicate work, do we?
    I'll going to attach my current work (not yet, doing some clean up). State as of now is a (hopefully, not tested yet) serializeable Func (UnityFunc). Missing yet is GUI, I would take a look at UnityEditorInternal.UnityEventDrawer which is the property drawer for the nice UnityEvent GUI used for Button clicks for example. I guess that's the thing you mentioned about GUI, especially the Function selection on the object, right?
    So we already did duplicate work, best to work together as of now. My solution is generic, I actually needed it for the transitioning conditions ofd the new state system. Without them being serializeable the canvas would not be saved nor loaded correctly.
     
  31. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Hi, in the attachement, the folder "Plugins", i haven't touched to the other so...
    Here a list of what i have done, i think they might some other changed but i'm not so sure. You will probably find some comments (in french :p), i didn't cleaned it that much, sorry :<

    ConnectionTypes => added multiple types (all primitive)
    Node => added multiple outputs/inputs, update render to render those 2 fields, update draw connection for the 2 fields, updated : delete,
    added : getmultipleoutputatpos, getmultipleinputatpos, canapplyfunctionconnection, applyfunctionconnectio, removefunctionconnection
    Node Editor => i have put in the "checkInit" function the code to catch all the functions needed, not sure it was the correct place, i needed to find where was the call when the editor open and it seems it was this one. I also added a static variable containing a list of function (see class Function). I also updated the Input Events to catch the Node multiput and output to be detected.
    Nothing done to NodeEditorCallbackReceiver, NodeEditorGUI, NodeKnob, NodeTypes
    Added IA folder => my intention was to put here all the functions that will be detected by the Editor and be displayed on the Function Node. Maybe there is some work to do with that. For example create a Folder called "Functions" and all the class will extend a class and we will do something like you did with the class NodeTypes, fetch all the class that extend this and display them.
    Nodes => Added FunctionNode, read it if you want to understand what's done.
    Haven't touched to the others.
    Utilites => Added :
    Function : Represent a function
    FunctionResponse : Represent the different response that a function can propose.
    RTEditorGUI : I have pretty much changed everything here (or at least, i leaved what was done but i use my thing now). I have done a "kinda" generic field displayer. Read it, it's the functions : DisplayField and convertTo

    There is a lot of improvements to do. For example save doesn't really work... The input node render (and i'm not rendering it in the code ??) and it's badly placed. Here is what it currently look like :

    http://i.imgur.com/nSG9rio.png

    Basicly on the top there is the function that will be call, beneath are the parameters. You have multiple choice "Defined as, From Previous" and for some "From Scene" => defined as let you put a string that will be converted into the type of the parameter. For the moment it work only with primitive type. You'll need to implement the other one (inside the "Display Field" in the class RTEditorGUI). "From Previous" is displayed only if the result of the connected node is the same type as the current selected parameter, it will get the previous node function "response" and will use it in the current function. "From scene" is yet to be implemented but will be easy to. Under the parameters there is the "Add Result", that will let you add possible response to your function and then you will draw the road depending to the results. You have the possibility to choose a condition (<, <=, >, .... and default if none of the previous response are valid). Then you can draw the connections !

    It think that enough, if you have any question, any update tell me ! I'm not a pro using your framework and i have probably done a lot of mistakes... I'll listen to your comments and be happy to help you update this :)

    Have a good day
     

    Attached Files:

  32. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Thanks, I'll take a look at that. Alot of changes, I'll probably generate a diff to see what you've done:) That'll be alot easier. (EDIT: Cannot create a diff, you were using and old framework version... :( ) I'm not sure if I understood your approach correctly yet, but it seems pretty complicated for the matter of creating functions.
    You made a folder which contains custom functions which you want to fetch, and those will be displayed in the FunctionNode, then you store them in a static List in NodeEditor. That's what I understood so far...
    If that's correct, there are a few 'flaws' I'd call it with this approach.
    1) You have to create an extra script containing functions to register them... Extra layer of complexity I think. Why not fetch ALL functions on a selected object, like with the UnityEvent (Have you ever used one? Amazingly powerful. You can select and Object or Asset and call any function on it:) ).
    2) They're stored as statics independant of the active NodeCanvas. That means they're not serialized (don't blame you on that this is really annoying and hard to do) but they're also not saved with the canvas. Easy fdix when I've done the UnityFunc though. It handles serialization and enables to store the function with the canvas.
    I might have understood something wrong though:) I'll check back!
     
    Last edited: Feb 11, 2016
  33. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    You have understood what i wanted to do so far !

    1) I'll assume that when you talk about "object", you mean "gameobject" or "object in the scene". I was planning to create IA outside of runtime, so i need a class containing a bunch of functions (like a library) for example : isThereEnemyClose, canIAttack, canIMove, etc... I don't see the point of using a instantied object since the only thing i will do is call function from a library and use the result to determine a path. If i want to call a function from a GameObject in the scene ? Well i'll try to do my best in the function to catch the object i need and call the function. I have never used UnityEvent but you seem to use them a lot so i'll dig a bit in the documentation to see what it is !
    2) The only thing the static field is used for is to display the function Names inside the popup list. I think i'll save on the function node the name of the currently selected function (which will help me to save it) so when the node is load it automaticly pick the correct one on the list.

    Give me your opinion :)
     
  34. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    This might indeed be pretty useful for you, but I did not know your use case:) I was planning on doing it generic so we could implement it in Node Editor by default. I hope you understand that your use case will not necessarily match others...

    And Indeed I misunderstood the part with storing the functions as statics, and this is completely ok for your use case of fetching functions manually. But yeah, they are not generally useable. Sorry if that means your contribution does not help the project directly, I don't know if you want to do anything about it or not... After I've created the UnityFunc completely the other approach would be implemented in like 30 mins. Just because it's way more generic (we need it for other use cases either way, so it'd be best to use it).

    And with objects I mean not only scene objects (they'd be problematic either way because the canvas is an asset and as such cannot reference scene objects directly), but for example scripts to acess static members, assets (in the form of scriptable objects or prefabs) you can call functions on, including your use case, etc. It has many advantages to others but no disadvantage to you;)

    Btw only recently discovered UnityEvent through this thread (highly recommended to read!!!) but they're amazing. Didn't used it at all yet because I don't use the new UI system, but I took a look at it during developement of the UnityFunc very closely;)
     
    Last edited: Feb 11, 2016
  35. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Ok while I was searching for an easier way to make the 'functions picker' I stumbled upon this free project which basically puts the burden from me of creating a UnityFunc. Not only it supports that, but complete picking of functions, variables and with all possible combinations, like overloading functions. I did not test it yet (!!) but I guess we're then using another external utility:)

    Edit: Maybe not. Seems not too generic, there are some unsupported member types. Maybe I'll just dig into how UnityEvent did this and mimic/copy this. Even though the code is a single mess:(
     
    Last edited: Feb 11, 2016
  36. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    You won't believe it but before starting to develop Node Function i was looking how to do the same thing that UI events use and i couldn't find how do it... I think those will me help me on my work ! I think i will start on a fresh base with the Framework, maybe we can figure what we realy need together and how we can do it. My first need was to be able to develop an IA without doing it in code. I wanted to have a graphical view of my code (kinda like the Animation), export the IA (give him a name like "BrutalIA" or "PassiveIA" and then put them on a mob and on each update of the mob it will update regarding to the path we defined.
    What do you want to do with your Action Node ?

    PS : Thanks for the Unity Events, it's realy cool :)
     
  37. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Nothing. Absolutely nothing (useage of Action Node, that is):D
    I've put it as mid priority, nice-to-have generic solution on the roadmap, as it would be very useful for several occasions.
    Regarding the developement, I'm working on the function picker, taking reference from the UnityEvent. But until then I'll publish WIP stuff so you can work on it if you want, including the state system (tomorrow) :)
     
  38. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Then i'll wait your push and start my job :)
    Thanks !
     
  39. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Sorry for the delay, but a bug in the save system keeps me from committing right now. This both because of the switch to a generic nodeKnob list aswell as the new transitions that now need to be properly saved (UnityFunc not even taken into consideration). But I should have it fixed today, at worst tomorrow:)
     
  40. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Hey guys:)
    I just commited the WIP update, it seems pretty stable to me though. It has some major new features and changes, and I'm very excited about this:)
    Here's the full changelog:

    - State Behaviour added (WIP/Beta, State node, transition with transition time, no conditions yet due to lack of serializeable funcs, see UnityFunc)
    - new Caching system (continuous save, impossible to loose data, longer load times and slightly longer delay when creating nodes)
    - moved Save system to seperate script (-> NodeEditorSaveManager)
    - added WIP UnityFunc (Missing PropertyDrawer GUI (esp. function picker), intended use for action node and transition conditions)
    - added support for additional ScriptableObjects in nodes, nodeKnobs, etc. (see new functions GetScriptableObjects and CopyScriptableObjects)
    - improved NodeKnob system (now generic rect on the node, extendable, still more flexibility to come!)
    - save overhaul (due to alot of changes (transitions, nodeKnob improvement) save system has been updated. You might experience bugs (even though everything works for me), please send save file and log to me!)
    - lot more small things I can't remember;)

    Please notify me about any bugs!

    NOTES:
    - The error when first starting NodeEditor doesn't mean much, it just pops up because there is not lastSession save file yet. Also, in the interface the canvas name will always show up as lastSession. Both issues will be fixed:)
    - Also worth noting is that due to the updated save system, I had to write a small 'converter' which I plan to remove later on (way ahead though). So I recommend to re-save each of your saves to be on the save side;)
    - Additionally, the state system is not of any real use right now. It is intended to extend Transition types similar to ConnectionTypes and to create seperate State nodes of similar functuality as the basic given one. So you would be able to create Animation States next to Dialogue states and AI states:)

    @Kamigaku UnityFunc and (relative) Editor/UnityFuncEditor are the files you're interested about:) UnityFuncEditor is missing a part, for now it's just a text field for the func, need to extend this with a genericMenu like UnityEvent has. I have some plans on how to approach this, but I didn't finish it just yet... Because of this I also didn't tested it yet, so be careful;)
     
    Last edited: Feb 14, 2016
    Kamigaku and zyzyx like this.
  41. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Hey,

    Thanks for the update, quite a huge one.
    I've tried to understood and implement what you started but to be faire i don't know what to do... Do i have enough to start or i need to implement some things ?
    The UnityFunc seems to be just a "library" class so no use in GUI but the UnityFuncEditor seems to have all the GUI stuff implemented but it's not part of the Node solution (when i hope it with Visual studio, it is on a different solution then i can't use it on the Node).
    Can you give some help here ?

    Thanks
     
  42. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Yep, UnityFunc currently only serializes a function, a nice inspector like UnityEvent is still missing and would obviously be required for an Action Node.
    But why are you unable to access it? I admit I forgot to put it in thhe apropriate namespave but whatever:)
    You should be able to make the basics of the action node based on that serializeable func though:)
     
  43. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Ok I'm trying right now. There seems to be still some modifications needed to get it work, so I'll try. Get back later this evening on my progress:)
     
  44. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Ok for now I just improved the unityFunc, so it is actually suitable for an ActionNode (dynamicInvoke and such)
     
  45. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    I think i've got it ! I try to understand what you are doing and i think i'm close enough to know what you want to achieve. I'm waiting for the next push. I'm trying some things on my side while waiting.
     
  46. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Will you are still investigating on your side, i keep working on the Function node i posted earlier but i'm facing some problem (that maybe will you face too ?). Currently to put a value on a parameter i'm showing i text field that i convert to the parameter type, so on my function node i have an object array (not a Unity object, a real object) so that it represent pretty much all classes that exist.
    My problem is that when i want to serialize my node to save it, the object is not serialized. I think Serialization doesn't work on "object" type. Same for the type "Type", it can't be serialized.
    Do you have any tips to outcome this problem ?
     
  47. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Yep, that's where UnityFunc is intended to come in:) Note it has the properties at the top that represent the serialized func. I specified the targetObject to be UnityEngine.Object. There are other ways to overcome this, but it's ok enough for now. This way you can target any asset including scriptableobjects, monobehaviours, etc. and, as soon as I implemented it, static classes of any type. Regarding the types - I have a seperate class called FuncSerializedType which handles that - it is basically copying the one from UnityEvent, you can take a look at it.
    I do further research right now, there are soem things I plan to do in the next two days. Hard to split this up, though...
    Two types of things that still need to be implemented.
    1) The generic reflection method to return all methods and properties of a given object in a hierarchial structure storing MethodInfos and PropertyInfos aswell as FieldInfos. This is for the GUI. I have some plans for this, hope to get this running quickly, possibly tomorrow.
    2) The Node itself. For this I would have to send you the latest UnityFunc which supports anonymous invokes (dynamic invokes). When the supporting UnityFunc with GUI is finished, this is a breeze:)
     
  48. ScriptGeek

    ScriptGeek

    Joined:
    Mar 4, 2011
    Posts:
    45
    Before I say anything else I want to say that this framework is awesome. I've been working on a project that involves using this framework and I was looking at using the side window to display node properties. I read this quote here and I'm not sure what is meant by the editor window being just for demo purposes:

    The framework could easily be expanded with minimal work to provide a feature for those who wish to display node properties in the side window. If the side window rect was exposed (or a GUILayout.BeginVertical/EndVertical was called) and an overridable method was declared in the node base class as well as calling the overridden base class method to display the properties in the side window, developers using this framework could build their own node property editors within the side window. If it would be alright I would like to add this feature to the framework.
     
    Last edited: Feb 16, 2016
  49. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,186
    Hi, glad you find the Node Editor useful:)
    The EditorWindow is basically just a display for the actual core mechanics this Framework provides. For an extension using it, you'd likely integrate this framework into an existing or new editor window, and the provided demo window is intended to get you started quickly. Additionally it shows how to open canvases from the project panel and how to savely cache them.
    Hope that makes it clear:)

    I'd really appreciate any contribution that could improve this Framework:) Node properties on the side panel is defenitely a thing to consider, but it should be optional. Saying this because I'd imagine it would be some duplicate code (With NodeGUI) hanging around.But maybe you'll find a way around, like integrating it directly. I'd imagine you could block all non-property stuff when not on the side panels and replacing the NodeGUI with only the Inputs/Outputs GUI.
    Anyway, would be cool to have:)
     
  50. ScriptGeek

    ScriptGeek

    Joined:
    Mar 4, 2011
    Posts:
    45
    Thanks for the explanation on the EditorWindow, it clears things up nicely.

    Maybe there could be a bool flag set for the EditorWindow that determines if the node property GUIs should display in the side window, this could provide an option to toggle property GUI displays, so when enabled a separate method (something called NodePropertyGUI or something) could be a member of the node class that would handle the property GUI of the node.

    or

    A more automated process could be implemented using reflection to determine if the method (NodePropertyGUI) in the node base class is overridden, the node reference gets added to a List and if the List contains the currently selected node then call this overridden method. This way there's no configuration, just like in Unity's MonoBehaviour class, define the Start/Awake/Update/etc. methods and they get called automatically when appropriate.

    I like the reflection method better. If this sounds good, I'll get to work on it.
     
    Last edited: Feb 17, 2016