Local Area Spawner

Local Area Spawner

The Local Area Spawner is a helper component that can be used to define custom biomes or regions within your game world. Each Local Area Spawner can have any number of custom Spawn Entry references.

Properties

Min Object Count

Max Object Count

Defines how many AI Agents to Spawn in this Region. A random number of AI Agents will be spawned (between Min & Max)

Restrict to Navmesh Areas (Optional)

You can optionally restrict the spawning of AI Agents to specific Navmesh Areas. Check the Unity Navmesh Area documentation for more information about Navmesh Areas and how to use them

RegionSpawners

This defines the list of Spawn Entry AI Agents for this spawner. You can add a new entry to the list by clicking the ‘Add’ button. See the Spawn Entry section of the documentation for more information.

Fundamentals

LocalAreaSpawner defines a spawn region in the world. It does not spawn on its own; instead it’s driven by the RuntimeSpawner’s Region Spawn Loop whenever the player is inside the volume.

Use LocalAreaSpawner when you want:

  • Different spawn sets per area (streets vs interiors, forest vs town, etc.)

  • Local population caps that only apply while the player is nearby

  • Region-aware systems (e.g., “this biome should feel denser than that one”)

Requirements

  • Must have a BoxCollider on the same GameObject.

  • Collider must be set to isTrigger = true.

  • Please remember that for trigger events to be fired, one of the colliders must also have a Rigidbody component on it (either the player or the wave trigger)

  • A RuntimeSpawner must exist in the scene; LocalAreaSpawner will auto-register with it.


Responsibilities

  • Defines a 3D region volume where region-based spawning can occur.

  • Exposes a per-region min/max population target.

  • Holds region-specific SpawnEntries used when the player is inside.

  • Emits a static event when the player enters/exits the region.

  • Automatically registers/unregisters itself with the RuntimeSpawner.


Key Properties

These are the main properties teams are likely to read or tweak at runtime.

Region definition

  • BoxCollider ThisCollider The collider that defines the region. Assigned automatically in OnEnable().

  • List<string> RestrictToNavmeshAreas Optional list of NavMesh area names to constrain spawn placement in this region (overrides global NavMesh area constraints from RuntimeSpawner for this region only).

  • List<SpawnEntry> CustomRegionSpawners Spawn entries used only while the player is inside this region. The RegionSpawnLoop will pick from these when populating the region.

Population targets

  • int MinObjectCount Minimum number of objects the region tries to maintain when active.

  • int MaxObjectCount Maximum number of objects the region is allowed to maintain when active.

These are authoring-time values used by the RegionSpawnLoop unless overridden.

Runtime state

  • bool IsActiveSpawner() Returns true if the player is currently inside this region’s trigger.

  • float DensityMult { get; set; } = 1f Runtime-only scalar you can use from higher-level systems (e.g., a RegionPopulationController) to represent “how dense this region should feel” relative to others. Not used directly by the spawn loop; meant for your own logic/UI/debugging.

  • (int min, int max) DesiredRangeOverride { get; set; } = (-1, -1) Optional runtime override for the region’s population window.

    • When both min and max are non-negative, the RegionSpawnLoop uses this range instead of the authored MinObjectCount / MaxObjectCount.

    • Set to (-1, -1) to revert back to authored values.


Events

public static event Action<LocalAreaSpawner, bool> onPlayerIsInRegion;

Raised whenever the player enters or leaves any LocalAreaSpawner.

  • LocalAreaSpawner – the region instance.

  • booltrue on enter, false on exit.

The event is fired from:

  • OnTriggerStay when a collider with the correct playerTag is inside.

  • OnTriggerExit when that collider leaves.

The playerTag is pulled from the owning RuntimeSpawner’s PlayerTag property.


Runtime Behaviour & Lifecycle

Discovery and registration

On Start():

  • The component finds a RuntimeSpawner in the scene.

  • Caches the spawner’s PlayerTag.

  • Calls spawner.RegisterRegion(this) so the region is known to the central system.

On OnDisable() / OnDestroy():

  • If previously registered, it calls spawner.UnregisterRegion(this).

This makes it safe to enable/disable regions at runtime or load/unload them via additive scenes.

Player presence

  • While the player’s collider stays inside the BoxCollider with the correct tag, isActiveSpawner is set to true and onPlayerIsInRegion(this, true) is raised.

  • When the player exits, isActiveSpawner is set to false and onPlayerIsInRegion(this, false) is raised.

  • The RuntimeSpawner listens to this event to maintain an internal list of active regions and drive the RegionSpawnLoop.


Editor-Only Helpers

These are used by the inspector and gizmos, but good for users to know:

