Spawn Director

Overview

The Spawn Director controls the overall intensity curve of your encounter. Instead of every spawner behaving at a fixed difficulty, the director applies step-based multipliers that adjust pacing over time.

One thing to note: the Spawn Director directly overrides the 'global population' count on the main Runtime Spawner with the 'Concurrent Alive Cap' for the current Intensity Step. This directly affects how many spawned entities can be active and affects all of the other spawners - Global, Wave and Local Area Spawner!

This lets designers “script” the escalation of tension without hand-authoring every wave.

Core Concepts

Intensity Profile

A ScriptableObject defining steps. Each step sets:

  • Concurrent Alive Cap – how many enemies can exist at once. Overrides the Max Global Population setting on the main Runtime Spawner.

  • Global Spawn Window – min/max delay between spawn events.

  • Spawn Rate Multiplier – speeds up or slows down all spawn rates.

  • Wave Rate Multiplier – adjusts timing between waves.

  • Advance Modes

    • Manual: Game code calls StepUp(), StepDown(), or SetStep().

    • Auto By Time: Steps advance automatically every N seconds.

  • Start Step Defines which step the director begins on.

Usage Patterns

  • Cinematic Escalation: Start at step 0 (calm), advance every 60s until reaching the climax step.

  • Dynamic Difficulty: Use game triggers (player health low, objective destroyed, etc.) to call StepUp() or StepDown().

  • Replayable Missions: Adjust profile assets per mission type, reusing the same director logic.

Also see:

Intensity Profile

Fundamentals

SpawnDirector drives spawn system intensity over time or via explicit step changes. It reads an IntensityProfile and applies its values to a bound RuntimeSpawner, tuning:

  • Global concurrent alive cap

  • Global population window

  • Global spawn rate multiplier

  • Wave interval multiplier

It provides a simple step-based interface for controlling pacing (early game, mid game, late game, final push, etc.).


Responsibilities

  • Bind to a RuntimeSpawner and update its tuning values.

  • Apply a configurable IntensityProfile with multiple steps.

  • Advance steps:

    • Automatically by time, or

    • Manually via API (StepUp, StepDown, SetStep).

  • Notify listeners whenever the active step changes.

RuntimeSpawner calls into SpawnDirector during its own lifecycle to initialize and drive the director.


Requirements

  • A RuntimeSpawner in the scene.

  • An IntensityProfile asset with one or more steps, each defining:

    • concurrentAliveCap

    • globalRange (min/max global population)

    • spawnRateMult

    • waveRateMult

    • Advance mode and per-step timing (in the profile).

There is typically one SpawnDirector per gameplay session or scene.


Key Fields and Properties

Profile

public IntensityProfile Profile
  • The profile that defines:

    • Step list (steps)

    • Advance mode (AdvanceMode.Manual or AdvanceMode.AutoByTime)

    • Timing (secondsPerStep for auto mode).

Can be assigned in the inspector or at runtime.

Step information

public int CurrentStep  { get; }
public int MaxStep      { get; }
public int StepsCount   { get; }
  • CurrentStep Current applied step index (0-based).

  • MaxStep Highest valid step index (0-based), or 0 if the profile is empty.

  • StepsCount Number of steps defined by the profile, or 0 if none.

Events

public event Action<int> OnStepChanged;

Raised whenever the active step changes. Payload: the new step index (0-based).

This event allows other systems (e.g. RegionPopulationController, UI, audio) to react in sync with spawn intensity.


Lifecycle (RuntimeSpawner integration)

SpawnDirector is controlled from RuntimeSpawner:

Init

public void Init(RuntimeSpawner spawner)
  • Binds the director to the RuntimeSpawner instance.

  • Called from RuntimeSpawner.Init().

Begin

public void Begin()
  • Validates that both Profile and RuntimeSpawner are set and that the profile has at least one step.

  • Applies the starting step (startStep, clamped to valid range).

  • If the profile’s advance mode is AutoByTime, starts an internal coroutine (AutoAdvance) to advance steps over time.

  • Called from RuntimeSpawner.StartSpawners().

Stop / Pause / Resume / End

public void Stop()   // Alias for Pause
public void Pause()
public void Resume()
public void End()
  • Pause() Stops auto-advance without resetting the current step.

  • Resume() Restarts auto-advance from the current step if:

    • A valid profile is present.

    • A spawner is bound.

    • The profile is configured for timed advance.

  • End() Fully stops the director and clears transient state (no step reset). Called from RuntimeSpawner.StopSpawners() or equivalent when tearing down the session.


Step Control API

These methods provide manual control over the current step.

public void StepUp(int delta = 1)
public void StepDown(int delta = 1)
public void SetStep(int index)
  • StepUp(delta) Increments the current step by delta, clamped to the range [0, MaxStep].

  • StepDown(delta) Decrements the current step by delta, clamped to [0, MaxStep].

  • SetStep(index) Sets the current step to index, clamped to [0, MaxStep].

