Skip to main content

Enemies

An enemy is an ECS entity created by EnemyEntityFactory. Its behaviour is driven by EnemyAISystem each frame via a strategy that composes one or more behaviours.

Components

Every enemy entity is created with the following components:

ComponentRole
TransformComponentPosition, rotation, and scale
SpriteComponentTexture, derived from EnemyType at spawn time
EnemyTagComponentMarks the entity as an enemy; holds textureName and scale
VelocityComponentMovement vector, written each frame by the active behaviour
EnemyStateComponentHolds the enemy's strategy instance
CollisionBoxComponentAxis-aligned bounding box
RoomMemberComponentBinds the enemy to its room (used for bounds-aware wandering and cleanup)

Enemy Types

Enemy types are defined as static constants on EnemyType in ECS/Enemy/EnemyType.swift. Each constant bundles all properties of that enemy: texture, scale, mass, contact damage, and AI strategy.

Current Enemy Types:

TypeTextureScaleMassContact DamageStrategy
.charger"Charger"1.01520.0StandardStrategy() — chases
.mummy"Mummy"1.01010.0TimidStrategy() — chases, flees below 20% HP
.ranger"Ranger"0.7555.0StandardStrategy with orbit + shoot attack
.tower"Tower"1.52015.0StandardStrategy — stationary, shoots on detection

The final in-world scale is baseScale × type.scale, where baseScale is passed in at spawn time.

Spawning an Enemy

Use EnemyEntityFactory to create an enemy entity:

EnemyEntityFactory(at: position, type: .mummy, baseScale: scale).make(in: world)

// baseScale defaults to 1 if omitted
EnemyEntityFactory(at: position, type: .tower).make(in: world)

In normal gameplay, enemies are spawned by MapSystem at the enemy spawn points generated for a room.

Adding a New Enemy Type

Add a single static let block to EnemyType.swift — no other files need to change:

public static let goblin = EnemyType(
textureName: "Goblin",
scale: 0.85,
mass: 8,
contactDamage: 12.0,
strategy: StandardStrategy()
)

Also add the corresponding texture asset to the asset catalog.

All properties are required by the compiler, so a new definition cannot be accidentally left incomplete.

Customising AI After Spawn

To override a spawned enemy's strategy, replace EnemyStateComponent after creation — addComponent overwrites any existing component of the same type:

let enemy = EnemyEntityFactory(at: position, type: .goblin).make(in: world)
world.addComponent(
component: EnemyStateComponent(
strategy: TimidStrategy(detectionRadius: 200, fleeThreshold: 0.4)
),
to: enemy
)

EnemyStateComponent defaults to StandardStrategy(), so you only supply what you want to override.

See Enemy AI System for all available strategies, behaviours, and their configurable parameters.