Prototype

Intent: Create new objects by cloning an existing configured instance rather than constructing from scratch each time.

Note

The book Game Programming Patterns notes that the classic Prototype design pattern — where each class implements a clone() method — is rarely the best approach in modern games, because most games no longer use a separate class per monster/entity type. The more useful takeaway is the concept of spawning via a template object. In Acron this maps naturally to a factory that reuses a pre-loaded NodePath template, combined with ModelPool for file-level caching.

Acron Mapping

Pattern role

Acron equivalent

Prototype (template)

A pre-configured NodePath (or Actor) stored as a template

Clone (shallow)

.instanceTo(parent) — shared geometry, new transform

Clone (deep / independent)

.copyTo(parent) — copies the full node subtree

Spawner

Factory function that calls copy/instance and applies overrides

How to Implement

  1. Load and fully configure the prototype once at startup:

    proto = loader.loadModel('enemy.bam')
    proto.setScale(1.5)
    proto.setColorScale(1, 0.2, 0.2, 1)
    
  2. Choose clone depth:

    • Independent copy (can be modified without affecting others): clone = proto.copyTo(render)

    • Shared geometry (position-only variation, cheaper): clone = proto.instanceTo(render)

  3. Apply per-clone overrides after cloning: clone.setPos(x, y, z).

  4. Attach Python-side state (health, AI data) to a wrapper object that holds a reference to the NodePath.

  5. For Actor (animated), you must subclass or re-load — Actor does not support copyTo() properly. Use a factory that calls Actor(model, anims) each time, relying on ModelPool to share the underlying geometry.

Code Sketch (Python)

class EnemySpawner:
    def __init__(self):
        # Build prototype once
        self._proto = loader.loadModel('assets/goblin.bam')
        self._proto.setScale(1.0)

    def spawn(self, pos, color_scale=(1, 1, 1, 1)):
        clone = self._proto.copyTo(render)
        clone.setPos(pos)
        clone.setColorScale(*color_scale)
        return Enemy(node=clone)

class Enemy:
    def __init__(self, node):
        self.node = node
        self.health = 30

    def destroy(self):
        self.node.removeNode()

Pitfalls

  • copyTo() vs instanceTo(): copyTo() duplicates geometry data — use it when instances need independent render attributes. instanceTo() is cheaper but all instances share the same material state.

  • Actor cloning: Actor.copyTo() does not copy animation bindings. For animated enemies use a factory that constructs a fresh Actor per instance; ModelPool ensures the .bam is loaded only once.

  • Prototype visible in scene: after building the prototype, call proto.detachNode() so it isn’t rendered. Keep a Python reference to prevent garbage collection.