Interfaces: Shoot-'Em-Up Enemy Example
Interfaces and Implements are Extended-mode features. This example demonstrates their value and deliberately limited scope. All examples use modern dialect syntax: a colon annotates a custom Type and a period accesses a field or calls a Method. For exact rules and a runnable program, see the Interfaces language reference and Type Lists language reference.
The problem without interfaces
A shoot-'em-up commonly has drones, turrets, bosses, and other enemies. Each enemy needs to update, receive damage, participate in collision detection, award a score, and clean up its resources. Type methods already let each concrete Type keep its behaviour beside its data:
Type Drone Field entity Field health Field fireCooldown# Method Update(world:GameWorld, dt#) PointEntity Self.entity, world.playerEntity MoveEntity Self.entity, 0, 0, 4.0 * dt Self.fireCooldown = Self.fireCooldown - dt If Self.fireCooldown <= 0 FireEnemyBullet Self.entity Self.fireCooldown = 1.5 EndIf End Method Method ApplyHit(damage) Self.health = Self.health - damage End Method End Type Type Turret Field entity Field health Field rotation# Field fireCooldown# Method Update(world:GameWorld, dt#) Self.rotation = Self.rotation + 30.0 * dt RotateEntity Self.entity, 0, Self.rotation, 0 Self.fireCooldown = Self.fireCooldown - dt If Self.fireCooldown <= 0 FireSpread Self.entity Self.fireCooldown = 2.0 EndIf End Method Method ApplyHit(damage) Self.health = Self.health - damage End Method End TypeThe Types are tidy, but the game systems must know every concrete enemy. Updating them requires one loop per Type:
Function UpdateEnemies(world:GameWorld, dt#) Local drone:Drone For drone = Each Drone drone.Update(world, dt) Next Local turret:Turret For turret = Each Turret turret.Update(world, dt) Next Local boss:Boss For boss = Each Boss boss.Update(world, dt) Next End FunctionDamage routing repeats the same knowledge. If collision detection produces an entity handle, the program must search every enemy collection:
Function ApplyEnemyHit(entity, damage) Local drone:Drone For drone = Each Drone If drone.entity = entity drone.ApplyHit(damage) Return EndIf Next Local turret:Turret For turret = Each Turret If turret.entity = entity turret.ApplyHit(damage) Return EndIf Next Local boss:Boss For boss = Each Boss If boss.entity = entity boss.ApplyHit(damage) Return EndIf Next End FunctionCleanup, scoring, counting, and collision processing acquire similar branches. Adding a Kamikaze Type means revisiting every system. An alternative is one large Enemy Type with a kind field:
Type Enemy Field kind Field entity Field health Field velocity# Field rotation# Field fireCooldown# Field phase Field phaseTime# Field orbitRadius# Field targetEntity Method Update(world:GameWorld, dt#) Select Self.kind Case ENEMY_DRONE UpdateDrone Self, world, dt Case ENEMY_TURRET UpdateTurret Self, world, dt Case ENEMY_BOSS UpdateBoss Self, world, dt End Select End Method End TypeThis permits one Type list, but every object carries fields it may never use, and every operation grows another Select block.
Defining the common role
An interface describes only what the enemy-management systems require. It contains Method signatures but no fields or implementations:
Interface Enemy Method Update(world:GameWorld, dt#) Method ApplyHit(damage) Method IsDead() Method CollisionEntity() Method ScoreValue() Method OnDestroyed() End InterfaceA concrete Type explicitly promises to provide every signature:
Type Drone Implements Enemy Field entity Field health Field fireCooldown# Method Update(world:GameWorld, dt#) PointEntity Self.entity, world.playerEntity MoveEntity Self.entity, 0, 0, 4.0 * dt Self.fireCooldown = Self.fireCooldown - dt If Self.fireCooldown <= 0 FireEnemyBullet Self.entity Self.fireCooldown = 1.5 EndIf End Method Method ApplyHit(damage) Self.health = Self.health - damage End Method Method IsDead() Return Self.health <= 0 End Method Method CollisionEntity() Return Self.entity End Method Method ScoreValue() Return 50 End Method Method OnDestroyed() CreateSmallExplosion Self.entity FreeEntity Self.entity End Method End TypeA Turret implements the same contract while retaining unrelated data and behaviour:
Type Turret Implements Enemy Field entity Field health Field rotation# Field fireCooldown# Method Update(world:GameWorld, dt#) Self.rotation = Self.rotation + 30.0 * dt RotateEntity Self.entity, 0, Self.rotation, 0 Self.fireCooldown = Self.fireCooldown - dt If Self.fireCooldown <= 0 FireSpread Self.entity Self.fireCooldown = 2.0 EndIf End Method Method ApplyHit(damage) Self.health = Self.health - damage End Method Method IsDead() Return Self.health <= 0 End Method Method CollisionEntity() Return Self.entity End Method Method ScoreValue() Return 100 End Method Method OnDestroyed() CreateMediumExplosion Self.entity FreeEntity Self.entity End Method End TypeOne registry and one set of systems
An Interface does not make Each Enemy iterate unrelated concrete Type lists. A typed Enemy List provides one explicit membership set without a wrapper Type:
Global enemies:Enemy List = New List Function AddEnemy(enemy:Enemy) enemies.Add(enemy) End FunctionThe update system now depends on the Enemy contract rather than every implementing Type:
Function UpdateEnemies(world:GameWorld, dt#) For enemy:Enemy = Each enemies enemy.Update(world, dt) Next End FunctionDamage routing likewise becomes one operation:
Function ApplyEnemyHit(entity, damage) For enemy:Enemy = Each enemies If enemy.CollisionEntity() = entity enemy.ApplyHit(damage) Return EndIf Next End FunctionCleanup and scoring no longer contain a branch per enemy Type. Typed List traversal permits safe removal of the current enemy. The List releases its stored reference; object deletion remains explicit:
Function RemoveDeadEnemies() For enemy:Enemy = Each enemies If enemy.IsDead() AddScore enemy.ScoreValue() enemy.OnDestroyed() enemies.Remove(enemy) Delete enemy EndIf Next End FunctionThe removal order matters: remove the entry first, then Delete the enemy. Remove and Clear never delete contained objects. At shutdown, remove and delete remaining enemies before deleting the List container:
For enemy:Enemy = Each enemies enemies.Remove(enemy) enemy.OnDestroyed() Delete enemy Next Delete enemiesWhen enemy contains a Turret, enemy.Update(world, dt) selects Turret.Update. When it contains a Drone, the same source call selects Drone.Update. Calls through a concrete Drone or Turret variable remain statically dispatched.
Adding a new enemy
A Kamikaze Type can provide the existing contract without changing the update, damage, cleanup, scoring, or registry Types:
Type Kamikaze Implements Enemy Field entity Field health Field speed# Method Update(world:GameWorld, dt#) PointEntity Self.entity, world.playerEntity MoveEntity Self.entity, 0, 0, Self.speed * dt If EntityDistance(Self.entity, world.playerEntity) < 1.5 DamagePlayer 25 Self.health = 0 EndIf End Method Method ApplyHit(damage) Self.health = Self.health - damage End Method Method IsDead() Return Self.health <= 0 End Method Method CollisionEntity() Return Self.entity End Method Method ScoreValue() Return 75 End Method Method OnDestroyed() CreateSmallExplosion Self.entity FreeEntity Self.entity End Method End TypeThe wave factory must still choose which concrete object to create. That choice is genuine and belongs at construction:
Select waveEnemyKind Case ENEMY_DRONE AddEnemy CreateDrone(x, y) Case ENEMY_TURRET AddEnemy CreateTurret(x, y) Case ENEMY_KAMIKAZE AddEnemy CreateKamikaze(x, y) End SelectInterfaces eliminate repeated Type choices during every frame; they do not pretend that concrete Types never need to be chosen.
Use interfaces selectively
This pattern is valuable for a modest number of behaviourally different objects such as enemies, weapons, game states, and controllers. Thousands of identical bullets or particles are usually better represented by direct, concrete data collections.
The intended dependency is simple: without interfaces, every game system knows every enemy Type. With interfaces, each game system knows Enemy, and each concrete enemy Type knows how to fulfil that contract. The design does not require an EnemyEntry wrapper, interface inheritance, default implementations, reflection, overloads, generics, access modifiers, or a general class hierarchy.