Skip to content

Advanced Features: Custom Post-Processing Steps

DunGen's generation process involves several stages: creating the main path, adding branches, placing props, handling the lock & key system, etc. Sometimes, you need to inject your own custom logic during this process, specifically after the main dungeon layout (Dungeon data structure) exists but before generation is fully marked as complete.

This is useful for:

  • Integrating third-party systems (like pathfinding baking or custom culling setups) that need the final level geometry.
  • Running complex analysis or modification routines on the generated Dungeon data.
  • Implementing custom prop placement logic that depends on the overall dungeon structure.
  • Performing tasks that need to happen after DunGen's built-in post-processing (like lock/key placement) but before the final GenerationStatus.Complete event fires.

DunGen offers three ways to add your custom post-processing logic:

  1. Registering Steps via Code: Use the RegisterPostProcessHook method on the DungeonGenerator for fine-grained control.
  2. Creating Custom Adapters: Derive a component from BaseAdapter for a more component-based approach, often used for integrations.
  3. Post-Process Steps in a Custom Generation Pipeline: Define custom post-processing steps in a Generation Pipeline.

Post-processing steps registered via code or adapters are executed first (in the order defined by their priority, highest first), followed by any post-processing steps defined in a custom generation pipeline (which are executed in the order they appear in the list).


Method 1: Registering Steps via Code (RegisterPostProcessHook)

This method allows you to dynamically register a C# method to be called during the post-processing phase.

How it Works:

  1. Get a reference to your DungeonGenerator instance.
  2. Call the generator.RegisterPostProcessHook() method, providing your callback method and configuration options.
  3. DunGen will execute your callback method at the appropriate time during its post-processing phase.

The Method Signature:

PostProcessHook RegisterPostProcessHook(Action<GenerationContext> callback, int priority = 0);
  • Action<GenerationContext> callback: This is your custom method (or lambda expression) that will be executed. It receives the GenerationContext instance as an argument, allowing you to access the current state of the generated dungeon.
  • int priority: Determines the execution order. Higher priority numbers execute first. Steps with the same priority have an undefined execution order relative to each other.
  • Return Value: The method returns a PostProcessHook object that represents your registered step. You can use this object to unregister the step later if needed.

Example:

using DunGen;
using DunGen.Generation;
using DunGen.PostProcessing;
using UnityEngine;

public class MyCustomPostProcessor : MonoBehaviour
{
    // Assign your Runtime Dungeon component in the inspector
    public RuntimeDungeon RuntimeDungeon;

    private PostProcessHook hook;


    private void Start()
    {
        if (RuntimeDungeon == null)
            return;

        // Register our custom method with the default priority and store
        // the returned hook so we can unregister it later
        hook = RuntimeDungeon.Generator.RegisterPostProcessHook(MyCustomPostProcessLogic);
    }

    private void OnDestroy()
    {
        if (RuntimeDungeon == null)
            return;

        // Unregister our custom method
        RuntimeDungeon.Generator.UnregisterPostProcessHook(hook);
    }

    // This is the method that gets called by DunGen
    private void MyCustomPostProcessLogic(GenerationContext context)
    {
        Debug.Log("Running custom post-process step!");

        // Access the generated dungeon data
        Dungeon dungeon = context.Dungeon;

        // Perform your custom logic here
        Debug.Log($"Dungeon has {dungeon.AllTiles.Count} tiles.");
    }
}

Use Cases: Ideal for simple one-off post-processing tasks defined in specific scripts.


Method 2: Creating Custom Adapters (BaseAdapter)

This approach is more component-oriented and is the standard way DunGen integrates systems like Pathfinding and Culling. You create a new script that inherits from DunGen.Adapters.BaseAdapter.

How it Works:

  1. Create a new C# script.
  2. Make it inherit from DunGen.Adapters.BaseAdapter.
  3. Override the abstract Run(GenerationContext context) method. This is where your custom logic goes.
  4. Attach this new component script to the same GameObject that has the RuntimeDungeon component.
  5. DunGen will automatically find all BaseAdapter components on that GameObject and execute their Run methods during the post-processing phase.

Adapter Properties:

  • Priority (Inspector Field): Inherited from BaseAdapter. This integer value controls the execution order of all post-processing steps within the same phase. Higher priority adapters run first.

Example:

using DunGen.Adapters;
using DunGen.Generation;
using UnityEngine;

public class MyCustomAdapter : BaseAdapter
{
    protected override void Run(GenerationContext context)
    {
        Debug.Log($"There are {context.Dungeon.AllTiles.Count} tiles in the dungeon");
    }
}

Use Cases: Excellent for creating reusable integration components (like the built-in pathfinding/culling adapters), especially when the logic might need configuration via Inspector fields on the component itself. Easier to manage as self-contained units.


Method 3: Post-Process Steps in a Custom Generation Pipeline

For projects that use a custom Generation Pipeline, a custom post-process step can be added to the pipeline by creating a new class that implements the IPostProcessStep interface.

Example:

using DunGen.Generation;
using DunGen.PostProcessing;
using System.Collections;
using UnityEngine;

public class MyCustomPostProcessStep : IPostProcessStep
{
    public string DisplayName => "Print Room Count";


    public IEnumerator Execute(GenerationContext context)
    {
        Debug.Log($"Dungeon has {context.Dungeon.AllTiles.Count} rooms.");

        // Since this is a simple synchronous operation, we need to yield break to satisfy the IEnumerator return type
        yield break;
    }
}

This new class will then appear in the dropdown when configuring the Post-Processing step in your custom generation pipeline asset.

Custom Post-Process Step in Pipeline


Choosing the Right Method

  • RegisterPostProcessStep:
    • Use when the logic is part of a script that isn't naturally attached to the RuntimeDungeon GameObject.
    • Use when you need to dynamically add/remove steps based on runtime conditions.
    • Best for simple, one-off post-processing tasks.
  • BaseAdapter:
    • Use for creating self-contained, reusable integration components (preferred for integrations like pathfinding, culling, etc.).
    • Use when you want to configure the step via the Inspector (add public fields to your adapter).
    • Generally simpler setup.
  • IPostProcessStep in a Custom Generation Pipeline:
    • Use when you already have a custom generation pipeline and want to keep all generation logic centralized.
    • Use when you want to share the same post-processing step across multiple dungeons by reusing the pipeline asset.
    • Best for complex generation workflows.

Custom post-processing provides a vital hook for tailoring the generated dungeon and integrating it fully with your game systems.