Update Grid System wiki with standalone layer API, current constructor, SpatialHash, callback signatures, Tiled/LDtk refs
parent
dd2a24f623
commit
cbf263d0f8
1 changed files with 433 additions and 245 deletions
456
Grid-System.md
456
Grid-System.md
|
|
@ -11,17 +11,16 @@ The Grid System is McRogueFace's core spatial container for roguelike game maps.
|
||||||
- [#148](../issues/148) - Dirty Flag RenderTexture Caching (Closed - Implemented)
|
- [#148](../issues/148) - Dirty Flag RenderTexture Caching (Closed - Implemented)
|
||||||
- [#147](../issues/147) - Dynamic Layer System (Closed - Implemented)
|
- [#147](../issues/147) - Dynamic Layer System (Closed - Implemented)
|
||||||
- [#123](../issues/123) - Chunk-based Grid Rendering (Closed - Implemented)
|
- [#123](../issues/123) - Chunk-based Grid Rendering (Closed - Implemented)
|
||||||
|
- [#115](../issues/115) - SpatialHash for Entity Queries (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/GridLayers.h` / `src/GridLayers.cpp` - ColorLayer and TileLayer
|
- `src/GridLayers.h` / `src/GridLayers.cpp` - ColorLayer and TileLayer
|
||||||
- `src/UIGridPoint.h` - Individual grid cell (walkability, transparency)
|
- `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/SpatialHash.h` / `src/SpatialHash.cpp` - Spatial indexing for entities
|
||||||
|
|
||||||
**API Reference:**
|
---
|
||||||
- See [mcrfpy.Grid](../docs/api_reference_dynamic.html#Grid) in generated API docs
|
|
||||||
- See [mcrfpy.Entity](../docs/api_reference_dynamic.html#Entity) in generated API docs
|
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
|
|
@ -36,7 +35,7 @@ The Grid System uses a three-layer architecture for sophisticated roguelike feat
|
||||||
- Files: `src/GridLayers.h`, `src/GridLayers.cpp`
|
- 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
|
||||||
- Used for pathfinding and FOV calculations
|
- Used for pathfinding and FOV calculations
|
||||||
- Integration: libtcod via `src/UIGrid.cpp`
|
- Integration: libtcod via `src/UIGrid.cpp`
|
||||||
|
|
||||||
|
|
@ -44,148 +43,294 @@ 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.
|
---
|
||||||
|
|
||||||
### Dynamic Layer System
|
## Creating a Grid
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
**Entity Lifecycle:**
|
|
||||||
- Entities live on exactly 0 or 1 grids
|
|
||||||
- `grid.entities.append(entity)` sets `entity.grid = grid`
|
|
||||||
- `grid.entities.remove(entity)` sets `entity.grid = None`
|
|
||||||
- Entity removal handled automatically on grid destruction
|
|
||||||
|
|
||||||
**Spatial Queries:**
|
|
||||||
- Currently: Linear iteration through entity list
|
|
||||||
- Planned: SpatialHash for O(1) lookups (see [#115](../issues/115))
|
|
||||||
|
|
||||||
## Sub-Pages
|
|
||||||
|
|
||||||
- [[Grid-Rendering-Pipeline]] - How grid renders each frame (chunks, dirty flags, caching)
|
|
||||||
- [[Grid-TCOD-Integration]] - FOV, pathfinding, walkability
|
|
||||||
- [[Grid-Entity-Lifecycle]] - Entity creation, movement, removal
|
|
||||||
|
|
||||||
## Common Tasks
|
|
||||||
|
|
||||||
### Creating a Grid
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import mcrfpy
|
import mcrfpy
|
||||||
|
|
||||||
# Create grid with default tile layer
|
# Basic grid (gets a default TileLayer at z_index=-1)
|
||||||
grid = mcrfpy.Grid(
|
grid = mcrfpy.Grid(
|
||||||
grid_size=(50, 50), # 50x50 cells
|
grid_size=(50, 50), # 50x50 cells
|
||||||
pos=(100, 100), # Screen position
|
pos=(100, 100), # Screen position
|
||||||
size=(400, 400), # Viewport size in pixels
|
size=(400, 400) # Viewport size in pixels
|
||||||
texture=my_texture # Texture for default TileLayer
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Or create with specific layer configuration
|
# Grid with specific layers passed at creation
|
||||||
|
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1)
|
||||||
|
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||||
grid = mcrfpy.Grid(
|
grid = mcrfpy.Grid(
|
||||||
grid_size=(50, 50),
|
grid_size=(50, 50),
|
||||||
pos=(100, 100),
|
pos=(100, 100),
|
||||||
size=(400, 400),
|
size=(400, 400),
|
||||||
layers={"terrain": "tile", "highlights": "color"}
|
layers=[terrain, fog]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Or start empty and add layers manually
|
# Grid with no layers (add them later)
|
||||||
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={})
|
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)
|
scene = mcrfpy.Scene("game")
|
||||||
|
scene.children.append(grid)
|
||||||
|
mcrfpy.current_scene = scene
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setting Tile Properties
|
---
|
||||||
|
|
||||||
|
## Layer System
|
||||||
|
|
||||||
|
Layers are standalone objects created independently, then added to grids:
|
||||||
|
|
||||||
|
### TileLayer
|
||||||
|
|
||||||
|
Renders per-cell sprite indices from a texture atlas:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Access layers for visual properties
|
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png")
|
||||||
tile_layer = grid.layers[0] # First layer (usually tiles)
|
|
||||||
tile_layer.set(x, y, 42) # Set sprite index at cell
|
|
||||||
|
|
||||||
color_layer = grid.add_layer("color", z_index=-2)
|
# Create standalone layer
|
||||||
color_layer.set(x, y, mcrfpy.Color(64, 64, 128)) # Blue tint
|
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture)
|
||||||
|
|
||||||
# Access grid point for world properties
|
# Add to grid
|
||||||
point = grid.at(x, y)
|
grid.add_layer(terrain)
|
||||||
|
|
||||||
|
# Set individual tiles
|
||||||
|
terrain.set((5, 3), 42) # Set sprite index at cell (5, 3)
|
||||||
|
index = terrain.at((5, 3)) # Get sprite index: 42
|
||||||
|
terrain.fill(0) # Fill entire layer with sprite 0
|
||||||
|
terrain.set((5, 3), -1) # Set to -1 for transparent (no tile drawn)
|
||||||
|
```
|
||||||
|
|
||||||
|
### ColorLayer
|
||||||
|
|
||||||
|
Renders per-cell RGBA colors (fog of war, highlights, overlays):
|
||||||
|
|
||||||
|
```python
|
||||||
|
fog = mcrfpy.ColorLayer(name="fog", z_index=1)
|
||||||
|
grid.add_layer(fog)
|
||||||
|
|
||||||
|
# Set individual cells
|
||||||
|
fog.set((5, 3), mcrfpy.Color(0, 0, 0, 200)) # Dark fog
|
||||||
|
fog.set((5, 3), mcrfpy.Color(0, 0, 0, 0)) # Clear (transparent)
|
||||||
|
color = fog.at((5, 3)) # Get color
|
||||||
|
fog.fill(mcrfpy.Color(0, 0, 0, 255)) # Fill entire layer black
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
# Typical layer stack
|
||||||
|
background = mcrfpy.ColorLayer(name="bg", z_index=-3) # Furthest back
|
||||||
|
terrain = mcrfpy.TileLayer(name="terrain", z_index=-2) # Terrain tiles
|
||||||
|
items = mcrfpy.TileLayer(name="items", z_index=-1) # Items below entities
|
||||||
|
# --- entities render here (z_index = 0) ---
|
||||||
|
fog = mcrfpy.ColorLayer(name="fog", z_index=1) # Fog above entities
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Layers
|
||||||
|
|
||||||
|
```python
|
||||||
|
# List all layers
|
||||||
|
for layer in grid.layers:
|
||||||
|
print(f"{type(layer).__name__} '{layer.name}' at z={layer.z_index}")
|
||||||
|
|
||||||
|
# Get layer by name
|
||||||
|
terrain = grid.layer("terrain")
|
||||||
|
|
||||||
|
# Remove a layer
|
||||||
|
grid.remove_layer(fog)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cell Properties (GridPoint)
|
||||||
|
|
||||||
|
Each cell has world-state properties accessed via `grid.at(x, y)`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
point = grid.at(10, 15)
|
||||||
point.walkable = True # Can entities walk here?
|
point.walkable = True # Can entities walk here?
|
||||||
point.transparent = True # Can see through for FOV?
|
point.transparent = True # Can see through for FOV?
|
||||||
|
|
||||||
|
# Read properties
|
||||||
|
print(point.walkable) # True
|
||||||
|
print(point.transparent) # True
|
||||||
|
|
||||||
|
# List entities at this cell
|
||||||
|
entities_here = point.entities # List of Entity objects
|
||||||
```
|
```
|
||||||
|
|
||||||
### FOV and Pathfinding
|
---
|
||||||
|
|
||||||
|
## FOV (Field of View)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Compute field of view from position
|
# Set up transparent/opaque cells
|
||||||
grid.compute_fov(entity.grid_x, entity.grid_y, radius=10)
|
for x in range(50):
|
||||||
|
for y in range(50):
|
||||||
|
grid.at(x, y).transparent = True
|
||||||
|
|
||||||
# Check if cell is visible
|
# Mark walls as opaque
|
||||||
if grid.is_in_fov(target_x, target_y):
|
grid.at(5, 5).transparent = False
|
||||||
print("Target is visible!")
|
|
||||||
|
|
||||||
# A* pathfinding
|
# Compute FOV from position
|
||||||
path = grid.find_path(start_x, start_y, end_x, end_y)
|
grid.compute_fov((10, 10), radius=8)
|
||||||
|
|
||||||
# Dijkstra maps for AI
|
# Query visibility
|
||||||
grid.compute_dijkstra([(goal_x, goal_y)])
|
if grid.is_in_fov((12, 14)):
|
||||||
distance = grid.get_dijkstra_distance(entity.grid_x, entity.grid_y)
|
print("Cell is visible!")
|
||||||
path_to_goal = grid.get_dijkstra_path(entity.grid_x, entity.grid_y)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mouse Events
|
### FOV Algorithms (`mcrfpy.FOV`)
|
||||||
|
|
||||||
Grids support mouse interaction at both the grid level and cell level:
|
- `FOV.BASIC` - Simple raycasting
|
||||||
|
- `FOV.DIAMOND` - Diamond-shaped
|
||||||
|
- `FOV.SHADOW` - Shadow casting (recommended)
|
||||||
|
- `FOV.PERMISSIVE_0` through `FOV.PERMISSIVE_8` - Permissive variants
|
||||||
|
- `FOV.RESTRICTIVE` - Restrictive precise angle
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Grid-level events (screen coordinates)
|
grid.compute_fov((10, 10), radius=10, algorithm=mcrfpy.FOV.SHADOW)
|
||||||
def on_grid_click(x, y, button):
|
```
|
||||||
print(f"Grid clicked at pixel ({x}, {y}), button {button}")
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pathfinding
|
||||||
|
|
||||||
|
### A* Pathfinding
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Set walkable cells
|
||||||
|
for x in range(50):
|
||||||
|
for y in range(50):
|
||||||
|
grid.at(x, y).walkable = True
|
||||||
|
grid.at(5, 5).walkable = False # Wall
|
||||||
|
|
||||||
|
# Find path (returns AStarPath object)
|
||||||
|
path = grid.find_path((0, 0), (10, 10))
|
||||||
|
|
||||||
|
if path:
|
||||||
|
print(f"Path length: {len(path)} steps")
|
||||||
|
print(f"Origin: {path.origin}")
|
||||||
|
print(f"Destination: {path.destination}")
|
||||||
|
|
||||||
|
# Iterate all steps
|
||||||
|
for step in path:
|
||||||
|
print(f" Step: ({step.x}, {step.y})")
|
||||||
|
|
||||||
|
# Or walk step-by-step
|
||||||
|
next_step = path.walk() # Advances and returns next Vector
|
||||||
|
upcoming = path.peek() # Look at next step without advancing
|
||||||
|
remaining = path.remaining # Steps remaining
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dijkstra Maps
|
||||||
|
|
||||||
|
For multi-target or AI pathfinding:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Create Dijkstra map from a root position
|
||||||
|
dm = grid.get_dijkstra_map((5, 5))
|
||||||
|
|
||||||
|
# Query distance from root to any position
|
||||||
|
distance = dm.distance((10, 10)) # Float distance
|
||||||
|
print(f"Distance: {distance}")
|
||||||
|
|
||||||
|
# Get path from a position back to the root
|
||||||
|
path = dm.path_from((10, 10)) # List of Vector objects
|
||||||
|
for step in path:
|
||||||
|
print(f" ({step.x}, {step.y})")
|
||||||
|
|
||||||
|
# Get single next step toward root
|
||||||
|
next_step = dm.step_from((10, 10)) # Single Vector
|
||||||
|
|
||||||
|
# Clear cached maps when grid changes
|
||||||
|
grid.clear_dijkstra_maps()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Entity Management
|
||||||
|
|
||||||
|
Entities live on grids via the `entities` collection:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Create and add entity
|
||||||
|
player = mcrfpy.Entity(grid_pos=(10, 10), sprite_index=0, name="player")
|
||||||
|
grid.entities.append(player)
|
||||||
|
print(player.grid == grid) # True
|
||||||
|
|
||||||
|
# Query entities
|
||||||
|
for entity in grid.entities:
|
||||||
|
print(f"{entity.name} at ({entity.grid_x}, {entity.grid_y})")
|
||||||
|
|
||||||
|
# Spatial queries (SpatialHash - O(k) complexity)
|
||||||
|
nearby = grid.entities_in_radius((10, 10), 5.0)
|
||||||
|
for entity in nearby:
|
||||||
|
print(f"Nearby: {entity.name}")
|
||||||
|
|
||||||
|
# Remove entity
|
||||||
|
player.die() # Removes from grid and SpatialHash
|
||||||
|
```
|
||||||
|
|
||||||
|
See [[Entity-Management]] for detailed entity documentation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Camera Control
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Center viewport on 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
|
||||||
|
|
||||||
|
# Center on tile coordinates (convenience method)
|
||||||
|
grid.center_camera((14.5, 8.5)) # Centers on middle of tile (14, 8)
|
||||||
|
|
||||||
|
# Zoom (1.0 = normal, 2.0 = 2x zoom in, 0.5 = zoom out)
|
||||||
|
grid.zoom = 1.5
|
||||||
|
|
||||||
|
# Animate camera movement
|
||||||
|
grid.animate("center_x", target_x, 0.5, mcrfpy.Easing.EASE_IN_OUT)
|
||||||
|
grid.animate("center_y", target_y, 0.5, mcrfpy.Easing.EASE_IN_OUT)
|
||||||
|
grid.animate("zoom", 2.0, 0.3, mcrfpy.Easing.EASE_OUT_QUAD)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mouse Events
|
||||||
|
|
||||||
|
Grids support mouse interaction at both element and cell levels:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Element-level events (screen coordinates)
|
||||||
|
def on_grid_click(pos, button, action):
|
||||||
|
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
|
||||||
|
print(f"Grid clicked at pixel ({pos.x}, {pos.y})")
|
||||||
|
|
||||||
grid.on_click = on_grid_click
|
grid.on_click = on_grid_click
|
||||||
grid.on_enter = lambda: print("Mouse entered grid")
|
grid.on_enter = lambda pos: print("Mouse entered grid")
|
||||||
grid.on_exit = lambda: print("Mouse left grid")
|
grid.on_exit = lambda pos: print("Mouse left grid")
|
||||||
grid.on_move = lambda x, y: print(f"Mouse at ({x}, {y})")
|
|
||||||
|
|
||||||
# Cell-level events (grid coordinates)
|
# Cell-level events (grid coordinates)
|
||||||
def on_cell_click(grid_x, grid_y):
|
def on_cell_click(cell_pos, button, action):
|
||||||
print(f"Cell ({grid_x}, {grid_y}) clicked!")
|
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
|
||||||
point = grid.at(grid_x, grid_y)
|
x, y = int(cell_pos.x), int(cell_pos.y)
|
||||||
# Do something with the cell...
|
point = grid.at(x, y)
|
||||||
|
point.walkable = not point.walkable # Toggle walkability
|
||||||
|
|
||||||
grid.on_cell_click = on_cell_click
|
grid.on_cell_click = on_cell_click
|
||||||
grid.on_cell_enter = lambda x, y: print(f"Entered cell ({x}, {y})")
|
grid.on_cell_enter = lambda cell_pos: highlight_cell(cell_pos)
|
||||||
grid.on_cell_exit = lambda x, y: print(f"Left cell ({x}, {y})")
|
grid.on_cell_exit = lambda cell_pos: clear_highlight(cell_pos)
|
||||||
|
|
||||||
# Query currently hovered cell
|
# Query currently hovered cell
|
||||||
if grid.hovered_cell:
|
if grid.hovered_cell:
|
||||||
|
|
@ -193,53 +338,96 @@ if grid.hovered_cell:
|
||||||
print(f"Hovering over ({hx}, {hy})")
|
print(f"Hovering over ({hx}, {hy})")
|
||||||
```
|
```
|
||||||
|
|
||||||
### Camera Control
|
See [[Input-and-Events]] for callback signature details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Perspective System
|
||||||
|
|
||||||
|
Set a perspective entity to enable FOV-based rendering:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Center viewport on a position (pixel coordinates within grid space)
|
# Set perspective (fog of war from this entity's viewpoint)
|
||||||
grid.center = (player.grid_x * 16 + 8, player.grid_y * 16 + 8)
|
grid.perspective = player_entity
|
||||||
|
|
||||||
# Or set components individually
|
# Disable perspective (show everything)
|
||||||
grid.center_x = player.grid_x * 16 + 8
|
grid.perspective = None
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Level Import Integration
|
||||||
|
|
||||||
|
Grids integrate with external level editors via Tiled and LDtk import systems:
|
||||||
|
|
||||||
|
### Tiled Import
|
||||||
|
|
||||||
|
```python
|
||||||
|
tileset = mcrfpy.TileSetFile("assets/dungeon.tsj")
|
||||||
|
tilemap = mcrfpy.TileMapFile("assets/level1.tmj")
|
||||||
|
|
||||||
|
# Apply tilemap layers to grid
|
||||||
|
for layer_data in tilemap.layers:
|
||||||
|
tile_layer = mcrfpy.TileLayer(name=layer_data.name, z_index=-1, texture=tileset.texture)
|
||||||
|
grid.add_layer(tile_layer)
|
||||||
|
# ... apply tile data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wang Tile Terrain Generation
|
||||||
|
|
||||||
|
```python
|
||||||
|
wang_set = tileset.wang_set("Terrain")
|
||||||
|
terrain_data = wang_set.resolve(intgrid) # Resolve terrain to tile indices
|
||||||
|
wang_set.apply(tile_layer, terrain_data) # Apply to layer
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Performance Characteristics
|
## Performance Characteristics
|
||||||
|
|
||||||
**Implemented Optimizations:**
|
**Implemented Optimizations:**
|
||||||
- **Chunk-based rendering** ([#123](../issues/123)): Large grids divided into chunks, only visible chunks rendered
|
- **Chunk-based rendering** ([#123](../issues/123)): Large grids divided into chunks
|
||||||
- **Dirty flag system** ([#148](../issues/148)): Layers track changes, skip redraw when unchanged
|
- **Dirty flag system** ([#148](../issues/148)): Layers track changes, skip redraw when unchanged
|
||||||
- **RenderTexture caching**: Each chunk cached to texture, reused until dirty
|
- **RenderTexture caching**: Each chunk cached to texture, reused until dirty
|
||||||
- **Viewport culling**: Only cells within viewport are processed
|
- **Viewport culling**: Only cells within viewport are processed
|
||||||
|
- **SpatialHash** ([#115](../issues/115)): O(k) entity queries instead of O(n)
|
||||||
|
|
||||||
**Current Performance:**
|
**Current Performance:**
|
||||||
- Grids of 1000x1000+ cells render efficiently
|
- Grids of 1000x1000+ cells render efficiently
|
||||||
- Static scenes near-zero CPU (cached textures reused)
|
- Static scenes near-zero CPU (cached textures reused)
|
||||||
- Entity rendering: O(visible entities)
|
- Entity queries: O(k) where k is nearby entities (not total)
|
||||||
|
|
||||||
**Profiling:** Use the F3 overlay or `mcrfpy.setTimer()` with `mcrfpy.getMetrics()` to measure grid performance. See [[Performance-and-Profiling]].
|
|
||||||
|
|
||||||
## Known Issues & Limitations
|
|
||||||
|
|
||||||
**Current Limitations:**
|
|
||||||
- **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).
|
|
||||||
- No tile-level animation yet (planned: [#124](../issues/124))
|
|
||||||
- Entity spatial queries are O(n) (planned: SpatialHash [#115](../issues/115))
|
|
||||||
|
|
||||||
**Workarounds:**
|
|
||||||
- For fog of war: Use a `ColorLayer` with positive z_index, update cell colors based on `is_in_fov()` results
|
|
||||||
- For entity queries: Use list comprehension filtering on `grid.entities`
|
|
||||||
|
|
||||||
## Related Systems
|
|
||||||
|
|
||||||
- [[UI-Component-Hierarchy]] - Grid inherits from UIDrawable
|
|
||||||
- [[Animation-System]] - Grid properties are animatable (pos, zoom, center, etc.)
|
|
||||||
- [[Performance-and-Profiling]] - Grid rendering instrumented with metrics
|
|
||||||
- [[Entity-Management]] - Entities live within Grid containers
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Last updated: 2025-11-29*
|
## Grid Properties Reference
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `grid_size` | `(int, int)` | Grid dimensions (read-only) |
|
||||||
|
| `grid_w`, `grid_h` | int | Width/height in cells (read-only) |
|
||||||
|
| `pos` | Vector | Screen position |
|
||||||
|
| `size` | Vector | Viewport size in pixels |
|
||||||
|
| `center` | Vector | Camera center (pixel coordinates) |
|
||||||
|
| `center_x`, `center_y` | float | Camera center components |
|
||||||
|
| `zoom` | float | Camera zoom level |
|
||||||
|
| `fill_color` | Color | Background color |
|
||||||
|
| `perspective` | Entity or None | FOV perspective entity |
|
||||||
|
| `entities` | EntityCollection | Entities on this grid |
|
||||||
|
| `layers` | list | Rendering layers (sorted by z_index) |
|
||||||
|
| `children` | UICollection | UI overlays |
|
||||||
|
| `hovered_cell` | `(x, y)` or None | Currently hovered cell (read-only) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Systems
|
||||||
|
|
||||||
|
- [[Entity-Management]] - Entities live within Grid containers
|
||||||
|
- [[Grid-Rendering-Pipeline]] - How grid renders each frame
|
||||||
|
- [[Grid-TCOD-Integration]] - FOV, pathfinding, walkability details
|
||||||
|
- [[Grid-Interaction-Patterns]] - Click handling, selection, context menus
|
||||||
|
- [[Animation-System]] - Grid properties are animatable (pos, zoom, center)
|
||||||
|
- [[Input-and-Events]] - Mouse callback signatures
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last updated: 2026-02-07*
|
||||||
Loading…
Add table
Add a link
Reference in a new issue