Top 20 Game Programming Patterns

Top 20 Game Programming Patterns

Data structures are foundational concepts in computer science that enable the efficient organization and manipulation of data. Understanding these structures is crucial for solving complex programming problems and optimizing code performance. Here, we delve into the top 20 data structures that are essential for modern programming, covering a range from basic to more advanced constructs.

Game Programming Patterns - the book. by Bob Nystrom

  1. Name: Command

    Description: Encapsulates a request as an object, thereby allowing for parameterization and queuing of requests.

    Use Cases:

    Implementing game controls, undo operations, macro commands.

    Example of a Command

    
    class Command {
      execute() { /* Command execution logic */ }
    }
    
    class Invoker {
      constructor(command) {
        this.command = command;
      }
      invoke() {
        this.command.execute();
      }
    }
    
    
  2. Name: Flyweight

    Description: Reduces the cost of creating and manipulating a large number of similar objects.

    Use Cases:

    Rendering forests, crowds, or particle systems where objects share states.

    Example of a Flyweight

    
    class Flyweight {
      constructor(sharedState) {
        this.sharedState = sharedState;
      }
    }
    
    
  3. Name: Observer

    Description: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

    Use Cases:

    Updating game UI, event handling, notifications.

    Example of an Observer

    
    class Observer {
      update(data) { /* Handle update */ }
    }
    
    class Subject {
      constructor() {
        this.observers = [];
      }
      addObserver(observer) {
        this.observers.push(observer);
      }
      notify(data) {
        this.observers.forEach(observer => observer.update(data));
      }
    }
    
    
  4. Name: Prototype

    Description: Creates new objects by copying an existing object, known as the prototype.

    Use Cases:

    Spawning identical game objects with slight variations, object pooling.

    Example of a Prototype

    
    class Prototype {
      clone() {
        return Object.create(this);
      }
    }
    
    
  5. Name: Singleton

    Description: Ensures a class has only one instance and provides a global point of access to it.

    Use Cases:

    Managing the game state, accessing a database or filesystem, logging.

    Example of a Singleton

    
    class GameManager {
      static instance;
      static getInstance() {
        if (!GameManager.instance) {
          GameManager.instance = new GameManager();
        }
        return GameManager.instance;
      }
    }
    
    
  6. Name: State

    Description: Allows an object to alter its behavior when its internal state changes.

    Use Cases:

    Character state management (idle, running, jumping), game level states.

    Example of a State

    
    class State {
      handle(context) { /* Handle state */ }
    }
    
    class Context {
      constructor(state) {
        this.state = state;
      }
      request() {
        this.state.handle(this);
      }
    }
    
    
  7. Name: Component

    Description: Allows composition of entities from individual parts rather than inheriting from a base or parent class.

    Use Cases:

    Creating game entities with mix-and-match capabilities, enhancing flexibility in game object behavior.

    Example of a Component

    
    class Component {
      constructor(entity) {
        this.entity = entity;
      }
    }
    
    class Entity {
      constructor() {
        this.components = [];
      }
      addComponent(component) {
        this.components.push(component);
      }
    }
    
    
  8. Name: Factory Method

    Description: Defines an interface for creating an object but lets subclasses decide which class to instantiate.

    Use Cases:

    Creating different enemy types, level generation, customizable game objects.

    Example of a Factory Method

    
    class Creator {
      createObject() {
        throw new Error("This method should be overridden");
      }
    }
    
    class ConcreteCreator extends Creator {
      createObject() {
        return new ConcreteProduct();
      }
    }
    
    
  9. Name: Game Loop

    Description: A central game control flow mechanism that updates the game state and renders the game at a constant rate.

    Use Cases:

    Most real-time games, animations, real-time simulations.


    Below is an example of a Game Loop:

    
    function gameLoop() {
      update();
      render();
      requestAnimationFrame(gameLoop);
    }
    requestAnimationFrame(gameLoop);
    
    
  10. Name: Update Method

    Description: Separates the game state update logic from the rendering logic, typically called within the game loop.

    Use Cases:

    Real-time game updates, physics simulations, AI behavior updates.

    Example on a Update Method

    
    class GameObject {
      update() {
        // Update object state
      }
    }
    
    
  11. Name: Spatial Partition

    Description: Divides the game world into distinct sectors to reduce the number of calculations required for spatial interactions.

    Use Cases:

    Collision detection, efficient rendering of only visible objects, AI pathfinding.


    An example of a Spatial Partition

    
    // Simplified example: Creating a grid for spatial partitioning
    class Grid {
      constructor() {
        this.cells = [];
      }
      add(object, x, y) {
        // Add object to grid cell
      }
    }
    
    
  12. Name: Decorator

    Description: Adds new functionality to an object dynamically without altering its structure.

    Use Cases:

    Adding power-ups to a player character, dynamic game object abilities.


    Example of a Decorator

    
    class Decorator {
      constructor(component) {
        this.component = component;
      }
      operation() {
        this.component.operation();
        // Added behavior
      }
    }
    
    
  13. Name: Strategy

    Description: Enables an object to change its behavior by changing its strategy object.

    Use Cases:

    Changing AI tactics, player control schemes, game difficulty levels.

    Example of a Strategy

    
    class Strategy {
      algorithm() { /* Implementation */ }
    }
    
    class Context {
      constructor(strategy) {
        this.strategy = strategy;
      }
      executeStrategy() {
        this.strategy.algorithm();
      }
    }
    
    
  14. Name: Memento

    Description: Captures and externalizes an object's internal state without violating encapsulation, so the object can be returned to this state later.

    Use Cases:

    Save game functionality, undo features in game editors.

    Example of a Memento

    
    class Memento {
      constructor(state) {
        this.state = state;
      }
    }
    
    
  15. Name: Builder

    Description: Separates the construction of a complex object from its representation so that the same construction process can create different representations.

    Use Cases:

    Creating complex game levels, character customization, complex object configurations.

    Example of a Builder

    
    class Builder {
      buildPartA() {}
      buildPartB() {}
      getResult() {
        // Return the final product
      }
    }
    
    
  16. Name: Chain of Responsibility

    Description: Passes a request along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.

    Use Cases:

    Input command processing, event handling systems where multiple objects may handle an event.

    Example of a Chain of Responsibility

    
    class Handler {
      constructor(successor = null) {
        this.successor = successor;
      }
      handle(request) {
        if (this.canHandle(request)) {
          // Handle request
        } else if (this.successor) {
          this.successor.handle(request);
        }
      }
      canHandle(request) {
        // Determine if the handler can handle the request
        return false;
      }
    }
    
    
  17. Name: Mediator

    Description: Defines an object that encapsulates how a set of objects interact. The mediator promotes loose coupling by keeping objects from referring to each other explicitly.

    Use Cases:

    Simplifying communication between multiple objects or classes, such as UI elements and game logic.

    Example of a Mediator

    
    class Mediator {
      notify(sender, event) {
        // Handle notification
      }
    }
    
    class Component {
      constructor(mediator) {
        this.mediator = mediator;
      }
      send(event) {
        this.mediator.notify(this, event);
      }
    }
    
    
  18. Name: Adapter (Wrapper)

    Description: Allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

    Use Cases:

    Integrating external libraries or APIs, compatibility layers between new code and legacy systems.

    Example of a Adapter (Wrapper)

    
    class Adapter {
      constructor(adaptee) {
        this.adaptee = adaptee;
      }
      request() {
        return this.adaptee.specificRequest();
      }
    }
    
    class Adaptee {
      specificRequest() {
        // Specific request processing
        return "Adaptee's behavior";
      }
    }
    
    
  19. Name: Visitor

    Description: Allows for one or more operation to be applied to a set of objects at runtime, decoupling the operations from the object structure.

    Use Cases:

    Implementing operations over complex object structures, like game worlds or UI hierarchies, without changing the objects themselves.

    Example on Skip Lists

    
    class Visitor {
      visitConcreteElementA(element) {
        // Visit ConcreteElementA
      }
      visitConcreteElementB(element) {
        // Visit ConcreteElementB
      }
    }
    
    class ConcreteElementA {
      accept(visitor) {
        visitor.visitConcreteElementA(this);
      }
    }
    
    class ConcreteElementB {
      accept(visitor) {
        visitor.visitConcreteElementB(this);
      }
    }
    
    
  20. Name: Event Queue

    Description: Manages game events or messages in a queue, processing them sequentially to avoid conflicts and ensure predictable execution order.

    Use Cases:

    Handling game events, input processing, scripting and command execution, where the timing and order of operations are crucial.

    Example on B-Trees

    
    class EventQueue {
      constructor() {
        this.queue = [];
      }
      addEvent(event) {
        this.queue.push(event);
      }
      processEvents() {
        while (this.queue.length > 0) {
          const event = this.queue.shift();
          // Process event
        }
      }
    }
    
    
  21. Conclusion

    In this blog post, we explored the top 20 game programming patterns that can greatly enhance the design and development of video games. These patterns provide solutions to common problems and challenges faced by game developers, such as managing game events, decoupling operations from object structures, and ensuring predictable execution order.

    By applying these patterns, developers can improve the maintainability, extensibility, and overall quality of their game code. They can also promote code reusability and facilitate collaboration among team members.

    Whether you're a beginner or an experienced game developer, understanding and utilizing these programming patterns can greatly enhance your game development skills and help you create more robust and efficient games.

    Remember, mastering these patterns takes practice and experience. So, don't hesitate to experiment with them in your own game projects and explore how they can be adapted to suit your specific needs.

    Happy coding and happy game development!