Skip to main content

Developer Guide: Extending Dynamic Dungeon

This guide is for developers who want to extend the toolset by creating custom nodes, constraints, or integrating with the procedural pipeline via code.


1. Creating a Custom Node

All nodes in the World Generator graph must implement the IGenNode interface. For performance, it is highly recommended to use Unity's Job System and Burst Compiler.

Basic Node Structure

using DynamicDungeon.Runtime.Core;
using DynamicDungeon.Runtime.Graph;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

[NodeCategory("MyCategory")]
[NodeDisplayName("My Custom Node")]
public class MyCustomNode : IGenNode
{
private string _nodeId;
private string _nodeName;

// Define your ports here
public IReadOnlyList<NodePortDefinition> Ports => _ports;
private NodePortDefinition[] _ports;

public JobHandle Schedule(NodeExecutionContext context)
{
// 1. Get input channels
NativeArray<float> input = context.GetFloatChannel("Input");

// 2. Get/Create output channels
NativeArray<float> output = context.GetFloatChannel("Output");

// 3. Schedule a Job
MyCustomJob job = new MyCustomJob {
Input = input,
Output = output
};

return job.Schedule(output.Length, 64, context.InputDependency);
}
}

Key Attributes

  • [NodeCategory("Name")]: categorises the node in the searcher.
  • [NodeDisplayName("Name")]: The name displayed on the node header.
  • [Description("Text")]: Tooltip shown when hovering over the node.

2. Port Definitions & Data Types

The system supports five primary data types for ports:

TypePort DefinitionC# Underlying Type
FloatChannelType.FloatNativeArray<float>
IntChannelType.IntNativeArray<int>
Bool MaskChannelType.BoolMaskNativeArray<byte> (0 or 1)
Point ListChannelType.PointListNativeList<float2>
PlacementsChannelType.PlacementsNativeList<PlacementRecord>

3. The Execution Pipeline

The Schedule method is called once per generation attempt. You are responsible for:

  1. Retrieving the NativeArray or NativeList from the NodeExecutionContext.
  2. Configuring your Job.
  3. Returning a JobHandle to ensure the graph execution stays parallel and non-blocking.
tip

Always use [BurstCompile] on your Jobs to ensure maximum performance during the generation phase.


4. Customising the Solver (Constraint Generator)

The Constraint Generator can be extended by implementing custom room selection logic or connectivity rules.

Scripting the Generation

You can trigger generation from your own scripts using the DungeonGenerationService:

DungeonGenerationService service = new DungeonGenerationService();
DungeonGenerationResult result = await service.GenerateLayoutAsync(myRequest);

if (result.Success)
{
// Do something with the layout
Debug.Log($"Dungeon generated with {result.Layout.Rooms.Count} rooms.");
}

5. Map Diagnostics API

You can run diagnostics programmatically to automate unit tests for your procedural levels.

GeneratedMapDiagnosticRules rules = new GeneratedMapDiagnosticRules();
GeneratedMapDiagnosticGrid grid = GeneratedMapDiagnostics.BuildGrid(myTargets, rules, myRegistry);
GeneratedMapDiagnosticResult result = GeneratedMapDiagnostics.RunAStar(grid, startKey, endKey, rules);

if (result.Success)
{
// Path exists!
}