Spawning System¶
Summary: Project Eternal's spawning systems handle items, projectiles, AOE effects, and enemies.
UItemSpawnermanages ground-validated item placement with spacing checks.ULootComponentgenerates drops using loot tables. Projectiles spawn via animation notifies with pattern support. All spawning is server-authoritative.
Table of Contents¶
- Why This Architecture
- Spawning Architecture
- Item Spawning
- Loot System
- Projectile Spawning
- Projectile Patterns
- AOE Spawning
- Enemy Spawning
- Enemy Initialization
- Source References
- Related Systems
- Recent Changes
Why This Architecture¶
Design Goals¶
The spawning system is built around four principles:
- Server Authority - All spawning validated and executed on server
- Location Validation - Ground traces and spacing prevent bad placements
- Animation-Driven - Projectiles sync with combat animations
- Data-Driven Patterns - Complex attacks configured in data assets
Spawn Type Ownership¶
| Spawn Type | System | Server Authority |
|---|---|---|
| Items | UItemSpawner |
Required |
| Loot Drops | ULootComponent |
Required |
| Projectiles | UProjectileAbility |
Required |
| AOE Effects | AEternalAOEActor |
Required |
| Enemies | Level/Spawner | Required |
Spawning Architecture¶
+----------------+
| Spawn Types |
+----------------+
|
+-- Items
| +-- UItemSpawner (Location Validation)
| +-- ULootComponent (Drop Generation)
| +-- AItemActor (World Pickup)
|
+-- Projectiles
| +-- UProjectileAbility
| +-- UAnimNotify_SpawnProjectile
| +-- AProjectileBase / AHomingProjectile
| +-- UProjectilePatternDataAsset
|
+-- AOE Effects
| +-- AEternalAOEActor (Shape Detection)
|
+-- Enemies
+-- AEnemySpawner (Placement + Filtering)
+-- UEncounterDataAsset (Weighted Enemy Pool)
+-- UEnemyDataAsset (Enemy Configuration + Tags)
+-- AEternalEnemy (Character)
Item Spawning¶
Location Validation¶
Items must spawn on valid ground with proper spacing:
FindValidSpawnLocation(DesiredLocation)
|
v
+-------------------+
| Trace: Start |
| = Desired + 70 Z |
| End = Desired-500Z|
+-------------------+
|
v
+-------------------+
| Hit ground? |----NO----> Use DesiredLocation
+-------------------+
| YES
v
+-------------------+
| Check spacing vs |
| batch locations |
+-------------------+
|
+-- Too close? --> Offset by ItemSpacing
|
+-- OK --> Add to batch, return location
Spawn Configuration¶
| Parameter | Default | Purpose |
|---|---|---|
DropSpawnAngleMin |
-85 | Random spread min |
DropSpawnAngleMax |
85 | Random spread max |
DropSpawnDistanceMin |
10 | Min distance from source |
DropSpawnDistanceMax |
50 | Max distance from source |
RelativeSpawnElevation |
70 | Trace start height |
LootSpawnRadius |
100 | Max spawn area |
ItemSpacing |
50 | Min distance between items |
Spawn Methods¶
| Method | Use Case | Generates New Item? |
|---|---|---|
SpawnNewItem |
Create from manifest | Yes (with modifiers) |
SpawnNewItemAtLocation |
Create at specific transform | Yes (with modifiers) |
SpawnExistingItem |
Drop owned item | No (preserves properties) |
Batch Spawning¶
When spawning multiple items, track positions to prevent overlap:
SpawnLootInWorld(Items, DropLocation)
|
v
Clear CurrentBatchSpawnLocations
|
v
For each Item:
|
+-- Random offset within LootSpawnRadius
+-- SpawnNewItemAtLocation
|
+-- FindValidSpawnLocation
|
+-- Add to CurrentBatchSpawnLocations
Loot System¶
Loot Component Configuration¶
| Property | Type | Purpose |
|---|---|---|
LootTable |
ULootTableDataAsset* | Drop table reference |
LevelOverride |
int32 | Override source level |
SourceContextTags |
FGameplayTagContainer | Additional context |
QuantityMultiplier |
float | Scale drop count |
bGuaranteedDrop |
bool | Always drop specific item |
GuaranteedItemManifest |
UItemManifestDataAsset* | Guaranteed item |
Drop Flow¶
DropLoot(InstigatingController)
|
+-- HasAuthority()? --> NO: return
|
+-- Build LootContext
| - SourceLevel
| - SourceTags
|
+-- Generate from LootTable
| ULootGenerator::GenerateLoot(
| LootTable,
| Context,
| QuantityMultiplier
| )
|
+-- SpawnLootInWorld(GeneratedItems)
|
+-- (if bGuaranteedDrop)
SpawnGuaranteedItem()
Context Building¶
FLootSourceContext
|
+-- SourceLevel: LevelOverride or Owner level
+-- SourceTags: Component-configured tags
Projectile Spawning¶
Ability-Based Spawning¶
Projectiles spawn from abilities with damage spec:
UProjectileAbility::SpawnProjectile()
|
+-- HasAuthority()? --> NO: return nullptr
|
+-- Determine SpawnTransform
| +-- bSpawnAtSocket? --> Get socket transform
| +-- else --> Actor transform + offset
|
+-- Create DamageEffectSpec
|
+-- SpawnActor<AProjectileBase>
|
+-- InitializeProjectile(Config, Instigator, Spec, Target)
Projectile Configuration¶
| Parameter | Type | Purpose |
|---|---|---|
Behavior |
EProjectileBehavior | Movement type |
InitialSpeed |
float | Starting velocity |
MaxSpeed |
float | Speed cap |
Acceleration |
float | Speed increase/second |
GravityScale |
float | Gravity multiplier |
HomingAccelerationMagnitude |
float | Turn rate |
MaxHomingAnglePerSecond |
float | Turn limit |
HoverDuration |
float | DelayedHoming wait time |
Projectile Behaviors¶
| Behavior | Description |
|---|---|
Straight |
Linear trajectory, no guidance |
Homing |
Immediate target tracking |
DelayedHoming |
Hover, then track target |
Arc |
Ballistic parabola |
Animation Notify Spawning¶
UAnimNotify_SpawnProjectile::Notify()
|
+-- HasAuthority()? --> NO: return
|
+-- Get AbilitySystemComponent
|
+-- Find active ProjectileAbility
|
+-- bSpawnFullPattern?
+-- YES --> SpawnProjectilePattern()
+-- NO --> SpawnProjectile()
Projectile Patterns¶
Pattern Architecture¶
UProjectilePatternDataAsset
|
+-- TArray<FProjectilePatternEntry> PatternEntries
| +-- PositionOffset
| +-- RotationOffset
| +-- SpawnDelay
|
+-- bRelativeToCaster (local vs world space)
+-- bShareHomingTarget (all track same target)
+-- bUseStaggeredSpawn (time between spawns)
Pattern Entry¶
| Field | Type | Purpose |
|---|---|---|
PositionOffset |
FVector | Spawn offset |
RotationOffset |
FRotator | Direction offset |
SpawnDelay |
float | Delay before spawn |
Built-in Pattern Generators¶
| Generator | Parameters | Result |
|---|---|---|
| Circle | NumProjectiles, Radius, Height | Ring around origin |
| Line | NumProjectiles, Spacing | Row of projectiles |
| Cone | NumProjectiles, ConeAngle | Fan pattern |
Pattern Generation Example: Circle¶
GenerateCirclePattern()
|
v
AngleStep = 360 / NumProjectiles
|
v
For i in 0..NumProjectiles:
|
+-- Angle = AngleStep * i (radians)
|
+-- Position = (Cos(Angle) * Radius,
| Sin(Angle) * Radius,
| Height)
|
+-- Rotation = (0, AngleStep * i, 0)
|
+-- Delay = bStaggered ? StaggerDelay * i : 0
Pattern Spawning Flow¶
SpawnProjectilePattern()
|
+-- Get base transform
+-- Get shared target (if enabled)
+-- Create damage spec
|
+-- For each PatternEntry:
| |
| +-- Calculate spawn transform
| | (relative or world space)
| |
| +-- SpawnDelay > 0?
| +-- YES --> SetTimer, spawn later
| +-- NO --> Spawn immediately
AOE Spawning¶
AOE Shapes¶
| Shape | Detection Method | Use Case |
|---|---|---|
Cone |
Multi-trace fan | Breath attacks |
Arc |
Sweeping trace | Melee swings |
Sphere |
Overlap sphere | Explosions |
Cylinder |
Ground circle | Ground effects |
Shape Configuration¶
| Parameter | Type | Purpose |
|---|---|---|
Shape |
EAOEShape | Detection shape |
Range |
float | Max distance |
Angle |
float | Cone/arc angle |
Radius |
float | Trace thickness |
TraceSegments |
int32 | Cone trace count |
OriginSocket |
FName | Attach point |
LocalOffset |
FVector | Position offset |
EffectDuration |
float | Lifetime |
bTraceComplex |
bool | Complex collision |
AOE Detection Flow¶
OnAoeSpawned()
|
+-- SpawnEffect() (Niagara)
|
+-- ExecuteDetection()
| |
| +-- Switch on Shape:
| Cone --> DetectCone()
| Arc --> DetectArc()
| Sphere --> DetectSphere()
| Cylinder --> DetectCylinder()
|
+-- DeduplicateHits()
|
+-- For each unique hit:
| OnAOEHit.Broadcast(HitActor, Hit)
|
+-- SetLifeSpan(EffectDuration)
Cone Detection¶
DetectCone()
|
+-- Origin = Actor location
+-- Forward = Actor forward
+-- HalfAngle = Angle / 2
+-- AngleStep = Angle / TraceSegments
|
+-- For each segment:
| |
| +-- CurrentAngle = -HalfAngle + (AngleStep * i)
| +-- TraceDirection = Forward.RotateAngleAxis(CurrentAngle)
| +-- TraceEnd = Origin + (TraceDirection * Range)
| |
| +-- SweepMultiByObjectType
| (sphere sweep along trace)
|
+-- Collect all hits
Hit Deduplication¶
Multiple traces may hit the same actor:
DeduplicateHits(AllHits)
|
+-- For each hit:
| |
| +-- Skip if actor == instigator
| +-- Skip if already in HitActors set
| |
| +-- Add to HitActors set
| +-- Add to UniqueHits array
|
+-- Return UniqueHits
Enemy Spawning¶
Encounter Resolution¶
Enemy spawners resolve their encounter through a layered chain. Each layer is optional; the system falls through to the next until it finds a valid encounter.
Per-room-type encounter (DomainDungeonConfig::RoomTypeEncounters)
└─ fallback ─> Current encounter (set by WorldMap node or domain default)
└─ fallback ─> nullptr (no enemies spawn)
Per-room-type encounters let domains define different enemy pools for different room types. A treasure room gets a lighter encounter, optional rooms get elite packs, combat rooms get the standard mix. Configured on UDomainDungeonConfig::RoomTypeEncounters — unmapped room types fall back to the domain's DefaultEncounter.
Current encounter is set once per dungeon run via DungeonSubsystem::SetAreaContext(), resolved from the world map node's encounter or the domain's default.
For standalone maps (no dungeon pipeline), ADebugAreaContext bootstraps the area context directly.
Spawn Filtering¶
Two controls determine which enemies appear at which spawner:
Enemy Tags (FEnemyData::EnemyTags) classify enemies by role, tier, or family — e.g., Enemy.Role.Melee, Enemy.Role.Ranged, Enemy.Tier.Elite. Tags are set per enemy data asset.
Spawner Filter (AEnemySpawner::SpawnFilter) is a FGameplayTagQuery that restricts which enemies from the encounter pool this spawner can produce. A tower spawner filters for Enemy.Role.Ranged, a ground spawner for Enemy.Role.Melee. Empty filter accepts everything.
When filtering, SelectEnemy() only considers pool entries whose enemy tags match the query. If no entries match, it falls back to unfiltered selection (graceful degradation).
Spawner Configuration¶
Each AEnemySpawner placed in a room level has:
| Property | Purpose |
|---|---|
RoomType |
Which room type this spawner is in (default: Combat). Drives encounter resolution. |
SpawnFilter |
Tag query filtering which enemies this spawner can produce |
SpawnCountRange |
Min/max enemies to spawn (clamped to available spawn points) |
bSpawnOnBeginPlay |
Auto-spawn on level load or wait for explicit call |
Spawner Events¶
| Delegate | Payload | When Fired |
|---|---|---|
OnEnemySpawned |
AEternalEnemy* |
After each enemy is spawned and initialized |
Used by ARoomEncounterActor to track enemies for room completion. Encounter actors in the same streaming level bind to all spawners' OnEnemySpawned delegates to register enemies for death tracking.
Spawn points are defined by child USceneComponents on the spawner actor. If none exist, the actor's own transform is used as a single spawn point.
Enemy Initialization¶
Enemy Spawn Flow¶
AEternalEnemy spawned (placed or runtime)
|
v
PossessedBy(Controller)
|
+-- HasAuthority()? --> NO: return
|
+-- Cache AIController
|
+-- AIController->Initialize(EnemyDataAsset)
| |
| +-- Run BehaviorTree
| +-- Configure Blackboard
|
+-- InitAbilityActorInfo()
| |
| +-- Init ASC
| +-- Apply PrimaryAttributes
| +-- Apply SecondaryAttributes
| +-- Grant StartupAbilities
|
+-- ApplyBalanceScaling() (if BalanceConfig set)
| |
| +-- UBalanceSubsystem->CalculateScaledStats()
| +-- Init Health, Armor, Poise, BaseDamage
|
+-- SetupHitTracing()
Balance System Integration¶
Enemies can be scaled dynamically via FEnemyBalanceConfig:
| Parameter | Purpose |
|---|---|
AreaLevel |
Base difficulty (dungeon depth, zone) |
ThreatTier |
Encounter intensity (Normal → Boss) |
Archetype |
Combat role (Balanced, Brute, Skirmisher) |
The Balance System applies multipliers from JSON config files in Config/Balance/:
- EnemyScaling.json - Growth curves per attribute
- ThreatTiers.json - Tier multipliers and loot bonuses
- Archetypes.json - Role-based stat modifiers
Enemy Components¶
| Component | Purpose |
|---|---|
UEternalAbilitySystemComponent |
GAS integration |
UEternalAttributeSet |
Health, damage stats |
UEnemyCombatComponent |
Hit reactions |
ULootComponent |
Death drops |
USkeletalMeshComponent (weapon) |
Visual weapon |
Data Asset Initialization¶
InitAbilityActorInfo()
|
+-- EternalAbilitySystemComponent->InitAbilityActorInfo()
|
+-- if (EnemyDataAsset):
|
+-- Apply PrimaryAttributes GE
+-- Apply SecondaryAttributes GE
|
+-- For each StartupAbility:
GiveAbility(FGameplayAbilitySpec)
Source References¶
Item Spawning¶
UItemSpawnerclass - Source/ProjectEternal/Public/Inventory/Items/ItemSpawner.h:15FindValidSpawnLocation()- Source/ProjectEternal/Private/Inventory/Items/ItemSpawner.cpp:78AItemActor- Source/ProjectEternal/Public/Inventory/Items/ItemActor.h:18
Loot System¶
ULootComponentclass - Source/ProjectEternal/Public/Loot/Components/LootComponent.h:22DropLoot()- Source/ProjectEternal/Private/Loot/Components/LootComponent.cpp:45ULootGenerator- Source/ProjectEternal/Public/Loot/LootGenerator.h:12
Projectile System¶
UProjectileAbilityclass - Source/ProjectEternal/Public/Abilities/ProjectileAbility.h:25SpawnProjectile()- Source/ProjectEternal/Private/Abilities/ProjectileAbility.cpp:67AProjectileBase- Source/ProjectEternal/Public/Combat/Projectiles/ProjectileBase.h:28FProjectileConfiguration- Source/ProjectEternal/Public/Combat/Projectiles/ProjectileTypes.h:18
Projectile Patterns¶
UProjectilePatternDataAsset- Source/ProjectEternal/Public/Combat/Projectiles/Data/ProjectilePatternDataAsset.h:35- Pattern generators - Source/ProjectEternal/Private/Combat/Projectiles/Data/ProjectilePatternDataAsset.cpp:78
Animation Notify¶
UAnimNotify_SpawnProjectile- Source/ProjectEternal/Public/Combat/AnimNotifies/AnimNotify_SpawnProjectile.h:12
AOE System¶
AEternalAOEActorclass - Source/ProjectEternal/Public/Actor/AOE/EternalAOEActor.h:28- Detection methods - Source/ProjectEternal/Private/Actor/AOE/EternalAOEActor.cpp:89
FAOEShapeConfig- Source/ProjectEternal/Public/Types/AOETypes.h:15
Enemy System¶
AEternalEnemyclass - Source/ProjectEternal/Public/AI/Enemy/EternalEnemy.h:22PossessedBy()- Source/ProjectEternal/Private/AI/Enemy/EternalEnemy.cpp:67InitAbilityActorInfo()- Source/ProjectEternal/Private/AI/Enemy/EternalEnemy.cpp:98
Related Systems¶
- Enemy AI - AI controller initialization
- Combat System - Projectile damage
- GAS Overview - Ability activation
- Inventory System - Item containers
Recent Changes¶
| Date | Change | Reason |
|---|---|---|
| 2026-03 | Added OnEnemySpawned delegate | Encounter tracking binds to spawner events |
| 2026-03 | Added spawn control: enemy tags, spawner filtering, per-room-type encounters | Control which enemies spawn where |
| 2026-02 | Added Balance System integration | Enemy stat scaling at spawn time |
| - | Initial documentation | Document spawning architecture |
Future Considerations¶
| Enhancement | Benefit | Complexity |
|---|---|---|
| Object Pooling | Reduce allocation overhead | Medium |
| Spawn Rate Limiting | Prevent frame drops | Low |
| Navmesh Validation | Ensure reachable items | Medium |
| Spawn Prediction | Reduce perceived latency | High |
| Spawn Statistics | Debug spawn patterns | Low |