Roguelike Camera Architecture: Multi-Layer Viewport, Spatial Query, and LOD Systems

A complete pattern guide for building performant camera systems in roguelike games across Phaser 3, Unity, and Godot engines.

---

Why Single-Camera Systems Fail in Roguelikes

Traditional single-camera approaches break down in roguelikes because the camera must serve multiple roles simultaneously:

This guide presents a 5-layer CameraArchitecture pattern that integrates all these responsibilities.

---

Layer 1: Camera Rig (Smart Tracking)

class CameraRig {
  private state: CameraState = CameraState.FOLLOW;
  private bounds: Rectangle;
  private target: GameObject;

  update(delta: number) {
    switch (this.state) {
      case CameraState.FOLLOW:
        const targetPos = this.predictPosition(this.target, delta);
        this.virtualCam.panTo(targetPos, delta);
        break;
      case CameraState.LOCKED:
        this.clampTargetToView();
        break;
      case CameraState.TRANSITION:
        this.followPath(delta);
        break;
    }
    this.clampToBounds();
  }

  private predictPosition(target: GameObject, dt: number): Vector2 {
    return target.position.add(target.velocity.multiply(dt * 5));
  }
}

Room-Based Camera Lock (Dead Cells Pattern)

class RoomCameraLock {
  private currentRoom: Rectangle | null = null;

  update(playerPos: Vector2) {
    if (this.currentRoom && this.currentRoom.contains(playerPos)) {
      this.state = CameraState.LOCKED;
      return;
    }
    const nextRoom = this.findRoomAt(playerPos);
    if (nextRoom && nextRoom !== this.currentRoom) {
      this.transitionTo(nextRoom);
    }
  }
}

---

Layer 2: LOD & Culling Controller

Distance-based 3-zone activation system:

         ┌──────────────────────────────────┐
         │          FAR ZONE (Dormant)     │  No updates
         │   ┌────────────────────────┐     │
         │   │    MID ZONE (Low Freq)  │     │  5fps updates
         │   │   ┌──────────────┐    │     │
         │   │   │ NEAR ZONE    │    │     │  Full 60fps
         │   │   │  (Active)    │    │     │
         │   │   │  ◉ Player    │    │     │
         │   │   └──────────────┘    │     │
         │   └────────────────────────┘     │
         └──────────────────────────────────┘
enum EntityActivity {
  ACTIVE,    // Full updates every frame
  LOW_FREQ,  // 5-10fps updates
  DORMANT,   // Updates stopped
  SLEEPING,  // Virtualized in memory
}

class ActivityManager {
  update(camera: Camera) {
    const camCenter = camera.center;
    for (const entity of this.world.entities) {
      const dist = Vector2.distance(camCenter, entity.position);
      if (dist < this.nearRadius) {
        entity.activity = EntityActivity.ACTIVE;
      } else if (dist < this.midRadius) {
        entity.activity = EntityActivity.LOW_FREQ;
      } else {
        entity.activity = EntityActivity.DORMANT;
      }
    }
  }
}

---

Layer 3: Spatial Query System

Essential for roguelike game logic:

class SpatialHashGrid {
  private cellSize: number = 64;
  private cells: Map<string, Set<Entity>>;

  key(x: number, y: number): string {
    const cx = Math.floor(x / this.cellSize);
    const cy = Math.floor(y / this.cellSize);
    return `${cx},${cy}`;
  }

  queryRect(rect: Rectangle): Entity[] {
    // O(1) lookup for entities in rectangle area
  }

  queryCircle(center: Vector2, radius: number): Entity[] {
    // Range queries for AoE effects
  }
}

---

Layer 4: Screen Effects Pipeline

class ScreenEffectsPipeline {
  onDamage(event: DamageEvent) {
    if (event.isCritical) {
      this.shake.intensity(8, 150);
      this.hitStop.duration(80);
      this.slowMotion.scale(0.3, 200);
    }
  }

  onPlayerDeath() {
    this.shake.intensity(12, 500);
    this.hitStop.duration(300);
    this.slowMotion.scale(0.1, 500);
  }
}

---

Layer 5: Viewport Composer

Three-camera setup for final rendering:

class CameraComposition {
  constructor(scene) {
    this.worldCamera = new CameraRig(cam);
    this.fxCamera = scene.add.camera(0, 0, cam.width, cam.height);
    this.uiCamera = scene.add.camera(0, 0, cam.width, cam.height);
  }
}

---

Engine-Specific Implementation Notes

GameCamera StyleActive SystemSpatial Query
HadesDynamic zoomRoom-basedRoom-level
Dead CellsRoom-lockRoom toggleNone (room-level)
Risk of Rain 23D trackingDistance-basedSphere cast
Vampire SurvivorsPlayer followFull activeNone

---

Performance Budget (60fps Reference)

16.6ms frame budget:
├─ Rendering: 8ms (48%)
├─ Spatial Query: 1ms (6%)
├─ Effects: 2ms (12%)
└─ Entity Update: 4ms (24%)

---