Region Spawn Library

Global Rule-Based Spawn Configuration for Regions

The Region Spawn Library is an optional but powerful ScriptableObject used to centrally control which enemies (SpawnEntries) appear in which LocalAreaSpawner regions, and how that distribution changes across intensity steps (via SpawnDirector).

Instead of manually editing every region’s CustomRegionSpawners list, the Region Spawn Library lets you define global rules such as:

  • “Industrial tiles spawn Workers and Riot Drones in steps 0–1, and add Mechs in step 2.”

  • “Residential tiles never spawn Heavy units.”

  • “Any tile tagged Urban gets additional police reinforcements at high intensity.”

  • “If no rule matches, fall back to basic enemies.”

These rules are applied automatically at runtime by the Region Population Controller, which resolves the appropriate spawn set for each region and updates it when the global intensity step changes.


When to Use This System

Use the Region Spawn Library if your game has:

1. Multiple Tiles / Biomes / Regions

If your levels or world consist of many distinct areas (e.g., rooms, tiles, districts, zones), and each should have its own enemy composition.

2. Intensity Progression

If you are using a SpawnDirector to increase encounter difficulty over time, and you want the type of enemies (not just quantity) to change dynamically.

3. Procedural or Streaming Environments

The library allows regions to be spawned/destroyed at runtime, with no extra setup — they will automatically inherit globally authored rules.

4. Desire for Centralized Authoring

Instead of editing 100+ LocalAreaSpawners manually, designers can maintain one library asset.


How It Fits Into the Overall Runtime Spawner Architecture

Below is the full pipeline showing where the Region Spawn Library sits:

Intensity Profile  →  SpawnDirector  →  Region Population Controller  →  LocalAreaSpawner
                                  (drives step)              (resolves spawn lists)           (spawns from lists)

Breakdown:

1. SpawnDirector

  • Controls the current intensity step (0, 1, 2, …).

  • Broadcasts step changes to any system that listens.

2. Region Population Controller

  • Listens for:

    • New regions (onRegionRegistered from RuntimeSpawner),

    • Step changes (from SpawnDirector).

  • For each LocalAreaSpawner, it:

    1. Reads its region tags.

    2. Checks which library rules match the region.

    3. Applies step-based weighting.

    4. Builds the region’s CustomRegionSpawners list.

    5. Applies per-region min/max overrides (density scaling).

    6. Prewarms pools for all resolved entries.

3. LocalAreaSpawner

  • Now holds a dynamically constructed spawn list.

  • When active (player inside), it feeds these entries into the global/regional spawn loops in RuntimeSpawner.

4. RuntimeSpawner

  • Handles actual spawning, placement, pooling, despawn timing, etc.


Why This Matters

Without Region Spawn Library:

  • Designers would hand-maintain spawner lists per region.

  • Adding a new enemy type requires editing 20+ regions.

  • Balancing step-based difficulty requires manually adjusting each region’s lists.

With Region Spawn Library:

  • Designers maintain one centralized asset.

  • Adding a new enemy type is as simple as adding a rule.

  • Regions automatically update on:

    • Step changes

    • Region activation

    • Region registration (procedural worlds)

  • All SpawnEntry pools are ensured automatically.

This dramatically reduces authoring overhead while increasing consistency across the entire world.


When NOT to Use It

You can skip the Region Spawn Library if:

  • Your game only has one global spawn set.

  • Your regions are hand-tuned and intentionally unique.

  • You do not use intensity steps / SpawnDirector.

  • Your game only uses wave or triggered encounters with fixed spawn lists.

It is completely optional — the Runtime Spawner operates perfectly without it.


Authoring Workflow Summary

  1. Create the Library

    Create → Runtime Spawner → Libraries → Region Spawn Library
  2. Add Rules Each rule adds one SpawnEntry to regions that match tag + step filters.

  3. Add Fallback Entries Used only when no rules match a region.

  4. Set Region Tags Regions provide their tags via:

    • Implementing IRegionTagProvider on a component, or

    • Using the region GameObject’s Unity Tag.

  5. Assign Library Add a RegionPopulationController to your scene and assign the library.

  6. Play Mode The controller:

    • Resolves spawn lists per region,

    • Updates them when the intensity step changes,

    • Applies min/max scaling per region,

    • Prewarms all pools automatically.


Quick Example

“Industrial zones should spawn Workers at the start, then Riot Drones in mid game, and Mechs in late game.”

You’d create three rules:

Rule 1:
  entry = Worker
  requiredTags = ["Industrial"]
  minStep=0, maxStep=1

Rule 2:
  entry = RiotDrone
  requiredTags = ["Industrial"]
  minStep=1, maxStep=2

Rule 3:
  entry = Mech
  requiredTags = ["Industrial"]
  minStep=2, maxStep=3

All industrial tiles immediately adopt these rules — no hand configuration required.


Quick Diagram

          ┌────────────────────┐
          │  Region Spawn       │
          │     Library         │
          └─────────┬──────────┘
                    │ (rules)

          ┌────────────────────┐
          │ Region Population  │
          │     Controller     │
          └─────────┬──────────┘
                    │ (resolved spawn list)

          ┌────────────────────┐
          │ LocalAreaSpawner   │
          │ (active region)    │
          └─────────┬──────────┘
                    │ (entries)

          ┌────────────────────┐
          │   RuntimeSpawner   │
          │ (actual spawning)  │
          └────────────────────┘

Foundation

RegionSpawnLibrary is a ScriptableObject that defines global, designer-authored spawn rules for LocalAreaSpawner regions.

At runtime, RegionPopulationController queries this asset to build each region’s CustomRegionSpawners list based on:

  • Region tags (semantic labels, e.g. "Industrial", "Residential").

  • Current SpawnDirector step.

  • Optional step-based weights.

