Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Simple node editor

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

  1. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    Thanks for the quick answer Seneral :)
    That's exactely the error I am getting:

    MissingReferenceException: The object of type 'Texture2D' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    UnityEngine.Texture.get_width () (at C:/buildslave/unity/build/artifacts/generated/common/runtime/TextureBindings.gen.cs:48)
    NodeEditorFramework.NodeKnob.GetGUIKnob () (at Assets/Plugins/Node_Editor/Framework/NodeKnob.cs:142)
    NodeEditorFramework.Node.DrawConnections () (at Assets/Plugins/Node_Editor/Framework/Node.cs:130)
    NodeEditorFramework.NodeEditor.DrawSubCanvas (NodeEditorFramework.NodeCanvas nodeCanvas, NodeEditorFramework.NodeEditorState editorState) (at Assets/Plugins/Node_Editor/Framework/NodeEditor.cs:187)
    NodeEditorFramework.NodeEditor.DrawCanvas (NodeEditorFramework.NodeCanvas nodeCanvas, NodeEditorFramework.NodeEditorState editorState) (at Assets/Plugins/Node_Editor/Framework/NodeEditor.cs:88)
    NodeEditorFramework.NodeEditorWindow.OnGUI () (at Assets/Plugins/Editor/NodeEditorWindow.cs:113)
    System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)

    I don't know if this can help, but I guess it's exactly what you are talking about :)
     
  2. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    Also, since I added a few lines of code to the NodeEditorWindow.cs script (they are not the ones generating the errors though), the errors referring to that script might be a ouples of line below or above.
     
  3. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    Switched back to the original code, and the editor window doesen't work even with that.
    Basically if you change scene the editor mutates into that and doesn#t even let you add nodes to the scene :S

    EDIT: I narrowed down the problem and it comes from the NodeKnob.cs script at this line:

    Vector2 size = new Vector2 ((knobTexture.width/knobTexture.height) * NodeEditorGUI.knobSize, (knobTexture.height/knobTexture.width) * NodeEditorGUI.knobSize);

    basically the knobTexture variable content gets lost when you load a different scene in the editor and the error spreads to all the remaining code.
    It's weird though, since the canvas texture, for example, is loaded correctly.
     

    Attached Files:

    Last edited: Dec 9, 2015
  4. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Yes, that's it. I marked that texture as NonSerializable, because I don't want the knob texture to be stored in the save file, but to fetch it again everytime on initialize. In theory that should work, though I didn't know that the script database is reloaded when reloading a scene. I'm not sure if that caused the error, there are still some other curiousities. Seems like I need to rework the initialization system to handle every case correct:)

    The screen you send indicates the GUI skin is also lost, including all textures. This is fixed, but that seems not to have any reference to the error.
     
  5. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    Actually, if it was only for the textures, it wouldn't be a big problem, the thing is that this somehow prevents you to add new nodes after you switch scenes, unless you manually reopen the node editor and load the right canvas after you switched scene.

    After removing the fetched knob texture from the nodeknob script in the drawnode function, i managed to be able to load a scene without crashing everything, but some textures are still missing and I still can't add nodes :(
     

    Attached Files:

  6. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    You probably can't add nodes because of all the missing texture errors;) I suppose so atleast, nothing should interupt the creating process directly...
     
  7. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    I guess so too... is there a way to deactivate all the textures?

    EDIT: i removed all the textures and guiskins/styles, but it still wont' let me add a node after opening another scene, i have to close and reopen the editor window.
    Nice thing is that it doesn't give the texture error anymore so it's loading scenes without any problem.
     
    Last edited: Dec 9, 2015
  8. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Today I started working on a generic way to help editor extensions to catch such things like BeforeSeneLoad, AfterSceneLoad, BeforeEnterPlaymode, AfterEnterPlaymode, BeforeLeavePlaymode, AfterLeavePlaymode, etc. though a simple callback. Hopefully I'm finishing that tomorrow so I can give you a proper fix to this bug, and even more: Saving and restoring the last session and even unsaved canvases automatically;). Playmode stuff, which is the hardest, is already ok now;)
    Initially I planned to release the next version (the 99th on GitHub! Yay!) when I finish the documentation I'm working on, with alot of code cleanup coming along, but this fix is more important;) The documentation is a complete rewrite of the old one, which is out of date either way, and should be alot cleaner and overall more professional looking:)

    And the fact you cannot even create nodes could be because the node types weren't fetched at that point, whih is done o initialization, too.

    Seneral
     
    manpower13 likes this.
  9. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    Well, i kinda managed to have everything working in the end :D
    I just have one more question, how do you make an output have only one connection? like if you draw a new one the old will be deleted.
     
  10. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    You'd modify the Source code, I suppose;) Take a look at Node.cs/AddConnection (around l.500). Currently, only a new connection is added (output.connections.Add (input)). Hope that helps:)
    But, is there anything specific you need this for? If you wan't to implement a state machine (my first guess), that's not exactly what you'd want I suppose;)
     
  11. crdmrn

    crdmrn

    Joined:
    Dec 24, 2013
    Posts:
    60
    I'm using this to make a scene connector. Every scene in my game gets batch loaded on the canvas as a "SceneNode" with inputs and outputs equal to the number of exits in that room (that's why i needed the node editor not to reset when loading a new scene). connectinge exits outputs and inputs and then baking them to the scenes lets you connect them without having to manually input all the parameters one by one.
    Of course since one door can only get you to one other door, i want the output connections to be only one. ;)
     
  12. LazyMan

    LazyMan

    Joined:
    Dec 7, 2013
    Posts:
    22
    Hello again!
    Great update on github!

    Look at code below - i use this small feature for my NoiseEngine.
    I will try to explain...

    NoiseEngine default NodeGUI override:

    Code (CSharp):
    1. public override void NodeGUI()
    2. {
    3.         ...
    4.         GUILayout.EndVertical();
    5.         GUILayout.BeginVertical();
    6.         NoiseEngineNodeHelper.DrawKnobIfInputsYane(Outputs[0], Inputs);
    7.         Outputs[0].DisplayLayout();
    8.  
    9.         GUILayout.EndVertical();
    10.         GUILayout.EndHorizontal();
    11.         ...
    12. }
    And here is NoiseEngineNodeHelper DrawKnobIfInputsYane method:
    Code (CSharp):
    1. ...
    2.  
    3. public static void DrawKnobIfInputsYane(NodeKnob knob, List<NodeInput> inputs)
    4. {
    5.        if (CheckInputs(inputs))
    6.               knob.Draw = true;
    7.        else
    8.               knob.Draw = false;
    9. }
    10.  
    11. ...
    12.  
    13. public static bool CheckInputs(List<NodeInput> inputs)
    14. {
    15.        if(inputs.Count > 0)
    16.        {
    17.               foreach(NodeInput input in inputs)
    18.               {
    19.                      if (input.connection == null)
    20.                      return false;
    21.               }
    22.        }
    23.        return true;
    24. }
    25.  
    26. ...
    So as u can see my ModuleBase system so S***ty and as result i'am need some tweaks.

    Btw, it will be good if other users can tweak knobs drawing state. Very usefull, if u need connect all inputs of node to get the output knob, you know.

    p.s Sorry for my Eng. Yane-Nane :D
     
  13. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Useful, indeed:) Problem I see though is, when you have all inputs connected aswell as the output, and then clear an input, the output knob gets hidden, but the connection still persists (and is drawn!!). I'll try to add something similar soon:)
    Btw, there's already Node.hasUnassignedInputs, which is the same as CheckInputs for you, and return value reversed. #26 on Github goes into the same direction: making the system more flexible to modifications...
     
  14. LazyMan

    LazyMan

    Joined:
    Dec 7, 2013
    Posts:
    22
    Yeah, i know. In my case i will use my extension's :) Need to mind about structure of NoiseEngine extension at all...
    Your new ScriptableObject save system was tested. All is good, but i have Type Mismatch on my nodes after save.
    I still using my own system.

    "Don't touch it, if it work's good."
     
  15. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    On what exactly did you get type mismatch? This can only happen if I missed to add a meta file (though all necessary should be provided now) or you have a ScriptableObject in any of your nodes. In the latter case, I'm planning something which should support those. I will make it optional, turned of by default, as I will need to search each node for any ScriptableObjects using reflection, so it would uneccessarily slow down node saving/loading. I think I adressed that somewhere on the repo, in some issue or something like that, and provided an additional way to handle that...

    You're right to keep your own solution if you get problems with the Node Editor:) Just tell me about them, so I can make Node Editor a worthy option to consider one day;)
     
  16. LazyMan

    LazyMan

    Joined:
    Dec 7, 2013
    Posts:
    22
    It is a good idea. But by spending some time in code you can write own saver/loader for each custom node. It is not a problem. Realy.
    BTW you can do some sort of abstract MotherClass or Interface for special save/load behaviour.
     
  17. iddqd

    iddqd

    Joined:
    Apr 14, 2012
    Posts:
    398
    Serenal, really great work with your node editor!

    Regarding the RuntimeNodeEditor.cs - do you have any idea why the Nodes don't show up on the canvas?
    I can add them via right click, but the canvas stays empty.

    Thanks!
     
  18. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Hm, that's odd. Works for me;) Does this always happen? Does absolutely no Node show?
     
  19. iddqd

    iddqd

    Joined:
    Apr 14, 2012
    Posts:
    398
    Yeah no node shows, maybe I've got it setup wrong?
    I just created an empty GameObject and added RuntimeNodeEditor.cs - i can see and zoom the grid...
     
  20. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Hm, I'll take a closer look at that tomorrow, but did you try to specify a canvas path (Relative to assets folder)?
     
  21. iddqd

    iddqd

    Joined:
    Apr 14, 2012
    Posts:
    398
    Well yes, i put Node_Editor into the Plugins Folder, and then i assigned a canvas - however i did not specify a state.
    I also tried to just assign a Canvas Name.

    I can send you my test project tomorrow, so you can see what I'm doing wrong.
    Thanks so far.
     
  22. dk__

    dk__

    Joined:
    Aug 8, 2015
    Posts:
    3
    It's looking cool! Any chance it can be used for in-game editing? I'm trying to find a system allowing me to define complex graphs in Game rather than in the editor. Is it possible or do you know of any other project that could be used as a starting point? Thanks!
     
  23. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    @dk__ It is partially possible right now, you currently can't create new canvases and some controls are editor only and will not work. But I'm planning a custom save type and other ingame controls which should fix this:)
    But this will take a bit and I don't know of other editors which support this....
     
  24. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Hey guys, check this out:)
    I managed to built a WebGL version of NodeEditor;)
    First there were some problems with FastReflection by vexe, as it emits code at runtime, but thanks to his amazing support the scaling system is now using a more specific, even faster working method for reflection, by directly creating delegates (which is only possible due to the specific needs of the scaling system, static members only) :) That means we do not need to use FastReflection anymore, but without it and vexe we would still use slow default Reflection;)
    There were (and sometimes still are) other obstacles I found, but it is working pretty ok now (with some minor problems). Be sure to check it out;)

    I'm planning some other major things until the next update (or those after), including (but not limited to or promised!):
    - Nested scaling support; There is currently a problem in the scaling system where nested scale areas get shifted somehow, and even after several hours I don't know how to counteract it using the existing scale... :confused:
    - Multiple selection of Nodes and Grouping Nodes
    - Undo support; I've mentioned my custom WIP UndoPro command-pattern integration in the default Unity Undo system before. Currently I'm exploring a promising new approach and it looks like I'm gonna make it:cool:
    - More runtime controls and increased support
    - Official standalone, WebGL and mobile demos? (touch is already working just fine, both in Editor and RT;) )
    - Hopefully the state machine behaviour; It's actually nearly finished, the design decisions are already made and most stuff should be easy to do:)
    - Save overhaul; Another overhaul to allow for a secondary save format (binary/xml?) for runtime saving and loading, extended control over how to save nodes (so you can save Node-specific scriptable objects along with it), improved and faster auto save, custom save file inspector, etc...
    - 'Action' Node; allows for simple method calling on objects of static classes with dynamic input knobs for parameter
     
    zyzyx and manpower13 like this.
  25. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    563
    This is great! I have a question. I am trying to create a node that has an output type of List<Vector3>. What should the outputType be called so that TryGetValue() works?
     
  26. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    I assume this question is directed at me?
    well, you first have to tell the Framework that this type exists, so it knows additional informations like color about it. In ConnectionTypes.cs, at the very Bottom, you'll find an Interface ITypeDeclaration. You need a class somewhere in your project that extends this class for the Framework to find, just like 'FloatType' below:) Depending on which name you give it, you would adress it. It should look about like this:
    Code (csharp):
    1.  
    2. public class Vector3ListType : ITypeDeclaration
    3. {
    4. public string name { get { return "Vector 3 List"; } }
    5. public Color col { get { return Color.brown; } }
    6. public string InputKnob_TexPath { get { return "Textures/In_Knob.png"; } }
    7. public string OutputKnob_TexPath { get { return "Textures/Out_Knob.png"; } }
    8. public Type Type { get { return typeof(List<Vector3>); } }
    9. }
    10.  
    Sorry that I don't have a proper documentation right now, the one I planned is delayed a bit...
     
    Last edited: Jan 7, 2016
    manpower13 likes this.
  27. smoketh

    smoketh

    Joined:
    Sep 14, 2012
    Posts:
    26
    Thanks a ton.
    This helped me to improve my GPU-based terrain generation by moving function calls from hardcode into the node editor.

    And some results

     
    Seneral and keenanwoodall like this.
  28. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Looking great smoketh, very excited to see more of it:)
    Do you have any future plans, like the full suite with splatmap and objects generation and such? I'd really like to see something like Terrain Composer in a Node Editor format;)
    Some sidenotes for the release, I'd recommend to put the parameters outside of the node but to a seperate panel, maybe on the side window, similar to Substance Designer, and only having the previews inside the bodies:) I also would create a complete new Editor instead of using the given Editor window, so it gives that unique feel;)
     
  29. smoketh

    smoketh

    Joined:
    Sep 14, 2012
    Posts:
    26
    Is there an override somewhere to display additional parameters on side window?
     
  30. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    No, and thats supposed to be so. Basically, the Node Editor Framework is just the canvas part of the Editor Window and handles all stuff related to it. The rest of the Editor window is basically just a placeholder to give you a simple window as a 'demo'.
    Thats what I meant with building your own editor;) This could then include toolbar, side panel showing the parameters and other stuff along with the canvas. Hope that makes sense...
    Showing the Node Parameters is then just a matter of accessing the active node through the EditorState. Actually, I'd be best to make a overideable methode in the Node base class for the side window, showing all inputs by default...
    Seneral
     
    manpower13 likes this.
  31. bocs

    bocs

    Joined:
    May 9, 2009
    Posts:
    335
    Anyone else seeing it very dark in unity 5.3?


     
  32. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    I have no clue what could cause this .... Are the GUI textures in the project normal, or do they look black, too? Because it seems only the custom textures are affected. I currently do not know exactly the latest version I'm using, but it's 5.3.x. I'll take a look if I experience the same in that exact version tomorrow:)
     
  33. bocs

    bocs

    Joined:
    May 9, 2009
    Posts:
    335
    lol..i've had nothing but problems with 5.3.x releases
    just installed the latest patch today to see if it's "stable"...

    1st was a new project (dark)...created a 2nd project
    and now it looks right...but went to the unity about and this happens.
    closed the editor, reloading the project crashes unity on startup (even deleting library so it will rebuild didn't fix the crash).

    IDK, don't waste too much time on it...going to stick with 5.2 and check 5.3 again in a few more patches.

     
  34. smoketh

    smoketh

    Joined:
    Sep 14, 2012
    Posts:
    26
    That's gamma color space switched to linear.
     
  35. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Your right, and this happens in every Unity version:(
    To fix this, the textures need to be imported with either the 'Legacy GUI or Editor GUI' option, which works for almost every texture, or with the 'Bypass sRGB Sampling' option checked (-> Linear Rendering Docs, last section). We need the last option for 'NE_Box' and 'NE_Button' because these are sprites, so you need to set their types to advances and with the following options:
    NE_Sprites import settings.jpg
    That should do it:)
     
  36. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Hello,

    Thanks for this awesome extension. I'm currently using it to create an AI and i'm trying to extend it to be able to use function call (in a certain way :)). I wanted to know, is there a possibility to use it "outside" of the editor ? As a single .exe file that i could share to my friends and let them play with ?

    Thanks for the answer
     
  37. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    I don't know exactly what you mean... Like, they could test it without Unity? You could built it, then you have atleast limited capabilities (for now)
     
  38. Kamigaku

    Kamigaku

    Joined:
    Mar 23, 2014
    Posts:
    25
    Yes exactly, how could i built it ?
     
  39. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    There's a component called RuntimeNodeEditor. You have too specify a canvas to load, and can't load others, because I didn't have he time to extend its functuality yet. Also there are limits in regard to the Editor-only GUI, f.E. we don' have Object Pickers.
    But in overall it's the same:)
     
  40. CommunityUS

    CommunityUS

    Joined:
    Sep 2, 2011
    Posts:
    186
    I ran into the same issue with the current repo. I am not sure if this is a Unity version discrepancy or just a case of accidentally leaving things commented out, and adding to many GUI Groups or what but this is what I found.

    I am currently playing around in Unity 5.0 (one of the early releases) and found I had to make the below changes.

    Code (CSharp):
    1. rootRect = new Rect (0, 0, Screen.width, Screen.height);
    2. canvasRect = new Rect (0, 0, Screen.width, Screen.height);
    ...uncomment those (near top)

    Code (CSharp):
    1. public void OnGUI ()
    2.     {
    3.         if (canvas != null && state != null)
    4.         {
    5.             NodeEditor.checkInit ();
    6.             if (NodeEditor.InitiationError)
    7.             {
    8.                 GUILayout.Label ("Initiation failed! Check console for more information!");
    9.                 return;
    10.             }
    11.  
    12.             try
    13.             {
    14.                 GUI.BeginGroup (rootRect); // One group is allowed only
    15.                 // NO other groups possible here!!
    16.  
    17.                 state.canvasRect = canvasRect;
    18.                 NodeEditor.DrawCanvas (canvas, state /*,rootRect*/);
    19.  
    20.                 GUI.EndGroup ();
    21.             }
    22.             catch (UnityException e)
    23.             { // on exceptions in drawing flush the canvas to avoid locking the ui.
    24.                 NewNodeCanvas ();
    25.                 Debug.LogError ("Unloaded Canvas due to exception in Draw!");
    26.                 Debug.LogException (e);
    27.             }
    28.         }
    ...make your OnGUI() look like this on the same script.
     
  41. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    I've never got the same problem so I could not check if it was gone, but I did changes recently (not publish yet) which look very similar. I think the problem was when you do specify a canvas but no editor state. What I did now is way simpler, it only allows you to specify a path;P
     

    Attached Files:

  42. CommunityUS

    CommunityUS

    Joined:
    Sep 2, 2011
    Posts:
    186
    Yea, I noticed that, can the canvas and editor states now be changed back to privates then?

    It looks like you have all sorts of fun goodies coming soon, I am excited and plan to help on this repo when I can. The runtime thing is what made you stand out for me. In the link you teased quoted below it looks like we have some new context menu states available? Are these planned to be released on your next push? (fingers crossed)
    I am only a hour or 2 in on the project, but I think I saw somewhere a GenericMenu was added. Any chance this was added to help with Editor and RuntimeEditor crossover? If so, can you briefly hit on that so I can know where your thought process was on it.

    Great library, and looks pretty clean too.
     
  43. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Thanks for the kind words:)
    Yes, they can be set to private. There's a something that prevents us from just specifying the canvas file: At runtime, we cannot get the path of it, and we need that path to load any associated Editor States from the canvas file. We can't manually assign the editorState either, because it's hidden as a 'subfile' of the canvas. Of course we could generate a new one, but then pan and zoom values would be resetted. So, we need to specify the path for it to work right now.

    The states you see in the WebGL demo were actually there for a while, but they do not do anything atm. I've stopped working on them for a while to focus on other things, but they actually do not need that much work anymore. If anyone really needs them, you can tell me, I can shift my focus then:)

    About the GenericMenu, I've developed it a while ago and now it's being used both in the editor and at runtime already. You can find it in OverlayGUI.cs, the underlying 'engine' can also be used for popups and similar stuff, but currently I've again set my focus elsewhere;)
    It's basically a mimic of the built-in GenericMenu, where I did not simply copy the default one. It's missing one rather unimportant feature ((de-)activate items) but it does the job just fine:)

    Some of these things could also be something other devs willing to contribute could connect to... I'm thinking about a roadmap and more details what is planned and what's the current status.
     
  44. CommunityUS

    CommunityUS

    Joined:
    Sep 2, 2011
    Posts:
    186
    My immediate planned use of this code is - as a state machine. I am happy to work on this myself. However, it would help if you could kinda give me an idea of where you left off and what you had in mind. Esp. what scripts to look at, and if you had new scripts planned, what structure did you have in mind if any?

    Of course I may or may not totally follow that approach for my own project, but would be happy to try to do it the planned way first. I of course have my own needs and structure that I will need to adapt this code set to. But if I can first do it in a way that makes sense for this project and then adapt it to mine in a separate fork all is well.

    For instance my approach would be (keep in mind, I totally fresh to the project here)
    - Combine Node.cs and StateNode.cs into just StateNode
    - Create new abstract class AbstractState
    -- then create a BehaviourState : AbstractState that inherits the AbstractState
    --- BaseState : AbstractState
    ---- AttackState : BaseState

    Lets look at what a FloatField : AbstractState might look like:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using NodeEditorFramework;
    4.  
    5. #if UNITY_EDITOR
    6. using UnityEditor;
    7. #endif
    8.  
    9. public class FloatField : AbstractState {
    10.    
    11. #if UNITY_EDITOR
    12.     public float floatVar;
    13.    
    14.     public FloatField(Vector2 position){
    15.         this.Position= position;
    16.         this.Size= new Vector2(140,40);
    17.         this.Title= "Float";
    18.     }
    19.    
    20.     public override void DrawState ()
    21.     {
    22.         Color color = GUI.color;
    23.         if(AbstractState.LastSelection != null && AbstractState.LastSelection.Equals(this)){
    24.             GUI.color=Color.green;
    25.         }
    26.         GUILayout.BeginArea(AreaRect,Title,"window");
    27.         GUI.color=color;
    28.        
    29.         foreach(StateNode node in Nodes){
    30.             node.DrawNode(AreaRect);
    31.         }
    32.         floatVar= EditorGUILayout.FloatField("Value",floatVar);
    33.         GUILayout.EndArea();
    34.         InputNode.DrawNode(new Vector2(AreaRect.x+2,AreaRect.y+2));
    35.        
    36.     }
    37. #endif
    38. }
    39.  

    AbstractState

    nodes
    slection
    lastSelection
    input
    title
    areaRect
    position
    size
    x
    y
    DrawState()
    HandleEvents()
    OnGUI()
    Save() -- need to know more about this process.


    BehaviourState

    tagNode
    initialStateNode
    inputNode (to become attribute)
    tags
    initialState
    attributes
    BehaviourState()
    Init()
    OnGUI()
    Save() -- need to know more about this process.

    BaseState

    animationNode
    transitionNode
    id
    anim
    transitions
    startTimeTransition
    breakTimeTransition
    listenTo
    BaseState()
    Init()
    HandleState()
    CheckTrans()
    StartTimeTransition()
    OnGUI()
    Save() -- need to know more about this process.

    AttackState

    damageNode
    inputName (to become attributeNameNode)
    inputNodeName (to become attributeName)
    dmg (damage)
    time
    AttackState()
    HandleState()
    Init()
    OnGUI()
    Save() -- need to know more about this process.


    ...Then there would be a BaseAttribute : AbstractState, and you could have the AttackState interact with the players attributes.
    Code (CSharp):
    1. nameNode= new StateNode("Name",this,typeof(StringField));
    2. baseValueNode= new StateNode("Base Value",this,typeof(FloatField));
    3. regenerationRateNode= new StateNode("Regeneration Rate",this,typeof(FloatField));
    4. curValueNode=new StateNode("Current Value",this);
    this.nodes.Add(regenerationRateNode); (nodes would actually be a get setter to Nodes on inherited AbstractState)
    ...etc.

    Code (CSharp):
    1. AttackState() {
    2. this.attributeNameNode= new StateNode("Player Attribute",this,typeof(StringField));
    3. this.Nodes.Add(attributeNameNode);
    4. }
    5.  
    6. Init() {
    7. this.attributeNameNode= new StateNode("Player Attribute",this,typeof(StringField));
    8. this.Nodes.Add(damageNode);
    9. this.Nodes.Add(attributeNameNode);
    10. }
    11.  
    12. public override void OnGUI ()
    13. {
    14.     base.OnGUI ();
    15.     damage=EditorGUILayout.FloatField("Damage", damage);
    16.     attributeName=EditorGUILayout.TextField("Attribute", attributeName);
    17. }
    ok...that is terrible sudo code. But I am just trying to think through what all will be needed and how it will interact when done.

    I did have a thought on the save system as this is very important to me. Even though AssetBundles are now a non pro feature (because everything is available to all platforms on Unity 5 etc.) The save system relying on ScriptableObjects exclusively for the means of saving limits us needlessly. I suggest eventually offering a enum setting in a manager somewhere that would allow people to choose how to save, and they could save to SO or binary.

    The nice thing about binary is this gives legs to the runtime code department, and the runtime saving.

    Examples of what the Save() might look like in each case above:

    AbstractState.cs

    Code (CSharp):
    1. public virtual void Save(FileStream fileStream, BinaryFormatter formatter){
    2.        
    3.     }
    BehaviourState.cs
    Code (CSharp):
    1. public override void Save (FileStream fileStream, BinaryFormatter formatter)
    2.     {
    3.         tags= tagNode.GetStringList();
    4.         initialState= initialStateNode.GetBaseState();
    5.         attributes= attribute.GetBaseAttribute();//was inputNode
    6.        
    7.         foreach(BaseAttribute attribute in attributes){
    8.             attribute.Setup();
    9.         }
    10.            
    11.         formatter.Serialize(fileStream,tags);
    12.         formatter.Serialize(fileStream,initialState.id);
    13.         formatter.Serialize(fileStream,attributes);
    14.        
    15.         x=Position.x;
    16.         y= Position.y;
    17.         formatter.Serialize(fileStream,x);
    18.         formatter.Serialize(fileStream,y);
    19.        
    20.     }
    AttackState.cs
    Code (CSharp):
    1. public override void Save (System.IO.FileStream fileStream, System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter)
    2.     {
    3.         AnimationClip animClip= animNode.GetAnimationClip();
    4.         if(animClip != null){
    5.             animation= animNode.GetAnimationClip().name;
    6.         }else{
    7.             animation=animNode.GetString();
    8.         }
    9.         transitions= transitionNode.GetBaseTransition();
    10.         foreach(BaseTransition transition in transitions){
    11.             transition.Setup();
    12.         }
    13.         dmg= damageNode.GetFloat();
    14.         attributeName= attributeNameNode.GetString();//was inputNode or something
    15.         x = Position.x;
    16.         y = Position.y;
    17.         formatter.Serialize(fileStream,this);
    18.     }

    BaseState.cs
    Code (CSharp):
    1. public override void Save (System.IO.FileStream fileStream, System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter)
    2.     {
    3.         AnimationClip animationClip= animationNode.GetAnimationClip();
    4.         if(animationClip != null){
    5.             animation= animationNode.GetAnimationClip().name;
    6.         }else{
    7.             animation=animationNode.GetString();
    8.         }
    9.         transitions= transitionNode.GetBaseTransition();
    10.         foreach(BaseTransition transition in transitions){
    11.             transition.Setup();
    12.         }
    13.         x = Position.x;
    14.         y = Position.y;
    15.         formatter.Serialize(fileStream,this);
    16.     }
    Lastly the binary Save/Load first concept.
    Code (CSharp):
    1. private void OnSave(){
    2.         BehaviourState ai = null;
    3.         foreach (AbstractState mState in states) {
    4.             if (mState is BehaviourState) {
    5.                 _bs = mState as BehaviourState;
    6.             }
    7.         }
    8.        
    9.         if (ai == null) {
    10.             if (DisplayDialog ("No Behavior", "There is no Behaviour, you should create one before save.", "Create", "Cancel")) {
    11.                 OnCreate (new BehaviourState (new Vector2 (position.width / 2 + scrollPosition.x, position.height / 2 + scrollPosition.y)));
    12.             }
    13.             return;
    14.         }
    15.        
    16.         string mPath = EditorUtility.SaveFilePanelInProject (
    17.                     "Save BehaveState",
    18.                     "New BS.bytes",
    19.                     "bytes", "");
    20.        
    21.         if (mPath.Equals (string.Empty)) {
    22.             return;
    23.         }
    24.        
    25.         int cnt = 0;
    26.         for (int i=0; i< states.Count; i++) {
    27.             if (states [i] is BaseState) {
    28.                 ((BaseState)states [i]).id = i;
    29.                 cnt++;
    30.             }
    31.         }
    32.        
    33.         FileStream fileStream = new FileStream (mPath, FileMode.Create);
    34.         BinaryFormatter formatter = new BinaryFormatter ();
    35.         formatter.Binder = new VersionDeserializationBinder ();
    36.        
    37.         _bs.Save (fileStream, formatter);
    38.         formatter.Serialize (fileStream, cnt);
    39.        
    40.         for (int i=0; i< states.Count; i++) {
    41.             if (states [i] is BaseState) {
    42.                 states [i].Save (fileStream, formatter);
    43.             }
    44.         }
    45.        
    46.         fileStream.Close ();
    47.         AssetDatabase.Refresh ();
    48.     }
    49.    
    50.     private void OnLoad ()
    51.     {
    52.         string mPath = EditorUtility.OpenFilePanel (
    53.             "Save BehaveState",
    54.             "New BS.bytes",
    55.             "bytes", "");
    56.        
    57.         FileStream fs = new FileStream (mPath, FileMode.Open);
    58.         BinaryFormatter formatter = new BinaryFormatter ();
    59.         formatter.Binder = new VersionDeserializationBinder ();
    60.        
    61.         List<string> tags = (List<string>)formatter.Deserialize (fs);
    62.         int initialStateId = (int)formatter.Deserialize (fs);
    63.         List<BaseAttribute> attributes = (List<BaseAttribute>)formatter.Deserialize (fs);
    64.        
    65.         BehaviourState ai = new BehaviourState (new Vector2 ((float)formatter.Deserialize (fs), (float)formatter.Deserialize (fs)));
    66.         OnCreate (ai);
    67.        
    68.         int cnt = (int)formatter.Deserialize (fs);
    69.        
    70.         foreach (string tag in tags) {
    71.             StringField tagField = new StringField (_bs.Position + new Vector2 (_bs.Size.x + 50, -50));
    72.             tagField.stringVar = tag;
    73.             OnCreate (tagField);
    74.             _bs.tagNode.ConnectTo (tagField.InputNode);
    75.         }
    76.        
    77.         for (int i=0; i< cnt; i++) {
    78.             BaseState state = (BaseState)formatter.Deserialize (fs);
    79.             state.Init (new Vector2 (state.x, state.y));
    80.             OnCreate (state);
    81.             foreach (BaseTransition transition in state.transitions) {
    82.                 transition.Init (new Vector2 (transition.x, transition.y));
    83.                 OnCreate (transition);
    84.                 state.transitionNode.ConnectTo (transition.InputNode);
    85.             }
    86.        
    87.             StringField animationField = new StringField (state.Position + new Vector2 (state.Size.x + 50, -70));
    88.             OnCreate (animationField);
    89.             animationField.stringVar = state.animation;
    90.             state.animationNode.ConnectTo (animationField.InputNode);
    91.            
    92.             if (state is AttackState) {
    93.                 AttackState attackState = state as AttackState;
    94.                
    95.                 FloatField attackDamageField = new FloatField (state.Position + new Vector2 (state.Size.x + 50, -20));
    96.                 attackDamageField.floatVar = attackState.damage;
    97.                 OnCreate (attackDamageField);
    98.                 attackState.damageNode.ConnectTo (attackDamageField.InputNode);
    99.                
    100.                 StringField attackAttributeNameField = new StringField (state.Position + new Vector2 (state.Size.x + 50, 30));
    101.                 attackAttributeNameField.stringVar = attackState.attributeName;
    102.                 OnCreate (attackAttributeNameField);
    103.                 attackState.attributeNameNode.ConnectTo (attackAttributeNameField.InputNode);
    104.             }
    105.  
    106.             /**Add other states here**/
    107.             //like flee, follow, walk, patrol etc.
    108.            
    109.  
    110.        
    111.         _bs.initialStateNode.ConnectTo (GetState (initialStateId).InputNode);
    112.        
    113.         for (int i=0; i< states.Count; i++) {
    114.             if (states [i] is BaseTransition) {
    115.                 (states [i] as BaseTransition).toStateNode.ConnectTo (GetState ((states [i] as BaseTransition).toStateId).InputNode);
    116.                
    117.                 /*Example of FloatField Class given above*/
    118.                 FloatField priorityField = new FloatField ((states [i] as BaseTransition).Position + new Vector2 ((states [i] as BaseTransition).Size.x + 40, -20));
    119.                 priorityField.floatVar = (states [i] as BaseTransition).priority;
    120.                 OnCreate (priorityField);
    121.                 (states [i] as BaseTransition).priorityNode.ConnectTo (priorityField.InputNode);
    122.                
    123.                
    124.                 /**if transition types here**/
    125.                
    126.                 if (states [i] is TypeTransition) {
    127.                         /*StringField can be inferred from FloatField example*/
    128.                         StringField listnerField = new StringField ((states [i] as TypeTransition).Position + new Vector2 ((states [i] as TypeTransition).Size.x + 40, 30));
    129.                         OnCreate (listnerField);
    130.                         (states [i] as TypeTransition).listenToNode.ConnectTo (listnerField.InputNode);
    131.                         listnerField.stringVar = (states [i] as TypeTransition).listenTo;
    132.                 }
    133.                
    134.                 if (states [i] is Comparer) {
    135.                     FloatField comparerValueField = new FloatField ((states [i] as Comparer).Position + new Vector2 ((states [i] as Comparer).Size.x + 40, 30));
    136.                     OnCreate (comparerValueField);
    137.                     (states [i] as Comparer).compareValueNode.ConnectTo (comparerValueField.InputNode);
    138.                     comparerValueField.floatVar = (states [i] as Comparer).compareValue;
    139.                 }
    140.             }
    141.         }
    142.        
    143.         foreach (BaseAttribute attribute in attributes) {
    144.             attribute.Init (new Vector2 (attribute.x, attribute.y));
    145.             OnCreate (attribute);
    146.             _bs.attributeNode.ConnectTo (attribute.InputNode);
    147.            
    148.             /*StringField can be inferred from FloatField example*/
    149.             StringField attributeNameField = new StringField (attribute.Position + new Vector2 (attribute.Size.x + 50, -80));
    150.             attributeNameField.stringVar = attribute.name;
    151.             OnCreate (attributeNameField);
    152.             attribute.nameNode.ConnectTo (attributeNameField.InputNode);
    153.            
    154.             FloatField attributeBaseValueField = new FloatField (attribute.Position + new Vector2 (attribute.Size.x + 50, -30));
    155.             attributeBaseValueField.floatVar = attribute.baseValue;
    156.             OnCreate (attributeBaseValueField);
    157.             attribute.baseValueNode.ConnectTo (attributeBaseValueField.InputNode);
    158.            
    159.             FloatField attributeRegenerationField = new FloatField (attribute.Position + new Vector2 (attribute.Size.x + 50, 20));
    160.             attributeRegenerationField.floatVar = attribute.regenerationRate;
    161.             OnCreate (attributeRegenerationField);
    162.             attribute.regenerationRateNode.ConnectTo (attributeRegenerationField.InputNode);
    163.            
    164.             foreach (Comparer attributeComparer in attribute.comparerTransitions) {
    165.                 attributeComparer.Init (new Vector2 (attributeComparer.x, attributeComparer.y));
    166.                 OnCreate (attributeComparer);
    167.                 attribute.curValueNode.ConnectTo (attributeComparer.InputNode);
    168.                
    169.                 FloatField comparerPriorityField = new FloatField (attributeComparer.Position + new Vector2 (attributeComparer.Size.x + 40, -20));
    170.                 comparerPriorityField.floatVar = attributeComparer.priority;
    171.                 OnCreate (comparerPriorityField);
    172.                 attributeComparer.priorityNode.ConnectTo (comparerPriorityField.InputNode);
    173.                
    174.                 if (! (attributeComparer is Something)) {
    175.                     FloatField comparerValueField = new FloatField (attributeComparer.Position + new Vector2 (attributeComparer.Size.x + 40, 30));
    176.                     comparerValueField.floatVar = attributeComparer.compareValue;
    177.                     OnCreate (comparerValueField);
    178.                     attributeComparer.compareValueNode.ConnectTo (comparerValueField.InputNode);
    179.                 }
    180.                 attributeComparer.toStateNode.ConnectTo (GetState (attributeComparer.toStateId).InputNode);
    181.             }
    182.         }
    183.     }
    end brainstorm :)
     
  45. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Indeed a huge brainstormxD And too much to follow in that short of time - but what I'd like to add first: I actually started to make a Roadmap and task explanation document right after my last post and also finished the documentation, which pretty much is exactly what you are asking for;) Pretty creepy coincidence....
    Anyway, I was about to upload it, along with the proper documentation. The roadmap indeed explains a bit the State system I had in mind (more an integration into the current system than what you are proposing), and also includes a binary save format. Pretty much the same thoughts we had. The document should be an outline of what has to be done, how you can help and where to get started. The new documentation (some parts still WIP) should help you, too. Wait a minute, I'm deploying it now;)
     
  46. CommunityUS

    CommunityUS

    Joined:
    Sep 2, 2011
    Posts:
    186
    If only there was some type of Elder Scrolls renewal project I could browse in meantime while I wait. Alas, no one would ever do something so cool so I am left to twiddle my fingers. ;-)
     
  47. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Wait, what? Please, I beg you, look at my signature, isn't it big enough? o_O

    :D

    I just published the update, another major one:)
    Brand new Documentation, Roadmap/Tasksheet added, WebGL build support, Bezier Drawing and GUI Scaling speedup (replacing Fast.Reflection), some restructures and several improvements, bugfixes, etc.

    Tell me if the Roadmap/Tasksheet is to your liking;)

    I'll link documentation and tasksheet here, to finally get somebody to read it:p Really, I think these are pretty good ;)
     

    Attached Files:

  48. CommunityUS

    CommunityUS

    Joined:
    Sep 2, 2011
    Posts:
    186
    Excellent. I did at least make it half way through the old docs before I started playing on my own. :)

    I never got around to playing the PC version of the elder scrolls series, let alone play with papyrus. Of course I played Xbox version, feels less like work.

    OffT: Is papyrus the same as...
    http://www.eclipse.org/papyrus/

    OnT: Any chance papyrus and our Node Editor share commonality in approach? If so, to what degree?
     
  49. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,185
    Then you'd best try the new docs;) The old were crap, besides being out of date, the new ones are cleaner and I hope they do their job fine:)

    I assume you mean papyrus the programming language for the elder scrolls? As I said, it's a programming language, and the site you linked seems to be a graph modeler from what I understand. Actually, I've not much clue what that program is exactly, so I can't judge the similarity with Node Editor in design - but I assume they are far off. In Node Editor, you could create a variety of different node styles, with different layouts, specs and other stuff without modifying the framework code, to some degree, and with increasing (planned) support. So I guess, you could extend Node Editor to support all the graph standards Papyrus supports, like UML.

    Also, before I forget or nobody reads it, I need mathematical help with the Nested Scaling support;) I ask anybody with greater knowledge of matrices to write me a PM and I'll gladly explain him/her the problem and hand him the testing environment I've built;)
    The basic problem is outlined in the tasksheet.
     
    manpower13 likes this.
  50. iddqd

    iddqd

    Joined:
    Apr 14, 2012
    Posts:
    398
    great update, many thanks!