How to Use FlowGraphFunctionAsset to Create Reusable Local Functions in Ceres

Use FlowGraphFunctionAsset to store sub-graphs as Unity ScriptableObjects that can be invoked from any other flow graph via Execute Custom Function nodes, enabling modular, maintainable visual scripting.

FlowGraphFunctionAsset is a core component of the Ceres visual scripting framework for Unity (akikurisu/ceres). It allows you to encapsulate complex logic into reusable functions that exist as standalone assets, rather than being embedded directly into a single graph. This article explains how to create, configure, and invoke these assets based on the actual source implementation.

What Is FlowGraphFunctionAsset?

FlowGraphFunctionAsset is a ScriptableObject that serializes a sub-graph along with its input and output parameter metadata. According to the source in Runtime/Flow/FlowGraphFunctionAsset.cs (lines 22-24), the asset stores this data in a serializedInfo field containing input and return parameters. When invoked, the asset's GetFlowGraph() method (lines 29-41) instantiates a fresh FlowGraph instance from this serialized data, allowing the function to execute in isolation while maintaining access to the parent graph's context.

Architecture Overview

The system relies on three primary components working together:

  • FlowGraphFunctionAsset (Runtime): Holds the serialized sub-graph and parameter definitions. Located at Runtime/Flow/FlowGraphFunctionAsset.cs.

  • FlowGraphFunctionRegistry (Editor): Scans the project for all function assets and maintains a lookup table. When assets change, the static OnFunctionUpdate event (lines 40-42 in Editor/Flow/FlowGraphFunctionRegistry.cs) clears the cache. The GetFlowGraphFunctions(Type runtimeType) method (lines 57-61) filters functions by compatible runtime type.

  • FlowNode_ExecuteCustomFunction (Runtime): The base execution node that resolves the asset reference and runs the sub-graph. Found in Runtime/Flow/Models/Nodes/CustomFunctions/FlowNode_ExecuteCustomFunction.cs, this node stores the asset reference in functionAsset (lines 18-19) and caches the compiled sub-graph using a WeakReference during Compile() (lines 63-68).

Creating a FlowGraphFunctionAsset

Step 1: Create the Asset

Right-click in the Unity Project window and select Create → Ceres → Flow Graph Function. This creates a new FlowGraphFunctionAsset file. The inspector will display an empty sub-graph editor where you can build your function logic.

Step 2: Define Inputs and Outputs

Open the asset and use the blackboard to add ports:

  • Click + Function to add a Custom Function Input node for each parameter.
  • Add a Custom Function Output node to define the return value.

These definitions are serialized into the asset's serializedInfo field. The parameter types and names defined here determine the port layout when the function is invoked from other graphs.

Step 3: Configure the Runtime Type (Optional)

If the function should only be available to specific graph types (e.g., only ActorFlowGraph or ComponentFlowGraph), select the appropriate Runtime Type in the asset inspector. The FlowGraphFunctionRegistry uses this filter in GetFlowGraphFunctions() to ensure type safety across your project.

Using FlowGraphFunctionAsset in Other Graphs

Once created, you can invoke the function from any compatible flow graph:

  1. Via Node Search: Press Ctrl+Space (or Space) in the graph editor and type the function name. Select Execute Flow Graph Function to create an execution node.

  2. Via Blackboard: In the target graph's blackboard, click +Function → select your asset. This creates a local function reference.

The generated node (based on FlowNode_ExecuteCustomFunction) automatically exposes input ports matching your function's parameters and an output port for the return value. At runtime, the node resolves the functionAsset reference, calls GetFlowGraph() to obtain the sub-graph instance, and executes it via functionGraph.ExecuteEventAsyncInternal.

Code Examples

Example: Creating a Simple AddInts Function

While the asset is created through the Unity Editor, the resulting structure contains this logical definition:

// Conceptual representation of the serialized sub-graph in FlowGraphFunctionAsset
// File: Runtime/Flow/FlowGraphFunctionAsset.cs

public class AddIntsFunction : FlowGraphFunctionAsset
{
    // serializedInfo stores:
    //   Input Parameter: int a
    //   Input Parameter: int b  
    //   Return Parameter: int result
    
    // GetFlowGraph() creates instance with:
    //   CustomFunctionInput node (ports: a, b)
    //   Add node (a + b)
    //   CustomFunctionOutput node (port: result)
}

