Skip to content

Advanced Features: Runtime Events & Code Execution

After DunGen has finished generating the dungeon layout, placing tiles, handling props, and potentially running integrations like pathfinding, you'll often need to execute your own custom game logic. This could involve:

  • Spawning the player character at the start tile.
  • Initializing enemies or interactive objects placed within tile prefabs.
  • Activating global game systems that depend on the dungeon layout.
  • Fading in from a loading screen.

DunGen provides two primary mechanisms to hook into the generation process and run your code once the dungeon is ready:

  1. On Specific Objects: Implement an interface (IDungeonCompleteReceiver) on components attached to GameObjects within your Tile prefabs.
  2. Globally: Listen to completion or status change events directly from the DungeonGenerator instance.

Method 1: On Objects Inside the Dungeon (IDungeonCompleteReceiver)

This method is ideal when you want logic to run that is specific to a particular GameObject or prefab placed as part of the dungeon.

How it Works:

  1. Create a C# script.
  2. Implement the DunGen.IDungeonCompleteReceiver interface in your script.
  3. This interface requires you to implement one method: void OnDungeonComplete(Dungeon dungeon).
  4. Attach this script component to a GameObject that is part of one of your Tile prefabs.
  5. After DunGen successfully generates the entire dungeon layout, it will search all GameObjects within the final layout for components that implement IDungeonCompleteReceiver and call their OnDungeonComplete method.

Parameters:

  • Dungeon dungeon: Provides access to the data structure representing the completed dungeon layout (containing information about tiles, doorways, paths, etc.).

Example: Spawning an Item or Enemy within a Tile

using UnityEngine;
using DunGen;

// Attach this script to a GameObject inside one of your Tile prefabs
public class PostGenerationSpawner : MonoBehaviour, IDungeonCompleteReceiver
{
    // Assign the prefab to spawn in the Inspector
    public GameObject PrefabToSpawn;

    public void OnDungeonComplete(Dungeon dungeon)
    {
        // Check if we actually have a prefab assigned
        if (PrefabToSpawn == null)
        {
            Debug.LogWarning("PrefabToSpawn is not set!", this);
            return;
        }

        Debug.Log($"Dungeon complete! Spawning '{PrefabToSpawn.name}' at {transform.position}", this);

        // Instantiate the prefab at this GameObject's position and rotation
        // Parent it to this object's parent (likely the Tile root) for organisation
        // NOTE: If you're using Tile pooling, you will need to clean up the spawned objects manually
        GameObject instance = Instantiate(PrefabToSpawn, transform.position, transform.rotation, transform.parent);

        // Optional: You could add logic here to initialize the spawned object
        // EnemyController enemy = instance.GetComponent<EnemyController>();
        // if(enemy != null) { enemy.Initialize(); }
    }
}

Use Cases:

  • Activating specific logic within a room once the whole dungeon is ready.
  • Spawning enemies or items at designated locations within tiles.
  • Initializing puzzle elements placed inside prefabs.

Method 2: Globally via Generator Events

This method is suitable when you need to run logic that relates to the dungeon generation process as a whole, rather than a specific object within it. You subscribe to an event on the DungeonGenerator.

How it Works:

  1. Get a reference to your RuntimeDungeon component and its underlying DungeonGenerator instance.
  2. The DungeonGenerator has a delegate/event called OnGenerationComplete.
  3. Create a method in your script that matches the required signature: void OnGenerationComplete(DungeonGenerator generator, GenerationStatus status)
  4. Subscribe your method to the OnGenerationComplete event (using +=).
  5. Crucially, remember to unsubscribe from the event (using -=) when your listener object is destroyed or disabled to prevent errors.

Parameters:

  • DungeonGenerator generator: The instance of the generator that fired the event.

Example: Spawning the Player After Dungeon Completion

public class GlobalDungeonCompleteListener : MonoBehaviour
{
    // Assign your Runtime Dungeon in the Inspector
    public RuntimeDungeon RuntimeDungeon;
    // Assign your player prefab in the Inspector
    public GameObject PlayerPrefab;


    private void Start()
    {
        if (RuntimeDungeon == null)
        {
            Debug.LogError("RuntimeDungeon is not assigned!", this);
            return;
        }

        var generator = RuntimeDungeon.Generator;

        // Subscribe to the event
        generator.OnGenerationComplete += HandleDungeonCompletion;

        // Optional: If generation might already be complete before Start runs
        // (e.g., if GenerateOnStart was true), check current status.
        if (generator.Status == GenerationStatus.Complete)
            HandleDungeonCompletion(generator);
    }

    void OnDestroy()
    {
        // IMPORTANT: Unsubscribe when this object is destroyed
        if (RuntimeDungeon != null)
            RuntimeDungeon.Generator.OnGenerationComplete -= HandleDungeonCompletion;
    }

    private void HandleDungeonCompletion(DungeonGenerator generator)
    {
        Debug.Log("Dungeon Generation Complete! Spawning Player...");

        if (PlayerPrefab == null)
        {
            Debug.LogError("Player Prefab is not assigned!", this);
            return;
        }

        // Get the start tile location from the generated dungeon data
        if (generator.CurrentDungeon != null)
        {
            var startTile = generator.CurrentDungeon.MainPathTiles[0];
            Vector3 spawnPosition = startTile.transform.position;
            // You might want to offset this slightly or use a specific spawn point transform within the start tile
            Instantiate(PlayerPrefab, spawnPosition, Quaternion.identity);
        }
        else
            Debug.LogError("Could not find generated dungeon to spawn player!", this);
    }
}

Use Cases:

  • Spawning the player character.
  • Initializing global managers or systems after the level is built.
  • Signaling that loading is complete and fading in the game view.

Choosing the Right Method

  • Use IDungeonCompleteReceiver for logic tightly coupled to specific prefabs or locations within the dungeon.
  • Use the OnGenerationComplete event for global actions that need to happen once the entire dungeon generation process is finished.