A playful modular behavior system for Unity
Noddle Flow is a lightweight node-based sequencer for creating character and AI behaviors.
It turns βspaghetti codeβ into an elegant bowl of logic noodles: every action, every condition, every loop is a node in a flow.
Instead of juggling Update() calls and nested if/else, you build a graph of actions.
Each node represents a step β moving, waiting, playing an animation, or sending an event β and the executor handles transitions automatically.
- Visual Graph Editor (GraphView-based)
Create, connect, and test your logic visually in the Unity Editor. - Runtime Executor
Executes the graph at runtime, node by node, with full coroutine support. - Event System
Trigger or stop flows usingTriggerEvent(string name). - Reusable Blocks
Each node is a modularScriptableObjectβ extend, duplicate, remix.
| Node | Description |
|---|---|
| GoToTransform | Move towards a target until reached. |
| FollowTransform | Continuously follow a target until an event stops it. |
| PlayAnimation | Trigger an Animator state and wait for it to end. |
| WaitForEvent | Pause execution until a custom event is fired. |
| WaitSeconds | Simple timer node for delays or pacing. |
| Decision | Branch the flow toward different outputs based on a numeric condition |
| DestroyTarget | Destroy a gameobject |
| Debug | Show a preset message in the Unity console |
Extend the system with your own nodes β
- Add the GraphToolKit package to your project (com.unity.graphtoolkit)
- Install Noddle Flow as a Unity package
- Add a FlowExecutor component to your scene
- Create a new FlowGraph asset and start adding nodes (at least a StartNode and an EndNode ;))
- Link your nodes and press Play
FollowTransform(Player) β WaitEvent(go_fight_monster) β FollowTransform(monster) β PlayAnimation(EatBanana)
The agent follows the player, waits for an event, runs to the monster, then eat a banana.
Use a small StateMachineBehaviour to signal animation completion:
public class NotifyOnExit : StateMachineBehaviour {
public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
animator.GetComponent<GraphExecutor>()?.NotifyAnimationEnded();
}
}Attach this script to the animation state that ends your action.
To add your own block:
[CreateAssetMenu(menuName="NoddleFlow/Nodes/MyCustomNode")]
public class MyCustomNode : BaseNode {
protected override void OnDefinePorts(IPortDefinitionContext context) {
// Add your own ports here
context.AddInputPort<bool>("TriggerIn").Build();
context.AddOutputPort<bool>("TriggerOut").Build();
}
public override AiBlockExecutor ConvertToExecutor(AiRuntimeGraph aiRuntimeGraph) {
// Create the executable version of your node here
// Add the ports to the appropriate GenericDictionnary
// in AiRuntimeGraph
}
}Then drop it in your graph and connect it like any other node.
Create his runtime behavior in Assets/Scripts/NoddleFlow/Behaviours
public class MyCustomNodeExecutor : AiBlockExecutor {
public string myVariableUuid;
public override async Task Execute(GraphExecutor graphExecutor) {
// the runtime behaviour of your node (use the variables stored on the
// data on the AiRuntimeGraph genericDictionnaries
await graphExecutor.runtimeGraph.executors[outputUuidTrigger].Execute(graphExecutor);
}
}Created with love (and too many noodles) by Ours Agile Studio.
If you enjoy it, consider offering a coffee: https://ko-fi.com/aqueuse β
Pull requests, new node ideas, and bug hunts are always welcome!
