Update Rendering and Visuals

John McCardle 2026-02-07 23:50:14 +00:00
commit f9db137d41

@ -1,370 +1,370 @@
# Rendering and Visuals # Rendering and Visuals
How to create and display visual elements in McRogueFace. How to create and display visual elements in McRogueFace.
## Quick Reference ## Quick Reference
**Systems:** [[UI-Component-Hierarchy]], [[Grid-System]], [[Animation-System]] **Systems:** [[UI-Component-Hierarchy]], [[Grid-System]], [[Animation-System]]
**Key Types:** **Key Types:**
- `Frame` - Rectangles and containers - `Frame` - Rectangles and containers
- `Caption` - Text rendering - `Caption` - Text rendering
- `Sprite` - Images and sprite sheets - `Sprite` - Images and sprite sheets
- `Grid` - Tilemaps with layers - `Grid` - Tilemaps with layers
- `Entity` - Grid-based sprites - `Entity` - Grid-based sprites
--- ---
## Basic Rendering ## Basic Rendering
### Creating UI Elements ### Creating UI Elements
```python ```python
import mcrfpy import mcrfpy
# Create scene # Create scene
scene = mcrfpy.Scene("game") scene = mcrfpy.Scene("game")
ui = scene.children ui = scene.children
# Rectangle/Frame # Rectangle/Frame
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150), frame = mcrfpy.Frame(pos=(100, 100), size=(200, 150),
fill_color=mcrfpy.Color(50, 50, 50)) fill_color=mcrfpy.Color(50, 50, 50))
frame.outline_color = mcrfpy.Color(255, 255, 255) frame.outline_color = mcrfpy.Color(255, 255, 255)
frame.outline = 2 frame.outline = 2
ui.append(frame) ui.append(frame)
# Text # Text
label = mcrfpy.Caption(text="Hello World!", pos=(10, 10)) label = mcrfpy.Caption(text="Hello World!", pos=(10, 10))
label.font_size = 24 label.font_size = 24
label.fill_color = mcrfpy.Color(255, 255, 255) label.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(label) ui.append(label)
# Sprite # Sprite
sprite = mcrfpy.Sprite(x=50, y=50, sprite_index=0) sprite = mcrfpy.Sprite(x=50, y=50, sprite_index=0)
ui.append(sprite) ui.append(sprite)
mcrfpy.current_scene = scene mcrfpy.current_scene = scene
``` ```
--- ---
## Textures and Sprite Sheets ## Textures and Sprite Sheets
### Loading Textures ### Loading Textures
```python ```python
# Load a sprite sheet with tile dimensions # Load a sprite sheet with tile dimensions
texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16) texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16)
# Use with sprite # Use with sprite
sprite = mcrfpy.Sprite(x=100, y=100) sprite = mcrfpy.Sprite(x=100, y=100)
sprite.texture = texture sprite.texture = texture
sprite.sprite_index = 5 # Use 6th sprite (0-indexed) sprite.sprite_index = 5 # Use 6th sprite (0-indexed)
``` ```
### Sprite Sheets with Grids ### Sprite Sheets with Grids
```python ```python
# Load tileset for grid layers # Load tileset for grid layers
texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16) texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16)
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture) terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture)
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400),
layers=[terrain]) layers=[terrain])
# Set tiles using the TileLayer # Set tiles using the TileLayer
for x in range(50): for x in range(50):
for y in range(50): for y in range(50):
terrain.set((x, y), 0) # Floor tile index terrain.set((x, y), 0) # Floor tile index
``` ```
--- ---
## Z-Order Layering ## Z-Order Layering
Control render order with `z_index` (higher = front): Control render order with `z_index` (higher = front):
```python ```python
scene = mcrfpy.Scene("game") scene = mcrfpy.Scene("game")
ui = scene.children ui = scene.children
# Background layer (z=0) # Background layer (z=0)
background = mcrfpy.Frame(pos=(0, 0), size=(800, 600), background = mcrfpy.Frame(pos=(0, 0), size=(800, 600),
fill_color=mcrfpy.Color(20, 20, 20)) fill_color=mcrfpy.Color(20, 20, 20))
background.z_index = 0 background.z_index = 0
ui.append(background) ui.append(background)
# Game layer (z=10) # Game layer (z=10)
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(50, 50), size=(700, 500)) grid = mcrfpy.Grid(grid_size=(50, 50), pos=(50, 50), size=(700, 500))
grid.z_index = 10 grid.z_index = 10
ui.append(grid) ui.append(grid)
# UI layer (z=100) # UI layer (z=100)
hud = mcrfpy.Frame(pos=(0, 0), size=(800, 50), hud = mcrfpy.Frame(pos=(0, 0), size=(800, 50),
fill_color=mcrfpy.Color(30, 30, 30, 200)) fill_color=mcrfpy.Color(30, 30, 30, 200))
hud.z_index = 100 hud.z_index = 100
ui.append(hud) ui.append(hud)
# Renders: background -> grid -> hud # Renders: background -> grid -> hud
mcrfpy.current_scene = scene mcrfpy.current_scene = scene
``` ```
**Implementation:** Automatic sorting by z_index in `src/Scene.cpp::render()` **Implementation:** Automatic sorting by z_index in `src/Scene.cpp::render()`
--- ---
## Colors ## Colors
### Color Creation ### Color Creation
```python ```python
# RGB # RGB
red = mcrfpy.Color(255, 0, 0) red = mcrfpy.Color(255, 0, 0)
# RGBA (with alpha/transparency) # RGBA (with alpha/transparency)
semi_transparent = mcrfpy.Color(255, 255, 255, 128) semi_transparent = mcrfpy.Color(255, 255, 255, 128)
# Access components # Access components
color = mcrfpy.Color(100, 150, 200, 255) color = mcrfpy.Color(100, 150, 200, 255)
# color.r, color.g, color.b, color.a # color.r, color.g, color.b, color.a
``` ```
### Color Application ### Color Application
```python ```python
# Frame colors # Frame colors
frame.fill_color = mcrfpy.Color(50, 50, 50) frame.fill_color = mcrfpy.Color(50, 50, 50)
frame.outline_color = mcrfpy.Color(255, 255, 255) frame.outline_color = mcrfpy.Color(255, 255, 255)
# Text color # Text color
caption.fill_color = mcrfpy.Color(255, 255, 0) # Yellow text caption.fill_color = mcrfpy.Color(255, 255, 0) # Yellow text
# Opacity # Opacity
sprite.opacity = 0.5 # 50% transparent sprite.opacity = 0.5 # 50% transparent
``` ```
--- ---
## Visual Effects ## Visual Effects
### Animations ### Animations
See [[Animation-System]] for complete animation documentation. See [[Animation-System]] for complete animation documentation.
```python ```python
# Fade in # Fade in
frame.animate("opacity", 1.0, 1.0, mcrfpy.Easing.EASE_IN_QUAD) frame.animate("opacity", 1.0, 1.0, mcrfpy.Easing.EASE_IN_QUAD)
# Color transition # Color transition
frame.animate("fill_color", (255, 0, 0, 255), 0.5, mcrfpy.Easing.LINEAR) frame.animate("fill_color", (255, 0, 0, 255), 0.5, mcrfpy.Easing.LINEAR)
# Movement # Movement
frame.animate("x", 500.0, 0.3, mcrfpy.Easing.EASE_OUT_CUBIC) frame.animate("x", 500.0, 0.3, mcrfpy.Easing.EASE_OUT_CUBIC)
frame.animate("y", 300.0, 0.3, mcrfpy.Easing.EASE_OUT_CUBIC) frame.animate("y", 300.0, 0.3, mcrfpy.Easing.EASE_OUT_CUBIC)
# With completion callback # With completion callback
def on_done(target, prop, value): def on_done(target, prop, value):
print(f"{prop} reached {value}") print(f"{prop} reached {value}")
frame.animate("x", 500.0, 0.3, mcrfpy.Easing.EASE_OUT_CUBIC, callback=on_done) frame.animate("x", 500.0, 0.3, mcrfpy.Easing.EASE_OUT_CUBIC, callback=on_done)
``` ```
### Visibility ### Visibility
```python ```python
# Show/hide elements # Show/hide elements
sprite.visible = False # Hidden sprite.visible = False # Hidden
sprite.visible = True # Visible sprite.visible = True # Visible
# Opacity # Opacity
sprite.opacity = 0.0 # Invisible sprite.opacity = 0.0 # Invisible
sprite.opacity = 0.5 # Semi-transparent sprite.opacity = 0.5 # Semi-transparent
sprite.opacity = 1.0 # Opaque sprite.opacity = 1.0 # Opaque
``` ```
--- ---
## Common Patterns ## Common Patterns
### HUD/UI Overlay ### HUD/UI Overlay
```python ```python
def create_hud(scene): def create_hud(scene):
ui = scene.children ui = scene.children
hud = mcrfpy.Frame(pos=(0, 0), size=(800, 60), hud = mcrfpy.Frame(pos=(0, 0), size=(800, 60),
fill_color=mcrfpy.Color(30, 30, 30, 200)) fill_color=mcrfpy.Color(30, 30, 30, 200))
hud.z_index = 100 hud.z_index = 100
ui.append(hud) ui.append(hud)
health_label = mcrfpy.Caption(text="HP: 100/100", pos=(10, 10)) health_label = mcrfpy.Caption(text="HP: 100/100", pos=(10, 10))
health_label.font_size = 18 health_label.font_size = 18
health_label.fill_color = mcrfpy.Color(255, 255, 255) health_label.fill_color = mcrfpy.Color(255, 255, 255)
hud.children.append(health_label) hud.children.append(health_label)
return health_label return health_label
def update_hud(health_label, current_hp, max_hp): def update_hud(health_label, current_hp, max_hp):
health_label.text = f"HP: {current_hp}/{max_hp}" health_label.text = f"HP: {current_hp}/{max_hp}"
if current_hp < max_hp * 0.3: if current_hp < max_hp * 0.3:
health_label.fill_color = mcrfpy.Color(255, 0, 0) # Red health_label.fill_color = mcrfpy.Color(255, 0, 0) # Red
elif current_hp < max_hp * 0.6: elif current_hp < max_hp * 0.6:
health_label.fill_color = mcrfpy.Color(255, 255, 0) # Yellow health_label.fill_color = mcrfpy.Color(255, 255, 0) # Yellow
else: else:
health_label.fill_color = mcrfpy.Color(0, 255, 0) # Green health_label.fill_color = mcrfpy.Color(0, 255, 0) # Green
``` ```
### Particle-Like Effects ### Particle-Like Effects
```python ```python
def create_explosion(scene, x, y): def create_explosion(scene, x, y):
"""Create explosion effect using animated frames.""" """Create explosion effect using animated frames."""
import random import random
ui = scene.children ui = scene.children
particles = [] particles = []
for i in range(10): for i in range(10):
p = mcrfpy.Frame(pos=(x, y), size=(4, 4), p = mcrfpy.Frame(pos=(x, y), size=(4, 4),
fill_color=mcrfpy.Color(255, 200, 50)) fill_color=mcrfpy.Color(255, 200, 50))
p.z_index = 50 p.z_index = 50
target_x = x + random.randint(-50, 50) target_x = x + random.randint(-50, 50)
target_y = y + random.randint(-50, 50) target_y = y + random.randint(-50, 50)
p.animate("x", float(target_x), 0.5, mcrfpy.Easing.EASE_OUT_QUAD) p.animate("x", float(target_x), 0.5, mcrfpy.Easing.EASE_OUT_QUAD)
p.animate("y", float(target_y), 0.5, mcrfpy.Easing.EASE_OUT_QUAD) p.animate("y", float(target_y), 0.5, mcrfpy.Easing.EASE_OUT_QUAD)
p.animate("opacity", 0.0, 0.5, mcrfpy.Easing.LINEAR) p.animate("opacity", 0.0, 0.5, mcrfpy.Easing.LINEAR)
particles.append(p) particles.append(p)
ui.append(p) ui.append(p)
# Cleanup after animation # Cleanup after animation
def cleanup(runtime): def cleanup(runtime):
for p in particles: for p in particles:
ui.remove(p) ui.remove(p)
timer = mcrfpy.Timer("explosion_cleanup", cleanup, 600) timer = mcrfpy.Timer("explosion_cleanup", cleanup, 600)
``` ```
### Health Bars ### Health Bars
```python ```python
class HealthBar: class HealthBar:
def __init__(self, parent, pos, width, height, max_hp): def __init__(self, parent, pos, width, height, max_hp):
self.max_hp = max_hp self.max_hp = max_hp
self.width = width self.width = width
# Background (red) # Background (red)
self.bg = mcrfpy.Frame(pos=pos, size=(width, height), self.bg = mcrfpy.Frame(pos=pos, size=(width, height),
fill_color=mcrfpy.Color(255, 0, 0)) fill_color=mcrfpy.Color(255, 0, 0))
self.bg.z_index = 90 self.bg.z_index = 90
parent.children.append(self.bg) parent.children.append(self.bg)
# Foreground (green) # Foreground (green)
self.fg = mcrfpy.Frame(pos=pos, size=(width, height), self.fg = mcrfpy.Frame(pos=pos, size=(width, height),
fill_color=mcrfpy.Color(0, 255, 0)) fill_color=mcrfpy.Color(0, 255, 0))
self.fg.z_index = 91 self.fg.z_index = 91
parent.children.append(self.fg) parent.children.append(self.fg)
def set_hp(self, current): def set_hp(self, current):
current = max(0, min(current, self.max_hp)) current = max(0, min(current, self.max_hp))
ratio = current / self.max_hp ratio = current / self.max_hp
target_width = int(self.width * ratio) target_width = int(self.width * ratio)
self.fg.animate("w", float(target_width), 0.2, self.fg.animate("w", float(target_width), 0.2,
mcrfpy.Easing.EASE_OUT_QUAD) mcrfpy.Easing.EASE_OUT_QUAD)
if ratio < 0.3: if ratio < 0.3:
self.fg.fill_color = mcrfpy.Color(255, 0, 0) self.fg.fill_color = mcrfpy.Color(255, 0, 0)
elif ratio < 0.6: elif ratio < 0.6:
self.fg.fill_color = mcrfpy.Color(255, 255, 0) self.fg.fill_color = mcrfpy.Color(255, 255, 0)
``` ```
--- ---
## Grid Rendering ## Grid Rendering
See [[Grid-System]] for complete grid documentation. See [[Grid-System]] for complete grid documentation.
### Basic Grid ### Basic Grid
```python ```python
# Load tileset # Load tileset
texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16) texture = mcrfpy.Texture("assets/sprites/tileset.png", 16, 16)
terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture) terrain = mcrfpy.TileLayer(name="terrain", z_index=-1, texture=texture)
# Create grid with layer # Create grid with layer
grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400),
layers=[terrain]) layers=[terrain])
# Set tiles via the TileLayer # Set tiles via the TileLayer
for x in range(50): for x in range(50):
for y in range(50): for y in range(50):
if x == 0 or x == 49 or y == 0 or y == 49: if x == 0 or x == 49 or y == 0 or y == 49:
terrain.set((x, y), 1) # Wall terrain.set((x, y), 1) # Wall
else: else:
terrain.set((x, y), 0) # Floor terrain.set((x, y), 0) # Floor
``` ```
### Grid Viewport ### Grid Viewport
```python ```python
# Zoom # Zoom
grid.zoom = 2.0 grid.zoom = 2.0
# Center camera on tile coordinates # Center camera on tile coordinates
grid.center_camera((25, 25)) grid.center_camera((25, 25))
# Animate viewport # Animate viewport
grid.animate("zoom", 1.5, 1.0, mcrfpy.Easing.EASE_IN_OUT) grid.animate("zoom", 1.5, 1.0, mcrfpy.Easing.EASE_IN_OUT)
grid.animate("center_x", 100.0, 1.0, mcrfpy.Easing.EASE_IN_OUT) grid.animate("center_x", 100.0, 1.0, mcrfpy.Easing.EASE_IN_OUT)
``` ```
--- ---
## Performance Tips ## Performance Tips
### Minimize Draw Calls ### Minimize Draw Calls
```python ```python
# Good: Group related UI in Frames # Good: Group related UI in Frames
hud_frame = mcrfpy.Frame(pos=(0, 0), size=(800, 600)) hud_frame = mcrfpy.Frame(pos=(0, 0), size=(800, 600))
hud_frame.children.append(label1) hud_frame.children.append(label1)
hud_frame.children.append(label2) hud_frame.children.append(label2)
hud_frame.children.append(label3) hud_frame.children.append(label3)
# Avoid: Many top-level UI elements # Avoid: Many top-level UI elements
# (Each top-level element is a separate draw call) # (Each top-level element is a separate draw call)
``` ```
### Use Visibility ### Use Visibility
```python ```python
# Hide off-screen elements # Hide off-screen elements
for element in all_elements: for element in all_elements:
if not in_viewport(element): if not in_viewport(element):
element.visible = False element.visible = False
``` ```
### Reuse Textures ### Reuse Textures
```python ```python
# Good: One texture, many sprites # Good: One texture, many sprites
texture = mcrfpy.Texture("assets/sprites/all_sprites.png", 16, 16) texture = mcrfpy.Texture("assets/sprites/all_sprites.png", 16, 16)
for i in range(100): for i in range(100):
sprite = mcrfpy.Sprite(x=i * 20, y=0) sprite = mcrfpy.Sprite(x=i * 20, y=0)
sprite.texture = texture sprite.texture = texture
sprite.sprite_index = i sprite.sprite_index = i
``` ```
--- ---
## Related Documentation ## Related Documentation
- [[UI-Component-Hierarchy]] - UI element details - [[UI-Component-Hierarchy]] - UI element details
- [[Grid-System]] - Grid rendering details - [[Grid-System]] - Grid rendering details
- [[Animation-System]] - Animating visual properties - [[Animation-System]] - Animating visual properties
- [[Performance-and-Profiling]] - Rendering performance - [[Performance-and-Profiling]] - Rendering performance
--- ---
*Last updated: 2026-02-07* *Last updated: 2026-02-07*