Each of these calls routes through ApplyStep, which:

  1. Updates _currentStep.

  2. Retrieves the step data from Profile.steps[_currentStep].

  3. Invokes OnStepChanged(_currentStep).

  4. Applies tuning to the bound RuntimeSpawner:

    • _spawner.SetConcurrentAliveCap(step.concurrentAliveCap);

    • _spawner.SetGlobalSpawnWindow(step.globalRange.x, step.globalRange.y);

    • _spawner.SetSpawnRateMultiplier(step.spawnRateMult);

    • _spawner.SetWaveRateMultiplier(step.waveRateMult);


Auto-Advance Behaviour

When Profile.advance == IntensityProfile.AdvanceMode.AutoByTime:

private IEnumerator AutoAdvance()
{
    while (enabled && profile)
    {
        yield return new WaitForSeconds(profile.secondsPerStep);
        StepUp();
    }
}
  • Runs while the component is enabled and a profile is assigned.

  • Waits for profile.secondsPerStep between step advances.

  • Each tick calls StepUp(), which clamps at MaxStep. (The profile defines whether to stop at the last step or if other behaviour is desired.)

Auto-advance can be paused/resumed via Pause() and Resume() and is started from Begin().


Typical Usage Patterns

1. Basic intensity progression over time

Configuration:

  1. Create an IntensityProfile asset (e.g. SurvivalProfile).

  2. Define several steps:

    • Step 0: low population, slow spawn rate.

    • Step 1: medium population, medium spawn rate.

    • Step 2: high population, fast spawn rate.

  3. Set:

    • advance = AutoByTime

    • secondsPerStep = 60 (for example).

In the scene:

  1. Add RuntimeSpawner and configure spawn entries.

  2. Add SpawnDirector and assign:

    • Profile = SurvivalProfile

    • startStep = 0

  3. Ensure RuntimeSpawner finds the director in Init() (this happens automatically if both are on the same GameObject, or via the existing discovery logic).

At runtime:

  • The spawner initializes and starts.

  • SpawnDirector.Begin() applies step 0.

  • Every 60 seconds, AutoAdvance increases the step:

    • Step 1, then Step 2, adjusting:

      • MaxObjectCount

      • Global min/max population

      • Spawn rates

      • Wave rates


2. Manual step control from game state

For a mission-driven or wave-based mode where steps are tied to objectives:

using UnityEngine;
using MegaCrush.Spawner;

public class MissionPacing : MonoBehaviour
{
    [SerializeField] private SpawnDirector director;

    public void OnObjective1Complete()
    {
        director.SetStep(1);   // Medium intensity
    }

    public void OnObjective2Complete()
    {
        director.SetStep(2);   // High intensity
    }

    public void OnBossPhase()
    {
        director.SetStep(director.MaxStep); // Final intensity
    }
}
  • Attach this script to an object managing mission flow.

  • Call SetStep in response to mission events, cutscenes, or checkpoints.


3. Hooking other systems to step changes

Other systems can subscribe to OnStepChanged to adjust their own behaviour in sync with spawn intensity:

using UnityEngine;
using MegaCrush.Spawner;

public class IntensityHUD : MonoBehaviour
{
    [SerializeField] private SpawnDirector director;

    private void OnEnable()
    {
        if (director != null)
            director.OnStepChanged += HandleStepChanged;
    }

    private void OnDisable()
    {
        if (director != null)
            director.OnStepChanged -= HandleStepChanged;
    }

    private void HandleStepChanged(int step)
    {
        // Example: update HUD, music, or post-processing
        Debug.Log($"Spawn intensity step changed to: {step}");
    }
}

Common uses:

  • Updating UI “Threat Level” indicators.

  • Blending music layers.

  • Adjusting non-spawn systems (e.g. loot quality, timer pressure) in lockstep with spawn difficulty.


4. Pausing and resuming intensity progression

If the game can be paused or enter a safe zone:

using UnityEngine;
using MegaCrush.Spawner;

public class PauseHandler : MonoBehaviour
{
    [SerializeField] private SpawnDirector director;

    public void OnGamePaused()
    {
        director.Pause();
    }

    public void OnGameResumed()
    {
        director.Resume();
    }
}

With this setup:

  • Auto-advance is stopped while the game is paused.

  • When the game resumes, the director continues advancing from the same step.


5. Switching profiles at runtime

To change pacing model (for example, between different difficulty modes):

using UnityEngine;
using MegaCrush.Spawner;

public class DirectorProfileSwitcher : MonoBehaviour
{
    [SerializeField] private SpawnDirector director;
    [SerializeField] private IntensityProfile normalProfile;
    [SerializeField] private IntensityProfile hardProfile;

    public void UseNormal()
    {
        director.Profile = normalProfile;
        director.SetStep(0);
        director.Resume();
    }

    public void UseHard()
    {
        director.Profile = hardProfile;
        director.SetStep(0);
        director.Resume();
    }
}

This allows the same scene to support multiple pacing profiles without changing the underlying spawn setup.

Last updated