Upgrading from 1.3.x → 1.4.0

Version 1.4.0 adds designer-friendly macro control for local (“tile”) spawns, deterministic global seeding, and editor polish. It’s backward compatible - if you don’t opt in, your 1.3 setups keep working.


What changed

🗺️ Region-scale population control (new)

  • RegionPopulationController (scene component) scales each active LocalAreaSpawner via a step-driven density curve, can enable/disable sparse regions, resolves each region’s spawn list from a global RegionSpawnLibrary, and pre-warms pools.

  • RegionSpawnLibrary (ScriptableObject) defines rule-based SpawnEntry mixes per region using tags, director step ranges, and optional weights. Rules can append or replace a region’s authored list; a fallback list covers “no match” cases.

  • SpawnDirector integration so region density & mixes track your intensity steps automatically.

🎲 Deterministic global seeding (new)

  • RuntimeSpawner now supports a Global Seed with a Use Deterministic Seed toggle.

  • Spawns use a reproducible PCG32 stream derived by a seed router from:

    • Global Seed

    • The SpawnEntry’s stable identity

    • Context hints (e.g., SourceName such as wave/table name, region name)

  • Works across Global, Region, and Wave flows; group sizes/order are stable per seed.

  • API: SetGlobalSeed(int seed) and event RuntimeSpawner.onSeedChanged. (Call before any spawns to lock a run.)

🧩 LocalAreaSpawner runtime knobs (non-destructive)

  • DesiredRangeOverride (min,max) + DensityMult are runtime-only. Region loop honors the override; authored Min/Max remain your baseline.

🔄 Region loop awareness

  • RegionSpawnLoop now respects DesiredRangeOverride if set, else uses the authored Min/Max.

🧰 Editor/Inspector updates

  • New inspectors for RegionPopulationController and RegionSpawnLibrary.

  • LocalAreaSpawner inspector shows runtime overrides/readouts for debugging.

  • Unified package header (Docs / API / Support) in inspectors.


Migration steps

  1. Update to 1.4.0. No breaking changes; existing scenes behave like 1.3 unless you opt in to new features.

  2. (Optional) Enable deterministic runs

    • In RuntimeSpawner, enable Use Deterministic Seed and set Global Seed (Inspector), or

    • Call SetGlobalSeed(yourSeed) from your game’s RNG/bootstrap before StartSpawners(). Tip: listen to RuntimeSpawner.onSeedChanged if you mirror the seed elsewhere.

  3. (Optional) Adopt region-scale control

    • Add RegionPopulationController to your scene and assign RuntimeSpawner.

    • (Optional) Assign SpawnDirector so step changes drive density/mixes automatically.

    • Create a RegionSpawnLibrary (Create → Runtime Spawner → Region Spawn Library) and assign it.

  4. Tag your regions

    • Basic: set a Unity Tag on each LocalAreaSpawner GameObject.

    • Multi-tag tiles: implement RegionPopulationController.IRegionTagProvider on a component to return string[].

  5. Author rules in the RegionSpawnLibrary

    • Use Required (ALL) and Any tag filters, Min/Max Step, and optional Weight by Step.

    • Check Replace Instead of Append for rules that should override a tile’s authored list.

    • Add Fallback Entries to avoid empty tiles when nothing matches.

  6. Tune density

    • In RegionPopulationController, adjust the density curve (x=step 0..1 → multiplier) and the Enable Threshold (regions below this are temporarily unregistered).

  7. Playtest

    • Use the LocalAreaSpawner inspector’s runtime panel to confirm DesiredRangeOverride, Density, and resolved entries.

    • Validate global/region caps and each SpawnEntry.maxPopulation.


Notes

  • Back-compat: Without RegionPopulationController, 1.4 behaves like 1.3 (authored Min/Max & lists).

  • Determinism scope: With the same Global Seed, scene content, and timing, spawn choice/order/grouping are reproducible. External nondeterminism (e.g., physics side-effects, asynchronous NavMesh baking, time-based triggers) can still change outcomes.

  • Procedural tiles: Controller auto-tracks regions as they register/unregister via RuntimeSpawner.


Troubleshooting

  • Tile is silent: Density fell below Enable Threshold → controller unregistered that region. Raise threshold or increase curve at that step.

  • New enemy didn’t appear where expected: Check region tags, rule step ranges/weights, and that caps (SpawnEntry.maxPopulation, region/global caps) allow it.

  • Run isn’t deterministic: Ensure Use Deterministic Seed is ON and SetGlobalSeed(...) is called before any spawns; avoid frame-time dependent randomness in your own scripts.


FAQ

Do I have to use the new controller/library? No - opt-in. Existing region setups continue to work.

Will the controller overwrite my authored Min/Max? No. It writes a runtime override; authored values remain your baseline.

What drives “step”? Typically SpawnDirector (intensity profile). If you have your own system, call ApplyStepFromDirector(step, stepsCount) on the controller.

How do I seed the run for reproducible testing? Set Use Deterministic Seed and either set the Global Seed in the inspector or call SetGlobalSeed(seed) during bootstrap (before StartSpawners()).

Can I give a tile multiple semantic tags? Yes - implement IRegionTagProvider on a component and return a string[].

Last updated