Update Grid-System wiki for dynamic layer system, mouse events, performance improvements
parent
634af7540f
commit
6b212e8995
212
Grid-System.-.md
212
Grid-System.-.md
|
|
@ -5,16 +5,17 @@ The Grid System is McRogueFace's core spatial container for roguelike game maps.
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
**Related Issues:**
|
**Related Issues:**
|
||||||
- [#124](../../issues/124) - Grid Point Animation (Tier 1 - Active)
|
- [#113](../../issues/113) - Batch Operations for Grid (Open - Tier 1)
|
||||||
- [#123](../../issues/123) - Grid Subgrid System (Tier 1 - Active)
|
- [#124](../../issues/124) - Grid Point Animation (Open - Tier 1)
|
||||||
- [#114](../../issues/114) - CellView API (Tier 1 - Active)
|
- [#150](../../issues/150) - User-driven Layer Rendering (Closed - Implemented)
|
||||||
- [#113](../../issues/113) - Batch Operations for Grid (Tier 1 - Active)
|
- [#148](../../issues/148) - Dirty Flag RenderTexture Caching (Closed - Implemented)
|
||||||
- [#64](../../issues/64) - Grid-Entity-GridPointState TCOD Updates
|
- [#147](../../issues/147) - Dynamic Layer System (Closed - Implemented)
|
||||||
- [#50](../../issues/50) - Grid Background Color (Closed - Implemented)
|
- [#123](../../issues/123) - Chunk-based Grid Rendering (Closed - Implemented)
|
||||||
|
|
||||||
**Key Files:**
|
**Key Files:**
|
||||||
- `src/UIGrid.h` / `src/UIGrid.cpp` - Main grid implementation
|
- `src/UIGrid.h` / `src/UIGrid.cpp` - Main grid implementation
|
||||||
- `src/UIGridPoint.h` - Individual grid cell (visual layer)
|
- `src/GridLayers.h` / `src/GridLayers.cpp` - ColorLayer and TileLayer
|
||||||
|
- `src/UIGridPoint.h` - Individual grid cell (walkability, transparency)
|
||||||
- `src/UIGridPointState.h` - Per-entity perspective/knowledge
|
- `src/UIGridPointState.h` - Per-entity perspective/knowledge
|
||||||
- `src/UIEntity.h` / `src/UIEntity.cpp` - Entity system (lives on grid)
|
- `src/UIEntity.h` / `src/UIEntity.cpp` - Entity system (lives on grid)
|
||||||
|
|
||||||
|
|
@ -28,10 +29,11 @@ The Grid System is McRogueFace's core spatial container for roguelike game maps.
|
||||||
|
|
||||||
The Grid System uses a three-layer architecture for sophisticated roguelike features:
|
The Grid System uses a three-layer architecture for sophisticated roguelike features:
|
||||||
|
|
||||||
1. **Visual Layer** (`UIGridPoint`)
|
1. **Visual Layer** (Rendering Layers)
|
||||||
- What's displayed: tile sprite, colors, animations
|
- What's displayed: tile sprites, colors, overlays
|
||||||
- Shared by all viewers
|
- Implemented via `ColorLayer` and `TileLayer` objects
|
||||||
- File: `src/UIGridPoint.h`
|
- Multiple layers per grid with z_index ordering
|
||||||
|
- Files: `src/GridLayers.h`, `src/GridLayers.cpp`
|
||||||
|
|
||||||
2. **World State Layer** (`TCODMap`)
|
2. **World State Layer** (`TCODMap`)
|
||||||
- Physical properties: walkable, transparent, cost
|
- Physical properties: walkable, transparent, cost
|
||||||
|
|
@ -42,9 +44,41 @@ The Grid System uses a three-layer architecture for sophisticated roguelike feat
|
||||||
- Per-entity knowledge: what each entity has seen/explored
|
- Per-entity knowledge: what each entity has seen/explored
|
||||||
- Enables fog of war, asymmetric information
|
- Enables fog of war, asymmetric information
|
||||||
- File: `src/UIGridPointState.h`
|
- File: `src/UIGridPointState.h`
|
||||||
|
- **Note:** Python access currently limited - see Known Issues
|
||||||
|
|
||||||
This architecture follows proven patterns from Caves of Qud, Cogmind, and DCSS.
|
This architecture follows proven patterns from Caves of Qud, Cogmind, and DCSS.
|
||||||
|
|
||||||
|
### Dynamic Layer System
|
||||||
|
|
||||||
|
Grids support multiple rendering layers that can be added/removed at runtime:
|
||||||
|
|
||||||
|
| Layer Type | Purpose | Methods |
|
||||||
|
|------------|---------|---------|
|
||||||
|
| `ColorLayer` | Solid colors per cell (fog, highlights) | `set(x, y, color)`, `at(x, y)`, `fill(color)` |
|
||||||
|
| `TileLayer` | Texture sprites per cell (terrain, items) | `set(x, y, sprite_index)`, `at(x, y)`, `fill(index)` |
|
||||||
|
|
||||||
|
**z_index semantics:**
|
||||||
|
- Negative z_index: Renders **below** entities
|
||||||
|
- Zero or positive z_index: Renders **above** entities
|
||||||
|
- Lower z_index renders first (behind higher z_index)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Create grid with no default layers
|
||||||
|
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={})
|
||||||
|
|
||||||
|
# Add layers explicitly
|
||||||
|
background = grid.add_layer("color", z_index=-2) # Furthest back
|
||||||
|
tiles = grid.add_layer("tile", z_index=-1) # Above background, below entities
|
||||||
|
fog_overlay = grid.add_layer("color", z_index=1) # Above entities (fog of war)
|
||||||
|
|
||||||
|
# Access existing layers
|
||||||
|
for layer in grid.layers:
|
||||||
|
print(f"{type(layer).__name__} at z={layer.z_index}")
|
||||||
|
|
||||||
|
# Remove a layer
|
||||||
|
grid.remove_layer(fog_overlay)
|
||||||
|
```
|
||||||
|
|
||||||
### Grid → Entity Relationship
|
### Grid → Entity Relationship
|
||||||
|
|
||||||
**Entity Lifecycle:**
|
**Entity Lifecycle:**
|
||||||
|
|
@ -59,10 +93,9 @@ This architecture follows proven patterns from Caves of Qud, Cogmind, and DCSS.
|
||||||
|
|
||||||
## Sub-Pages
|
## Sub-Pages
|
||||||
|
|
||||||
- [[Grid-Rendering-Pipeline]] - How grid renders each frame
|
- [[Grid-Rendering-Pipeline]] - How grid renders each frame (chunks, dirty flags, caching)
|
||||||
- [[Grid-TCOD-Integration]] - FOV, pathfinding, walkability
|
- [[Grid-TCOD-Integration]] - FOV, pathfinding, walkability
|
||||||
- [[Grid-Entity-Lifecycle]] - Entity creation, movement, removal
|
- [[Grid-Entity-Lifecycle]] - Entity creation, movement, removal
|
||||||
- [[Grid-Performance-Optimization]] - Dirty flags, culling, batch ops
|
|
||||||
|
|
||||||
## Common Tasks
|
## Common Tasks
|
||||||
|
|
||||||
|
|
@ -71,91 +104,142 @@ This architecture follows proven patterns from Caves of Qud, Cogmind, and DCSS.
|
||||||
```python
|
```python
|
||||||
import mcrfpy
|
import mcrfpy
|
||||||
|
|
||||||
# Create 50x50 grid with 16x16 tile size
|
# Create grid with default tile layer
|
||||||
grid = mcrfpy.Grid(50, 50, 16, 16)
|
grid = mcrfpy.Grid(
|
||||||
grid.texture = mcrfpy.createTexture("tileset.png")
|
grid_size=(50, 50), # 50x50 cells
|
||||||
grid.pos = (100, 100) # Screen position
|
pos=(100, 100), # Screen position
|
||||||
|
size=(400, 400), # Viewport size in pixels
|
||||||
|
texture=my_texture # Texture for default TileLayer
|
||||||
|
)
|
||||||
|
|
||||||
|
# Or create with specific layer configuration
|
||||||
|
grid = mcrfpy.Grid(
|
||||||
|
grid_size=(50, 50),
|
||||||
|
pos=(100, 100),
|
||||||
|
size=(400, 400),
|
||||||
|
layers={"terrain": "tile", "highlights": "color"}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Or start empty and add layers manually
|
||||||
|
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={})
|
||||||
|
terrain = grid.add_layer("tile", z_index=-1, texture=my_texture)
|
||||||
|
|
||||||
# Add to scene
|
# Add to scene
|
||||||
mcrfpy.sceneUI("game").append(grid)
|
mcrfpy.sceneUI("game").append(grid)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Details:** See `src/UIGrid.cpp::PyInit()` for constructor implementation
|
|
||||||
|
|
||||||
### Setting Tile Properties
|
### Setting Tile Properties
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Access individual cell
|
# Access layers for visual properties
|
||||||
cell = grid.at((x, y))
|
tile_layer = grid.layers[0] # First layer (usually tiles)
|
||||||
|
tile_layer.set(x, y, 42) # Set sprite index at cell
|
||||||
|
|
||||||
# Visual properties
|
color_layer = grid.add_layer("color", z_index=-2)
|
||||||
cell.tilesprite = 42 # Sprite index in texture
|
color_layer.set(x, y, mcrfpy.Color(64, 64, 128)) # Blue tint
|
||||||
cell.color = mcrfpy.Color(255, 255, 255)
|
|
||||||
|
|
||||||
# World properties (TCOD integration)
|
# Access grid point for world properties
|
||||||
grid.walkable((x, y), True) # Can entities walk here?
|
point = grid.at(x, y)
|
||||||
grid.transparent((x, y), True) # Can see through?
|
point.walkable = True # Can entities walk here?
|
||||||
|
point.transparent = True # Can see through for FOV?
|
||||||
```
|
```
|
||||||
|
|
||||||
**Implementation:** `src/UIGrid.cpp::walkable()`, `src/UIGrid.cpp::transparent()`
|
|
||||||
|
|
||||||
### FOV and Pathfinding
|
### FOV and Pathfinding
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Compute field of view from entity position
|
# Compute field of view from position
|
||||||
grid.compute_fov(entity.x, entity.y, radius=10)
|
grid.compute_fov(entity.grid_x, entity.grid_y, radius=10)
|
||||||
|
|
||||||
|
# Check if cell is visible
|
||||||
|
if grid.is_in_fov(target_x, target_y):
|
||||||
|
print("Target is visible!")
|
||||||
|
|
||||||
# A* pathfinding
|
# A* pathfinding
|
||||||
path = entity.path_to(target_x, target_y)
|
path = grid.find_path(start_x, start_y, end_x, end_y)
|
||||||
|
|
||||||
|
# Dijkstra maps for AI
|
||||||
|
grid.compute_dijkstra([(goal_x, goal_y)])
|
||||||
|
distance = grid.get_dijkstra_distance(entity.grid_x, entity.grid_y)
|
||||||
|
path_to_goal = grid.get_dijkstra_path(entity.grid_x, entity.grid_y)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Implementation:**
|
### Mouse Events
|
||||||
- FOV: `src/UIGrid.cpp::compute_fov()` (TCOD integration)
|
|
||||||
- Pathfinding: `src/UIEntity.cpp::path_to()` (uses TCOD A*)
|
Grids support mouse interaction at both the grid level and cell level:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Grid-level events (screen coordinates)
|
||||||
|
def on_grid_click(x, y, button):
|
||||||
|
print(f"Grid clicked at pixel ({x}, {y}), button {button}")
|
||||||
|
|
||||||
|
grid.on_click = on_grid_click
|
||||||
|
grid.on_enter = lambda: print("Mouse entered grid")
|
||||||
|
grid.on_exit = lambda: print("Mouse left grid")
|
||||||
|
grid.on_move = lambda x, y: print(f"Mouse at ({x}, {y})")
|
||||||
|
|
||||||
|
# Cell-level events (grid coordinates)
|
||||||
|
def on_cell_click(grid_x, grid_y):
|
||||||
|
print(f"Cell ({grid_x}, {grid_y}) clicked!")
|
||||||
|
point = grid.at(grid_x, grid_y)
|
||||||
|
# Do something with the cell...
|
||||||
|
|
||||||
|
grid.on_cell_click = on_cell_click
|
||||||
|
grid.on_cell_enter = lambda x, y: print(f"Entered cell ({x}, {y})")
|
||||||
|
grid.on_cell_exit = lambda x, y: print(f"Left cell ({x}, {y})")
|
||||||
|
|
||||||
|
# Query currently hovered cell
|
||||||
|
if grid.hovered_cell:
|
||||||
|
hx, hy = grid.hovered_cell
|
||||||
|
print(f"Hovering over ({hx}, {hy})")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Camera Control
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Center viewport on a position (pixel coordinates within grid space)
|
||||||
|
grid.center = (player.grid_x * 16 + 8, player.grid_y * 16 + 8)
|
||||||
|
|
||||||
|
# Or set components individually
|
||||||
|
grid.center_x = player.grid_x * 16 + 8
|
||||||
|
grid.center_y = player.grid_y * 16 + 8
|
||||||
|
|
||||||
|
# Zoom (1.0 = normal, 2.0 = 2x zoom in, 0.5 = zoom out)
|
||||||
|
grid.zoom = 1.5
|
||||||
|
```
|
||||||
|
|
||||||
## Performance Characteristics
|
## Performance Characteristics
|
||||||
|
|
||||||
**Current:**
|
**Implemented Optimizations:**
|
||||||
- Grid rendering: O(visible_cells) - renders only viewport
|
- **Chunk-based rendering** ([#123](../../issues/123)): Large grids divided into chunks, only visible chunks rendered
|
||||||
- Entity rendering: O(entities) - culls out-of-bounds (see `src/UIGrid.cpp::render()`)
|
- **Dirty flag system** ([#148](../../issues/148)): Layers track changes, skip redraw when unchanged
|
||||||
- Static grids: Inefficient - redraws unchanged cells every frame
|
- **RenderTexture caching**: Each chunk cached to texture, reused until dirty
|
||||||
|
- **Viewport culling**: Only cells within viewport are processed
|
||||||
|
|
||||||
**Optimizations In Progress:**
|
**Current Performance:**
|
||||||
- [#116](../../issues/116): Dirty flag system - only redraw on changes
|
- Grids of 1000x1000+ cells render efficiently
|
||||||
- [#113](../../issues/113): Batch operations - reduce Python/C++ boundary crossings
|
- Static scenes near-zero CPU (cached textures reused)
|
||||||
- [#115](../../issues/115): SpatialHash - O(1) entity spatial queries
|
- Entity rendering: O(visible entities)
|
||||||
- [#123](../../issues/123): Subgrid system - divide large grids into chunks
|
|
||||||
|
|
||||||
**Profiling:** Press F3 in-game to see grid render metrics (see [[Performance-and-Profiling]])
|
**Profiling:** Use the F3 overlay or `mcrfpy.setTimer()` with `mcrfpy.getMetrics()` to measure grid performance. See [[Performance-and-Profiling]].
|
||||||
|
|
||||||
## Design Proposals
|
|
||||||
|
|
||||||
Active architectural evolution:
|
|
||||||
|
|
||||||
- [[Proposal-Next-Gen-Grid-Architecture]] - Subgrid system, tile animation
|
|
||||||
- [[Proposal-Next-Gen-Entity-System]] - Flexible entity content, multi-tile entities
|
|
||||||
|
|
||||||
## Known Issues & Limitations
|
## Known Issues & Limitations
|
||||||
|
|
||||||
**Current Limitations:**
|
**Current Limitations:**
|
||||||
- No tile-level animation (planned: [#124](../../issues/124))
|
- **FOV Python access limited:** `compute_fov()` works but fog-of-war overlay must be managed manually via color layers. Per-entity perspective (`UIGridPointState`) not yet exposed to Python. See [#113 discussion](../../issues/113).
|
||||||
- Large grids (>200x200) can have rendering overhead
|
- No tile-level animation yet (planned: [#124](../../issues/124))
|
||||||
- Entity iteration is O(n) for spatial queries
|
- Entity spatial queries are O(n) (planned: SpatialHash [#115](../../issues/115))
|
||||||
|
|
||||||
**Workarounds:**
|
**Workarounds:**
|
||||||
- Keep grids reasonably sized (< 100x100 for best performance)
|
- For fog of war: Use a `ColorLayer` with positive z_index, update cell colors based on `is_in_fov()` results
|
||||||
- Use entity culling (automatically enabled in `UIGrid::render()`)
|
- For entity queries: Use list comprehension filtering on `grid.entities`
|
||||||
- Profile with F3 overlay to identify bottlenecks
|
|
||||||
|
|
||||||
## Related Systems
|
## Related Systems
|
||||||
|
|
||||||
- [[UI-Component-Hierarchy]] - Grid inherits from UIDrawable
|
- [[UI-Component-Hierarchy]] - Grid inherits from UIDrawable
|
||||||
- [[Animation-System]] - Grid properties are animatable (pos, zoom, etc)
|
- [[Animation-System]] - Grid properties are animatable (pos, zoom, center, etc.)
|
||||||
- [[Performance-and-Profiling]] - Grid rendering instrumented with ScopedTimer
|
- [[Performance-and-Profiling]] - Grid rendering instrumented with metrics
|
||||||
- [[Entity-Management]] - Entity use case depends on Grid system
|
- [[Entity-Management]] - Entities live within Grid containers
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**See Also:**
|
*Last updated: 2025-11-29*
|
||||||
- Tutorial: [McRogueFace Does The Entire Roguelike Tutorial](../../roguelike_tutorial/)
|
|
||||||
- Build Guide: CLAUDE.md in repository root
|
|
||||||
Loading…
Reference in New Issue