Update Grid Rendering Pipeline
parent
87921ac87c
commit
aa5205e8ad
1 changed files with 389 additions and 389 deletions
|
|
@ -1,390 +1,390 @@
|
|||
# Grid Rendering Pipeline
|
||||
|
||||
## Overview
|
||||
|
||||
The Grid rendering pipeline handles multi-layer tilemap rendering with chunk-based caching for optimal performance. It supports arbitrary numbers of rendering layers, viewport culling, zoom, and entity rendering.
|
||||
|
||||
**Parent Page:** [[Grid-System]]
|
||||
|
||||
**Related Pages:**
|
||||
- [[Performance-and-Profiling]] - Metrics and profiling tools
|
||||
- [[Entity-Management]] - Entity rendering within grids
|
||||
- [[Grid-TCOD-Integration]] - FOV and pathfinding
|
||||
|
||||
**Key Files:**
|
||||
- `src/UIGrid.cpp::render()` - Main rendering orchestration
|
||||
- `src/GridLayers.cpp` - ColorLayer and TileLayer rendering
|
||||
- `src/UIGrid.cpp::renderChunk()` - Per-chunk RenderTexture management
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Layer-Based Rendering
|
||||
|
||||
Grids render content through **layers** rather than per-cell properties. Each layer is either a `ColorLayer` (solid colors) or `TileLayer` (texture sprites).
|
||||
|
||||
**Render order is determined by z_index:**
|
||||
- Layers with `z_index < 0` render **below** entities
|
||||
- Entities render at the z=0 boundary
|
||||
- Layers with `z_index >= 0` render **above** entities (overlays)
|
||||
|
||||
Within each group, lower z_index values render first (behind higher values).
|
||||
|
||||
```
|
||||
z_index: -3 -2 -1 0 +1 +2
|
||||
| | | | | |
|
||||
[background] [tiles] [ENTITIES] [fog] [UI overlay]
|
||||
```
|
||||
|
||||
### Chunk-Based Caching
|
||||
|
||||
Large grids are divided into **chunks**. Each chunk maintains its own `sf::RenderTexture` that caches the rendered result.
|
||||
|
||||
**Key concepts:**
|
||||
- Only chunks intersecting the viewport are considered for rendering
|
||||
- Each chunk tracks whether its content is "dirty" (needs redraw)
|
||||
- Static content renders once, then the cached texture is reused
|
||||
|
||||
### Dirty Flag Propagation
|
||||
|
||||
When layer content changes, only affected chunks are marked dirty:
|
||||
|
||||
1. `layer.set((x, y), value)` marks the containing chunk as dirty
|
||||
2. On next render, dirty chunks redraw to their RenderTexture
|
||||
3. Clean chunks simply blit their cached texture
|
||||
|
||||
This means a 1000x1000 grid with one changing cell redraws only one chunk, not 1,000,000 cells.
|
||||
|
||||
---
|
||||
|
||||
## Render Pipeline Stages
|
||||
|
||||
### Stage 1: Viewport Calculation
|
||||
|
||||
Calculate which chunks and cells are visible based on camera position, zoom, and grid dimensions.
|
||||
|
||||
**Key properties:**
|
||||
- `center` - Camera position in pixel coordinates (Vector)
|
||||
- `zoom` - Scale factor (1.0 = normal, 2.0 = 2x magnification)
|
||||
- `size` - Viewport dimensions in screen pixels
|
||||
|
||||
### Stage 2: Below-Entity Layers
|
||||
|
||||
For each layer with `z_index < 0`, sorted by z_index:
|
||||
1. Determine which chunks intersect viewport
|
||||
2. For each visible chunk:
|
||||
- If dirty: redraw layer content to chunk's RenderTexture
|
||||
- Draw chunk's cached texture to output
|
||||
|
||||
### Stage 3: Entity Rendering
|
||||
|
||||
Entities render at the z=0 boundary:
|
||||
1. Iterate entity collection
|
||||
2. Cull entities outside viewport bounds
|
||||
3. Draw visible entity sprites at interpolated positions
|
||||
|
||||
### Stage 4: Above-Entity Layers
|
||||
|
||||
For each layer with `z_index >= 0`, sorted by z_index:
|
||||
- Same chunk-based rendering as Stage 2
|
||||
- These layers appear as overlays (fog, highlights, UI elements)
|
||||
|
||||
### Stage 5: Final Compositing
|
||||
|
||||
All rendered content is drawn to the window.
|
||||
|
||||
---
|
||||
|
||||
## Creating and Managing Layers
|
||||
|
||||
### Standalone Layer Objects
|
||||
|
||||
Layers are created as standalone objects, then attached to grids:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(800, 600), layers=[])
|
||||
|
||||
# Create layers as standalone objects
|
||||
background = mcrfpy.ColorLayer(name="background", z_index=-2)
|
||||
grid.add_layer(background)
|
||||
|
||||
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=tileset)
|
||||
grid.add_layer(terrain)
|
||||
|
||||
# Overlay above entities (z_index >= 0)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
grid.add_layer(fog)
|
||||
```
|
||||
|
||||
### Passing Layers at Construction
|
||||
|
||||
You can also pass layers during Grid construction:
|
||||
|
||||
```python
|
||||
bg = mcrfpy.ColorLayer(name="background", z_index=-2)
|
||||
tiles = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=tileset)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(50, 50),
|
||||
pos=(0, 0),
|
||||
size=(800, 600),
|
||||
layers=[bg, tiles, fog]
|
||||
)
|
||||
```
|
||||
|
||||
### Accessing Layers
|
||||
|
||||
```python
|
||||
# By name
|
||||
fog_layer = grid.layer("fog")
|
||||
|
||||
# All layers (returns tuple)
|
||||
all_layers = grid.layers
|
||||
print(f"Grid has {len(all_layers)} layers")
|
||||
```
|
||||
|
||||
### Removing Layers
|
||||
|
||||
```python
|
||||
grid.remove_layer(fog_layer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layer Operations
|
||||
|
||||
### ColorLayer
|
||||
|
||||
```python
|
||||
cl = mcrfpy.ColorLayer(name="highlights", z_index=1)
|
||||
grid.add_layer(cl)
|
||||
|
||||
# Fill all cells with one color
|
||||
cl.fill(mcrfpy.Color(0, 0, 0, 255))
|
||||
|
||||
# Set individual cell
|
||||
cl.set((5, 5), mcrfpy.Color(255, 0, 0, 128))
|
||||
|
||||
# Read cell value
|
||||
color = cl.at((5, 5))
|
||||
print(f"Color: ({color.r}, {color.g}, {color.b}, {color.a})")
|
||||
```
|
||||
|
||||
### TileLayer
|
||||
|
||||
```python
|
||||
tl = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=tileset)
|
||||
grid.add_layer(tl)
|
||||
|
||||
# Set tile sprite index
|
||||
tl.set((5, 5), 42)
|
||||
|
||||
# Read tile index
|
||||
idx = tl.at((5, 5))
|
||||
|
||||
# Fill all cells
|
||||
tl.fill(0) # All floor tiles
|
||||
```
|
||||
|
||||
### Layer Properties
|
||||
|
||||
```python
|
||||
layer = grid.layer("fog")
|
||||
|
||||
# Visibility toggle
|
||||
layer.visible = False # Hide layer
|
||||
layer.visible = True # Show layer
|
||||
|
||||
# Z-index (render order)
|
||||
layer.z_index = 2 # Move to front
|
||||
|
||||
# Grid dimensions (read-only)
|
||||
w, h = layer.grid_size
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
**Static Grids:**
|
||||
- Near-zero CPU cost after initial render
|
||||
- Cached chunk textures reused frame-to-frame
|
||||
- Only viewport calculation and texture blitting
|
||||
|
||||
**Dynamic Grids:**
|
||||
- Cost proportional to number of dirty chunks
|
||||
- Single-cell changes affect only one chunk
|
||||
- Bulk operations should batch changes before render
|
||||
|
||||
**Large Grids:**
|
||||
- 1000x1000+ grids render efficiently
|
||||
- Only visible chunks processed
|
||||
- Memory scales with grid size (chunk textures)
|
||||
|
||||
---
|
||||
|
||||
## FOV Overlay Pattern
|
||||
|
||||
The most common overlay pattern is fog of war using a ColorLayer:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(800, 600), layers=[])
|
||||
|
||||
# Background and terrain layers (below entities)
|
||||
bg = mcrfpy.ColorLayer(name="bg", z_index=-2)
|
||||
grid.add_layer(bg)
|
||||
bg.fill(mcrfpy.Color(20, 20, 30, 255))
|
||||
|
||||
# Fog overlay (above entities, z_index >= 0)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
grid.add_layer(fog)
|
||||
fog.fill(mcrfpy.Color(0, 0, 0, 255))
|
||||
|
||||
# Make cells traversable
|
||||
for x in range(50):
|
||||
for y in range(50):
|
||||
grid.at(x, y).transparent = True
|
||||
grid.at(x, y).walkable = True
|
||||
|
||||
# After computing FOV, update fog
|
||||
grid.compute_fov((25, 25), radius=10)
|
||||
w, h = grid.grid_size
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
if grid.is_in_fov((x, y)):
|
||||
fog.set((x, y), mcrfpy.Color(0, 0, 0, 0)) # Transparent
|
||||
else:
|
||||
fog.set((x, y), mcrfpy.Color(0, 0, 0, 192)) # Dimmed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multiple Overlay Layers
|
||||
|
||||
Pre-create multiple overlay layers and toggle between them:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(800, 600), layers=[])
|
||||
|
||||
highlight = mcrfpy.ColorLayer(name="highlight", z_index=1)
|
||||
danger = mcrfpy.ColorLayer(name="danger", z_index=2)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=3)
|
||||
grid.add_layer(highlight)
|
||||
grid.add_layer(danger)
|
||||
grid.add_layer(fog)
|
||||
|
||||
def show_danger_zones():
|
||||
highlight.visible = False
|
||||
danger.visible = True
|
||||
fog.visible = False
|
||||
|
||||
def show_fog_of_war():
|
||||
highlight.visible = False
|
||||
danger.visible = False
|
||||
fog.visible = True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recreating Legacy Three-Layer Rendering
|
||||
|
||||
The old system rendered: background color, tile sprite, FOV overlay. Here's the modern equivalent:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(50, 50),
|
||||
pos=(100, 100),
|
||||
size=(400, 400),
|
||||
layers=[]
|
||||
)
|
||||
|
||||
# Layer 1: Background colors (z=-2, behind everything)
|
||||
background = mcrfpy.ColorLayer(name="background", z_index=-2)
|
||||
grid.add_layer(background)
|
||||
background.fill(mcrfpy.Color(20, 20, 30, 255))
|
||||
|
||||
# Layer 2: Tile sprites (z=-1, above background, below entities)
|
||||
tiles = mcrfpy.TileLayer(name="tiles", z_index=-1, texture=tileset)
|
||||
grid.add_layer(tiles)
|
||||
|
||||
# Layer 3: FOV overlay (z=+1, above entities)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
grid.add_layer(fog)
|
||||
|
||||
# Populate layers
|
||||
for x in range(50):
|
||||
for y in range(50):
|
||||
tiles.set((x, y), calculate_tile_index(x, y))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: Layer Changes Don't Appear
|
||||
|
||||
**Cause:** Use layer methods (`set()`, `fill()`) which automatically mark chunks dirty.
|
||||
|
||||
### Issue: Overlay Appears Behind Entities
|
||||
|
||||
**Cause:** Layer z_index is negative.
|
||||
|
||||
**Fix:** Use `z_index >= 0` for overlays:
|
||||
```python
|
||||
overlay = mcrfpy.ColorLayer(name="overlay", z_index=1) # Not z_index=-1
|
||||
```
|
||||
|
||||
### Issue: Performance Degrades with Many Changes
|
||||
|
||||
**Cause:** Each `set()` call marks a chunk dirty; scattered changes mean many chunk redraws.
|
||||
|
||||
**Fix:** For bulk updates, use `fill()` for uniform values:
|
||||
```python
|
||||
# Single operation - efficient
|
||||
layer.fill(mcrfpy.Color(0, 0, 0, 0))
|
||||
|
||||
# Many individual calls - slower but necessary for non-uniform data
|
||||
for x in range(100):
|
||||
for y in range(100):
|
||||
layer.set((x, y), compute_value(x, y))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Quick Reference
|
||||
|
||||
**Grid Properties:**
|
||||
- `center` - Camera position (Vector, pixels in grid space)
|
||||
- `zoom` - Scale factor (float)
|
||||
- `layers` - All layers (tuple, sorted by z_index)
|
||||
- `fill_color` - Grid background color (behind all layers)
|
||||
- `grid_size` - Grid dimensions (tuple)
|
||||
|
||||
**Grid Methods:**
|
||||
- `add_layer(layer)` - Attach a layer object
|
||||
- `remove_layer(layer)` - Detach a layer
|
||||
- `layer("name")` - Get layer by name
|
||||
|
||||
**Layer Construction:**
|
||||
- `mcrfpy.ColorLayer(name="...", z_index=N)` - Color overlay
|
||||
- `mcrfpy.TileLayer(name="...", z_index=N, texture=tex)` - Tile sprites
|
||||
|
||||
**Layer Properties:**
|
||||
- `z_index` - Render order (int)
|
||||
- `visible` - Show/hide (bool)
|
||||
- `grid_size` - Dimensions (tuple, read-only)
|
||||
|
||||
**Layer Methods:**
|
||||
- `set((x, y), value)` - Set cell (Color or int)
|
||||
- `at((x, y))` - Get cell value
|
||||
- `fill(value)` - Fill all cells
|
||||
|
||||
---
|
||||
|
||||
**Navigation:**
|
||||
- [[Grid-System]] - Parent page, layer concepts
|
||||
- [[Grid-TCOD-Integration]] - FOV computation details
|
||||
- [[Performance-and-Profiling]] - Metrics and optimization
|
||||
# Grid Rendering Pipeline
|
||||
|
||||
## Overview
|
||||
|
||||
The Grid rendering pipeline handles multi-layer tilemap rendering with chunk-based caching for optimal performance. It supports arbitrary numbers of rendering layers, viewport culling, zoom, and entity rendering.
|
||||
|
||||
**Parent Page:** [[Grid-System]]
|
||||
|
||||
**Related Pages:**
|
||||
- [[Performance-and-Profiling]] - Metrics and profiling tools
|
||||
- [[Entity-Management]] - Entity rendering within grids
|
||||
- [[Grid-TCOD-Integration]] - FOV and pathfinding
|
||||
|
||||
**Key Files:**
|
||||
- `src/UIGrid.cpp::render()` - Main rendering orchestration
|
||||
- `src/GridLayers.cpp` - ColorLayer and TileLayer rendering
|
||||
- `src/UIGrid.cpp::renderChunk()` - Per-chunk RenderTexture management
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Layer-Based Rendering
|
||||
|
||||
Grids render content through **layers** rather than per-cell properties. Each layer is either a `ColorLayer` (solid colors) or `TileLayer` (texture sprites).
|
||||
|
||||
**Render order is determined by z_index:**
|
||||
- Layers with `z_index < 0` render **below** entities
|
||||
- Entities render at the z=0 boundary
|
||||
- Layers with `z_index >= 0` render **above** entities (overlays)
|
||||
|
||||
Within each group, lower z_index values render first (behind higher values).
|
||||
|
||||
```
|
||||
z_index: -3 -2 -1 0 +1 +2
|
||||
| | | | | |
|
||||
[background] [tiles] [ENTITIES] [fog] [UI overlay]
|
||||
```
|
||||
|
||||
### Chunk-Based Caching
|
||||
|
||||
Large grids are divided into **chunks**. Each chunk maintains its own `sf::RenderTexture` that caches the rendered result.
|
||||
|
||||
**Key concepts:**
|
||||
- Only chunks intersecting the viewport are considered for rendering
|
||||
- Each chunk tracks whether its content is "dirty" (needs redraw)
|
||||
- Static content renders once, then the cached texture is reused
|
||||
|
||||
### Dirty Flag Propagation
|
||||
|
||||
When layer content changes, only affected chunks are marked dirty:
|
||||
|
||||
1. `layer.set((x, y), value)` marks the containing chunk as dirty
|
||||
2. On next render, dirty chunks redraw to their RenderTexture
|
||||
3. Clean chunks simply blit their cached texture
|
||||
|
||||
This means a 1000x1000 grid with one changing cell redraws only one chunk, not 1,000,000 cells.
|
||||
|
||||
---
|
||||
|
||||
## Render Pipeline Stages
|
||||
|
||||
### Stage 1: Viewport Calculation
|
||||
|
||||
Calculate which chunks and cells are visible based on camera position, zoom, and grid dimensions.
|
||||
|
||||
**Key properties:**
|
||||
- `center` - Camera position in pixel coordinates (Vector)
|
||||
- `zoom` - Scale factor (1.0 = normal, 2.0 = 2x magnification)
|
||||
- `size` - Viewport dimensions in screen pixels
|
||||
|
||||
### Stage 2: Below-Entity Layers
|
||||
|
||||
For each layer with `z_index < 0`, sorted by z_index:
|
||||
1. Determine which chunks intersect viewport
|
||||
2. For each visible chunk:
|
||||
- If dirty: redraw layer content to chunk's RenderTexture
|
||||
- Draw chunk's cached texture to output
|
||||
|
||||
### Stage 3: Entity Rendering
|
||||
|
||||
Entities render at the z=0 boundary:
|
||||
1. Iterate entity collection
|
||||
2. Cull entities outside viewport bounds
|
||||
3. Draw visible entity sprites at interpolated positions
|
||||
|
||||
### Stage 4: Above-Entity Layers
|
||||
|
||||
For each layer with `z_index >= 0`, sorted by z_index:
|
||||
- Same chunk-based rendering as Stage 2
|
||||
- These layers appear as overlays (fog, highlights, UI elements)
|
||||
|
||||
### Stage 5: Final Compositing
|
||||
|
||||
All rendered content is drawn to the window.
|
||||
|
||||
---
|
||||
|
||||
## Creating and Managing Layers
|
||||
|
||||
### Standalone Layer Objects
|
||||
|
||||
Layers are created as standalone objects, then attached to grids:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(800, 600), layers=[])
|
||||
|
||||
# Create layers as standalone objects
|
||||
background = mcrfpy.ColorLayer(name="background", z_index=-2)
|
||||
grid.add_layer(background)
|
||||
|
||||
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=tileset)
|
||||
grid.add_layer(terrain)
|
||||
|
||||
# Overlay above entities (z_index >= 0)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
grid.add_layer(fog)
|
||||
```
|
||||
|
||||
### Passing Layers at Construction
|
||||
|
||||
You can also pass layers during Grid construction:
|
||||
|
||||
```python
|
||||
bg = mcrfpy.ColorLayer(name="background", z_index=-2)
|
||||
tiles = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=tileset)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(50, 50),
|
||||
pos=(0, 0),
|
||||
size=(800, 600),
|
||||
layers=[bg, tiles, fog]
|
||||
)
|
||||
```
|
||||
|
||||
### Accessing Layers
|
||||
|
||||
```python
|
||||
# By name
|
||||
fog_layer = grid.layer("fog")
|
||||
|
||||
# All layers (returns tuple)
|
||||
all_layers = grid.layers
|
||||
print(f"Grid has {len(all_layers)} layers")
|
||||
```
|
||||
|
||||
### Removing Layers
|
||||
|
||||
```python
|
||||
grid.remove_layer(fog_layer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layer Operations
|
||||
|
||||
### ColorLayer
|
||||
|
||||
```python
|
||||
cl = mcrfpy.ColorLayer(name="highlights", z_index=1)
|
||||
grid.add_layer(cl)
|
||||
|
||||
# Fill all cells with one color
|
||||
cl.fill(mcrfpy.Color(0, 0, 0, 255))
|
||||
|
||||
# Set individual cell
|
||||
cl.set((5, 5), mcrfpy.Color(255, 0, 0, 128))
|
||||
|
||||
# Read cell value
|
||||
color = cl.at((5, 5))
|
||||
print(f"Color: ({color.r}, {color.g}, {color.b}, {color.a})")
|
||||
```
|
||||
|
||||
### TileLayer
|
||||
|
||||
```python
|
||||
tl = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=tileset)
|
||||
grid.add_layer(tl)
|
||||
|
||||
# Set tile sprite index
|
||||
tl.set((5, 5), 42)
|
||||
|
||||
# Read tile index
|
||||
idx = tl.at((5, 5))
|
||||
|
||||
# Fill all cells
|
||||
tl.fill(0) # All floor tiles
|
||||
```
|
||||
|
||||
### Layer Properties
|
||||
|
||||
```python
|
||||
layer = grid.layer("fog")
|
||||
|
||||
# Visibility toggle
|
||||
layer.visible = False # Hide layer
|
||||
layer.visible = True # Show layer
|
||||
|
||||
# Z-index (render order)
|
||||
layer.z_index = 2 # Move to front
|
||||
|
||||
# Grid dimensions (read-only)
|
||||
w, h = layer.grid_size
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
**Static Grids:**
|
||||
- Near-zero CPU cost after initial render
|
||||
- Cached chunk textures reused frame-to-frame
|
||||
- Only viewport calculation and texture blitting
|
||||
|
||||
**Dynamic Grids:**
|
||||
- Cost proportional to number of dirty chunks
|
||||
- Single-cell changes affect only one chunk
|
||||
- Bulk operations should batch changes before render
|
||||
|
||||
**Large Grids:**
|
||||
- 1000x1000+ grids render efficiently
|
||||
- Only visible chunks processed
|
||||
- Memory scales with grid size (chunk textures)
|
||||
|
||||
---
|
||||
|
||||
## FOV Overlay Pattern
|
||||
|
||||
The most common overlay pattern is fog of war using a ColorLayer:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(800, 600), layers=[])
|
||||
|
||||
# Background and terrain layers (below entities)
|
||||
bg = mcrfpy.ColorLayer(name="bg", z_index=-2)
|
||||
grid.add_layer(bg)
|
||||
bg.fill(mcrfpy.Color(20, 20, 30, 255))
|
||||
|
||||
# Fog overlay (above entities, z_index >= 0)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
grid.add_layer(fog)
|
||||
fog.fill(mcrfpy.Color(0, 0, 0, 255))
|
||||
|
||||
# Make cells traversable
|
||||
for x in range(50):
|
||||
for y in range(50):
|
||||
grid.at(x, y).transparent = True
|
||||
grid.at(x, y).walkable = True
|
||||
|
||||
# After computing FOV, update fog
|
||||
grid.compute_fov((25, 25), radius=10)
|
||||
w, h = grid.grid_size
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
if grid.is_in_fov((x, y)):
|
||||
fog.set((x, y), mcrfpy.Color(0, 0, 0, 0)) # Transparent
|
||||
else:
|
||||
fog.set((x, y), mcrfpy.Color(0, 0, 0, 192)) # Dimmed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multiple Overlay Layers
|
||||
|
||||
Pre-create multiple overlay layers and toggle between them:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(0, 0), size=(800, 600), layers=[])
|
||||
|
||||
highlight = mcrfpy.ColorLayer(name="highlight", z_index=1)
|
||||
danger = mcrfpy.ColorLayer(name="danger", z_index=2)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=3)
|
||||
grid.add_layer(highlight)
|
||||
grid.add_layer(danger)
|
||||
grid.add_layer(fog)
|
||||
|
||||
def show_danger_zones():
|
||||
highlight.visible = False
|
||||
danger.visible = True
|
||||
fog.visible = False
|
||||
|
||||
def show_fog_of_war():
|
||||
highlight.visible = False
|
||||
danger.visible = False
|
||||
fog.visible = True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recreating Legacy Three-Layer Rendering
|
||||
|
||||
The old system rendered: background color, tile sprite, FOV overlay. Here's the modern equivalent:
|
||||
|
||||
```python
|
||||
grid = mcrfpy.Grid(
|
||||
grid_size=(50, 50),
|
||||
pos=(100, 100),
|
||||
size=(400, 400),
|
||||
layers=[]
|
||||
)
|
||||
|
||||
# Layer 1: Background colors (z=-2, behind everything)
|
||||
background = mcrfpy.ColorLayer(name="background", z_index=-2)
|
||||
grid.add_layer(background)
|
||||
background.fill(mcrfpy.Color(20, 20, 30, 255))
|
||||
|
||||
# Layer 2: Tile sprites (z=-1, above background, below entities)
|
||||
tiles = mcrfpy.TileLayer(name="tiles", z_index=-1, texture=tileset)
|
||||
grid.add_layer(tiles)
|
||||
|
||||
# Layer 3: FOV overlay (z=+1, above entities)
|
||||
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||
grid.add_layer(fog)
|
||||
|
||||
# Populate layers
|
||||
for x in range(50):
|
||||
for y in range(50):
|
||||
tiles.set((x, y), calculate_tile_index(x, y))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: Layer Changes Don't Appear
|
||||
|
||||
**Cause:** Use layer methods (`set()`, `fill()`) which automatically mark chunks dirty.
|
||||
|
||||
### Issue: Overlay Appears Behind Entities
|
||||
|
||||
**Cause:** Layer z_index is negative.
|
||||
|
||||
**Fix:** Use `z_index >= 0` for overlays:
|
||||
```python
|
||||
overlay = mcrfpy.ColorLayer(name="overlay", z_index=1) # Not z_index=-1
|
||||
```
|
||||
|
||||
### Issue: Performance Degrades with Many Changes
|
||||
|
||||
**Cause:** Each `set()` call marks a chunk dirty; scattered changes mean many chunk redraws.
|
||||
|
||||
**Fix:** For bulk updates, use `fill()` for uniform values:
|
||||
```python
|
||||
# Single operation - efficient
|
||||
layer.fill(mcrfpy.Color(0, 0, 0, 0))
|
||||
|
||||
# Many individual calls - slower but necessary for non-uniform data
|
||||
for x in range(100):
|
||||
for y in range(100):
|
||||
layer.set((x, y), compute_value(x, y))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Quick Reference
|
||||
|
||||
**Grid Properties:**
|
||||
- `center` - Camera position (Vector, pixels in grid space)
|
||||
- `zoom` - Scale factor (float)
|
||||
- `layers` - All layers (tuple, sorted by z_index)
|
||||
- `fill_color` - Grid background color (behind all layers)
|
||||
- `grid_size` - Grid dimensions (tuple)
|
||||
|
||||
**Grid Methods:**
|
||||
- `add_layer(layer)` - Attach a layer object
|
||||
- `remove_layer(layer)` - Detach a layer
|
||||
- `layer("name")` - Get layer by name
|
||||
|
||||
**Layer Construction:**
|
||||
- `mcrfpy.ColorLayer(name="...", z_index=N)` - Color overlay
|
||||
- `mcrfpy.TileLayer(name="...", z_index=N, texture=tex)` - Tile sprites
|
||||
|
||||
**Layer Properties:**
|
||||
- `z_index` - Render order (int)
|
||||
- `visible` - Show/hide (bool)
|
||||
- `grid_size` - Dimensions (tuple, read-only)
|
||||
|
||||
**Layer Methods:**
|
||||
- `set((x, y), value)` - Set cell (Color or int)
|
||||
- `at((x, y))` - Get cell value
|
||||
- `fill(value)` - Fill all cells
|
||||
|
||||
---
|
||||
|
||||
**Navigation:**
|
||||
- [[Grid-System]] - Parent page, layer concepts
|
||||
- [[Grid-TCOD-Integration]] - FOV computation details
|
||||
- [[Performance-and-Profiling]] - Metrics and optimization
|
||||
- [[Entity-Management]] - Entity rendering
|
||||
Loading…
Add table
Add a link
Reference in a new issue