This lets you define “which enemies appear in which tiles, at which intensity step” in one central place, instead of editing every region individually.


Asset Layout

public sealed class RegionSpawnLibrary : ScriptableObject
{
    public List<Rule> rules = new();
    public List<SpawnEntry> fallbackEntries = new();
}
  • rules All authored rules. RegionPopulationController scans this list whenever:

    • The director step changes, or

    • A new region is registered.

  • fallbackEntries Used only when no rules match a given region:

    • If empty → that region gets no entries from the library (only its own CustomRegionSpawners, if any).

    • If populated → these entries are used as a default set.


Rule Definition

[Serializable]
public struct Rule
{
    public string      name;
    public SpawnEntry  entry;

    // Filters
    public string[]    requiredTags;
    public string[]    anyTags;
    public int         minStep;
    public int         maxStep;

    // Weights
    public AnimationCurve weightByStep;

    // Behavior
    public bool        replaceInsteadOfAppend;

    // Caps (optional)
    public int         perRegionCap;
}

Each rule controls how one SpawnEntry is introduced into matching regions.

Identification

  • name Human-readable identifier for inspectors/logs. Matching and population are based on entry, not this name.

  • entry The SpawnEntry (enemy, critter, object, etc.) that will be added to matching regions.


Filters

[Header("Filters")]
public string[] requiredTags;
public string[] anyTags;
public int      minStep;
public int      maxStep;

These determine when and where the rule applies.

  • requiredTags (“ALL” filter) All of these tags must be present on the region:

    • Tags are provided by the game via RegionPopulationController.IRegionTagProvider or the region’s GameObject tag.

    • Empty array → skip this filter.

  • anyTags (“ANY” filter) At least one of these tags must be present on the region:

    • Empty array → skip this filter.

  • minStep / maxStep Inclusive SpawnDirector step range:

    • Rule only applies when:

      currentStep >= minStep && currentStep <= maxStep
    • Use this to phase enemies in/out across intensity steps.

If either tag filter or step range fails, the rule does not contribute its entry to that region.


Weights

[Header("Weights")]
public AnimationCurve weightByStep;

Optional weighting curve by normalized step (0..1):

  • X-axis: normalized step, computed from currentStep and total step count.

  • Y-axis: weight (≥ 0).

    • 0 → effectively disables this rule at that normalized step.

    • 1 → neutral.

    • > 1 → higher priority when sorting.

RegionPopulationController uses:

public float EvaluateWeight(in Rule rule, int currentStep, int stepsCount)
  • If weightByStep is null or empty → weight = 1.

  • Otherwise → evaluates the curve and clamps to ≥ 0.

Rules for a region are sorted by:

  1. Weight (descending).

  2. Entry name (ascending, as a tie-break).


Behavior

[Header("Behavior")]
public bool replaceInsteadOfAppend;
  • replaceInsteadOfAppend = false Matching rules append their entry to the region’s existing CustomRegionSpawners.

  • replaceInsteadOfAppend = true Matching this rule can cause the resolved list to replace the region’s authored entries instead of appending.

If any matched rule has replaceInsteadOfAppend = true, the controller will treat the final result as a “replace” set rather than an “append” set (but the replacement itself is still a single resolved list).

Use this to define “global templates” that override local authoring when certain conditions are met.


Caps (Optional)

[Header("Caps")]
public int perRegionCap;
  • Optional per-region cap for this specific entry.

  • 0 = no extra cap.

  • Enforcement of this field is left to the calling system. The core spawn loops always respect SpawnEntry.maxPopulation globally.

You can use this field if you plan to extend the system with additional per-region constraints.


Weight Evaluation Helpers

public float EvaluateWeight(in Rule rule, int currentStep, int stepsCount)
{
    float s01 = NormalizeStep(currentStep, stepsCount);
    return (rule.weightByStep != null && rule.weightByStep.length > 0)
        ? Mathf.Max(0f, rule.weightByStep.Evaluate(s01))
        : 1f;
}

public static float NormalizeStep(int currentStep, int stepsCount)
{
    if (stepsCount <= 1) return 1f;
    int maxIndex = Mathf.Max(1, stepsCount - 1);
    return Mathf.Clamp01(currentStep / (float)maxIndex);
}
  • NormalizeStep Converts a 0-based step into a 0..1 value, using the max index (stepsCount - 1) as the upper bound.

  • EvaluateWeight Calls the curve (if present) at this normalized step, with safe defaults and clamping.


Authoring Workflow

  1. Create the library asset:

    • Create → Runtime Spawner → Libraries → Region Spawn Library

  2. Fill rules:

    For each enemy type / spawn entry you want to distribute:

    • Assign entry to that SpawnEntry.

    • Set requiredTags / anyTags to tile tags (e.g. "Industrial", "Residential", "Downtown").

    • Set minStep / maxStep to the director step range where this entry should be active.

    • Optionally, assign weightByStep:

      • For example, start at 0 and ramp to 1 so the enemy only appears in later steps.

    • Decide whether this rule should append or replace via replaceInsteadOfAppend.

  3. Optionally fill fallbackEntries:

    • Assign a few generic SpawnEntrys that should appear in tiles that match no rules.

  4. Assign the library to:

    • RegionPopulationController.library.

At runtime, when RegionPopulationController recomputes a region:

  • It obtains the region’s tags (from IRegionTagProvider or the GameObject tag).

  • Filters all rules by tags and minStep/maxStep.

  • Evaluates rule weights for the current step and sorts them.

  • Builds the final CustomRegionSpawners list (append or replace).

  • Calls RuntimeSpawner.EnsurePoolForEntry for each resolved SpawnEntry.

Last updated