-
Notifications
You must be signed in to change notification settings - Fork 417
Making the Node Editor Framework more modular and flexible #70
Comments
Wouldnt this be possible with a interface based approach? Might require some refactoring/moving of the code to make this work but you probably have to do this anyway. For instance you can have a INodeCalculation interface that provides the logic for calculating the nodes. Then you just have to insert the correct class depending on what kind of calculation you want. You could even switch out a class with a different class at runtime. I have done this with a pathfinder. All i have to do is insert the class that has some kind of algorithm (A_, Theta_, Jump Point or whatever). The logic is different but the interface is not. Sure it might require some thinking to make this actually work but in my opinion this would result in a nice structured and flexible codebase which ppl could even extend or modify if they wanted to by implementing the interfaces while it also forces you to think about your design. I would try to stay away from using partial classes for this as these are not really intended to do this. Their main usage is in automatic code generation like the visual studio designer so that auto generated code wont clutter up your code. |
@Barsonax Thanks for the ideas! Yes, for the calculation I had the exact same thoughts, I think this is the way to go. Also a reason why I did not yet made much effort of moving calculation code out of NodeEditor.cs (where it usually does not belong) because I had this in my head all the time... @ChicK00o developed something amazing with 5c5ddc4 that kinda goes in this direction, where there are different types of canvas types. Binding them with different calculation ways would be really powerful:) With this already existing as the base, it would be way easier to handle other stuff mentioned in the OP like the statemachine:) |
I know what you mean with not having enough time :P. Having full time job, doing some programming as hobby and still enjoying life just eats time. I could take a look at it and put the calculation code in a separate class that implements a interface as first step to achieving this. |
I made a pull request in which i pulled out the calculator logic from NodeEditor.cs into its own class that implements a interface. #90 |
Initial idea of partial classes implemented in 1641989 Enhancement of the calculation system as discussed is still in progress though:) |
I'm not sure if partial classes are a good idea for what you´re trying to do. Afaik partial classes are meant to be used as a structuering tool if your classes are getting big and you can't divide them into several meaningful classes or you need to work with multiple files for a class. I think Barsonax' approch is more appropriate as it's how people are used to extend existing libaries/modules. I would actually go a little further and would adive a rework of the internals of the NodeEditor. There several small quirks that are kind of odd. Though as of now i'm not familiar enough with the code to make concrete suggestions. |
Sorry for responding so late. Made up a response in my mind and thought about it - but forgot to answer:O I think it's a mix of both. I do think partial classes are powerful, when used correctly, because they allow changing some basic structural stuff of the framework without touching the framework code (so you can update without caring for your local changes). I do have ideas for this but I don't have much time to implement them:( |
Here is how I think the framework could be structured: A single Knob class, that is mainly just a container for the list of KnobModifiers. an abstract KnobModifier, mainly for allowing and denying connections.
Some Builtin Knob Modifiers, with temporary names. naming policy could be improved.
For Dynamic Input/Output (issue #91), the Node could implement an event handler that increases the number of knobs. For multiple input connections and restricting output (issue #74), the Node could simply use different knob modifiers, as the directionality and the connectionLimit are independent. For State machine support the Nodes could use a KMDirectional, KMConnectionLimit on the output, and something to make the conditions. Possibly use a monobehaviour with a reference to the canvas, and a variable for the current node, and call some functions on your node (through an interface you made all the state machine nodes extend), where that functions does all the logic for that state, including switching to the next state. For Node Areas/Groups (issue #41): we could either implement it monolithically or one of the modular ways.
For Serialization (issue #43), we could serialize the nodes in the canvas with their knobs & knobmods attached, but with a [Nonserialized] on the list of connections, and then go through and serialize all the connections as a big list of knob identifiers that can be reconstructed. one problem I can see right away is that it might be ugly to try and implement coloring the connections in this model, unless the knob mods get a chance to color the connection. This is just my view on the issue, so please tell me your thoughts and ideas before I start implementing this in about a week 😅 |
Thanks for your opinion, that's alot! :) First paragraph: Seems we agree here;) The calculation has already been lifted of NodeEditor.cs in the develop branch and especially in #109 , and the whole region 'Space Transformations' (including click detection) is also planned to be moved to more apropriate places. Regarding the Knob concept: I don't think I fully understand it yet, but it appears as if you only want ONE Knob class and define it's properties (how it behaves) through a KnobModifier? That would mean we remove InputKnob and OutputKnob as we know right now, and instead we make the whole 'Connection Knob concept' weakly typed, as far as I understand, against the language specifications. As to your statemachine idea, as far as I understand the MonoBehaviour is responsible for traversing the state machine? Then, again referring to #109 , this could soon be easily solved by extending NodeCanvasTraversal and NodeCanvas to easily set rules for statemachine canvases and how to traverse them. Also, MonoBehaviours are only applicable at runtime, not in the editor... Concerning node groups in #41 , we already solved that in a seperate branch that I yet forgot to merge (stupid me) ;) I'll try to merge it soon. Serialization is not really related to the framework structure directly I think, problem there is more whether we make the property saving generic or hardcoded, and to what degree... Can be implemented with basically every framework structure;) |
Thanks for the reply! |
Ah, now I got you! :) My suggestion when taking the current inheritance route:
My suggestion when taking KnobModifier route:
Note that I'm probably using the terms 'strong-typed' and 'weak-typed' wrong here, I hope you know what I mean... Basically, in comparision when checking if a knob can accept more than one inputs: weakly-typed: nodeKnob.hasModifier (typeof(ConnectionLimit)); and strong-typed: nodeKnob is NodeInput; Additionally, you were naming a KMNoRecursion Knob modifier - That's something we have to discuss. Where should it be defined whether recursion is allowed or not? In the Node itself, in the Knob as you are proposing, or in the actual traversal algorithm that in the end needs to handle this? |
Quick question, Why are InputKnob and OutputKnob seperate classes? The only reason I can see is the assymetry of the calculation and connection limits. A single class with a flag for which direction it is could simplify the code if we end up rolling with knob inheritance without any knob modifiers. |
Yep, the redundancy of the code in these classes is one problem right now. With suggestion number one (inheritance route) the only difference would then be the overwritten method |
I've begun implementation of this (latest commit as of writing this is (5a56c5) The CalculationGraph is completely disabled ATM, and will need to be reimplemented. I have held off on this because of the broken serialization. |
#70 Modular Canvas and Traversal behaviours This update streamlines the process of adding new types of canvases with new properties, behaviours, requirements and traversal algorithms to the framework in a modular way. Canvas behaviour can be scripted by controlling many aspects of the canvas and it's modifications. It's also possible to add new ways of traversing these canvases, either similar to the default calculation, a statemachine, a dialogue system, or just a data visualizer, this is now possible without workarounds. This is made possible by having a canvas type specify the basic properties and behaviours and a seperate traversal class manage the traversal of the canvas. Many canvas types could use the same traversal algorithm. WARNING: I seperated the default calculation behaviour from canvas base class, making it a seperate subclass, and NodeCanvas now serves only as a base class. Due to compability with previous save files (which would be broken if NodeCanvas was now abstract) it's now weakly limited (canvases with default type are possible but prohibited). In order to use previous saves, you first have to specify a valid type (any Canvas subclass) after loading. Also note later on NodeCanvas WILL be made abstract and thus older saves will be broken! Included is a Graph Canvas type with seperate (yet empty) traversal behaviour to show how easily canvases can be scripted. Changelog: - NodeCanvas is now the base class for every other canvas type - Added warning and side panel entry to convert canvas if the loaded canvas is of type NodeCanvas (base type). Rest of the framework is also mostly disabled if current canvas is of base type - Added NodeCanvasTraversal base class to add unique traversal algorithms to a specific canvas type - Added various callbacks to NodeCanvas like OnCreate, OnValidate, OnBeforeSavingCanvas, CanAddNode - Added NodeCanvas.CreateCanvas methods to create a canvas - NodeEditor is now completely lifted off the calculation code, API to calculate changed to `nodeCanvas.TraverseAll ()` and `nodeCanvas.OnNodeChange (Node)` - Added Graph canvas example - Fixed warnings in RootGraphNode
As we make the Node Editor Framework more general and add features like groups that not everyone might need, it also get's more complex and filled with potential useless features, depending on the needs.
I've the rough idea of modular system, that will make a lot of features modular and removable. Examples are the grouping, an additional XML save format (future) #43, the (currently default) forward calculation system, the statemachine behaviour (in developement) #34, or additional general nodes like the action-, expression- and conversion nodes (Branch)
That modular system would not be a part of the software code itself, but a general concept of using pre-processor options to integrate these modules.
Partial classes could be the main key to this. Each module could then add variables and functionality to the Node Editor without messing with the standard code.
Unfortunately, adding functionality to existing functions would not be possible.
Most of this is only theory so far, but it may help with the complexity and amount of features that are planned/implemented.
The text was updated successfully, but these errors were encountered: