Dynamic Layer System for Grid #147

Closed
opened 2025-11-29 02:10:17 +00:00 by john · 0 comments
Owner

Overview

Replace the current hardcoded grid layers with a flexible, user-defined layer system. Grids should only allocate memory for layers they actually use.

Current Problems

  1. Wasted memory: Every UIGridPoint has unused fields:

    • color_overlay (4 bytes) - never rendered
    • tile_overlay (4 bytes) - never rendered
    • uisprite (4 bytes) - never rendered
    • ~50% of per-cell memory is dead weight
  2. Inflexible: Can't have multiple tile layers (ground + roof) or multiple overlays

  3. Fixed entity ordering: Layers are hardcoded below/above entities

Proposed Design

Two Layer Types

ColorLayer: 4 bytes/cell (RGBA)

  • Use cases: fog of war, damage flash, zone highlighting, procedural terrain

TileLayer: 4 bytes/cell (sprite index into texture atlas)

  • Use cases: ground tiles, walls, roofs, decorations

Layer Ordering via z_index

# z_index < 0: renders below entities
# z_index >= 0: renders above entities

grid = mcrfpy.Grid(grid_size=(100, 100))  # No layers by default

# Add layers as needed
ground = grid.add_layer("tile", z_index=-2, texture=terrain_tex)
walls = grid.add_layer("tile", z_index=-1, texture=wall_tex)
fog = grid.add_layer("color", z_index=1)  # Above entities

Backwards Compatibility

Default Grid behavior creates one TileLayer at z_index=-1:

# These are equivalent:
grid = mcrfpy.Grid(texture=my_tex)
grid = mcrfpy.Grid()
grid.add_layer("tile", z_index=-1, texture=my_tex)

Memory Comparison (1000×1000 grid)

Configuration Current Proposed
TCOD-only (pathfinding) 22MB 2MB
Single tile layer 22MB 6MB
Tile + color overlay 22MB 10MB
2 tiles + 2 colors 22MB 18MB

API Design

# Layer management
layer = grid.add_layer(type, z_index, texture=None)
grid.remove_layer(layer)
grid.layers  # List of layers, sorted by z_index

# Layer access
layer = grid.layer(z_index)  # Get layer by z_index
layer = grid.layers[0]       # By index

# Cell access on layer
layer.at(x, y)              # Returns color or tile index
layer.set(x, y, value)      # Set color or tile index

# Convenience for single-layer grids (backwards compat)
grid.at(x, y).tilesprite    # Accesses first tile layer
grid.at(x, y).color         # Accesses first color layer

Implementation Notes

  • Layers are the unit of dirty-flag tracking (see #144 pattern)
  • Layers are the unit of sub-grid chunking (see #123)
  • Base grid retains: walkable[], transparent[], entity list, TCOD map
  • UIGridPoint becomes minimal or eliminated (just accessors into layer data)

Relationship to Other Issues

  • #123 Sub-grid System: Chunks apply per-layer, not to base grid
  • #113 Batch Operations: Batch ops work on specific layers
  • #144 Dirty Flags: Per-layer dirty tracking enables selective re-render
  • #67 Grid Stitching: Layers can be stitched independently

Definition of Done

  • ColorLayer and TileLayer classes implemented
  • grid.add_layer() / grid.remove_layer() API
  • Layer z_index ordering relative to entities
  • Backwards-compatible default behavior
  • Memory reduction validated
  • Existing tests pass
  • Documentation updated
## Overview Replace the current hardcoded grid layers with a flexible, user-defined layer system. Grids should only allocate memory for layers they actually use. ## Current Problems 1. **Wasted memory**: Every `UIGridPoint` has unused fields: - `color_overlay` (4 bytes) - never rendered - `tile_overlay` (4 bytes) - never rendered - `uisprite` (4 bytes) - never rendered - ~50% of per-cell memory is dead weight 2. **Inflexible**: Can't have multiple tile layers (ground + roof) or multiple overlays 3. **Fixed entity ordering**: Layers are hardcoded below/above entities ## Proposed Design ### Two Layer Types **ColorLayer**: 4 bytes/cell (RGBA) - Use cases: fog of war, damage flash, zone highlighting, procedural terrain **TileLayer**: 4 bytes/cell (sprite index into texture atlas) - Use cases: ground tiles, walls, roofs, decorations ### Layer Ordering via z_index ```python # z_index < 0: renders below entities # z_index >= 0: renders above entities grid = mcrfpy.Grid(grid_size=(100, 100)) # No layers by default # Add layers as needed ground = grid.add_layer("tile", z_index=-2, texture=terrain_tex) walls = grid.add_layer("tile", z_index=-1, texture=wall_tex) fog = grid.add_layer("color", z_index=1) # Above entities ``` ### Backwards Compatibility Default Grid behavior creates one TileLayer at z_index=-1: ```python # These are equivalent: grid = mcrfpy.Grid(texture=my_tex) grid = mcrfpy.Grid() grid.add_layer("tile", z_index=-1, texture=my_tex) ``` ### Memory Comparison (1000×1000 grid) | Configuration | Current | Proposed | |--------------|---------|----------| | TCOD-only (pathfinding) | 22MB | 2MB | | Single tile layer | 22MB | 6MB | | Tile + color overlay | 22MB | 10MB | | 2 tiles + 2 colors | 22MB | 18MB | ### API Design ```python # Layer management layer = grid.add_layer(type, z_index, texture=None) grid.remove_layer(layer) grid.layers # List of layers, sorted by z_index # Layer access layer = grid.layer(z_index) # Get layer by z_index layer = grid.layers[0] # By index # Cell access on layer layer.at(x, y) # Returns color or tile index layer.set(x, y, value) # Set color or tile index # Convenience for single-layer grids (backwards compat) grid.at(x, y).tilesprite # Accesses first tile layer grid.at(x, y).color # Accesses first color layer ``` ## Implementation Notes - Layers are the unit of dirty-flag tracking (see #144 pattern) - Layers are the unit of sub-grid chunking (see #123) - Base grid retains: `walkable[]`, `transparent[]`, entity list, TCOD map - `UIGridPoint` becomes minimal or eliminated (just accessors into layer data) ## Relationship to Other Issues - **#123 Sub-grid System**: Chunks apply per-layer, not to base grid - **#113 Batch Operations**: Batch ops work on specific layers - **#144 Dirty Flags**: Per-layer dirty tracking enables selective re-render - **#67 Grid Stitching**: Layers can be stitched independently ## Definition of Done - [ ] ColorLayer and TileLayer classes implemented - [ ] `grid.add_layer()` / `grid.remove_layer()` API - [ ] Layer z_index ordering relative to entities - [ ] Backwards-compatible default behavior - [ ] Memory reduction validated - [ ] Existing tests pass - [ ] Documentation updated
john closed this issue 2025-11-29 04:28:20 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: john/McRogueFace#147
No description provided.