Card 04/ 08
Prototype Example: Spawning Game Enemies From Templates
Configure one goblin with its stats and gear, then copy it for every goblin in the level.
A game that places many similar enemies in a level is a natural Prototype, and this example maps the template onto it. Each kind of enemy is configured once, with its health, gear and stats, and registered by name. Spawning an enemy copies the registered one and moves the copy into place.
| Participant | Class in this example |
|---|---|
| ConcretePrototype | Enemy, whose clone() uses copy.deepcopy |
| Prototype registry | EnemyRegistry, holding the goblin and the goblin-chief |
| Client | The code that calls registry.spawn |
import copy
class Enemy:
def __init__(self, kind: str, health: int, gear: list[str], stats: dict[str, int]) -> None:
self.kind = kind
self.health = health
self.gear = gear
self.stats = stats
self.position = (0, 0)
def clone(self) -> "Enemy":
return copy.deepcopy(self)
def __repr__(self) -> str:
return f"{self.kind} at {self.position}: {self.health} hp, {self.gear}, {self.stats}"
class EnemyRegistry:
def __init__(self) -> None:
self._prototypes: dict[str, Enemy] = {}
def register(self, name: str, prototype: Enemy) -> None:
self._prototypes[name] = prototype
def spawn(self, name: str, position: tuple[int, int]) -> Enemy:
enemy = self._prototypes[name].clone()
enemy.position = position
return enemy
registry = EnemyRegistry()
registry.register("goblin", Enemy("goblin", 30, ["club"], {"attack": 4, "defence": 2}))
registry.register("goblin-chief", Enemy("goblin", 60, ["axe", "shield"], {"attack": 8, "defence": 5}))
first = registry.spawn("goblin", (3, 7))
second = registry.spawn("goblin", (9, 2))
chief = registry.spawn("goblin-chief", (5, 5))
first.gear.append("torch")
first.stats["attack"] += 1
print(first)
print(second)
print(chief)goblin at (3, 7): 30 hp, ['club', 'torch'], {'attack': 5, 'defence': 2}
goblin at (9, 2): 30 hp, ['club'], {'attack': 4, 'defence': 2}
goblin at (5, 5): 60 hp, ['axe', 'shield'], {'attack': 8, 'defence': 5}The first goblin picks up a torch and gains a point of attack, and the second goblin, spawned from the same prototype, has neither: each copy has its own gear list and stats dictionary. The goblin chief is the same class with different data. A new kind of enemy is one more call to register, which is how games load enemy types from files rather than writing a class for each.
No Prototype interface appears, because Python checks that clone exists when it is called rather than when the code is written. The roles that matter remain: configured objects that copy themselves, and a registry the client asks by name.
← → move through sections and cards