Example: Executing from Another Graph

To call this function from C# or within another graph's logic:

using Ceres.Graph.Flow.CustomFunctions;
using Cysharp.Threading.Tasks;

// This node class is automatically generated or can be manually defined
// to match the function signature: (int, int) -> int
public sealed class FlowNode_CallAddInts : FlowNode_ExecuteCustomFunctionTReturn<int, int, int>
{
    // The base class handles:
    // 1. Reading functionAsset reference
    // 2. Passing inputs via PreExecuteCustomFunction (adds to evt.Args)
    // 3. Executing sub-graph via ExecuteEventAsyncInternal
    // 4. Writing return value via PostExecuteCustomFunction
}

Example: Custom Node Implementation

If you need to manually wire the execution without the generic base class:

using Ceres.Graph.Flow;
using Ceres.Graph.Flow.CustomFunctions;
using UnityEngine;

public class MyCustomFunctionCaller : FlowNode
{
    [SerializeField]
    private FlowGraphFunctionAsset functionAsset;
    
    private WeakReference<FlowGraph> _cachedGraph;
    
    public override void Compile(FlowGraphCompilationContext context)
    {
        // Cache the compiled sub-graph
        if (_cachedGraph == null || !_cachedGraph.TryGetTarget(out _))
        {
            var subGraph = context.AddOrCreateFunctionSubGraph(functionAsset);
            _cachedGraph = new WeakReference<FlowGraph>(subGraph);
        }
        base.Compile(context);
    }
    
    protected override async UniTask ExecuteAsync(FlowGraphContext ctx)
    {
        // Resolve and execute
        if (_cachedGraph.TryGetTarget(out var graph))
        {
            await graph.ExecuteEventAsyncInternal(ctx.Event);
        }
    }
}

Key Source Files

File Purpose Location
FlowGraphFunctionAsset.cs Defines the ScriptableObject that stores sub-graphs and parameter metadata. Implements GetFlowGraph() for runtime instantiation. Runtime/Flow/FlowGraphFunctionAsset.cs
FlowGraphFunctionRegistry.cs Editor-only singleton that scans for function assets and maintains a type-filtered lookup table. Handles cache invalidation via OnFunctionUpdate. Editor/Flow/FlowGraphFunctionRegistry.cs
FlowNode_ExecuteCustomFunction.cs Abstract base node that resolves functionAsset references and manages sub-graph execution. Uses WeakReference caching during compilation. Runtime/Flow/Models/Nodes/CustomFunctions/FlowNode_ExecuteCustomFunction.cs
flow_advanced.md Documentation covering local vs. shared functions, UI workflows, and screenshots. Documentation~/docs/flow_advanced.md

Summary

  • FlowGraphFunctionAsset is a Unity ScriptableObject that encapsulates reusable sub-graphs with defined inputs and outputs.
  • Create assets via Create → Ceres → Flow Graph Function, then define parameters using Custom Function Input/Output nodes.
  • The FlowGraphFunctionRegistry automatically discovers assets in the editor, while FlowNode_ExecuteCustomFunction handles runtime execution with weak-reference caching.
  • Reference functions in other graphs through the node search or blackboard to expose typed input and output ports automatically.

Frequently Asked Questions

How do I create a FlowGraphFunctionAsset in Unity?

Right-click in the Project window and select Create → Ceres → Flow Graph Function. This creates a new asset file that you can open in the graph editor to define your function logic and parameters.

What is the difference between a local function and a FlowGraphFunctionAsset?

A local function is embedded directly within a specific flow graph and can only be called from that graph. A FlowGraphFunctionAsset is a standalone ScriptableObject that can be referenced and executed from any compatible flow graph across your entire project, enabling true reusability.

How does Ceres cache function graphs for performance?

During compilation, FlowNode_ExecuteCustomFunction.Compile() stores the instantiated sub-graph in a WeakReference<FlowGraph>. This ensures the graph is built only once per compilation pass while allowing garbage collection if the reference is no longer needed, balancing performance with memory efficiency.

Can I restrict a FlowGraphFunctionAsset to specific graph types?

Yes. In the asset inspector, you can assign a specific Runtime Type (e.g., ActorFlowGraph or ComponentFlowGraph). The FlowGraphFunctionRegistry filters available functions based on this type in GetFlowGraphFunctions(Type runtimeType), ensuring type safety across your project.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →