"Patterns are not invented; they are discovered. They are the vocabulary of experienced developers." — Robert Nystrom, Game Programming Patterns
Games are unique software systems: real-time, state-heavy, performance-critical, and content-driven. Patterns help manage this complexity.
1. Command Pattern
The Problem
# ❌ Naive input handling — tightly coupled, hard to extend
def handle_input(player):
if key_pressed(Key.W):
player.move_forward()
if key_pressed(Key.S):
player.move_backward()
if key_pressed(Key.SPACE):
player.jump()
if key_pressed(Key.E):
player.interact()
# Adding: remapping, macros, replay, AI control → nightmare
// ❌ Naive input handling — tightly coupled, hard to extend
void handleInput(Player& player) {
if (keyPressed(Key::W)) player.moveForward();
if (keyPressed(Key::S)) player.moveBackward();
if (keyPressed(Key::Space)) player.jump();
if (keyPressed(Key::E)) player.interact();
// Adding: remapping, macros, replay, AI control → nightmare
}
// ❌ Naive input handling — tightly coupled, hard to extend
void handleInput(Player player) {
if (keyPressed(Key.W)) player.moveForward();
if (keyPressed(Key.S)) player.moveBackward();
if (keyPressed(Key.SPACE)) player.jump();
if (keyPressed(Key.E)) player.interact();
// Adding: remapping, macros, replay, AI control → nightmare
}
// ❌ Naive input handling — tightly coupled, hard to extend
void HandleInput(Player player)
{
if (KeyPressed(Key.W)) player.MoveForward();
if (KeyPressed(Key.S)) player.MoveBackward();
if (KeyPressed(Key.Space)) player.Jump();
if (KeyPressed(Key.E)) player.Interact();
// Adding: remapping, macros, replay, AI control → nightmare
}
The Solution: Command Pattern
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional
@dataclass
class Entity:
velocity: 'Vec3' = None
speed: float = 5.0
jump_force: float = 10.0
is_grounded: bool = True
@dataclass
class Vec3:
x: float = 0.0
y: float = 0.0
z: float = 0.0
# Command interface
class Command(ABC):
@abstractmethod
def execute(self, entity: Entity) -> None:
pass
def undo(self, entity: Entity) -> None:
"""Optional: for replay/time-rewind"""
pass
# Concrete commands
class MoveForwardCommand(Command):
def execute(self, entity: Entity) -> None:
if entity.velocity:
entity.velocity.z += entity.speed * 0.016 # dt ≈ 16ms
def undo(self, entity: Entity) -> None:
if entity.velocity:
entity.velocity.z -= entity.speed * 0.016
class JumpCommand(Command):
def execute(self, entity: Entity) -> None:
if entity.is_grounded and entity.velocity:
entity.velocity.y = entity.jump_force
class InteractCommand(Command):
def execute(self, entity: Entity) -> None:
print("Interacting with nearby object")
import java.util.*;
public final class Vec3 {
public float x, y, z;
public Vec3() {}
public Vec3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
}
public class Entity {
public final Vec3 velocity = new Vec3();
public float speed = 5.0f;
public float jumpForce = 10.0f;
public boolean isGrounded = true;
}
// Command interface
public interface Command {
void execute(Entity entity);
default void undo(Entity entity) {} // Optional: for replay/time-rewind
}
// Concrete commands
public final class MoveForwardCommand implements Command {
@Override
public void execute(Entity entity) {
entity.velocity.z += entity.speed * 0.016f;
}
@Override
public void undo(Entity entity) {
entity.velocity.z -= entity.speed * 0.016f;
}
}
public final class JumpCommand implements Command {
@Override
public void execute(Entity entity) {
if (entity.isGrounded) entity.velocity.y = entity.jumpForce;
}
}
public final class InteractCommand implements Command {
@Override
public void execute(Entity entity) {
// entity.interactWithNearby();
}
}
using System;
public readonly struct Vec3 {
public readonly float X, Y, Z;
public Vec3(float x = 0, float y = 0, float z = 0) { X = x; Y = y; Z = z; }
}
public class Entity {
public Vec3 Velocity { get; set; } = new Vec3();
public float Speed = 5.0f;
public float JumpForce = 10.0f;
public bool IsGrounded = true;
}
public interface ICommand {
void Execute(Entity entity);
void Undo(Entity entity);
}
public abstract class Command : ICommand {
public virtual void Undo(Entity entity) {} // Optional: for replay/time-rewind
public abstract void Execute(Entity entity);
}
public sealed class MoveForwardCommand : Command {
public override void Execute(Entity entity) {
entity.Velocity = new Vec3(entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z + entity.Speed * 0.016f);
}
public override void Undo(Entity entity) {
entity.Velocity = new Vec3(entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - entity.Speed * 0.016f);
}
}
public sealed class JumpCommand : Command {
public override void Execute(Entity entity) {
if (entity.IsGrounded) entity.Velocity = new Vec3(entity.Velocity.X, entity.JumpForce, entity.Velocity.Z);
}
}
public sealed class InteractCommand : Command {
public override void Execute(Entity entity) {
// entity.InteractWithNearby();
}
}
Input Mapping (Decoupled)
from typing import Dict
from abc import ABC
class InputHandler:
def __init__(self):
self.key_bindings: Dict[int, Command] = {}
self.command_history: list[Command] = [] # For undo/replay
self.history_index = 0
def bind(self, key: int, cmd: Command) -> None:
self.key_bindings[key] = cmd
def handle_input(self, entity) -> None:
for key, cmd in self.key_bindings.items():
if is_pressed(key):
cmd.execute(entity)
# Record for replay/undo
if self.history_index < len(self.command_history):
self.command_history = self.command_history[:self.history_index]
self.command_history.append(cmd)
self.history_index += 1
def undo(self, entity) -> None:
if self.history_index > 0:
self.history_index -= 1
self.command_history[self.history_index].undo(entity)
def save_replay(self, filename: str) -> None:
pass # Serialize command_history to file
def load_replay(self, filename: str) -> None:
pass # Deserialize from file
def is_pressed(key: int) -> bool:
return False # Placeholder
import java.util.*;
public class InputHandler {
private final Map<Integer, Command> keyBindings = new HashMap<>();
private final List<Command> commandHistory = new ArrayList<>();
private int historyIndex = 0;
public void bind(int key, Command cmd) {
keyBindings.put(key, cmd);
}
public void handleInput(Entity player) {
for (var entry : keyBindings.entrySet()) {
if (isPressed(entry.getKey())) {
Command cmd = entry.getValue();
cmd.execute(player);
// Record for replay/undo
if (historyIndex < commandHistory.size()) {
commandHistory.subList(historyIndex, commandHistory.size()).clear();
}
commandHistory.add(cmd);
historyIndex++;
}
}
}
public void undo(Entity entity) {
if (historyIndex > 0) {
historyIndex--;
commandHistory.get(historyIndex).undo(entity);
}
}
public void saveReplay(String filename) { /* Serialize commandHistory */ }
public void loadReplay(String filename) { /* Deserialize from file */ }
private boolean isPressed(int key) { return false; }
}
using System;
using System.Collections.Generic;
public class InputHandler {
private readonly Dictionary<int, ICommand> keyBindings = new();
private readonly List<ICommand> commandHistory = new();
private int historyIndex = 0;
public void Bind(int key, ICommand cmd) {
keyBindings[key] = cmd;
}
public void HandleInput(Entity player) {
foreach (var kvp in keyBindings) {
if (IsPressed(kvp.Key)) {
kvp.Value.Execute(player);
// Record for replay/undo
if (historyIndex < commandHistory.Count) {
commandHistory.RemoveRange(historyIndex, commandHistory.Count - historyIndex);
}
commandHistory.Add(kvp.Value);
historyIndex++;
}
}
}
public void Undo(Entity entity) {
if (historyIndex > 0) {
historyIndex--;
commandHistory[historyIndex].Undo(entity);
}
}
public void SaveReplay(string filename) { /* Serialize commandHistory */ }
public void LoadReplay(string filename) { /* Deserialize from file */ }
private bool IsPressed(int key) => false;
}
Player Remapping (Runtime)
from dataclasses import dataclass
from enum import IntEnum
class Key(IntEnum):
W = 87
S = 83
SPACE = 32
E = 69
SHIFT = 16
@dataclass
class PlayerConfig:
move_forward: int = Key.W
move_backward: int = Key.S
jump: int = Key.SPACE
interact: int = Key.E
dodge: int = Key.SHIFT
def apply_to(self, input_handler: InputHandler) -> None:
input_handler.bind(self.move_forward, MoveForwardCommand())
input_handler.bind(self.move_backward, MoveBackwardCommand())
input_handler.bind(self.jump, JumpCommand())
input_handler.bind(self.interact, InteractCommand())
input_handler.bind(self.dodge, DodgeCommand())
public enum Key {
W(87), S(83), SPACE(32), E(69), SHIFT(16);
public final int value;
Key(int v) { value = v; }
}
public record PlayerConfig(
Key moveForward,
Key moveBackward,
Key jump,
Key interact,
Key dodge
) {
public PlayerConfig() {
this(Key.W, Key.S, Key.SPACE, Key.E, Key.SHIFT);
}
public void applyTo(InputHandler input) {
input.bind(moveForward.value, new MoveForwardCommand());
input.bind(moveBackward.value, new MoveBackwardCommand());
input.bind(jump.value, new JumpCommand());
input.bind(interact.value, new InteractCommand());
input.bind(dodge.value, new DodgeCommand());
}
}
public enum Key { W = 87, S = 83, Space = 32, E = 69, Shift = 16 }
public record PlayerConfig(
Key MoveForward = Key.W,
Key MoveBackward = Key.S,
Key Jump = Key.Space,
Key Interact = Key.E,
Key Dodge = Key.Shift
) {
public void ApplyTo(InputHandler input) {
input.Bind((int)MoveForward, new MoveForwardCommand());
input.Bind((int)MoveBackward, new MoveBackwardCommand());
input.Bind((int)Jump, new JumpCommand());
input.Bind((int)Interact, new InteractCommand());
input.Bind((int)Dodge, new DodgeCommand());
}
}
Command Pattern Benefits in Games
Feature
Implementation
Remappable controls
Swap commands in keymap at runtime
Replay system
Serialize command history to file
Time rewind
Execute undo() in reverse (Braid-style)
Macro/scripting
Sequence commands, bind to single key
AI control
AI outputs same Command interface
Multiplayer
Send commands over network (deterministic)
Undo/Redo editor
Level editor uses same system
2. Entity-Component-System (ECS)
The Problem with Deep Inheritance
# ❌ Inheritance hierarchy hell
class Entity: ...
class Actor(Entity): ...
class Pawn(Actor): ...
class Character(Pawn): ...
class Enemy(Character): ...
class FlyingEnemy(Enemy): ...
# Want flying player? Can't inherit from both!
// ❌ Inheritance hierarchy hell
class Entity { /* ... */ };
class Actor : public Entity { /* ... */ };
class Pawn : public Actor { /* ... */ };
class Character : public Pawn { /* ... */ };
class Enemy : public Character { /* ... */ };
class FlyingEnemy : public Enemy { /* ... */ };
// Want flying player? Can't inherit from both!
// ❌ Inheritance hierarchy hell
class Entity { /* ... */ }
class Actor extends Entity { /* ... */ }
class Pawn extends Actor { /* ... */ }
class Character extends Pawn { /* ... */ }
class Enemy extends Character { /* ... */ }
class FlyingEnemy extends Enemy { /* ... */ }
// Want flying player? Can't inherit from both!
// ❌ Inheritance hierarchy hell
class Entity { /* ... */ }
class Actor : Entity { /* ... */ }
class Pawn : Actor { /* ... */ }
class Character : Pawn { /* ... */ }
class Enemy : Character { /* ... */ }
class FlyingEnemy : Enemy { /* ... */ }
// Want flying player? Can't inherit from both!
ECS Solution: Composition Over Inheritance
from dataclasses import dataclass
from typing import Dict, Type, Any
# Components = pure data (no behavior)
@dataclass
class Transform:
position: tuple = (0, 0, 0)
rotation: tuple = (0, 0, 0)
scale: tuple = (1, 1, 1)
@dataclass
class Velocity:
x: float = 0.0
y: float = 0.0
z: float = 0.0
@dataclass
class Health:
current: float = 100.0
maximum: float = 100.0
@dataclass
class Sprite:
texture_id: str
uv_rect: tuple = (0, 0, 1, 1)
@dataclass
class AIController:
behavior_tree: str
@dataclass
class PlayerControl:
input_handler: Any = None
# Entities = IDs (integers)
Entity = int
# Systems = behavior (operate on component combinations)
class MovementSystem:
def update(self, registry: Dict[Entity, Dict[Type, Any]], dt: float) -> None:
for entity, components in registry.items():
if Transform in components and Velocity in components:
transform = components[Transform]
velocity = components[Velocity]
x, y, z = transform.position
transform.position = (
x + velocity.x * dt,
y + velocity.y * dt,
z + velocity.z * dt
)
class RenderSystem:
def update(self, registry: Dict[Entity, Dict[Type, Any]]) -> None:
for entity, components in registry.items():
if Transform in components and Sprite in components:
transform = components[Transform]
sprite = components[Sprite]
# renderer.draw(sprite.texture_id, transform.position, ...)
import java.util.*;
import java.util.function.*;
// Components = pure data (records for immutability)
public record Transform(Vec3 pos, Vec3 rot, Vec3 scale) {}
public record Velocity(Vec3 value) {}
public record Health(float current, float max) {}
public record Sprite(String tex, Rect uv) {}
public record AIController(BehaviorTree bt) {}
public record PlayerControl(InputHandler input) {}
// Entity = ID
public record Entity(long id) {}
// Archetype-based storage (similar to flecs/EnTT)
public class Registry {
private final Map<Entity, Map<Class<?>, Object>> components = new HashMap<>();
public <T> T emplace(Entity e, T component) {
components.computeIfAbsent(e, k -> new HashMap<>())
.put(component.getClass(), component);
return component;
}
public <T> T get(Entity e, Class<T> type) {
var map = components.get(e);
return map != null ? type.cast(map.get(type)) : null;
}
public <T> Iterable<Entity> entitiesWith(Class<T>... types) {
return () -> components.entrySet().stream()
.filter(e -> Arrays.stream(types).allMatch(t -> e.getValue().containsKey(t)))
.map(Map.Entry::getKey)
.iterator();
}
}
// Systems = behavior
public class MovementSystem {
public void update(Registry reg, float dt) {
for (Entity e : reg.entitiesWith(Transform.class, Velocity.class)) {
Transform t = reg.get(e, Transform.class);
Velocity v = reg.get(e, Velocity.class);
t.pos = t.pos.add(v.value.mul(dt));
}
}
}
public class RenderSystem {
public void update(Registry reg) {
for (Entity e : reg.entitiesWith(Transform.class, Sprite.class)) {
Transform t = reg.get(e, Transform.class);
Sprite s = reg.get(e, Sprite.class);
renderer.draw(s.tex, t.pos, t.rot, s.uv);
}
}
}
using System;
using System.Collections.Generic;
// Components = pure data (records/structs)
public readonly record struct Transform(Vec3 Pos, Vec3 Rot, Vec3 Scale);
public readonly record struct Velocity(Vec3 Value);
public readonly record struct Health(float Current, float Max);
public readonly record struct Sprite(string Tex, Rect UV);
public readonly record struct AIController(BehaviorTree BT);
public readonly record struct PlayerControl(InputHandler Input);
public readonly record struct Entity(ulong ID);
// Archetype-based storage
public class Registry {
private readonly Dictionary<Entity, Dictionary<Type, object>> components = new();
public T Emplace<T>(Entity e, T component) where T : notnull {
if (!components.TryGetValue(e, out var map)) {
map = new Dictionary<Type, object>();
components[e] = map;
}
map[typeof(T)] = component;
return component;
}
public T Get<T>(Entity e) {
return components.TryGetValue(e, out var map) && map.TryGetValue(typeof(T), out var obj)
? (T)obj : default;
}
public IEnumerable<Entity> EntitiesWith(params Type[] types) {
foreach (var kvp in components) {
if (types.All(t => kvp.Value.ContainsKey(t))) yield return kvp.Key;
}
}
}
// Systems = behavior
public class MovementSystem {
public void Update(Registry reg, float dt) {
foreach (var e in reg.EntitiesWith(typeof(Transform), typeof(Velocity))) {
var t = reg.Get<Transform>(e);
var v = reg.Get<Velocity>(e);
t.Pos += v.Value * dt; // Requires mutable or replace
}
}
}
public class RenderSystem {
public void Update(Registry reg) {
foreach (var e in reg.EntitiesWith(typeof(Transform), typeof(Sprite))) {
var t = reg.Get<Transform>(e);
var s = reg.Get<Sprite>(e);
Renderer.Draw(s.Tex, t.Pos, t.Rot, s.UV);
}
}
}
#include <memory>
class Entity;
class State {
public:
virtual ~State() = default;
virtual void enter(Entity& entity) = 0;
virtual std::unique_ptr<State> update(Entity& entity, float dt) = 0;
virtual void exit(Entity& entity) = 0;
};
class IdleState : public State {
void enter(Entity& entity) override { /* play idle anim */ }
std::unique_ptr<State> update(Entity& entity, float dt) override {
if (entity.canSeePlayer()) return std::make_unique<ChaseState>();
return nullptr;
}
void exit(Entity& entity) override {}
};
class ChaseState : public State {
void enter(Entity& entity) override { entity.playAnimation("run"); }
std::unique_ptr<State> update(Entity& entity, float dt) override {
if (!entity.canSeePlayer())
return std::make_unique<SearchState>(entity.getLastKnownPlayerPos());
if (entity.inAttackRange())
return std::make_unique<AttackState>();
entity.moveToward(entity.getPlayerPos());
return nullptr;
}
void exit(Entity& entity) override { entity.stopAnimation(); }
};
class Entity {
std::unique_ptr<State> state = std::make_unique<IdleState>();
void changeState(std::unique_ptr<State> newState) {
state->exit(*this);
state = std::move(newState);
state->enter(*this);
}
void update(float dt) {
if (auto next = state->update(*this, dt)) changeState(std::move(next));
}
};
interface State {
void enter(Entity entity);
State update(Entity entity, float dt);
void exit(Entity entity);
}
class IdleState implements State {
public void enter(Entity e) { /* play idle anim */ }
public State update(Entity e, float dt) {
return e.canSeePlayer() ? new ChaseState() : null;
}
public void exit(Entity e) {}
}
class ChaseState implements State {
public void enter(Entity e) { e.playAnimation("run"); }
public State update(Entity e, float dt) {
if (!e.canSeePlayer()) return new SearchState(e.getLastKnownPlayerPos());
if (e.inAttackRange()) return new AttackState();
e.moveToward(e.getPlayerPos());
return null;
}
public void exit(Entity e) { e.stopAnimation(); }
}
public class Entity {
private State state = new IdleState();
public void changeState(State newState) {
state.exit(this);
state = newState;
state.enter(this);
}
public void update(float dt) {
State next = state.update(this, dt);
if (next != null) changeState(next);
}
}
public interface IState {
void Enter(Entity entity);
IState Update(Entity entity, float dt);
void Exit(Entity entity);
}
public class IdleState : IState {
public void Enter(Entity e) { /* play idle anim */ }
public IState Update(Entity e, float dt) =>
e.CanSeePlayer() ? new ChaseState() : null;
public void Exit(Entity e) {}
}
public class ChaseState : IState {
public void Enter(Entity e) => e.PlayAnimation("run");
public IState Update(Entity e, float dt) {
if (!e.CanSeePlayer()) return new SearchState(e.LastKnownPlayerPos);
if (e.InAttackRange()) return new AttackState();
e.MoveToward(e.PlayerPos);
return null;
}
public void Exit(Entity e) => e.StopAnimation();
}
public class Entity {
private IState state = new IdleState();
public void ChangeState(IState newState) {
state.Exit(this);
state = newState;
state.Enter(this);
}
public void Update(float dt) {
var next = state.Update(this, dt);
if (next != null) ChangeState(next);
}
}
4. Observer Pattern (Event System)
from enum import Enum
from typing import Callable, Dict, List
from collections import defaultdict
class GameEvent(Enum):
PLAYER_DIED = "player_died"
ENEMY_KILLED = "enemy_killed"
ITEM_COLLECTED = "item_collected"
LEVEL_COMPLETED = "level_completed"
class EventBus:
def __init__(self):
self._listeners: Dict[GameEvent, List[Callable]] = defaultdict(list)
def subscribe(self, event: GameEvent, callback: Callable) -> None:
self._listeners[event].append(callback)
def publish(self, event: GameEvent, data: dict = None) -> None:
for callback in self._listeners[event]:
callback(data or {})
# Usage
bus = EventBus()
bus.subscribe(GameEvent.ENEMY_KILLED, lambda data: {
player.add_xp(data.get("xp", 0)),
ui.show_xp_popup(data.get("xp", 0))
})
# Any system can publish
bus.publish(GameEvent.ENEMY_KILLED, {"xp": 100, "enemy_type": "goblin"})
#include <functional>
#include <unordered_map>
#include <vector>
#include <any>
enum class GameEvent { PlayerDied, EnemyKilled, ItemCollected, LevelCompleted };
class EventBus {
std::unordered_map<GameEvent, std::vector<std::function<void(const std::any&)>>> listeners;
public:
template<typename F>
void subscribe(GameEvent event, F&& callback) {
listeners[event].push_back(std::forward<F>(callback));
}
void publish(GameEvent event, const std::any& data = {}) {
for (auto& cb : listeners[event]) cb(data);
}
};
// Usage
EventBus bus;
bus.subscribe(GameEvent::EnemyKilled, [](const std::any& data) {
auto d = std::any_cast<std::unordered_map<std::string, int>>(data);
player.addXP(d.at("xp"));
ui.showXPPopup(d.at("xp"));
});
// Any system can publish
bus.publish(GameEvent::EnemyKilled,
std::unordered_map<std::string, int>{{"xp", 100}, {"enemy_type", "goblin"}});
import java.util.*;
import java.util.function.*;
enum GameEvent { PlayerDied, EnemyKilled, ItemCollected, LevelCompleted }
class EventBus {
private final Map<GameEvent, List<Consumer<Map<String, Object>>>> listeners = new EnumMap<>(GameEvent.class);
public void subscribe(GameEvent event, Consumer<Map<String, Object>> callback) {
listeners.computeIfAbsent(event, k -> new ArrayList<>()).add(callback);
}
public void publish(GameEvent event, Map<String, Object> data) {
for (var cb : listeners.getOrDefault(event, List.of())) cb.accept(data);
}
}
// Usage
EventBus bus = new EventBus();
bus.subscribe(GameEvent.EnemyKilled, data -> {
player.addXP((int) data.get("xp"));
ui.showXPPopup((int) data.get("xp"));
});
bus.publish(GameEvent.EnemyKilled, Map.of("xp", 100, "enemy_type", "goblin"));
using System;
using System.Collections.Generic;
public enum GameEvent { PlayerDied, EnemyKilled, ItemCollected, LevelCompleted }
public class EventBus {
private readonly Dictionary<GameEvent, List<Action<Dictionary<string, object>>>> _listeners = new();
public void Subscribe(GameEvent evt, Action<Dictionary<string, object>> cb) {
if (!_listeners.TryGetValue(evt, out var list)) _listeners[evt] = list = new();
list.Add(cb);
}
public void Publish(GameEvent evt, Dictionary<string, object> data = null) {
if (_listeners.TryGetValue(evt, out var list))
foreach (var cb in list) cb(data ?? new());
}
}
// Usage
var bus = new EventBus();
bus.Subscribe(GameEvent.EnemyKilled, data => {
player.AddXP((int)data["xp"]);
ui.ShowXPPopup((int)data["xp"]);
});
bus.Publish(GameEvent.EnemyKilled, new Dictionary<string, object> { ["xp"] = 100, ["enemy_type"] = "goblin" });
5. Object Pool Pattern
from typing import TypeVar, Generic, List, Callable
T = TypeVar('T')
class ObjectPool(Generic[T]):
def __init__(self, factory: Callable[[], T], initial_size: int = 100):
self._factory = factory
self._pool: List[T] = [factory() for _ in range(initial_size)]
self._active: List[T] = []
def acquire(self) -> T:
if self._pool:
obj = self._pool.pop()
else:
obj = self._factory()
self._active.append(obj)
return obj
def release(self, obj: T) -> None:
if obj in self._active:
self._active.remove(obj)
self._pool.append(obj)
def update_all(self, dt: float) -> None:
for obj in self._active[:]: # Copy to allow removal during iteration
obj.update(dt)
if obj.should_destroy:
self.release(obj)
# Particle example
class Particle:
def __init__(self):
self.position = (0, 0, 0)
self.velocity = (0, 0, 0)
self.lifetime = 1.0
self.should_destroy = False
def update(self, dt: float):
x, y, z = self.position
vx, vy, vz = self.velocity
self.position = (x + vx*dt, y + vy*dt, z + vz*dt)
self.lifetime -= dt
if self.lifetime <= 0:
self.should_destroy = True
pool = ObjectPool(Particle, initial_size=10000)
def spawn_explosion(pos: tuple):
import random
for _ in range(50):
p = pool.acquire()
p.position = (pos[0] + random.uniform(-0.5, 0.5),
pos[1] + random.uniform(-0.5, 0.5),
pos[2] + random.uniform(-0.5, 0.5))
p.velocity = (random.uniform(-5, 5), random.uniform(-5, 5), random.uniform(-5, 5))
p.lifetime = 1.0
p.should_destroy = False
#include <vector>
#include <memory>
#include <functional>
template<typename T>
class ObjectPool {
std::vector<T> pool;
std::vector<T*> active;
std::function<T()> factory;
public:
ObjectPool(std::function<T()> f, size_t initial = 100) : factory(f) {
pool.reserve(initial);
for (size_t i = 0; i < initial; ++i) pool.emplace_back(f());
}
T* acquire() {
T* obj;
if (!pool.empty()) {
obj = &pool.back();
pool.pop_back();
} else {
pool.emplace_back(factory());
obj = &pool.back();
}
active.push_back(obj);
return obj;
}
void release(T* obj) {
obj->~T();
// Move-released object back to pool
std::swap(*obj, pool.emplace_back());
pool.pop_back();
// Remove from active
auto it = std::find(active.begin(), active.end(), obj);
if (it != active.end()) active.erase(it);
}
template<typename F>
void updateAll(float dt, F&& updateFn) {
for (size_t i = 0; i < active.size(); ) {
updateFn(*active[i], dt);
if (active[i]->shouldDestroy) release(active[i]);
else ++i;
}
}
};
// Particle system — 10,000 particles
struct Particle {
Vec3 position, velocity;
float lifetime = 1.0f;
bool shouldDestroy = false;
};
ObjectPool<Particle> particlePool(10000, []{ return Particle{}; });
void spawnExplosion(Vec3 pos) {
for (int i = 0; i < 50; ++i) {
Particle* p = particlePool.acquire();
p->position = pos + randomVec3(0.5f);
p->velocity = randomVec3(5.0f);
p->lifetime = 1.0f;
p->shouldDestroy = false;
}
}
import java.util.*;
import java.util.function.*;
public class ObjectPool<T> {
private final Supplier<T> factory;
private final List<T> pool = new ArrayList<>();
private final List<T> active = new ArrayList<>();
public ObjectPool(Supplier<T> factory, int initial) {
this.factory = factory;
for (int i = 0; i < initial; i++) pool.add(factory.get());
}
public T acquire() {
T obj = pool.isEmpty() ? factory.get() : pool.remove(pool.size() - 1);
active.add(obj);
return obj;
}
public void release(T obj) {
active.remove(obj);
pool.add(obj);
}
public void updateAll(float dt, Consumer<T> updateFn) {
for (int i = 0; i < active.size(); ) {
T obj = active.get(i);
updateFn.accept(obj);
if (obj.shouldDestroy) release(obj);
else i++;
}
}
}
// Particle system — 10,000 particles
public class Particle {
public Vec3 position, velocity;
public float lifetime = 1.0f;
public boolean shouldDestroy = false;
}
ObjectPool<Particle> particlePool = new ObjectPool<>(Particle::new, 10000);
void spawnExplosion(Vec3 pos) {
Random rng = new Random();
for (int i = 0; i < 50; i++) {
Particle p = particlePool.acquire();
p.position = pos.add(randomVec3(0.5f));
p.velocity = randomVec3(5.0f);
p.lifetime = 1.0f;
p.shouldDestroy = false;
}
}
using System;
using System.Collections.Generic;
public class ObjectPool<T> where T : class, new() {
private readonly Func<T> _factory;
private readonly List<T> _pool = new();
private readonly List<T> _active = new();
public ObjectPool(Func<T> factory, int initial = 100) {
_factory = factory;
for (int i = 0; i < initial; i++) _pool.Add(factory());
}
public T Acquire() {
var obj = _pool.Count > 0 ? _pool[_pool.Count - 1] : _factory();
if (_pool.Count > 0) _pool.RemoveAt(_pool.Count - 1);
_active.Add(obj);
return obj;
}
public void Release(T obj) {
_active.Remove(obj);
_pool.Add(obj);
}
public void UpdateAll(float dt, Action<T> updateFn) {
for (int i = 0; i < _active.Count; ) {
var obj = _active[i];
updateFn(obj);
if (obj.ShouldDestroy) Release(obj);
else i++;
}
}
}
// Particle system — 10,000 particles
public class Particle {
public Vec3 Position, Velocity;
public float Lifetime = 1.0f;
public bool ShouldDestroy = false;
}
var particlePool = new ObjectPool<Particle>(() => new Particle(), 10000);
void SpawnExplosion(Vec3 pos) {
var rng = new Random();
for (int i = 0; i < 50; i++) {
var p = particlePool.Acquire();
p.Position = pos + RandomVec3(0.5f);
p.Velocity = RandomVec3(5.0f);
p.Lifetime = 1.0f;
p.ShouldDestroy = false;
}
}
6. Factory Pattern for Level Loading
from typing import Dict, Any
import json
class EntityFactory:
def __init__(self, registry, resources):
self.registry = registry
self.resources = resources
def create_enemy(self, enemy_type: str, pos: tuple) -> int:
e = self.registry.create()
self.registry.emplace(e, Transform(pos))
self.registry.emplace(e, Health(100, 100))
self.registry.emplace(e, Sprite(self.resources.get_texture(f"enemy_{enemy_type}")))
self.registry.emplace(e, AIController(load_behavior_tree(enemy_type)))
return e
def create_player(self, pos: tuple) -> int:
e = self.registry.create()
self.registry.emplace(e, Transform(pos))
self.registry.emplace(e, Health(100, 100))
self.registry.emplace(e, Sprite(self.resources.get_texture("player")))
self.registry.emplace(e, PlayerControl(input_handler))
return e
# Data-driven: load from JSON
def create_from_template(factory: EntityFactory, template: dict) -> int:
e = factory.registry.create()
for component_name, data in template.get("components", {}).items():
if component_name == "Transform":
factory.registry.emplace(e, Transform(data["pos"]))
elif component_name == "Health":
factory.registry.emplace(e, Health(data["current"], data["max"]))
# ... other components
return e
# Level loading
def load_level(factory: EntityFactory, level_path: str) -> None:
with open(level_path) as f:
level_data = json.load(f)
for entity_template in level_data.get("entities", []):
create_from_template(factory, entity_template)