FlowCanvas Forums › Support › Modular Design › Reply To: Modular Design
I ended up implementing Modular Macros myself, although I’m not very proud of the code. It’s a janky mess and I’m not even sure how it works, but hey it gets the job done for now!
Here’s the code for my Modular Macro Node:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
using FlowCanvas; using FlowCanvas.Macros; using NodeCanvas.Framework; using UnityEngine; namespace Assets.External.ParadoxNotion.FlowCanvas.Module.Nodes { public class ModularMacro : MacroNodeWrapper { private Macro currentInstance; private Macro currentSerialized; public override Macro macro { get { if (!Application.isPlaying || useAbstractMacro) return _macro; if (macroInput.value == null) return null; if (currentInstance != null && currentSerialized == macroInput.value) return currentInstance; currentSerialized = macroInput.value; currentInstance = Graph.Clone(macroInput.value); for (int i = 0; i < currentInstance.inputDefinitions.Count; i++) { var abstractIn = _macro.inputDefinitions<em class="d4pbbc-italic"></em>; var instanceIn = currentInstance.inputDefinitions<em class="d4pbbc-italic"></em>; if (abstractIn.type != typeof (Flow)) { currentInstance.entryFunctionMap[instanceIn.ID] = _macro.entryFunctionMap[abstractIn.ID]; } else { currentInstance.entryActionMap[abstractIn.ID] = currentInstance.entryActionMap[instanceIn.ID]; } } for (int i = 0; i < currentInstance.outputDefinitions.Count; i++) { var abstractOut = _macro.outputDefinitions<em class="d4pbbc-italic"></em>; var instanceOut = currentInstance.outputDefinitions<em class="d4pbbc-italic"></em>; if (abstractOut.type != typeof (Flow)) { currentInstance.exitFunctionMap[abstractOut.ID] = currentInstance.exitFunctionMap[instanceOut.ID]; } else { currentInstance.exitActionMap[instanceOut.ID] = _macro.exitActionMap[abstractOut.ID]; } } currentInstance.entry.BindPorts(); currentInstance.exit.BindPorts(); currentInstance.StartGraph(graphAgent, graphBlackboard, false, null); return currentInstance; } set { if (!Application.isPlaying) _macro = value; } } private bool useAbstractMacro; private ValueInput<Macro> macroInput; protected override void RegisterPorts() { macroInput = AddValueInput<Macro>("Macro"); useAbstractMacro = true; base.RegisterPorts(); useAbstractMacro = false; } } } |
The script will require some minor changes to the parent class, MacroNodeWrapper. I think it’s just marking some methods as virtual.
To use it, place the above node on your blackboard, and in the first “Macro” slot, provide it with the “Interface” macro. This “interface” Macro works much like a C# interface, in that it only defines the functions (or in this case, flow and value nodes for the macro) and should have no actual logic inside.
Once you’ve set the interface macro, refresh the ports.
To load modular macros, duplicate your interface macro and add logic to those new macros. You can then provide those new macros to the Modular Macro Node with a Blackboard variable.