Skip to content

Archived on 2026-10-24. The Godot rewrite is cancelled. Migration decisions and work checklists below are withdrawn. Original claims, code examples, file paths, and completion statements have not all been revalidated. Use the current documentation and issue register for maintenance decisions.

Enemy And Spawn System

Purpose

The enemy/spawn system controls wave schedules, special/miniboss timing, boss transitions, enemy state machines, enemy damage/death/rewards, and battlefield cleanup.

Current Unity Implementation

Main scripts:

Path Script Role
Assets/Script/Game/Entities/EnemySpawner/SpawnerData.cs SpawnerData, SpawnEvent Time-window spawn schedule data.
Assets/Script/Game/Entities/EnemySpawner/SpawnerEventManager.cs SpawnerEventManager Runtime wave, special, and boss spawn controller.
Assets/Script/Game/Entities/Enemies/Enemy.cs Enemy Enemy core: stats, state machine, target, damage, death, rewards, CC.
Assets/Script/Game/Entities/Enemies/Boss.cs Boss Boss subtype data: spawn delay/effect/icon/music.
Assets/Script/Game/Entities/Enemies/EnemyCore/EnemyBaseState.cs EnemyBaseState Base enemy state behavior and transitions.
Assets/Script/Game/Entities/Enemies/EnemyCore/EnemyAttackState.cs EnemyAttackState Attack cooldown/animation/range state base.
Assets/Script/Game/Entities/Enemies/EnemyState/*.cs ES*, EAS* Movement and attack states.
Assets/Script/Game/Entities/Enemies/Mechanic/*.cs EM* Reusable enemy mechanics like timed ability, summon, bullet shoot, mortar, heat.
Assets/Script/Game/Entities/Enemies/Boss/*.cs SA* Boss-specific special attacks.

Key Data

SpawnEvent fields:

  • timeSpawn: start time.
  • maxTimeToSpawn: end time.
  • enemy: prefab to spawn.
  • spawnRate: delay between spawns for this event.
  • spawnTop, spawnBottom, spawnLeft, spawnRight, spawnRandom: spawn direction flags.
  • isSpawning, isActive: runtime flags stored on spawn data object.

SpawnerEventManager key fields:

  • battleState: Battle, MinibossFight, BossFight, GameOver.
  • bossIsActive, isMiniBossSpawn.
  • boss, CreepSpawnData, SpecialSpawnData, EndGameTime.
  • healthBar, timerBar, bossIcon.
  • minibossFightTimer: suppresses creep spawning for special/miniboss events.

Enemy key data:

  • Type: EnemyType.Creep, Miniboss, Boss, Special.
  • Health/damage: maxHealth, difficulty health bonus, damage, DamageResistance.
  • Death/reward: killId, deathAnimationTime, reward, exp, gold, score, chaosFragment.
  • State: currentState, availableStates.
  • CC: poison/burn/freeze/stun dictionaries and modifiers.
  • Target: nearest player reference and position.

Runtime Flow

Spawn Initialization

  1. GameInitialization.PrepareSpawnAndGameManagerData() selects the current difficulty's creep and special SpawnerData, end game time, and boss.
  2. SpawnerEventManager.StartTheInitialization() waits for spawnerManagerInit.
  3. It caches Camera.main.gameObject, sets battleState = Battle, and resets all isSpawning/isActive flags in both spawner data assets.

Creep Spawning

  1. SpawnerEventManager.Update() calls CreepSpawnSystem().
  2. Each creep spawn event is checked against Timer.currentSpawnerTime.
  3. If within the event window and battleState == Battle, event is active.
  4. If not already spawning and no special/miniboss is active, it starts SpawnEnemy().
  5. SpawnEnemy() waits spawnRate, calculates an off-camera position based on direction flags, and gets the enemy from PoolManager.
  6. For creep enemies, isSpawning is reset to false after one spawn, allowing repeated spawns on the schedule.

Special / Miniboss Spawning

  1. SpecialSpawnSystem() checks special spawn data against Timer.currentTime.
  2. If a special/miniboss event starts, isMiniBossSpawn is set.
  3. minibossFightTimer is set to:
  4. 60 seconds for EnemyType.Miniboss.
  5. 10 seconds for EnemyType.Special.
  6. While isMiniBossSpawn is true, creep spawn coroutines are blocked.
  7. If a special/miniboss dies and its killId matches special spawn data, MiniBossDeath() clears isMiniBossSpawn and resets creep spawn active/spawning flags.

Boss Transition

  1. If a spawn event's enemy is EnemyType.Boss, SpawnEnemy() sets battleState = BossFight, plays boss music, and stops normal spawn.
  2. In SpecialSpawnSystem(), when battleState == BossFight and no boss is active, it starts SpawnBoss().
  3. SpawnBoss() stops timers, locks the camera, makes all current enemies flee, waits, spawns boss effect, waits boss delay, spawns boss, binds boss health bar, animates boss UI, and moves timer bar.
  4. On boss death, BossDeathFunc() starts ChangeStage().
  5. ChangeStage() restarts timers, clears creep flags, returns to Battle, hides boss UI, and unlocks camera following.

Enemy Runtime

  1. Enemy.OnEnable() resets status, applies difficulty health, finds nearest player, resets death/flee/CC state, emits GameEvents.OnEnemySpawn, loads first state.
  2. Enemy.Update() updates health bar, sprite flip, border death timer, state machine, CC timers, aggro timer, and cached player position.
  3. EnemyBaseState and subclasses control movement/attack transitions.
  4. Enemy.TakeDamage() subtracts damage, spawns damage popup, invokes damage event, retargets, handles knockback/blood/blink, and triggers death.
  5. Enemy.Death() emits enemy death/counter/target removal, adds score/chaos, plays audio, starts death animation, and drops rewards.
  6. Rewards are spawned from enemy reward tables as pooled Items.

Dependencies

  • Spawner depends on GameManager.instance.Timer, PoolManager, Camera.main, AudioResources, Game.Audio, Announcer, DOTween.
  • Enemy depends on Difficulty, GameEvents, GameManager, GameSession, PoolManager, UIResourceProvider, Save, audio resources, camera.
  • Player target tracking depends on enemy spawn/remove events.

Problems / Risks

  • Runtime flags are stored on SpawnerData ScriptableObject assets.
  • Special/miniboss spawn suppression is a hardcoded 10/60 second rule. If a kill event is missed, the game may appear to stall until the timer expires.
  • CheckSpecialKillId calls GetComponent<Enemy>() on spawner prefab entries repeatedly.
  • SpawnBoss() uses GameObject.FindGameObjectsWithTag("Enemy") to make all enemies flee.
  • Enemy.FindNearestPlayer and Enemy.FindNearestEnemy use scene-wide tag searches.
  • Enemy.Update() does many responsibilities per enemy every frame.
  • DeathTimer() checks camera/screen bounds every frame per enemy.
  • Reward spawning can create many pooled items; pool can grow without prewarm or cap.
  • Enemy.Rewarding() checks reward.Length == 0 || reward == null in unsafe order. If reward is null, reward.Length would throw. Unknown if Unity inspector guarantees non-null arrays here.

Migration Notes for Godot

Preserve:

  • Spawn windows, spawn rates, directions, special/miniboss suppression, boss transition behavior.
  • Enemy types and reward drops.
  • Difficulty health scaling.
  • Enemy state machine concepts.
  • Player target add/remove event behavior.
  • Boss UI/lock-camera phase.

Redesign:

SpawnScheduleResource
- immutable spawn event data

SpawnDirector
- owns runtime event state
- tracks active event ids separately from data
- emits boss_started, boss_defeated, special_started, special_cleared

EnemyScene
- HealthComponent
- EnemyStateMachine
- EnemyRewardDropper
- EnemyTargeting
- CrowdControlComponent

Use Godot groups for enemies/players, but avoid full group scans in hot paths. Prefer:

  • spawn/remove events into target registries,
  • spatial queries for nearby enemies,
  • cached player list from PlayerManager.

Do not copy:

  • Runtime mutation of spawn data resources.
  • Tag search targeting loops.
  • One large enemy script with state, reward, damage, and targeting all together.

Open Questions

  • Is the 60-second miniboss spawn suppression intended to feel like a wave pause?
  • What should happen if a miniboss leaves/dies by border timeout without matching killId?
  • Does every enemy prefab have valid HitPoints and state arrays?
  • Unknown / needs manual confirmation.