public void ShowHide(bool showHide)
  • Controls whether this region’s gizmo volume is drawn in the Scene view.

  • Called by RuntimeSpawner.ShowHideSpawnVolumes() when toggling region visualization.

OnDrawGizmos():

  • Draws the BoxCollider volume using RuntimeSpawnerSettings colors.

  • Uses a different color when isActiveSpawner is true (player inside).


Typical Usage Patterns

1. Simple Region Setup (Designer Workflow)

  1. Add a RuntimeSpawner to your scene and configure global settings.

  2. Create an empty child GameObject under the spawner, name it Downtown Region.

  3. Add:

    • BoxCollider (set isTrigger = true)

    • LocalAreaSpawner

  4. Resize the BoxCollider to cover the area in the world you want.

  5. In the LocalAreaSpawner inspector:

    • Set Min Object Count / Max Object Count (e.g., 5–25).

    • Add SpawnEntry assets to Custom Region Spawners (zombies, civilians, etc.).

  6. Press play:

    • When the player enters the volume, the region becomes active.

    • The RegionSpawnLoop maintains population between the min/max while the player remains inside.

No scripting required for the basic case.


2. Hooking into Region Enter/Exit (e.g., Music, UI, Difficulty)

using UnityEngine;
using MegaCrush.Spawner;

public class RegionHUD : MonoBehaviour
{
    private void OnEnable()
    {
        LocalAreaSpawner.onPlayerIsInRegion += HandleRegionPresence;
    }

    private void OnDisable()
    {
        LocalAreaSpawner.onPlayerIsInRegion -= HandleRegionPresence;
    }

    private void HandleRegionPresence(LocalAreaSpawner region, bool isInside)
    {
        if (isInside)
        {
            // Example: show region name, change music, adjust difficulty
            Debug.Log($"Entered region: {region.name}");
        }
        else
        {
            Debug.Log($"Left region: {region.name}");
        }
    }
}

Use this pattern for:

  • Showing a “Now entering Downtown” banner.

  • Switching ambient audio.

  • Adjusting threat level or director behaviour.


3. Dynamic Density / Population Control (RegionPopulationController-like)

Example of scaling region populations at runtime using DensityMult and DesiredRangeOverride:

using UnityEngine;
using MegaCrush.Spawner;

public class SimpleRegionPopulationScaler : MonoBehaviour
{
    [SerializeField] private float denseMultiplier = 1.5f;
    [SerializeField] private float sparseMultiplier = 0.5f;

    public void MakeRegionDense(LocalAreaSpawner region)
    {
        // Treat as "high interest" area – more enemies or NPCs
        region.DensityMult = denseMultiplier;

        var baseMin = region.MinObjectCount;
        var baseMax = region.MaxObjectCount;

        int scaledMin = Mathf.RoundToInt(baseMin * denseMultiplier);
        int scaledMax = Mathf.RoundToInt(baseMax * denseMultiplier);

        region.DesiredRangeOverride = (scaledMin, scaledMax);
    }

    public void MakeRegionSparse(LocalAreaSpawner region)
    {
        region.DensityMult = sparseMultiplier;

        var baseMin = region.MinObjectCount;
        var baseMax = region.MaxObjectCount;

        int scaledMin = Mathf.RoundToInt(baseMin * sparseMultiplier);
        int scaledMax = Mathf.RoundToInt(baseMax * sparseMultiplier);

        region.DesiredRangeOverride = (scaledMin, scaledMax);
    }

    public void ResetRegion(LocalAreaSpawner region)
    {
        region.DensityMult = 1f;
        region.DesiredRangeOverride = (-1, -1); // use authored values again
    }
}

You can drive this from:

  • A high-level director system.

  • A difficulty manager (“late-game districts stay denser”).

  • Faction control systems (regions owned by hostile factions are packed, friendly ones are calmer).


4. Programmatically Creating a Region at Runtime

If you want to spawn regions dynamically (e.g., procedural rooms):

using UnityEngine;
using MegaCrush.Spawner;

public class RuntimeRegionBuilder : MonoBehaviour
{
    [SerializeField] private RuntimeSpawner spawner;

    public LocalAreaSpawner CreateRegion(Vector3 position, Vector3 size)
    {
        var go = new GameObject("Runtime Region");
        go.transform.position = position;

        var collider = go.AddComponent<BoxCollider>();
        collider.isTrigger = true;
        collider.size = size;

        var region = go.AddComponent<LocalAreaSpawner>();

        // Optional: assign custom spawners, caps, etc.
        // region.CustomRegionSpawners.Add(...);

        // Registration will happen automatically in Start(),
        // but you can force it if the spawner is already available:
        if (spawner != null)
        {
            spawner.RegisterRegion(region);
        }

        return region;
    }
}

Last updated