Card 07/ 08

ExerciseDifficulty: Advanced2 min

Practice: Sorting Particle State Into Shared and Per-Particle

Colour, sprite, position, velocity and texture; which belong in the flyweight?

A particle system is a natural Flyweight, and this exercise asks you to sort its state and then build it. A game animates ten thousand particles, each either a spark or a puff of smoke. Every spark has the same colour, sprite and texture, and so does every puff. Each particle has its own position and velocity, which change every frame.

  1. Sort colour, sprite, texture, position and velocity into intrinsic and extrinsic.
  2. Write the flyweight and the particle that holds it.
  3. How many flyweight objects do ten thousand particles need?
  4. Should the flyweight be mutable, so the game can change a particle's colour when it fades?
Show a solution
Each field and its kind of state
FieldKindWhy
ColourIntrinsicThe same for every particle of one type
SpriteIntrinsicThe same for every particle of one type
TextureIntrinsicThe same for every particle of one type
PositionExtrinsicDifferent for each particle, and changes every frame
VelocityExtrinsicDifferent for each particle
py
import random
from dataclasses import dataclass


@dataclass(frozen=True)
class ParticleType:
    name: str
    colour: str
    sprite: str
    texture: str


@dataclass
class Particle:
    kind: ParticleType
    x: float
    y: float
    dx: float
    dy: float

    def move(self) -> None:
        self.x += self.dx
        self.y += self.dy


types = {
    "spark": ParticleType("spark", "orange", "spark.png", "glow"),
    "smoke": ParticleType("smoke", "grey", "puff.png", "soft"),
}
rng = random.Random(7)
particles = [
    Particle(types[rng.choice(["spark", "smoke"])], 0.0, 0.0, rng.uniform(-1, 1), rng.uniform(0, 2))
    for _ in range(10_000)
]
for particle in particles:
    particle.move()

print(f"particles: {len(particles)}")
print(f"particle types: {len({id(p.kind) for p in particles})}")
print(f"sparks: {sum(p.kind is types['spark'] for p in particles)}")
OutputRan with python:3.12-slim (Python 3.12.13)
particles: 10000
particle types: 2
sparks: 4983
Each particle holds a reference to one of two frozen `ParticleType` objects.

Ten thousand particles need two flyweights, one per type, as the count of distinct objects shows. The flyweight must not be mutable: changing the colour on a shared ParticleType would fade every spark at once. A fading colour differs per particle, so it is extrinsic, and belongs on Particle beside the position.