Type Object¶
Intent: Allow the flexible creation of new “classes” by creating a single class, each instance of which represents a different type of object.
Acron Mapping¶
Pattern role |
Acron equivalent |
|---|---|
Type/Breed object |
Python |
Instance |
|
Type registry |
Module-level dict keyed by type name, loaded at startup |
How to Implement¶
Define a
BreedDatadataclass with all per-type fields (max health, speed, model path, animations, sounds).Load breed definitions from a JSON file (or
Config.prcoverrides) at game startup into aBREEDSdict.Each game-entity instance stores a reference to its breed:
self.breed = BREEDS['dragon'].Instance-specific state (current health, position) lives on the instance; shared immutable data lives on the breed.
Load the shared model once per breed via
ModelPool— different instances share geometry automatically.
Code Sketch (Python)¶
import json
from dataclasses import dataclass
from typing import Dict
@dataclass(frozen=True)
class MonsterBreed:
name: str
max_health: int
speed: float
model_path: str
attack_anim: str
attack_damage: int
# Load breeds at startup
def load_breeds(path='data/breeds.json') -> Dict[str, MonsterBreed]:
with open(path) as f:
raw = json.load(f)
return {entry['name']: MonsterBreed(**entry) for entry in raw}
BREEDS = load_breeds()
class Monster:
def __init__(self, breed_name: str, pos):
self.breed = BREEDS[breed_name]
self.health = self.breed.max_health
# ModelPool caches the bam — all 'dragon' instances share geometry
self.actor = Actor(self.breed.model_path)
self.actor.reparentTo(render)
self.actor.setPos(pos)
def attack(self, target):
self.actor.play(self.breed.attack_anim)
target.health -= self.breed.attack_damage
def destroy(self):
self.actor.cleanup()
self.actor.removeNode()
Example data/breeds.json:
[
{"name": "dragon", "max_health": 200, "speed": 3.0,
"model_path": "assets/dragon.bam", "attack_anim": "bite", "attack_damage": 40},
{"name": "goblin", "max_health": 30, "speed": 6.0,
"model_path": "assets/goblin.bam", "attack_anim": "slash", "attack_damage": 8}
]
Pitfalls¶
Mutable breed data: mark the dataclass
frozen=Trueor use a plain dict read in read-only fashion. If instances can modify breed fields they’ll corrupt all other instances of that type.Breed inheritance: breeds sometimes need a parent breed (e.g.
fire_dragonextendsdragon). Implement manually by merging dicts at load time — avoid Python class inheritance for data objects.Animation per breed:
Actorrequires explicit animation dict at construction. Store animation file paths in the breed and pass them:Actor(breed.model_path, {'attack': breed.attack_anim_path}).