The Modular Wave System is a pair of wave managers with a clean C# API and a matching Game Creator 2 integration layer.
- Default Wave System (KingEdward.WaveSystem.Default): classic wave/stage progression, optional boss waves.
- Survivors‑Like Wave Syste (KingEdward.WaveSystem.SurvivorsLike): continuous spawning loop in a given duration (difficulty over time, elites, bosses).
- Core utilities (KingEdward.WaveSystem.Core): shared pieces (WaveCharacter, ObjectPool, SpawnArea, WaveSystemEffects).
- GC2 integration (KingEdward.WaveSystem.GC2Integration): Property Gets, Conditions, Triggers and one Instruction, under a separate namespace. Install via .unitypackage on Integrations folder.
You can drive everything directly from code, from GC2, or mix both.
Folder Structure
Core/
WaveCharacter.cs – generic character marker + death event.ObjectPool.cs – generic prefab pool, used by both managers.SpawnArea.cs – 3D spawn volume (sphere/box) for spawn positions.WaveSystemEffects.cs – shared VFX spawn/pool logic used by both managers.Default/
WaveManagerDefault.cs – classic wave manager.WaveData.cs – ScriptableObject describing each wave.SurvivorsLike/
WaveManagerSurvivors.cs – Survivors‑like manager.GameConfiguration.cs – global settings & difficulty curve.EnemySpawnData.cs – enemy entries for Survivors.BossData.cs – boss entries for Survivors.UI/
WaveDefaultUI.cs – HUD for Default manager.WaveSurvivorsUI.cs – HUD for Survivors manager.Editor/
WaveData and GameConfiguration.Gizmos/ – icons for all components and data assets.GC2Integration/Runtime/Visual Scripting/
Instructions/ – InstructionWaveCharacterDie.Conditions/ – default + survivors state checkers.Properties/ – Property Gets for both systems.Triggers/ – Events for all wave/game/boss/enemy signals.Icons/ – GC2 icon classes generated from the gizmos.Core Concepts
WaveCharacter (Core)
Namespace: KingEdward.WaveSystem.Core
Attach this to any GameObject that should participate in the wave systems (player or enemy).
IsPlayer / IsEnemy (read‑only).IsDead (read‑only).MarkAsPlayer() – marks this character as the player.MarkAsEnemy() – marks this character as an enemy.Die() – sets IsDead = true and fires OnDeath once.event Action<WaveCharacter> OnDeathHow to use from your own health/damage code:
using KingEdward.WaveSystem.Core;
public class EnemyHealth : MonoBehaviour
{
[SerializeField] private int maxHealth = 100;
private int currentHealth;
private WaveCharacter waveCharacter;
private void Awake()
{
currentHealth = maxHealth;
waveCharacter = GetComponent<WaveCharacter>();
if (waveCharacter == null)
{
waveCharacter = gameObject.AddComponent<WaveCharacter>();
waveCharacter.MarkAsEnemy();
}
}
public void TakeDamage(int amount)
{
currentHealth -= amount;
if (currentHealth <= 0 && waveCharacter != null)
{
waveCharacter.Die();
}
}
}
Both wave managers subscribe to
WaveCharacter.OnDeathon spawned enemies, so your only job is to callDie()when the enemy actually dies.
ObjectPool (Core)
Simple prefab pooling used by both managers to avoid excessive Instantiate/Destroy.
Main methods:
CreatePool(GameObject prefab, int initialSize, int maxSize) – create or ensure a pool exists (e.g. at prewarm).GameObject GetFromPool(GameObject prefab) – get from pool; creates pool with default sizes (5, 50) if missing.GameObject GetFromPool(GameObject prefab, int initialSize, int maxSize) – get from pool; if the pool does not exist, creates it with the given sizes (used when not prewarming).ReturnToPool(GameObject instance) – return an instance to its pool.Both managers handle pooling automatically; you typically do not need to call these yourself unless you want manual control elsewhere in your project.
SpawnArea (Core)
Namespace: KingEdward.WaveSystem.Core
Component that defines a 3D spawn volume for enemies. Can be a sphere or box, with visual gizmos similar to Unity's Colliders.
- Volume Types:
Sphere: spawns within a spherical volume with configurable radius.Box: spawns within a box volume with configurable size (X, Y, Z).- Methods:
GetRandomPosition() – returns a random position inside the 3D volume.GetRandomPositionInCircle(float minRadius) – returns a random position in a circle within the volume (for spawn patterns).GetRandomPositionInLine(float lineOffsetRange) – returns a random position along a line within the volume (for spawn patterns).Contains(Vector3 position) – checks if a position is inside the volume.- Gizmos: The volume is visualized in the Scene view with colored wireframe (semi-transparent when not selected, more opaque when selected). Uses Unity's Handles for better visual quality.
- Settings:
Gizmo Color: customize the gizmo color.Show Gizmo: toggle gizmo visibility.How to use:
SpawnArea component.Sphere or Box volume type.Radius (for sphere) or Size (for box - X, Y, Z dimensions).SpawnArea to the manager's Spawn Areas array.You can have multiple SpawnArea components in your scene; the manager will randomly pick one for each spawn. The volumes respect the GameObject's transform (position, rotation, scale).
Default Wave System
Components & Data
WaveManagerDefault (MonoBehaviour)
KingEdward.WaveSystem.DefaultWaveData (ScriptableObject)
KingEdward.WaveSystem.DefaultKingEdward/Wave System/Wave DataWaveData – configuring waves
Each WaveData asset defines a single wave:
- Fields
waveName (string) – used by UI.waveIcon (Sprite) – optional icon for UI.wavePattern (SpawnPattern) – default pattern for the wave:
Random – random spawn points, respecting min distance to player.Circle – around a spawn point, in a ring.CircleAroundPlayer – around the player, at a ring distance.Line – along a line given by spawn point right vector.Grid, Star, VFormation – formation patterns (use formationSpacing on enemy groups).isBossWave (bool) – if true, this wave uses boss spawn points.enemyGroups (list of EnemyGroup):
enemyPrefab: GameObject to spawn.count: number of enemies.spawnDelay: delay between each spawn in the group.spawnPattern: optional override (per group) of wavePattern.For boss waves, typically you add a single
EnemyGroupwith the boss prefab andcount = 1, and markisBossWave = true.
WaveManagerDefault – scene setup
1. Create a GameObject in your scene, e.g. Wave Manager Default.
2. Add the WaveManagerDefault component.
3. Referencing the manager: Prefer assigning the manager by reference in the inspector (e.g. on WaveDefaultUI, or on your own scripts that need to read state or subscribe to events). You can have multiple managers in the same scene (e.g. one per arena); each UI or script should reference the manager it uses. A static Instance is still set to the first active manager at runtime for backward compatibility and simple single-manager setups; when in doubt, use an explicit reference.
4. Assign fields:
Waves: list of WaveData assets in the order you want.Time Before First Wave: delay before first wave starts.Time Between Waves: delay between waves.Delay After Boss Defeat: delay before continuing after a boss.Spawn Areas: array of SpawnArea components used for normal enemy spawns.Boss Spawn Areas (optional): if set, boss waves will prefer these areas.WaveCharacter in the scene with IsPlayer = true.tag (“Player” by default).Use Object Pooling: enable to use ObjectPool automatically.Prewarm Pools: if true, pools are created at startup; if false, created on first spawn.Pool Initial Size, Pool Max Size – used for prefabs that appear only in normal waves.Boss Pool Initial Size, Boss Pool Max Size – used for prefabs that appear in waves marked as boss waves (isBossWave). Each prefab gets a single pool; if it appears in both normal and boss waves, boss sizes are used.The custom inspector groups these settings under Setup / Player / Performance with foldouts and warnings for missing waves or spawn areas.
Runtime API (code)
Get the manager by reference (assign in the inspector on your MonoBehaviour) or, in code-only cases, via the static Instance (first active manager). Prefer reference when you have multiple managers or want clear wiring.
using KingEdward.WaveSystem.Default;
// Option A: reference assigned in inspector (recommended)
[SerializeField] private WaveManagerDefault waveManager;
void SomeMethod()
{
if (waveManager != null && waveManager.IsGameActive)
Debug.Log($"Wave {waveManager.CurrentWave}/{waveManager.TotalWaves}, enemies left: {waveManager.EnemiesRemaining}");
}
// Option B: single-manager scene, no reference
var mgr = WaveManagerDefault.Instance;
if (mgr != null && mgr.IsGameActive)
Debug.Log($"Wave {mgr.CurrentWave}/{mgr.TotalWaves}");
- Read‑only properties
CurrentWave (1‑based index)EnemiesRemainingIsWaveActive, IsGameActive, IsBossWaveRemainingWaves, TotalWavesPlayer (GameObject)CurrentWaveName, CurrentWaveIcon- Control methods
StartGame() – starts the wave sequence.StartNextWave() – forces the next wave immediately.PauseGame() / ResumeGame() – toggles Time.timeScale and IsGameActive.- Events
event WaveEvent OnWaveStart – int waveNumberevent WaveEvent OnWaveClearevent WaveEvent OnStageClearevent WaveEvent OnBossDefeatedevent EnemyDeathEvent OnEnemyDeath – GameObject enemy