Workflow Runtime

Runtime Generation

Runtime generation lets you execute the same authored graph logic during play mode and turn the resulting ElementSet into live Unity objects?. The main runtime surface is the ProceduralGraphExecutor component, which now supports explicit generation, sync and async execution, and parameter changes from code before the first run. If you want the graph-side structure behind that API, see Node Graph Basics?.

Quick Start

Typical runtime workflow

  1. Assign a ProceduralGraph to a ProceduralGraphExecutor.
  2. Decide whether the component should auto-run when enabled with Generate On Enable.
  3. Set parameter overrides from code before generation if gameplay needs to control the graph.
  4. Call Generate(), GenerateSync(), or GenerateAsync() depending on how you want the caller to behave. The tradeoff is explained in Sync vs Async?.

Manual generation from a component

Disable Generate On Enable when you want gameplay code to decide exactly when the graph executes. That gives you a clean setup phase for parameter overrides before the first generation.

using System.Threading.Tasks;
using CuriousTrove.OctoShaper;
using UnityEngine;

public sealed class RuntimeExecutorDriver : MonoBehaviour
{
    [SerializeField] private ProceduralGraphExecutor executor;

    private void Start()
    {
        executor.GenerateOnEnable = false;
        executor.ExecuteAsAsync = false;

        executor.SetParameterOverride("Count", 12);
        executor.SetParameterOverride("Radius", 4.5f);

        executor.GenerateSync();
    }

    public void RegenerateNow()
    {
        executor.SetParameterOverride("Count", 24);
        executor.Generate();
    }

    public async Task RegenerateAsync()
    {
        executor.ExecuteAsAsync = true;
        executor.SetParameterOverride("Count", 32);
        await executor.GenerateAsync();
    }
}
  • Generate() respects the current runtime mode. In play mode it uses the async queue when Execute As Async is enabled; otherwise it runs synchronously.
  • GenerateSync() is the explicit immediate path.
  • GenerateAsync() is the explicit awaited path.
  • Use the Generate* methods as the public execution API on the component.

Changing parameter values from code

The executor exposes thin parameter helpers so callers do not need to reach into the underlying configuration object for common runtime workflows.

Available helpers

  • SetParameterOverride(Guid, object)
  • SetParameterOverride(string, object)
  • TrySetParameterOverride(string, object)
  • RemoveParameterOverride(Guid) and RemoveParameterOverride(string)
  • ClearParameterOverrides()

When to use which

  • Use name-based helpers when gameplay code knows the authored parameter names.
  • Use Guid-based helpers when you already cached the parameter id.
  • Use TrySetParameterOverride when a missing parameter should not throw.
using CuriousTrove.OctoShaper;
using UnityEngine;

public sealed class RuntimeParameterExample : MonoBehaviour
{
    [SerializeField] private ProceduralGraphExecutor executor;

    public void ApplyGameplayState(int count, float width)
    {
        executor.SetParameterOverride("Count", count);
        executor.SetParameterOverride("Width", width);
        executor.GenerateSync();
    }

    public void ResetToDefaults()
    {
        executor.ClearParameterOverrides();
        executor.GenerateSync();
    }
}

Direct graph execution without a component

If you need runtime execution without scene instancing, the supported bootstrap path is still small and explicit. This is useful when you want to inspect the main output yourself or feed it into your own systems. If you want the imported-asset version instead, see Procedural Prefab?.

using System.Threading.Tasks;
using CuriousTrove.OctoShaper;
using CuriousTrove.OctoShaper.Core.Data;
using UnityEngine;

public sealed class RuntimeGenerationExample : MonoBehaviour
{
    [SerializeField] private ProceduralGraph graph;

    public async Task<ElementSet> GenerateAsync(int count)
    {
        var context = OctoShaperRuntime.CreateExecutionContext(graph);
        var executor = OctoShaperRuntime.CreateExecutor(graph);
        var configuration = new ProceduralConfiguration();
        var mainInput = ElementSet.CreateImplicitSingle();

        configuration.SetParameterOverride(graph.Parameters[0].Id, count);

        await executor.ExecuteAsync(
            context,
            mainInput,
            configuration);

        return (ElementSet)context.GetMainOutputValue();
    }
}

Choosing the right runtime surface

  • Use ProceduralGraphExecutor when you want live scene generation from a component.
  • Use direct bootstrap through OctoShaperRuntime.CreateExecutionContext(...) and OctoShaperRuntime.CreateExecutor(...) when you want full control over the output without the default instancing behavior.
  • Use a procedural prefab when you want an imported asset that regenerates from graph inputs.

Practical guidance

If you want to drive generation from gameplay code, start by turning off Generate On Enable. That keeps the executor idle until you have applied the right parameter overrides and chosen whether the call should be sync or async.

Treat generated graph code as part of the runtime contract. If runtime execution fails unexpectedly, the first things to check are whether graph code was regenerated, whether the main output is actually an ElementSet, and whether the graph depends on editor-only assumptions.

For reusable gameplay systems, prefer wrapping your parameter names in your own code instead of scattering string literals across the project. That keeps runtime parameter control explicit and easier to refactor later.